Vue 浅析与实践
欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~
作者:曾柏羲
导语
入职接到的第一个需求是实现一个关于K歌实体售卖的ERP系统,管理系统过去做过不少,这次打算换个姿势,基于时下正热但早已不新鲜的Vue 2.0技术实现。本文首先对Vue的相关技术进行简单介绍与分析,接着总结开发实践(主要描述 Vuex 实践)过程中的流程规范,并记录在此过程中遇到的问题与关键点,最后做出一点实践的总结与思考。
Vue
图:vue 2.0
简介
众所周知,如今的前端框架/解决方案数不胜数,从最初的Backbone,到Angular和Meteor等,这当中有很多都为前端的工程化管理和建设提供了一整套解决方案,是一种“大”框架,但这样的框架往往具备一定的排它性,使得开发的自由和灵活度受到限制。
与此不同的是,Vue对自己的定位是一个渐进式的JavaScript框架,它最核心的部分是只是为了解决视图层方面的问题,提供声明式渲染和组件化管理模式。同时对于路由管理、状态管理和构建工作方面又有自己的解决方案,开发者可以自由地选择或者组合,从而能够更加灵活自如地进行项目开发。
响应式原理
手动改变DOM操作是件损耗性能的事情,几乎所有MVX框架都遵循一个原则:视图的状态应该由数据描述,并且通过数据驱动变化。
图:双向数据绑定
Vue采用发布者-订阅者模式实现双向数据绑定,首先Vue将会获取到需要监听的对象的所有属性,通过 Object.defineProperty 方法完成对象属性的劫持,将其转化为getter和setter,当属性被访问或修改时,立即将变化通知给订阅者,并由订阅者完成相应的逻辑操作,主要流程下图所示。
图:响应式原理
整个过程中主要涉及了 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 = , 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: ,
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.0 中,“渐进式框架”和“自底向上增量开发的设计”这两个概念是什么?
- Vue 2.0——渐进式前端解决方案
- 深入Vue 2,0响应式原理
- https://github.com/vuejs/vue/tree/dev/src/core/observer
相关阅读
此文已由作者授权腾讯云技术社区发布,转载请注明原文出处
Vue 浅析与实践的更多相关文章
- Vue.js最佳实践
Vue.js最佳实践 第一招:化繁为简的Watchers 场景还原: created(){ this.fetchPostList() }, watch: { searchInputValue(){ t ...
- [vue]webpack&vue组件工程化实践
[vue]全局组件和局部组件(嵌套+props引用父组件数据) [vue]组件篇 [vue]组件的创建(componet)和销毁(keep-alive缓存)和父子dom同步nextTick [vue] ...
- vue+axios新手实践实现登陆
vue+axios新手实践实现登陆 https://segmentfault.com/a/1190000015201803 增加 利用HTML5的history.replacestate()修改当前页 ...
- 7.云原生之Docker容器Dockerfile镜像构建浅析与实践
转载自:https://www.bilibili.com/read/cv15220707/?from=readlist Dockerfile 镜像构建浅析与实践 描述:Dockerfile是一个文本格 ...
- netty5 HTTP协议栈浅析与实践
一.说在前面的话 前段时间,工作上需要做一个针对视频质量的统计分析系统,各端(PC端.移动端和 WEB端)将视频质量数据放在一个 HTTP 请求中上报到服务器,服务器对数据进行解析.分拣后从不同的 ...
- Vue + webpack 项目实践
Vue.js 是一款极简的 mvvm 框架,如果让我用一个词来形容它,就是 “轻·巧” .如果用一句话来描述它,它能够集众多优秀逐流的前端框架之大成,但同时保持简单易用.废话不多说,来看几个例子: & ...
- nodejs, vue, webpack 项目实践
vue 及 webpack,均不需要与nodejs一期使用,他们都可以单独使用到任何语言的框架中. http://jiongks.name/blog/just-vue/ https://cn.vuej ...
- Vue.js最佳实践--给大量子孙组件传值(provide/inject)
开发中有个需求,有个Parent组件(例如div)下,输入框,下拉框,radiobutton等可编辑的子孙组件几百个,根据某个值统一控制Parent下面的所有控件的disabled状态 类似于这样,给 ...
- Vue 工程化最佳实践
目录结构 总览 api 目录用于存放 api 请求,文件名与模型名称基本一致,文件名使用小驼峰,方法名称与后端 restful 控制器一致. enums 目录存放 常量,与后端的常量目录对应 ...
随机推荐
- win10 uwp 分治法
其实我想说Path,因为最近在做一个简单的分治. 算法涉及到了一个平面几何的知识.就是三角形p1p2p3的面积等于以下行列式的二分之一: 而且当点P3 在射线P1P2的左侧的时候,表达式为正,右侧表达 ...
- (转)关于Tomcat的点点滴滴(体系架构、处理http请求的过程、安装和配置、目录结构、设置压缩和对中文文件名的支持、以及Catalina这个名字的由来……等)
转自:http://itfish.net/article/41668.html 总结Tomcat的体系架构.处理http请求的过程.安装和配置.目录结构.设置压缩和对中文文件名的支持.以及Cata ...
- C基本类型
C基本类型有: char:8位,可添加修改符signed或是unsigned short:16位,同有singed和unsigned int:32位,同有singed和unsigned long:在3 ...
- 为什么国外的 App 很少会有开屏广告?
前言: 笔者在知乎看到这个问题,觉得这的确是一个值得关注和回答的现象,遂写了回答并整理成本文发布在此抛砖引玉,欢迎讨论. 正文: 古话说得好,先问是不是,再问为什么. 对于「国外的 App 很少有开屏 ...
- 详解Java中的clone方法
详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...
- vue数据请求
我是vue菜鸟,第一次用vue做项目,写一些自己的理解,可能有些不正确,欢迎纠正. vue开发环境要配置本地代理服务.把config文件加下的index.js里的dev添加一些内容, dev: { e ...
- LeetCode 404. Sum of Left Leaves (左子叶之和)
Find the sum of all left leaves in a given binary tree. Example: 3 / \ 9 20 / \ 15 7 There are two l ...
- python自动化运维七:fabric
p { margin-bottom: 0.25cm; line-height: 120% } a:link { } p { margin-bottom: 0.25cm; line-height: 12 ...
- Samba服务安装及配置
服务器环境:CentOS6.9 Linux 2.6.32-696.10.1.el6.x86_64 安装Samba服务 过程中会安装3个服务smb(文件.打印共享服务,使用139.445端口).nmb( ...
- 记录我发现的第一个关于 Google 的 Bug
先贴上 Bug 链接: https://issuetracker.google.com/issues/68969655 Bug 本身是很简单的,就是 Google 的 Android 在线参考文档中, ...