本文主要从以下几个方面来探讨vue.js的设计思想及一些原理,在代码的编写上不会过多阐述,重点讲解其思想:
1.Vue.js的响应式原理,理解为什么修改数据视图会自动更新;怪脸·
2.虚拟DOM的概念与原理;
3.模板编译原理,理解Vue.js的模板是如何生效的;
4.Vue.js的整体架构设计与项目结构
5.深入理解Vue.js的生命周期,不同的生命周期钩子之间有什么区别,不同的生命周期间Vue.js内部到底发生了什么;
6.Vue.js提供的各种API的内部实现原理;
7.指令实现原理;
8.过滤器的实现原理;
9.使用Vue.js开发项目的最佳实践
1.变化侦测
从状态生成DOM,在输出到用户界面显示的一整套流程叫做渲染,应用在运行时不断的进行重新渲染,而响应式系统赋予框架重新渲染的能力,没有它,就没有重新渲染。
什么是变化侦测呢?简单来说,它主要包括两种类型,一种是推(push),一种是拉(pull)
所谓的pull简单来说,就是当状态发生变化时,并不知道是哪个状态变了,框架内部收到信号后,会进行一个暴力比对来找到那些DOM节点需要渲染,React就是使用的这种方式 而push类型就是将一个状态同时绑定很多依赖,当状态发生变化时,就会向所有依赖发送通知,让他们进行DOM更新操作,Vue.js就是采用这种类型,但是如果每个状态绑定的依赖越多,依赖追踪在内存上的开销就会越大,所以从Vue.js 2.0开始,就引入虚拟DOM,这样一个状态所绑定的不再是具体的DOM节点,而是一个组件,这样状态变化后,会通知到组件,组件内部再使用虚拟DOM比对时,就可以大大降低依赖数量,从而降低依赖追踪所消耗的内存
如何追踪变化:defineReactive来实现
追踪数据的变化主要是通过上面的defineReactive来实现
在能追踪变化的基础上,其次就是怎样去收集依赖。我们收集的依赖是window.target
,一般叫其Watcher
,它是一个中介,数据变化时通知它,然后它在通知其他的地方。
Watcher的原理主要是在getter和setter方法中,在getter方法中进行依赖收集,在setter方法中循环依赖列表,把所有的Watcher都通知一遍
2.虚拟DOM的概念与原理
3. 模板编译原理,理解Vue.js的模板是如何生效的
虚拟DOM简单来说,就是一个普通的javaScript对象,包含了tag、props、children三个属性
<div id="app">
<p class="text">hello,world<p>
</div>
将上面的HTML转化为虚拟DOM如下:
{
tag:'div',
props:{
id:'app'
},
children:[
{
tag:'p',
props:{
className:'text'
},
children:[
'hello world!!!'
]
}
]
}
这种用js对象表示DOM的树形结构,就是我们常说的虚拟DOM了,虚拟DOM提升性能的点在于DOM发生变化的时候,通过diff算法进行比对JavaScript原生对象,计算出需要变更的DOM,然后只对变化DOM进行操作,而不是更新整个视图。
接下来我们来探讨一下将一段HTML转化为虚拟DOM呢?
Vue使用的是render方法中的createElement
1. 如果Vue实例对象中有template参数选项,则将其作为模板编译成render函数
2. 如果没有template参数选项,则将外部的HTML作为模板编译(template),也就是说,template参数选项的优先级要比外部的HTML高
render函数的一般形式:
function render(tag,oprops,...children){
return {
tag,
props:props||{
},
children:children.flat()
}
}
浏览器条件下如何渲染虚拟DOM呢?
function render(vdom) {
// 如果是字符串或者数字,创建一个文本节点
if (typeof vdom === 'string' || typeof vdom === 'number') {
return document.createTextNode(vdom)
}
const {
tag, props, children } = vdom
// 创建真实DOM
const element = document.createElement(tag)
// 设置属性
setProps(element, props)
// 遍历子节点,并获取创建真实DOM,插入到当前节点
children
.map(render)
.forEach(element.appendChild.bind(element))
// 虚拟 DOM 中缓存真实 DOM 节点
vdom.dom = element
// 返回 DOM 节点
return element
}
function setProps (element, props) {
Object.entries(props).forEach(([key, value]) => {
setProp