Vue 浅析与实践

欢迎你们前往腾讯云社区,获取更多腾讯海量技术实践干货哦~html

做者:曾柏羲 前端

导语

入职接到的第一个需求是实现一个关于K歌实体售卖的ERP系统,管理系统过去作过很多,此次打算换个姿式,基于时下正热但早已不新鲜的Vue 2.0技术实现。本文首先对Vue的相关技术进行简单介绍与分析,接着总结开发实践(主要描述 Vuex 实践)过程当中的流程规范,并记录在此过程当中遇到的问题与关键点,最后作出一点实践的总结与思考。vue

Vue

图:vue 2.0react

简介

众所周知,现在的前端框架/解决方案数不胜数,从最初的Backbone,到Angular和Meteor等,这当中有不少都为前端的工程化管理和建设提供了一整套解决方案,是一种“大”框架,但这样的框架每每具有必定的排它性,使得开发的自由和灵活度受到限制。webpack

与此不一样的是,Vue对本身的定位是一个渐进式的JavaScript框架,它最核心的部分是只是为了解决视图层方面的问题,提供声明式渲染和组件化管理模式。同时对于路由管理、状态管理和构建工做方面又有本身的解决方案,开发者能够自由地选择或者组合,从而可以更加灵活自如地进行项目开发。ios

响应式原理

手动改变DOM操做是件损耗性能的事情,几乎全部MVX框架都遵循一个原则:视图的状态应该由数据描述,而且经过数据驱动变化。git

图:双向数据绑定github

Vue采用发布者-订阅者模式实现双向数据绑定,首先Vue将会获取到须要监听的对象的全部属性,经过 Object.defineProperty 方法完成对象属性的劫持,将其转化为getter和setter,当属性被访问或修改时,当即将变化通知给订阅者,并由订阅者完成相应的逻辑操做,主要流程下图所示。web

图:响应式原理ajax

整个过程当中主要涉及了 Observer、Dep 和 Watcher三个类,相关源码(已精简)以下:

Observer:

主要处理属性监听逻辑,将监听属性转化为get/set属性,当属性被访问时,调用dep.depend() 方法,而属性被修改时,则调用了dep.notify()方法。

export function defineReactive (
  obj: Object,
  key: string,
  val: any
) {
  const dep = new Dep()
  // 监听属性的get和set方法
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      dep.depend()
      return val
    },
    set: function reactiveSetter (newVal) {
      val = newVal
      dep.notify()
    }
  })
}复制代码


Dep:

担任发布者的角色,维护订阅者列表,负责订阅者的添加和通知工做,上面所提到的depend()和notify()方法在这里实现。

export default class Dep {
  id: number;
  // 订阅者列表
  subs: Array<Watcher>;
  // 添加订阅者
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }
  // 属性被访问时调用该方法,通知依赖的目标(即订阅者)添加该依赖,
  // 同时将其加入订阅者列表中(调用addSub()方法)
  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  // 当监听到依赖的属性发生改变时,通知订阅者执行状态更新操做
  notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}复制代码


Watcher:

担任订阅者角色,即上述代码中的 Dep.target,能够订阅多个Dep,在每次收到发布者消息通知时触发update()方法执行更新逻辑。

export default class Watcher {
  deps: Array<Dep>;
  newDeps: Array<Dep>;
  depIds: ISet;
  newDepIds: ISet;      
  // 为该指令添加依赖(发布者)
  addDep (dep: Dep) {
    const id = dep.id
    this.newDepIds.add(id)
    this.newDeps.push(dep)
    dep.addSub(this)
  }
  // 更新视图逻辑,依赖的属性值发生改变时触发
  update () {
    // 省略
  }
}复制代码


状态管理

过去为了实现父子组件或者平行组件的数据通讯,常见的作法是直接或间接地使用 props 属性和 emit() 方法来实现,这样的作法耦合度强,且难以应付复杂场景下的状态管理。

Vuex 的出现能够很好地规避此类问题,它是一种Vue应用的专用状态管理模式,负责集中式地存储和管理整个Vue应用程序的组件状态,实现更好的状态共享。Vuex将组件状态的存储和管理放在了 Store 里面,并为其提供了4种特性,分别是 state、actions、mutations 和 getters:

  • state,做为驱动应用的数据源,保存了组件的各类状态;
  • mutations,相似于事件,是改变 state 的惟一入口,且里面的操做必须是同步的;
  • actions,相似于 mutations,里面能够进行一些如 ajax 请求等异步的逻辑操做,但若是想对 state 的状态进行修改,必须经过 mutaions 完成;
  • getters,能够对 state 进行某些处理动做,并对处理后的结果提供访问接口。

图:vuex状态管理

Vuex状态管理流程如上图所示,主要分为以下四个步骤:

  • 视图层中的 Components 经过 this.$store.xxx 或 getters 方法从 state 中获取数据并渲染;
  • 用户在 Components 中执行某些动做(如点击按钮fetch数据)时,经过调用 dispatch() 方法将执行动做的指令发送到 Actions 中对应的方法;
  • Actions 解析请求指令,完成相应的逻辑(如ajax数据请求),并在最后(ajax请求结束后)经过 commit() 方法通知 mutations 对 state 状态进行修改;
  • Mutations 收到 commit 请求后,对 state 进行赋值操做,以完成数据的修改。

图:vue devtools

对于开发过程当中 Vuex 状态的追踪,能够经过 Vue Devtools 的 “Vuex” 一栏进行查看,如上图所示,安装方法能够自行搜索。

项目实践

图:K歌erp

简单介绍

