Skip to content

Latest commit

 

History

History
193 lines (100 loc) · 5.89 KB

File metadata and controls

193 lines (100 loc) · 5.89 KB

rollup 打包配置

rollup.config.js

  • 配置入口和出口文件
  • 采用 umd 打包方式挂载 global.Vue
  • 采用 .babelrc 的 @babel/preset-env 预设插件

package.json 配置

  • 运行 npm run dev 执行 rollup 打包 -c:指定配置文件 -w:监听文件变化

初始化数据

创建 Vue 构造函数

  • 传入用户选项 options
  • 调用初始化方法 _init

initMixin 扩展 init 方法

  • 将用户选项挂载在实例上
  • 初始化状态 initState
  • initData 初始化 data

响应式原理,对象属性劫持

创建 Observer 类

  • 调用 walk 循环对象,对属性进行劫持
  • difineReactive 内使用递归,进行深层属性劫持

数据方便使用

  • proxy 使用了 defineProperty

  • vm.key => vm._data.key

数组的函数劫持

实现数组内对象的劫持

  • 用 __ob__ 标记 Observer 类,并且使其变成不可枚举属性,防止 defineProperty 死循环,可以用作标记已观测数组

  • 数组不需要全部进行劫持,只需要对数组内对象进行劫持,调用 observeArray 循环监测对象

数组变异方法代理

  • 创造新数组原型指向旧数组原型 newArrayProto.__proto__ = Array.prototype

  • 重写数组变异方法,内部调用原来的方法,对新增的对象数据再次进行劫持

模板编译原理

原型上绑定 $mount 方法

  • 获取用户模板和用户选项,compileToFunction 将模板转换成 render 函数

parseHTML 生成 AST 语法树

  • 采用正则,循环匹配了开始标签,结束标签,标签属性,文本内容

  • 匹配一段截掉一段,同时利用栈型结构构建 AST 语法树

代码生成实现原理

将 AST 语法树拼接,codegen 实现

  • _c 为 createElement _v 为 createTextVNode _s 为 toString

  • 利用正则校验,改变正则的 lastIndex,用数组 tokens 保存数据,再拼接数据

调用 render 函数转换成虚拟 DOM

initLifeCycle 扩展增加 _update,_render 方法

  • ast 描述 JS,CSS,HTML 语言本身,vdom 描述 dom 本身,可以增加自定义属性

  • 模板引擎实现原理:with + new Function

  • createElementVNode 创建元素虚拟节点,createTextVNode 创建文本虚拟节点

虚拟 DOM 转换为真实 DOM

实现 path 方法

  • path 方法增加真实 DOM,删除旧 DOM

  • patchProps 设置元素属性值,createElm 递归生成元素节点或文本节点

  • 调用 vm._update(vm._render()) 更新视图

实现依赖收集

数据劫持时创建一个 Dep

  • 取值时让属性收集器记住当前 Watcher,赋值时通知 Dep 收集的 Watcher 更新

  • Dep 用数组 subs 收集 Watcher,通知时调用 Watcher.update 方法更新

  • watcher 用 Set 收集 Dep,一个 Watcher 对应一个组件

watcher 异步更新

  • 更新时调用 queueWatcher,将 watcher 放入队列 queue 中,并根据 id 去重

  • 采用定时器异步更新,调用 flushSchedulerQueue 循环执行 watcher

nextTick 实现原理

异步回调执行传入的函数

  • 将回调放入 callbacks 数组中,通过 Promise.resolve().then 方式去执行

  • 也可采用优雅降级的方式,根据不同运行环境调用不同 API 来执行 flushCallbacks

mixin 实现原理

调用 initGlobalAPI 扩展 mixin 方法

  • Vue.options 初始化,调用 mergeOptions 方法将用户选项与全局选项进行合并

  • 实例方法是优先采用子方法,生命周期是采用了策略模式,将多个方法进行合并成数组

生命周期钩子调用规则

  • 在不同代码执行时期调用生命周期钩子 callHook 方法,依次执行实例 $options 数组内对应生命周期的方法

computed 实现原理

initComputed 方法

  • 创建一个计算属性 watcher,lazy:true 标记为计算属性,dirty:true 用于脏值监测,不会直接执行

  • 调用 defineComputed 去监控 get 的变化 createComputedGetter

计算属性更新逻辑

  • 页面挂载时创建渲染 watcher,渲染 watcher 获取计算属性时触发计算属性 get

  • createComputedGetter 让计算属性 watcher 去收集上一层 watcher,执行 evaluate

  • evaluate 更新 dirty:false,调用计算属性 watcher 的 get 方法获取值

  • dep 触发 set 时,依次执行 subs 内的 watcher 进行更新,更新时计算属性值不变,只有在获取计算属性时值才进行改变

watch 实现原理

内部调用 $watch 创建用户 watcher

  • initWatch 方法针对 watch 不同的调用方式,调用 createWatcher 方法调用内部 $watch

  • 用 user:true 标记是用户 watcher,将新值和旧值传入 watch 回调函数

实现 diff 算法

patchVnode 方法比较虚拟节点

  • 虚拟节点保存在_vnode 上,将虚拟节点与新节点进行对比

同级比较

  • 不是相同节点:tag 和 key 都不相同 -> 直接进行替换

  • 文本节点:tag 不存在 -> 替换文本内容

  • 比较节点属性 -> 尽量复用

比较子节点

  • 采用双指针 + DFS 深度优先遍历算法进行比较,前后指针重合时停止

  • 比较顺序:newStart 与 oldStart,newEnd 与 oldEnd,newEnd 与 oldStart,newStart 与 oldEnd

  • newStartIndex <= newEndIndex 插入剩余的新节点,oldStartIndex <= oldEndIndex 删除剩余的旧节点

组件实现原理

组件生成虚拟节点

  • Vue.component 定义一个 Sub 实例,Sub.prototype.__proto__ = Vue.prototype,并且将用户选项进行合并

  • 组件合并时调用 strats.components,通过原型链的方式构建父子组件的关系

  • 将生成的组件 definition 记录在 Vue.options.components 上

  • 组件挂载时调用 createElementVNode,调用 isReservedTag 根据标签名判断是否是组件标签

  • 调用 createComponentVnode 创造组件虚拟节点,构建 data.hook.init 方法

组件的渲染

  • createElm 方法中调用 createComponent 执行组件的 data.hook.init(vnode) 挂载组件,返回组件实例的 $el