一、响应式:
/*
*
* 根据上图实现整体一个架构(包括MVVM类或者Vue类- 发布者,Watcher类-订阅者), 这里用到了一个订阅发布者设计模式
*
* 然后实现MVVM中的由M->V,把模型里面的数据绑定到视图
*
* 最后实现V->M, 当文本框输入文本的时候,由文本事件触发更新模型中的数据,同时也更新相对应的视图
*
*/
实现数据驱动视图的第一步, 组件data的数据一旦变化,立刻触发视图的更新
1、核心API --- Object.defineProperty : [作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性]
2、Object.defineProperty(obj, prop, desc) 【 监听对象、数组; 复杂对象、深度监听】
2.1、obj 需要定义属性的当前对象
2.2、prop 当前需要定义的属性名
2.3、desc 属性描述符
3、缺点(vue3.0启用Proxy--兼容性不好,且无法polyfill):
3.1、深度监听, 需要递归到底,一次计算量比较大,性能不好
3.2、无法监听原生数组, 需要特殊处理 【需要重新定义数组原型】
3.3、不能监听对象新增或删除属性 --- Vue.set 和 Vue.delete
二、模版编译 -- complier 【返回Vnode】
template -----》render过程
模版 ----》 真实DOM渲染的过程,中间有一个环节是把模板编译成 render 函数,这个过程我们把它称作编译。
Vue.js 提供了 2 个版本:
1、 Runtime + Compiler : 含编译代码的,可以把编译过程放在运行时做
2、Runtime only : 需要借助 webpack 的 vue-loader 事先把模板编译成 render函数
首先从[$mount]开始,可以看到,mount其实就是拿到了模板template,然后将这个template通过compileToFunctions方法编译成render函数, 主要是调用了compileToFunctions里面的compile方法。
const compiled = compile(template, options)
parse: 主要功能是将 template字符串解析成 AST(抽象语法树)--- 解释器;
optimize: 主要功能标记静态节点,为后面 patch 过程中对比新旧 VNode 树形结构做优化 --- 优化器 ;
generate: 主要功能就是根据 AST 结构拼接生成 render 函数的字符串 --- 生成器,如下:
{render: "with(this){return _c('div',{attrs:{"id":"test"}},[[_v(_s(val))]),_v(" "),_m(0)])}"}
后面通过 new Function 得到真正的渲染函数
render ----> VNode
关键: 主要通过 createElement 创建VNode
三、虚拟DOM & diff算法 -- vdom库 - snabbdom
【VNode 节点的形式来模拟一棵 Virtual DOM】
**虚拟DOM **
它产生的前提是浏览器中的 DOM 是很“昂贵"的,当我们频繁的去做 DOM 更新,会产生一定的性能问题
理解: 用JS模拟DOM结构, 计算出最小的变更,操作DOM。
snabbdom源码代码:
总结:
1、用JS模拟DOM结构(vnode)
2、新旧vnode对比,得出最小的更新范围,最后更新DOM ----- diff算法
3、数据驱动视图模式下,有效控制DOM操作
diff算法
【diff 算法是vdom中最核心、最关键的部分, 是实现Vue 和 React的重要基石】
【diff算法能在日常使用Vue React中体现出来 (key)】
- diff 即对比, 是一个比较广泛的概念,如 linux diff命令、git diff、文件对比器等等。
- 两个js对象也可以做diff
- 两棵树做diff, 如这里的VDOM diff
优化时间复杂度到 O(n)
1、之比较同一层级,不跨级比较
2、tag不相同, 则直接删掉重建, 不在深度比较
3、tag 和 key两者都相同,则认为是相同节点, 不再深度比较
关键函数: patch()、patchVnode()、addVnodes、removeVnodes、updataChildren()【两种情况下都有children 对比 key的重要性】