全民K歌初期在试水麦克风售卖活动中取得了良好的市场反馈,为继续推进周边实体的售卖,如今指望搭建一个属于本身的售卖平台。整个需求分为H5和PC两部分,其中H5为用户购买实体周边的入口,PC则是对用户的订单数据进行管理。本项目为需求中的PC部分,共由订单数据概览、待审核、待发货、已发货和退换货五页组成。

图:项目页面

页面具体介绍以下:

  • 概览页,包含了整个平台上的交易数据,货品的实时库存以及当前用户的待办事项;
  • 待审核,显示最新的订单数据,用户能够在该页中执行订单审核动做;
  • 待发货,已经经过审核的订单将会移动至该页,该页为管理员提供了物流信息导入和已经确认发货的动做;
  • 已发货,显示已经发货的订单列表,用户能够对发货的地址进行修改,同时也能够执行申请退款的操做;
  • 退换货,包含了“待处理“、“”已经过“和“已拒绝”的tab页。其中待处理tab显示了前端发起退货或erp上执行“申请退款“的订单列表,而用户在该列表中执行的动做(容许/拒绝退款)将会使数据移至“”已经过“或“已拒绝”的列表中。

此外,对于全部的列表页,须要提供批量操做,如批量审核、发货退款和物流信息导入等,并提供分页操做。同时登录须要经过K歌扫码完成,全部的CGI调用须要在K歌的登录态下进行。

项目构建

项目的构建使用 vue-cli 完成,具体操做流程以下:

图: vue-cli构建项目

因为公司网络缘由,在执行 vue-init webpack [project-name] 时会出现没法下载模板库的错误,解决办法能够是经过设置如图中所示的 npm 代理,或者是直接下载 vue 模板中的wepack库并在本地运行完成。

开始动手

图: 开发实践

(1) 目录规划

本文针对 vue-cli 构建出的项目进行结构上的调整,最终得出以下所示的目录结构:

图:项目目录结构

如上图,client目录为客户端的主要开发目录,主要包含了程序入口文件 app.js、客户端路由文件 router、客户端视图曾view、组件模块components和应用状态管理层(即Vuex)Store。

(2) Vuex规范

前面已经提到,订单相关的页面共有四页,对应着待审核、待发货、已发货和退换货四种状态,因为每种状态的相关操做逻辑不一样,在开发过程当中将Store中的order模块划分为review、ship、completed和refund四个子模块。下面代码展现了一个简化的review子模块的代码,其余模块的代码与之相似,都包含了 state、mutations、getters和actions对象。

// store/modules/order/review/index.js
const state = {
  re_order_data: {}
}
const mutations = {
  [types.GET_RE_ORDER_LIST] (state, data) {
    state.re_order_data = data
  }
}
const getters = {
  re_order_list: state => state.re_order_list
}
const actions = {
  getReOrderList ({commit , state, rootState}, params) {
    common.ajax({
      url: orderList,
      data:{
        type: 5,
        filter_by: STATUS_HAVE_PAY
      }
    }).always(res => {      
      commit('GET_RE_ORDER_LIST', res.data)
    })
  }
}
export default {
  state,
  mutations,
  getters,
  actions
}复制代码


此时你也许注意到,Store被划分红多个模块,而每一个模块里面可能又会有更细粒度的划分。在实际的运行过程当中,须要对这些模块进行整合,这里须要用到Vuex提供的modules属性,相关代码以下:

import app from './modules/app'
import menu from './modules/menu'
import order from './modules/order'

const store = new Vuex.Store({
  modules: {
    app,
    menu,
    ...order
  }
})复制代码


(3) 组件调用

组件对Vuex中state状态的调用逻辑一般是放在 data 或 computed 属性中,但须要注意的是,若是指望获得的是响应式的数据,则必须将调用逻辑放在计算属性 computed 中,这样当每次state状态发生变化时,computed 属性中的数据都会被从新计算,同时从新触发更新视图。此外组件也能够直接调用Vuex中的mutations和actions事件,这一般放在methods属性中进行。

(4) 其余

路由处理,对于一个单页应用,天然少不了路由处理,项目的路由使用官方的vue-router处理,使用router.beforeEach()方法在每次路由跳转前进行拦截,判断用户是否登陆,如没有登陆则跳转至登陆页。

网络请求,Vue 2.0开始再也不维护 vue-resourse,转而推荐 axios 做为标准的网络请求库,可是因为 axios 不支持 jsonp 跨域方式,遂放弃,在项目中使用了团队的 ajax 模块。

延迟加载,项目使用了webpack做为打包构建工具,打包结束后默认状况下会产生两个js文件:app.js和vendor.js,而项目在一开始就已经加载了这两个js文件,若是要想实现路由的延迟加载,须要将路由请求的组件定位为异步组件,并结合webpack的代码分割特性实现,方法是经过 require.ensure 的方式引入组件。

最后说句

本篇文章前半部分对 vue2.0 进行了浅析:分析关于响应式方面的源码,了解具体的实现原理与模式,并对Vuex 数据管理作了知识梳理与流程说明。后半部分则是在项目中应用了 Vue 的技术栈,并对实践过程当中的代码细节和关键点作了总结。

在整个开发过程当中,可以较为深入地体会到vue对于代码编写的温馨性(来自于组件化的管理方式)以及vuex对于代码组织方面的优雅。另外因为时间上的关系(其实是由于懒), 尚未仔细了解关于Vue 渲染方面的原理,但愿能够找个时间在后续补上这部份内容。

谢谢阅读,欢迎指正 (=^_^=)

参考文献

相关阅读

深刻 Vue2.x 的虚拟 DOM diff 原理
基于 TVUE 框架在中型移动端项目的直出同构实践
腾讯工程师们怎么玩 Vue.js?

此文已由做者受权腾讯云技术社区发布,转载请注明原文出处

相关文章
相关标签/搜索