vue数据层思路_Vue实现组件化的基本思路

Vue.js(以下简称Vue)是时下流行的前端开发库,一般搭配其插件Vue-Router,Vuex一起使用,行业中称为Vue全家桶。

Vue使用了MVVM的理念,将表现层(DOM)和数据层进行了分离,其基本思想是数据和DOM的一体化,操作数据即可变更DOM,表单交互亦可通过v-model指令改变数据,将前端开发者从DOM、数据两手抓的泥潭中解放出来。

但是,使用这种方法的代价还是明显的,那就是Vue本身,因为这对大多数开发者而言是一个黑盒子。如果不能大概了解它做了哪些事情,那么有的时候遇到一些问题还是很头疼的。

本文关于Vue实现组件化的基本思路,总结了一些其中发生的故事,希望对今后的开发学习有所帮助。

最基本的模型

Vue2.0之后并没有将DOM和数据进行直接绑定,而是采用了VNode类,也就是常说的虚拟DOM。虚拟DOM其实并不神奇,它就是一个JS对象,描述了DOM元素的一些特征,但是它的属性要比真实DOM少得多,这就使得操作更加方便省时。

数据层位于Vue实例中(以下简称VM),该实例的渲染函数执行可得到VNode,然后Vnode通过patch过程渲染成真实DOM,真实DOM又通过注册事件改变VM,这就是一个基本的模型。

Vue实例的由来

那么VM从何而来呢?考虑以下实际问题:

// App.js

new Vue({

name: 'App',

components: { M },

template: `

swamp
`

}).$mount('#app')

// M.vue

I am M
  • {{ item }}

export default {

name: 'M',

data () {

return {

listData: ['A', 'B', 'C', 'D', 'E', 'F'],

containerClass: 'container'

}

}

}

通过Vue的模板解析(parse)和Vue-loader,上述两种形式的组件定义都会最终变成一个options对象,这个对象包含了所有我们定义的属性,template被解析成渲染函数render和静态渲染函数staticRenderFns(这是Vue针对静态模板的优化)。上例中App.js通过new Vue得到第一个VM,接着执行render函数得到VNode,紧接着此VNode进入patch阶段,模板中的html标签是可以直接生成dom元素的;但是其中有一个M标签,这不是一个html标签,然而它作为一个组件已被注册在数据的components属性中,于是Vue拿到这个组件(一个options对象),然后通过Vue.extend(options)生成一个Vue的子类Sub,然后通过实例化这个Sub类得到M组件的VM(第二个VM)。当然,这个VM也会执行render和patch,然后插入到dom中去。

初始化patch过程

Vue有两类重要的Vnode,一种是占位Vnode(placeholder Vnode, 即上文中的M节点),它是一个虚拟的节点,作为M实际内容的父节点存在,这类节点的tag属性一般为Vue-component-2-M这种形式,而普通Vnode(Actual Vnode)的tag属性则是普通的html节点名称,如div, span, ul等。

普通Vnode节点的patch过程很简单,递归patch其子元素(createChildren),然后创建实际节点插入dom即可。

占位Vnode节点的patch过程则比较复杂,包含子元素的递归创建过程,上文所述M节点的VM创建即是在这个阶段进行的。

数据变更时的patch

如果通过某种操作变更了VM的数据,比如上面例子中,我们在M组件中调用this.containerClass = 'common'; this.listData = ['C', 'B', 'F', 'D', 'E', 'G', 'A'],此时M组件会再次render得到全新的VNode(这里再次render的触发基于双向数据绑定,这是Vue的一大核心,但不是本文重点),这个全新的VNode将与之前存在的VNode进行比较,得到差异后再patch进dom,从而完成更新。patch的含义是打补丁,用在这种场景再合适不过了。

VNode拥有和DOM类似的树形结构,在patch过程中,新老VNode进行同层比较:父节点与父节点比,子节点与子节点比,不会跨层比较(这其实是Vue针对树的编辑距离问题的一种处理,将时间复杂度降低到可以接受的程度)。在本例中,我们主要分析第三层的比较,以此阐述patch过程中diff算法的核心。

设老VNode的序列为O,新VNode的序列为N,分别保留指向序列开头和结尾的指针,称为Os, Oe, Ns, Ne。

diff的目的是调整O使其变为N(调整老DOM得到新DOM),使用了双指针算法:

首先是四次比较,比较的目的是发现相同的节点,用修改操作取代创建从而提高效率,这四次比较分别是:

Os - Ns, 如果VNode相同则把两个指针均向右移动,说明新老节点相同,不做处理;

Oe - Ne, 如果VNode相同则把两个指针均向左移动,说明新老节点相同,不做处理;

Os - Ne, 如果VNode相同说明在新的序列中这个节点应该被移动到Oe后边,直接在DOM层面处理移动,然后把Os右移,Ne左移;

Oe - Ns, 如果VNode相同说明在新的序列中这个节点应该被移动到Os前面,直接在DOM层面处理移动,然后把Ns右移,Oe左移;

如果以上四种情况都没有找到能够匹配的VNode,则在序列O中寻找Ns。可知如果采取遍历O序列的方式,diff算法的时间复杂度为O(min{len(O), len(N)} * len(O));而如果各个VNode拥有key属性,Vue会事先建立一个[key]-[Vnode]的散列表,依据Ns的key去查表只需要O(1)时间,则diff算法的时间复杂度为O(max{len(O), len(N)})。如果查找成功,就将找到的节点移动到Os的前方,并将Ns右移;

如果上述情况均不能成功,那就说明O序列中并没有Ns处的节点,只能创建一个新的节点,把它插入Os之前,并将Ns右移。

上述循环进行直到Os出现在Oe之前或者Ns出现在Ne之前,此时如果Oe仍然在Os之前或者同一位置,说明原始序列中的这些元素要被删除;如果Ne仍然在Ns之前或者同一位置,说明原始序列中的这些元素要被新添加,按情况执行对应的操作即可。

依据此算法,上面例子中的情形可以图示为:

以上就是最基本的Vue从定义到组件化到生成DOM的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值