溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何利用vue+vue-router+elementUI實現(xiàn)簡易通訊錄

發(fā)布時間:2020-10-15 04:04:09 來源:腳本之家 閱讀:235 作者:DoubleFlower 欄目:web開發(fā)

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

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

1. 所用技術(shù)

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

# 創(chuàng)建一個基于 webpack 模板的新項目

$ vue init webpack contact

$ cd contact

# 安裝依賴

$ npm install

$ npm run dev

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

3. 目錄結(jié)構(gòu)

項目根目錄:

如何利用vue+vue-router+elementUI實現(xiàn)簡易通訊錄 

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

src目錄:

如何利用vue+vue-router+elementUI實現(xiàn)簡易通訊錄

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,以及工具函數(shù),還會處理vue組件的加載,最后實例化vue。

5. App.vue

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

App.vue可以認(rèn)為是應(yīng)用最外層的一個容器。

<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">
   <h4>我的應(yīng)用</h4>
   <template v-for="(item, index) in menuData">
    <!-- 此處的index需顯示轉(zhuǎn)換為string,否則會報warn -->
    <el-submenu :index="'' + (index + 1)">
    <template slot="title">{{ item.name }}</template>
    <template v-for="(subItem, i) in item.value">
     <!-- 此處index格式為父級的index加上下劃線加上當(dāng)前的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: '', // 菜單當(dāng)前所在位置
  menuUniqueOpen: true, // 菜單項是否唯一開啟
  menuRouter: true // 是否開啟路由模式
  }
 },
 mounted: function () {
  ...
 },
 watch: {
  '$route' (to) {
  this.menuIndex = to.name
  }
 }
 }
</script>

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

6. 路由

在正式寫代碼之前,首先要確定要項目的結(jié)構(gòu),模塊如何劃分,哪個模塊對應(yīng)哪個路由。

因為整個項目現(xiàn)在就劃分出兩個大板塊,通訊錄與記賬本,所以路由第一級就只有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是相同的,這是為了讓在新增或者編輯聯(lián)系人頁面下,還能讓active狀態(tài)停留在左側(cè)我的聯(lián)系人上,可以看到App.vue中的代碼this.menuIndex = to.name就是進(jìn)行的該操作,

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

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

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

7. 聯(lián)系人列表頁 --- contact/list.vue

<template>
 <div class="contact-list">
 <div class="contact-list-header">
  <el-button @click="goToNew" type="primary">新增聯(lián)系人</el-button>
 </div>
 <div class="contact-list-content">
  <template>
  <div class="contact-list-wrap">
   <h4>高級檢索</h4>
   <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">
   <h4>聯(lián)系人列表</h4>
   <el-table
   :data="listNewData"
   
   @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('此操作將永久刪除該聯(lián)系人, 是否繼續(xù)?', '提示', {
   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相當(dāng)于該模塊的主頁,新增與編輯頁面通過右上角的新建按鈕或者列表中的編輯按鈕進(jìn)入,查看頁面通過引入View.vue作為一個彈窗放在列表頁中展示,不單獨設(shè)置路由。

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

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

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 () {
  // 需手動關(guān)閉彈窗,找到父組件中調(diào)用的地方進(jìn)行事件的觸發(fā)
  this.$parent.$refs.contactView.$emit('update:viewShow', false)
  }
 }

 }
</script>

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

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

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

8. 聯(lián)系人編輯(新增)頁 --- 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在路由僅僅只是參數(shù)變換的時候,是不會重新重新加載組件的,所以需要在beforeRouteUpdate中處理初始的數(shù)據(jù)。

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

9. 總結(jié)

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

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

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

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

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

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI