前言
面试竞争力越来越大,是时候撸一波Vue和React源码啦;
本文从20个层面来对比Vue和React的源码区别;
如果需要了解API的区别,请戳:
Vue 开发必须知道的 36 个技巧
React 开发必须知道的 34 个技巧
文章源码:请戳,原创码字不易,欢迎star!
1.Vue和React源码区别
1.1 Vue源码
来张Vue源码编译过程图
图片来源:分析Vue源码实现
1.1.1 挂载
初始化$mounted会挂载组件,不存在 render 函数时需要编译(compile);
1.1.2 compile
1.compile 分为 parse,optimize 和 generate,最终得到 render 函数;
2.parse 调用 parseHtml 方法,方法核心是利用正则解析 template 的指令,class 和 stype,得到 AST;
3.optimize 作用标记 static 静态节点,后面 patch,diff会跳过静态节点;
4.generate 是将 AST 转化为 render 函数表达式,执行 vm._render 方法将 render 表达式转化为VNode,得到 render 和 staticRenderFns 字符串;
5.vm._render 方法调用了 VNode 创建的方法createElement// render函数表达式
(function() {
with(this){
return _c('div',{ //创建一个 div 元素
attrs:{"id":"app"} //div 添加属性 id
},[
_m(0), //静态节点 header,此处对应 staticRenderFns 数组索引为 0 的 render function
_v(" "), //空的文本节点
(message) //判断 message 是否存在
//如果存在,创建 p 元素,元素里面有文本,值为 toString(message)
?_c('p',[_v("\n "+_s(message)+"\n ")])
//如果不存在,创建 p 元素,元素里面有文本,值为 No message.
:_c('p',[_v("\n No message.\n ")])
]
)
}
})
1.1.3 依赖收集与监听
这部分是数据响应式系统
1.调用 observer(),作用是遍历对象属性进行双向绑定;
2.在 observer 过程中会注册Object.defineProperty的 get 方法进行依赖收集,依赖收集是将Watcher 对象的实例放入 Dep 中;
3.Object.defineProperty的 set 会调用Dep 对象的 notify 方法通知它内部所有的 Watcher 对象调用对应的 update()进行视图更新;
4.本质是发布者订阅模式的应用
1.1.4 diff 和 patch
diff 算法对比差异和调用 update更新视图:
1.patch 的 differ 是将同层的树节点进行比较,通过唯一的 key 进行区分,时间复杂度只有 O(n);
2.上面将到 set 被触发会调用 watcher 的 update()修改视图;
3.update 方法里面调用 patch()得到同级的 VNode 变化;
4.update 方法里面调用createElm通过虚拟节点创建真实的 DOM 并插入到它的父节点中;
5.createElm实质是遍历虚拟 dom,逆向解析成真实 dom;
1.2 React 源码
来张React源码编译过程图
图片来源:React源码解析
1.2.1 React.Component
1.原型上挂载了setState和forceUpdate方法;
2.提供props,context,refs 等属性;
3.组件定义通过 extends 关键字继承 Component;
1.2.2 挂载
1.render 方法调用了React.createElement方法(实际是ReactElement方法);
2.ReactDOM.render(component,mountNode)的形式对自定义组件/原生DOM/字符串进行挂载;
3.调用了内部的ReactMount.render,进而执行ReactMount._renderSubtreeIntoContainer,就是将子DOM插入容器;
4.ReactDOM.render()根据传入不同参数会创建四大类组件,返回一个 VNode;
5.四大类组件封装的过程中,调用了mountComponet方法,触发生命周期,解析出 HTML;
1.2.3 组件类型和生命周期
1.ReactEmptyComponent,ReactTextComponent,ReactDOMComponent组件没有触发生命周期;
2.ReactCompositeComponent类型调用mountComponent方法,会触发生命周期,处理 state 执行componentWillMount钩子,执行 render,获得 html,执行componentDidMounted
1.2.4 data 更新 setState
细节请见 3.1
1.2.5 数据绑定
1.setState 更新 data 后,shouldComponentUpdate为 true会生成 VNode,为 false 会结束;
2.VNode会调用 DOM diff,为 true 更新组件;
1.3 对比
React:
1.单向数据流;
2.setSate 更新data 值后,组件自己处理;
3.differ 是首位是除删除外是固定不动的,然后依次遍历对比;
Vue:
1.v-model 可以实现双向数据流,但只是v-bind:value 和 v-on:input的语法糖;
2.通过 this 改变值,会触发 Object.defineProperty的 set,将依赖放入队列,下一个事件循环开始时执行更新时才会进行必要的DOM更新,是外部监听处理更新;
3.differcompile 阶段的optimize标记了static 点,可以减少 differ 次数,而且是采用双向遍历方法;
2.React 和 Vue 渲染过程区别
2.1 React
1.生成期(挂载):参照 1.2.1
2.更新: 参照1.1.3和 1.1.4
3.卸载:销毁挂载的组件
2.2 Vue
1.new Vue()初始化后initLifecycle(vm),initEvents(vm),initRender(vm),callHook(vm,beforeCreate),initState(vm),callHook(vm,created);A.initLifecycle, 建立父子组件关系,在当前实例上添加一些属性和生命周期标识。如:children、refs、_isMounted等;
B.initEvents,用来存放除@hook:生命周期钩子名称="绑定的函数"事件的对象。如:$on、$emit等;
C.initRender,用于初始化$slots、$attrs、$listeners;
D.initState,是很多选项初始化的汇总,包括:props、methods、data、computed 和 watch 等;
E.callHook(vm,created)后才挂载实例
2.compileToFunction:就是将 template 编译成 render 函数;
3.watcher: 就是执行1.2.3;
4.patch:就是执行 1.2.4
3.AST 和 VNode 的异同
1.都是 JSON 对象;
2.AST 是HTML,JS,Java或其他语言的语法的映射对象,VNode 只是 DOM 的映射对象,AST 范围更广;
3.AST的每层的element,包含自身节点的信息(tag,attr等),同时parent,children分别指向其父element和子element,层层嵌套,形成一棵树
itemid:{ {item.id}}
//转化为 AST 格式为
{
"type": 1,
"tag": "div",
"attrsList": [
{
"name": "id",
"value": "app"
}
],
"attrsMap": {
"id": "app"
},
"children": [
{
"type": 1,
"tag": "ul",
"attrsList": [],
"attrsMap": {},
"parent": {
"$ref": "$"
},
"children": [
{
"type": 1,
"tag": "li",
// children省略了很多属性,表示格式即可
}
],