如何利用vue+vue-router+elementUI实现简易通讯录

一个具有基本增删改查功能的通讯录,数据保存在本地的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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • vue-router配合ElementUI实现导航的实例

    在每个项目中路由是不可或缺的,最近学习vue-router和ElementUI配合使用实现导航栏,在学习的过程中遇到一个问题:点击浏览器的刷新之后页面停留在原来的位置,但是导航却是默认第一个. 由于接触前端时间不长,对于路由的概念不是特别清楚,按照文档写了之后完全不知道怎么下手了,请教了同事,同事的解决办法是利用vuex管理,但是vuex这块还没有接触过,所以这个问题就一直搁置了,今天周末自己在家学习偶然直到了可以使用$route.path设置默认选中的导航,但是设置之后没有什么效果,刷新时页面

  • 如何利用vue+vue-router+elementUI实现简易通讯录

    一个具有基本增删改查功能的通讯录,数据保存在本地的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+Router+Element实现简易导航栏

    本项目为大家分享了Vue+Router+Element实现简易导航栏的具体代码,供大家参考,具体内容如下 项目结构: 直接上代码:主要就是引入配置路由Router ①:引入Router(路由管理器) //config.js 页面 //导航栏 import Home from '../components/home' //首页 import Index from '../components/index' //视频平台 import Vid from '../components/vid_terr

  • Vue+ElementUI 封装简易PaginationSelect组件的详细步骤

    在实际开发工作中,经常会碰到当select下拉数据过需要做分页的情况这里简单介绍封装的一个Pagination-Select组件几个步骤封装的比较简易,可以根据自己的项目进行改动 /components/Pagination-Select/index.vue <template> <div id="PaginationSelect"> <el-select v-model="value" :placeholder="selec

  • 利用Vue模拟实现element-ui的分页器效果

    目录 1. 思路 1.1客户端 1.2服务器 2.服务器 2.1创建数据 2.2创建接口 3.客户端 3.1创建静态页面 3.2请求数据 3.3解析逻辑 4.总结 1. 思路 1.1客户端 利用vue相关的知识搭建基本页面, 上面四张图片,下面是分页器基本 效果静态显示.点击分页器实现 不同数据请求,显示不同图片 1.2服务器 根据客户端发送的数据进行数据 分段传输,比如,点击的是那一页 分页器每次需要展示几个数据, 2.服务器 创建数据(存放图片的网址,以及id) 创建接口,发送数据. 2.1

  • 如何利用Python+Vue实现简单的前后端分离

    目录 准备工作 前端 后端 数据库 总结 准备工作 安装Node环境 安装Python环境 注意:项目整个过程需要从后往前,即先数据库->后端->前端:启动流程也是先启动后端项目,再启动前端项目 前端 开发工具:Visual Studio Code(推荐).WebStorm 打开cmd,安装Vue脚手架,命令如下: npm install -g @vue/cli 创建Vue2项目,名为vue-axios vue create vue-axios 选择Manually select featur

  • 使用vue.js2.0 + ElementUI开发后台管理系统详细教程(二)

    在上篇文章给大家介绍了使用vue.js2.0 + ElementUI开发后台管理系统详细教程(一) 1. 引入路由工具vue-router,切换视图 # 安装vue-router cnpm install vue-router --save-dev 2. 使用vue-router main.js import Vue from 'vue' import App from './App' import VueRouter from 'vue-router' import routeConfig f

  • 使用vue.js2.0 + ElementUI开发后台管理系统详细教程(一)

    1. 根据官方指引,构建项目框架 # 安装vue $ cnpm install vue@2.1.6 # 全局安装 vue-cli $ cnpm install --global vue-cli # 创建一个基于 webpack 模板的新项目my-project $ vue init webpack my-project # 进入项目目录 $ cd my-project # 安装依赖,走你 $ cnpm install # 运行项目 $ cnpm run dev 2. 运行项目之后,会看到以下界面

  • 浅谈vue中改elementUI默认样式引发的static与assets的区别

    首先从这说起 vue项目中的elementUI的默认样式怎么改 由于elementUI的样式太单调,比如这个slider滑块 elementUI中的API是没办法改变这个slider的颜色的,可是老板喜欢很黄,非要用yellow色.

  • vue+Vue Router多级侧导航切换路由(页面)的实现代码

    当当当当当~我又来了. 在项目里经常会遇到侧导航切换页面的功能. 如果我们将侧导航做成公共组件,来调用的话,就会在每一个页面都引用该组件,在后期维护的时候比较麻烦,比如改参数. 所以此文将侧导航做成父页面组件,将切换的页面做成子页面,这样只需调用一次即可.大大减少了后期维护的麻烦 涉及功能点 侧导航支持多级 Vue Router的使用方法( 官方文档 ) 子父组件的写法 样式:elementUI 效果图 实现 --- 目录结构 --- Vue Router的使用方法 首先安装 npm insta

  • 解决vue项目router切换太慢问题

    问题定位: 随着项目增大,有一天突然发现页面切换时候,要等1-2s页面才切换过去,然后就开始定位问题,刚开始以为时页面组件太多导致的,通过删除组件,发现没啥改善,然后就在两个页面打印日志,第二页面created周期时间和路由切换时间相差不大,可以排除是页面渲染耗时.然后在第一个页面的destroyed周期里面打印日志,发现destroyed->router切换耗时1.5s左右,这时候定位问题是vue的destroyed周期耗时. destroyed周期耗时: 这时候就考虑destroyed为啥要

随机推荐