亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

如何利用vue+vue-router+elementUI實現簡易通訊錄

2019-11-19 11:35:26
字體:
來源:轉載
供稿:網友

一個具有基本增刪改查功能的通訊錄,數據保存在本地的localStorage中。

 demo地址: https://junjunhuahua.github.io

1. 所用技術

js框架: vue2  https://cn.vuejs.org/

ui框架: elementUI  http://element.eleme.io/#/zh-CN

腳手架: vue-cli

單頁: vue-router  https://router.vuejs.org/zh-cn/

模塊打包: webpack

2. 腳手架搭建

# 全局安裝 vue-cli$ npm install -g vue-cli# 創建一個基于 webpack 模板的新項目$ vue init webpack contact$ cd contact# 安裝依賴$ npm install$ npm run dev

這是vue官方基于webpack的腳手架,run dev后瀏覽器會自動打開localhost:8080,也可以使用run build命令,執行build命令后會自動將src目錄中的內容進行編譯打包壓縮,然后在dist目錄中可以看到這些文件

3. 目錄結構

項目根目錄:

 

build為構建項目所用的node代碼,config為構建時的一些配置項,dist為打包后(npm run build 用于發布)的代碼,node_modules為node模塊,src為開發時所用的代碼。

src目錄:

assets為全局css,圖片,以及一些工具類的js,components為vue的組件,router為路由配置,app.vue為主頁面的組件,config.js為目錄配置項,main.js為入口js

4. main.js

import Vue from 'vue'import App from './App.vue'import router from './router'import ElementUI from 'element-ui'import utils from './assets/utils.js'import 'element-ui/lib/theme-chalk/index.css'import './assets/normalize.css'Vue.use(ElementUI)Vue.use(utils)/* eslint-disable no-new */new Vue({ el: '#app', router, ElementUI, template: '<App/>', components: { App }})

main.js的主要工作是引入一些框架,全局css,以及工具函數,還會處理vue組件的加載,最后實例化vue。

5. App.vue

.vue文件是什么?  https://cn.vuejs.org/v2/guide/single-file-components.html

App.vue可以認為是應用最外層的一個容器。

<template> <div id="app"> <div class="app-left">  <el-row class="tac">  <el-col>   <el-menu :default-active="menuIndex" class="el-menu-vertical-demo"     background-color="#545c64" text-color="#fff" :unique-opened="menuUniqueOpen" :router="menuRouter"     active-text-color="#ffd04b">   <h3>我的應用</h3>   <template v-for="(item, index) in menuData">    <!-- 此處的index需顯示轉換為string,否則會報warn -->    <el-submenu :index="'' + (index + 1)">    <template slot="title">{{ item.name }}</template>    <template v-for="(subItem, i) in item.value">     <!-- 此處index格式為父級的index加上下劃線加上當前的index,index都需加1 -->     <router-link tag="span" :to="subItem.path">     <el-menu-item :index="subItem.name">{{ subItem.title }}</el-menu-item>     </router-link>    </template>    </el-submenu>   </template>   </el-menu>  </el-col>  </el-row> </div> <div class="app-right">  <router-view></router-view> </div> </div></template><script> import menuData from './config' export default { name: 'app', data () {  return {  menuData,  menuIndex: '', // 菜單當前所在位置  menuUniqueOpen: true, // 菜單項是否唯一開啟  menuRouter: true // 是否開啟路由模式  } }, mounted: function () {  ... }, watch: {  '$route' (to) {  this.menuIndex = to.name  } } }</script>

這邊偷了一個懶,沒有把左側的menu單獨做成一個vue而是混入App.vue中。

6. 路由

在正式寫代碼之前,首先要確定要項目的結構,模塊如何劃分,哪個模塊對應哪個路由。

因為整個項目現在就劃分出兩個大板塊,通訊錄與記賬本,所以路由第一級就只有contact和account兩種。

Vue.use(VueRouter)let myRouter = new VueRouter({ routes: [ {  path: '*',  component: () => import('../components/NotFoundComponent.vue') }, {  path: '/',  redirect: '/contact' }, {  path: '/contact',  name: 'Contact',  component: () => import('../components/contact/List.vue') }, {  path: '/contact/edit',  name: 'Contact',  component: () => import('../components/contact/Edit.vue') }, {  path: '/account',  name: 'Account',  component: () => import('../components/account/list.vue') } ]})

可以看到上面/contact和/contact/edit的name是相同的,這是為了讓在新增或者編輯聯系人頁面下,還能讓active狀態停留在左側我的聯系人上,可以看到App.vue中的代碼this.menuIndex = to.name就是進行的該操作,

雖然這樣vue會報一個warn告訴我別重名[捂臉],暫時能想到的就是這樣的操作方式了,有考慮過依靠判斷path來確定是否顯示高亮狀態,但是當目錄層級較深且較復雜的情況下,這樣就不是很靠譜了。

component這里為什么是這種形式,而不是直接用一個組件名呢,因為當路由開始多起來的時候,一下把所有的組件都加載進來會非常非常慢且會加載到許多當時并沒有用到的組件,通過import這種形式,可以讓webpack將路由變換時用到的組件分開打包,網頁會根據使用情況再進行

由于router是vue的組件,所以使用時記得要Vue.use一下。

7. 聯系人列表頁 --- contact/list.vue

<template> <div class="contact-list"> <div class="contact-list-header">  <el-button @click="goToNew" type="primary">新增聯系人</el-button> </div> <div class="contact-list-content">  <template>  <div class="contact-list-wrap">   <h3>高級檢索</h3>   <el-form ref="contactSearch" :model="searchParams" :inline=true>   <el-form-item label="姓名">    <el-input v-model="searchParams.name" placeholder="請輸入需要檢索的姓名"></el-input>   </el-form-item>   </el-form>   <el-button type="primary" size="mini" round @click="contactSearch('contactSearch')">搜索</el-button>  </div>  <div class="contact-list-wrap">   <h3>聯系人列表</h3>   <el-table   :data="listNewData"   style="width: 100%"   @row-click="viewContact"   :default-sort="{prop: 'name', order: 'descending'}"   >   <el-table-column    label="姓名"    prop="name"    sortable    width="180">   </el-table-column>        ...   <el-table-column    label="功能">    <template scope="scope">    <el-button size="mini" type="primary" @click.stop="editContact(scope)">編輯</el-button>    <el-button size="mini" @click.stop="deleteContact(scope)">刪除</el-button>    </template>   </el-table-column>   </el-table>  </div>  </template> </div> <contact-view ref="contactView" :viewData="curData" :viewShow.sync="viewShow"></contact-view> </div></template><script> import contactView from './View.vue' export default { data () { ... }, components: {  contactView }, computed: {  listNewData: function () { ... }, mounted: function () {  this.listData = this.utils.getLocalStorage('vueContact') }, methods: {  goToNew: function () {  this.$router.push('/contact/edit')  },  sexFormatter: function (row) { ... },  deleteContact: function (res) {  let data = res.row  this.$confirm('此操作將永久刪除該聯系人, 是否繼續?', '提示', {   confirmButtonText: '確定',   cancelButtonText: '取消',   type: 'warning',   callback: (action) => {   if (action === 'confirm') {    this.$delete(this.listData, data.id)    this.utils.setLocalStorage('vueContact', this.listData)   }   }  })  },  editContact: function (res) {  let data = res.row  this.$router.push({   path: '/contact/edit', query: {id: data.id}  })  },  viewContact: function (row) {  this.viewShow = true  this.curData = this.listData[row.id]  },  contactSearch: function () {  let data = this.utils.getLocalStorage('vueContact')  let newData = {}  for (let item in data) {   if (data[item].name.indexOf(this.searchParams.name) > -1) {   newData[item] = data[item]   }  }  this.listData = newData  } } }</script>

list.vue相當于該模塊的主頁,新增與編輯頁面通過右上角的新建按鈕或者列表中的編輯按鈕進入,查看頁面通過引入View.vue作為一個彈窗放在列表頁中展示,不單獨設置路由。

列表展示所使用的是elementUI的table組件

刪除對象時一定要使用$delete,否則不會觸發視圖更新

view.vue代碼:

<template> <div class="contact-view"> <el-dialog :before-close="closePop" ref="myDialog" :visible="viewShow">  <el-form :model="viewData" label-width="60px">  <el-form-item label="姓名" prop="name">   <el-input :readonly="true" v-model="viewData.name"></el-input>  </el-form-item>  ...  <el-form-item label="備注">   <el-input :readonly="true" type="textarea" v-model="viewData.desc"></el-input>  </el-form-item>  </el-form> </el-dialog> </div></template><script> export default { props: ['viewShow', 'viewData'], methods: {  closePop: function () {  // 需手動關閉彈窗,找到父組件中調用的地方進行事件的觸發  this.$parent.$refs.contactView.$emit('update:viewShow', false)  } } }</script>

這里有個比較值得注意的點,就是關閉查看彈窗,彈窗的開啟關閉狀態通過list也就是父級中的viewShow來控制,viewShow通過view也就是子級中的props流入到子級中,但是vue中的數據流向是默認是單向的,想要子級中修改父級屬性必須使用emit,詳見上面代碼。

這里原先使用elementUI的dialog組件的自己的關閉,會報錯,只能自己修改了。

ps: 為什么這里不用vuex處理父子組件的通信?因為如果是一個大型的后臺管理系統,像這樣的情況會經常發生,如果都放在vuex中管理,那vuex的體積會非常龐大,反而不利于維護。

8. 聯系人編輯(新增)頁 --- edit.vue

<template> <div class="contact-edit"> <el-form ref="contactForm" :model="form" :rules="rules" label-width="80px">  <el-form-item label="姓名" prop="name">  <el-input v-model="form.name"></el-input>  </el-form-item>  <el-form-item label="性別">  <el-select v-model="form.sex" placeholder="請選擇性別">   <el-option label="男" value="male"></el-option>   <el-option label="女" value="female"></el-option>  </el-select>  </el-form-item>    ...  <el-form-item label="備注">  <el-input type="textarea" v-model="form.desc"></el-input>  </el-form-item>  <el-form-item>  <el-button type="primary" @click="onSubmit('contactForm')">{{ btnName }}</el-button>  <el-button @click="cancelForm">取消</el-button>  </el-form-item> </el-form> </div></template><script> export default { data () {  var nameValid = (rule, value, callback) => {  if (!value) {   callback(new Error('姓名不能為空'))  } else {   callback()  }  }  var mobileValid = (rule, value, callback) => {  let phonePattern = /(^/s*$)|(^[1][3,4,5,7,8][0-9]{9}$)/  if (value && !phonePattern.test(value)) {   callback(new Error('手機號格式不正確'))  } else {   callback()  }  }  return {  type: '', // 控制是否是新建  ...  rules: {   name: [{validator: nameValid, trigger: 'blur'}],   mobile: [//   {required: true, message: '手機號不能為空', trigger: 'blur'},   {validator: mobileValid, trigger: 'blur'}   ]  }  } }, // 組件加載后的鉤子 mounted: function () {  this.checkPageStatus(this.$route.query.id) }, // 路由在組件中的鉤子 beforeRouteUpdate: function (to, from, next) {  this.checkPageStatus(to.query.id)  next() }, methods: {  // 檢查頁面是新建還是編輯  checkPageStatus: function (id) { ... },  cancelForm: function () {  this.$router.push('/contact')  },  onSubmit: function (formName) { ... } } }</script>

可以看到mounted與beforeRouteUpdate中的代碼有些重合,那是因為vue在路由僅僅只是參數變換的時候,是不會重新重新加載組件的,所以需要在beforeRouteUpdate中處理初始的數據。

nameValid與mobileValid為表單驗證的函數,el-form配置rules屬性名稱,然后data中相應的添加rules即可開啟表單驗證,但是有一點一定要注意el-form-item上一定要設置對應的prop屬性,rules才會生效。

9. 總結

非常簡單的一個項目,但是有幾個點一定要關注好:

模塊的劃分,模塊劃分要合理,盡量能保證模塊的復用性

狀態的管理,一定要明確什么東西要放vuex中,什么東西不用放,以免使項目的維護反而變得更復雜

如果是大型項目,路由中一定要讓.vue文件在需要時再引入,否則會加重初次加載的負擔

為了減少篇幅,刪減了很多不重要的代碼,需要查看源碼請移步,項目地址: https://github.com/junjunhuahua/vue-basic-demo

github上的項目已改為后臺提供接口,不再使用localStorage操作數據,后臺項目使用MongoDB+node實現,具體項目:https://github.com/junjunhuahua/mongodb-demo

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品久久久久久国产| 中文字幕精品www乱入免费视频| 国产精品视频公开费视频| 5566日本婷婷色中文字幕97| 久久99久久亚洲国产| 日韩av在线网站| 97超碰蝌蚪网人人做人人爽| 欧美日本中文字幕| 成人亚洲激情网| 亚洲成人av在线| 97久久久久久| 国产精品免费一区二区三区都可以| 中文国产成人精品久久一| 性欧美在线看片a免费观看| 中文国产成人精品久久一| 亚洲欧美精品一区二区| 精品偷拍一区二区三区在线看| 国产一区二区三区中文| 欧美激情精品久久久久久黑人| 色妞在线综合亚洲欧美| 亚洲人成在线播放| 国产香蕉一区二区三区在线视频| 久久久久久香蕉网| 国产成人精品在线观看| 亚洲国产高清福利视频| 亚洲电影av在线| 日韩亚洲国产中文字幕| 久久久视频在线| 亚洲xxxxx性| 成人黄色片网站| 欧美一区二区色| 欧美成人精品激情在线观看| 欧美性猛交xxxxx免费看| 欧美一级在线亚洲天堂| 国产偷亚洲偷欧美偷精品| 欧美重口另类videos人妖| 欧美午夜激情视频| 成人激情电影一区二区| 日本欧美中文字幕| 欧美中文在线免费| 亚洲免费精彩视频| 久久久女人电视剧免费播放下载| 亚洲精品第一国产综合精品| 午夜精品一区二区三区在线播放| 丝袜美腿亚洲一区二区| 国产精品男人爽免费视频1| 亚洲成av人片在线观看香蕉| 中文字幕九色91在线| 美女av一区二区三区| 亚洲japanese制服美女| 日韩在线观看视频免费| 亚洲精品国产精品国自产在线| 国产精品久久久久久久9999| 欧美另类99xxxxx| 91精品国产自产91精品| 亚洲成色777777女色窝| 韩日精品中文字幕| 色偷偷噜噜噜亚洲男人的天堂| 亚洲a区在线视频| 成人黄色大片在线免费观看| 91久久在线播放| 国产亚洲精品美女久久久久| 26uuu亚洲伊人春色| 国产在线视频91| 亚洲精品资源美女情侣酒店| 这里只有精品视频| 亚洲精品理论电影| 日韩在线欧美在线| 2018中文字幕一区二区三区| 亚洲人成毛片在线播放| 日韩中文字幕视频在线观看| 欧美一区二区视频97| 久久久精品国产一区二区| 日韩视频中文字幕| 不卡av日日日| 欧美日韩免费在线| 一区二区成人av| 久久99国产精品自在自在app| 琪琪第一精品导航| 亚洲精品一区中文字幕乱码| 97成人在线视频| 欧美亚州一区二区三区| 日韩av电影手机在线观看| 国产日韩专区在线| 色偷偷av亚洲男人的天堂| 国产精品久久久久99| 久久夜色撩人精品| 久久久久久久久久av| 国产在线98福利播放视频| 国产精品自产拍在线观| 日韩中文字幕在线| 亚洲欧美另类国产| 国产精品高潮粉嫩av| 成人av色在线观看| 九九热精品在线| 丁香五六月婷婷久久激情| 午夜精品久久久久久99热软件| 亚洲第一精品福利| 亚洲第一视频网站| 欧美最猛性xxxxx(亚洲精品)| 欧美大尺度在线观看| 91在线高清免费观看| 日韩中文有码在线视频| 亚洲变态欧美另类捆绑| 在线成人中文字幕| 91极品视频在线| 欧美另类高清videos| 中文字幕欧美精品日韩中文字幕| 国产视频精品va久久久久久| 美女久久久久久久久久久| 一区三区二区视频| 亚洲午夜激情免费视频| 亚洲香蕉成人av网站在线观看| 亚洲激情第一页| 国产精品尤物福利片在线观看| 日韩电影大全免费观看2023年上| 日韩av电影手机在线| 欧美黑人极品猛少妇色xxxxx| 日韩最新中文字幕电影免费看| 欧美成人免费观看| 最近2019中文字幕大全第二页| 国产精品久久久久久久久粉嫩av| 91九色视频导航| 91亚洲国产成人精品性色| 日韩免费在线播放| 日韩av电影在线播放| 精品国产欧美一区二区五十路| 高潮白浆女日韩av免费看| 亚洲欧美国产另类| 亚洲人精选亚洲人成在线| 日日骚av一区| 欧美—级a级欧美特级ar全黄| 国产在线精品成人一区二区三区| 91日本在线视频| 国内精品久久影院| 国产97人人超碰caoprom| 欧美人与性动交a欧美精品| 成人一区二区电影| 亚洲午夜久久久久久久| 精品亚洲夜色av98在线观看| 亚洲网站视频福利| 成人精品福利视频| 日本韩国在线不卡| 色中色综合影院手机版在线观看| 亚洲欧美日韩中文在线| 亚洲综合中文字幕在线观看| 欧美一级在线播放| 91夜夜未满十八勿入爽爽影院| 欧美黑人一区二区三区| 成人在线激情视频| 久久久久久高潮国产精品视| 久久亚洲精品一区二区| 91经典在线视频| 国内精品视频久久| 2023亚洲男人天堂| 亚洲男人天堂手机在线| 亚洲二区中文字幕| 91av在线影院| 日韩免费观看视频| 国产91色在线| 欧美成人sm免费视频| 91精品国产乱码久久久久久久久| 亚洲三级av在线| 在线精品国产成人综合|