- Vue2.x 源码解读
Vue2.x 源码解读
https://ustbhuangyi.github.io/vue-analysis/v2/prepare/
package.json
https://github.com/vuejs/vue/blob/dev/package.json
1 | "devDependencies": { |
flow
带类型的 js
1 | /*@flow*/ |
目录
1 | src |
Runtime Only VS Runtime + Compiler
Compiler: template string -> AST -> code,只要有 template string,包括 SFC,都需要 Compiler
Runtime 在运行时处理 new Vue() 等代码,构建后 vue.runtime.min.js 一般被嵌在 app.[hash].js 里
1 | // 需要编译器的版本 |
数据驱动(从 Vue 实例化到 DOM 更新)
总览
new Vue 到 vm.$mount
vm.$mount
vm._render
vm._update
组件内部工作流程
创建组件类型 VNode
合并配置
配置最终合并到 vm.$options,发生场景:
- new Vue 的 this._init() 即实例化的初始化时
- Vue.extend 即组件化时
生命周期
父子组件生命周期执行顺序
- 父 created
- 父 beforeMount
- 子 created
- 子 beforeMount
- 子 mounted
- 父 mounted
钩子执行处
在 vm.$options 中执行钩子数组,比如 vm.$options.created
beforeCreate created
beforeCreate -> initState -> created
initState 的作用是初始化 props、data、methods、watch、computed 等属性,所以 beforeCreate 时是访问不到 data props 等的
beforeMount mounted
beforeMount hook -> vm._render() -> vm._update() -> mounted hook
最后的 mounted hook,如果是组件 VNode 走到这一步,各个组件的 mounted hook 会被 push 到一个 queue,排队执行,先子后父
beforeUpdate & updated
mounted -> beforeUpdate hook -> 进入 Watcher 实例 -> updated hook
在 Watcher 实例中: vm._render() 生成 VNode -> vm._update() 更新 DOM
beforeDestroy destroy
beforeDestroy -> 一系列销毁动作如删除 watcher、钩子 -> destroy
destroy 钩子函数执行顺序是先子后父,和 mounted 过程一样
全局/局部组件
全局组件
- Vue.component
- 检测为组件类型,通过 Vue.extend, 然后赋给 Vue.options.components
- -> Sub.options.components // 子组件是 Vue.extend 而来,Vue.options 被合并到 Sub.options
- 取出 components 并参与 new VNode()
局部组件
- 取出 components 并参与 new VNode()
异步组件
webpack 构建 chunk 时标记 require、()=>import(‘组件’) 的组件在哪个 chunk,到时就到哪个 chunk 找
加载异步组件后会通过 forceRender 强制重新渲染
patch
会先给异步组件占位,方便后续的 patch 和 $forceUpdate()
响应式
Object.defineProperty
Object.defineProperty(obj, prop, descriptor)
存取型的 descriptor 包含两个 key:
- get: 属性的 getter 函数,访问属性时会触发
- set: 当属性值被修改时,会调用此函数
依赖收集 (getter) 派发更新 (setter)
基于 getter setter 和订阅模式来做依赖收集和派发更新,实现数据的响应式
nextTick
patch 是一个异步过程, VNode -> nextTick -> DOM
修改 msg 后 template 中 <div id="MSG"> {{ msg }} </div> 也正确修改了,此时浏览器上显示的 msg 也是修改后的,但是此时**这个 tag 的 DOM 还没更新**,console 下即知
1 | this.$refs.MSG; // DOM 未更新 |
计算属性 侦听属性
计算属性其实是 computed watcher
watcher options
- immediate: 刷新页面进入组件即触发一次该 watch 回调
扩展
event
只有组件节点才可以添加自定义事件,并且添加原生 DOM 事件需要使用 native 修饰符;而普通元素使用 .native 修饰符是没有作用的,也只能添加原生 DOM 事件
slot
普通插槽和作用域插槽作用域的不同源于 VNode 在哪里渲染
VNode 若直接在父组件渲染,则数据挂在父组件的 vm.$options 下,子组件肯定不能直接访问
作用域插槽是把部分数据相关的 VNode 也放到子组件中进行渲染,所以子组件就能直接访问
keep-alive
- 使用 slot
- 缓存了 VNode
- patch 时缓存过的组件不走 mounted hook
Vue-Router
Vue-Router 的 install 方法(使用 Vue.mixin,mixin 作用是合并 options)会给每一个组件注入 beforeCreate 和 destoryed 钩子函数,在 beforeCreate 做一些私有属性定义和路由初始化工作