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: `
<div>
<div>swamp</div>
<M />
</div>`
}).$mount('#app')
// M.vue
<template>
<div :class="containerClass">
<div>I am M</div>
<ul>
<li v-for="item in listData">{{ item }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'M',
data () {
return {
listData: ['A', 'B', 'C', 'D', 'E', 'F'],
containerClass: 'container'
}
}
}
</script>
通过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的过程。
Vue实现组件化的基本思路的更多相关文章
- VUE.JS组件化
VUE.JS组件化 前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎 ...
- vue的组件化运用(数据在两个组件互传,小问题总结)
一.vue的组件化应用 首先,知道有哪些相关的属性需要用到,再慢慢去理解,运用. 1.两个vue页面 2. slot占位符(可用可不用) 3.props内置属性 4.watch监听函数 5.impor ...
- 【06】Vue 之 组件化开发
组件其实就是一个拥有样式.动画.js逻辑.HTML结构的综合块.前端组件化确实让大的前端团队更高效的开发前端项目.而作为前端比较流行的框架之一,Vue的组件和也做的非常彻底,而且有自己的特色.尤其是她 ...
- Vue中组件化编码使用(实战练习一)
Vue中组件化编码的大致流程(初接触).组件之间的参数传递(最基础的形式).组件之间的配合完成一个需求 1.在Vue中进行组件化编码 1.1.组件化编码流程: (1).拆分静态组件:组件要按照功能点拆 ...
- iOS 组件化 —— 路由设计思路分析
原文 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高.为了更好的应对各种需求,开发人员从软件工程的角度,将App架构由原来简单的MVC变成MVVM,VIPER等复杂架构.更换适合业 ...
- vue.js组件化开发实践
前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎么实现,技术选型自然 ...
- Vue中组件化编码使用、实现组件之间的参数传递(实战练习二)
上一章节实现的是静态页面的设计.这一章节实现将数据抽取出来.通过组件间参数的传递来实现 上一章节链接地址:https://blog.csdn.net/weixin_43304253/article/d ...
- Vue中组件化编码 完成任务的添加、删除、统计、勾选需求(实战练习三完结)
上一个章节实现数据在组件之间的传递 .这一章主要是完成添加任务到任务栏.删除任务栏.统计任务完成情况.主要还是参数在各个组件之间的传递. 上一章节的链接地址:https://blog.csdn.net ...
- 大话大前端时代(一) —— Vue 与 iOS 的组件化
序 今年大前端的概念一而再再而三的被提及,那么大前端时代究竟是什么呢?大前端这个词最早是因为在阿里内部有很多前端开发人员既写前端又写 Java 的 Velocity 模板而得来,不过现在大前端的范围已 ...
- vue学习笔记(1)—— 组件化实现todoList
一.环境搭建 1.npm 大型应用时推荐用npm安装,npm能很好的和webpack等模块打包器配合使用.具体安装步骤请参考网上的诸多教程.完成后使用如下命令安装vue. $ npm install ...
随机推荐
- GoLand 和 Pycharm的 快捷键设置与常用插件
GoLand 插件 Gopher 美化进度条,让等待更优雅. CodeGlance pro 旁边浏览框. 快捷键设置 删除行: ctrl + L 重新格式化代码 ctrl + K 开始新行 ctrl ...
- 【Java学习Day05】LDEA的安装和使用
LDEA安装 进入LDEA所有版本下载地址,建议下载LDEA2018 3.6版本 安装好LDEA后双击打开LDEA点击Nest,选择合适的文件路径,个人不建议放在C盘 选择好合适的文件路径后点击Nex ...
- windows远程桌面之前用于连接到xxx的凭据无法工作
使用windows远程桌面连接时报错提示: 之前用于连接到xxx的凭据无法工作 确认输入的用户名密码没有问题 解决方法: 打开gpedit.msc组策略管理,依次找到 计算机配置 > windo ...
- Java基础__03.异常
什么是异常? 异常是指程序运行中出现的各种例外情况,如文件找不到.网络连接失败.传参错误...异常发生在程序运行期间,它影响了正常的程序执行流程. 异常体系结构: 在java中,异常是被当作对象来处理 ...
- Vue watch监听 date中的变量 与 数组或者对象的数据变化
直接看下面代码: 1.红色的的为一个对象,watch监听时.需要借助 computed 属性,否则watch监听打印出来的新旧值看不出.(注:方法可以随便写,但是 computed 中 与 watch ...
- 【C++】之前学习C++没有注意到的点或者学到了冷知识(待补充)
1.string和c_str() string str = "hello"; const char* cstr = str.c_str(); str = "yep,i m ...
- 三本书带您快速深入掌握Spring Boot应用开发《Spring Boot从零开始学(视频教学版)》
#好书推荐##好书奇遇季#三本书带您快速深入掌握Spring Boot应用开发<Spring Boot从零开始学(视频教学版)><Spring Boot应用开发实战><深 ...
- 四、配置docker MySQL 修改编码,支持 utf8mb4
docker 获取 mysql 5.7 版本的镜像. 将docker 里面的 3306 端口映射出来,否则虚拟机外,可能无法访问, 创建 docker 镜像 docker run -itd -p 33 ...
- ssh 登陆 Host key verification failed.
报错 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATION ...
- list.size() = 1 但显示 All elements are null
https://blog.csdn.net/weixin_43899069/article/details/124668722 if (CollectionUtils.isNotEmpty(list) ...