vue源码
2019-07-24 17:55:07 147 举报
AI智能生成
vue源码
作者其他创作
大纲/内容
index.js
_init 具体初始化操作
具体初始化操作
混入各部分模块
init.js
state.js
render.js
events.js
lifecycle.js
proxy.js
提供各模块具体实现函数以及Mixin函数
_init
判断环境是否启用性能检测
判断合并策略
判断代理方案
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm)
initState(vm)
initProvide(vm)
callHook(vm, 'created')
挂在实例vm.$mount(vm.$options.el)
Mixin
initMixin
混入init函数
执行各部分初始化操作
stateMixin
设置data和props的setter和getter
defineProperty在vue原型上定义
在原型上定义$watch函数
eventsMixin
Vue.prototype.$on
监听事件
如果传入的事件名为数组,则递归单个调用
将各个事件名在vue._event的部分存入回调fn
Vue.prototype.$once
一次性事件
将此回调fn放入一个on函数,并且on函数中在fn调用之前使用$off取消绑定
再将on函数通过$on进行绑定,实现一次性触发效果
Vue.prototype.$off
取消事件
如果传入的事件名为数组,则递归单个调用
检测是否在vm._events上
找到后splice删除
Vue.prototype.$emit
触发事件
找到vm._events[event]
如果是数组则toArray处理
invokeWithErrorHandling(cbs[i], vm, args, vm, info)执行
lifecycleMixin
_update
vm.__patch__把 VNode 渲染成真实的 DOM
这里可以看一下https://blog.csdn.net/qq_37939251/article/details/90682986
以及vue diff过程
$forceUpdate
vue里面有判断,如果新值 == 旧值, 那么就不触发watcher更新视图了,所以,如果非要更新就要调用 forceupdate 来强制更新了
$destroy
beforeDestroy
destroyed
renderMixin
installRenderHelpers(Vue.prototype)
一系列渲染方法
$nextTick
dom渲染完后的回调函数
这里可以同时看一下vue批量跟新策略https://km.sankuai.com/page/110756883
_render
如果有父节点,则先处理solt
获取原型上的render.call(vm._renderProxy, vm.$createElement) 创建元素
处理一位数组问题
处理如果是空节点
返回节点
代理
当前环境是开发环境,则调用initProxy方法
如果不是开发环境,则vue实例的_renderProxy属性指向vue实例本身。
详细解读可看 https://juejin.im/post/5b11db686fb9a01e5b10eae7
如果不是开发环境,则vue实例的_renderProxy属性指向vue实例本身。
详细解读可看 https://juejin.im/post/5b11db686fb9a01e5b10eae7
性能检测
开发环境下,标记时间
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
合并
存在option且有子组件?
Y:因为Vue动态合并策略非常慢,并且内部组件的选项都不需要特殊处理。initInternalComponent,内部组件调用此快捷方法,内部组件实例化。
N: 策略合并options vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)
详细解读可转至 https://github.com/CommanderXL/biu-blog/issues/20
Y:因为Vue动态合并策略非常慢,并且内部组件的选项都不需要特殊处理。initInternalComponent,内部组件调用此快捷方法,内部组件实例化。
N: 策略合并options vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)
详细解读可转至 https://github.com/CommanderXL/biu-blog/issues/20
initLifecycle
确定第一个非抽象父组件,然后建立连接,生产children数组
while向上定位第一个非抽象祖先元素
属性赋值
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
initEvents
获取父组件listeners,如果不为空
updateComponentListeners(vm, listeners)
updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
updateListeners (
on: Object,
oldOn: Object,
add: Function,
remove: Function,
createOnceHandler: Function,
vm: Component
)
on: Object,
oldOn: Object,
add: Function,
remove: Function,
createOnceHandler: Function,
vm: Component
)
for in 遍历父组件事件组
normalizeEvent标准化事件,根据我们的的事件名的一些特殊标识(之前在 addHandler 的时候添加上的)区分出这个事件是否有 once、capture、passive 等修饰符。
如果当前事件有回调函数fns,则createFnInvoker分别对单个函数或者函数数组做处理
如果是一次性事件,则返回createOnceHandler,在其中闭包中,res = fn.apply(null, arguments)记录函数执行结果,如果不为空,则说明函数已执行,然后取消_target.$off(event, onceHandler)
挂载事件 add(event.name, cur, event.capture, event.passive, event.params)
当我们第二次执行该函数的时候, 把之前绑定的 involer.fns 赋值为新的回调函数即可,这样就保证了事件回调只添加一次,之后仅仅去修改它的回调函数的引用。
initRender
如果有slot通过resolveSlots处理
定义两个创造元素的函数
_c():使用模板创建元素
$createElement():使用render方法创建元素
data无效返回空节点
没有tag返回空节点
检测:key值
如果子节点是函数的话,取出函数,置空children
依据normalizationType提供两种规范化children函数
手写render函数时 或者
编译 slot、v-for:
--------------------
normalizeChildren
编译 slot、v-for:
--------------------
normalizeChildren
children是基础类型返回createTextVNode
是数组返回normalizeArrayChildren(children)
normalizeArrayChildren
遍历 children,获得单个节点 c,然后对 c 的类型判断,如果是一个数组类型,则递归调用 normalizeArrayChildren; 如果是基础类型,则通过 createTextVNode 方法转换成 VNode 类型;否则就已经是 VNode 类型了,如果 children 是一个列表并且列表还存在嵌套的情况,则根据 nestedIndex 去更新它的 key。这里需要注意一点,在遍历的过程中,对这 3 种情况都做了如下处理:如果存在两个连续的 text 节点,会把它们合并成一个 text 节点。
正常render生成的,由于都是VNode,所以直接拍平children即可
---------------------------------------
simpleNormalizeChildren
---------------------------------------
simpleNormalizeChildren
在非生产模式下 $attrs与$listeners 只读
initInjections
initInjections(vm)
------------------
通过依赖注入导入依赖项,while向上查找存在的inject的祖先元素,存在的话就defineReactive至当前组件
------------------
通过依赖注入导入依赖项,while向上查找存在的inject的祖先元素,存在的话就defineReactive至当前组件
通过resolveInject()得到当前组件的注入项,向上遍历祖先组件,取其_provided得到vm.$options.inject的映射对象
如果存在inject的话,关闭响应式,在自身组件上定义响应式属性。注意到在定义响应式属性之前,toggleObserving(false),这意味着inject的值里面是没有__ob__的
initState
initProps
如果不是根组件则toggleObserving(false),取消对 Object Array 类型 Prop 深度观测,因为在父组件中已经深度观察过了
遍历传入的propsOptions
验证值,validateProp(key, propsOptions, propsData, vm)
给 props 设置响应式
如果没有key的话,_props设置代理访问
initMethods
key遍历传入的methods
如果不是函数类型报错
如果和props冲突报错
如果和vm键值冲突报错
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
initData
获取vm.$options.data,如果是函数类型,则calldata()执行得到返回结果
检测是否和Props,methods冲突
设置访问代理
为data开启观察者模式
initComputed
遍历获得computed键值对象,如果不是函数则取其.get
const getter = typeof userDef === 'function' ? userDef : userDef.get
判断是不是服务器端渲染,计算属性在服务器渲染的情况下只有getter。如果是服务器端渲染,Vue不会为计算属性添加Watcher。
经过判断后调用defineComputed定义计算属性。
initWatch
遍历watch数组
如果是值为数组类型,for批量createWatcher
否则单独创建
return vm.$watch(expOrFn, handler, options)
initProvide
export function initProvide (vm: Component) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}}
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}}
mount
vm.$mount(vm.$options.el) 挂载实例
如果没有render函数
template是字符串
template是字符串
如果是ID
--------
根据ID找到元素
template = idToTemplate(template)
--------
根据ID找到元素
template = idToTemplate(template)
如果元素节点
------------
取innerHTML
template = template.innerHTML
------------
取innerHTML
template = template.innerHTML
如果template选项不存在,那么使用el元素的outerHTML 作为模板内容
template = getOuterHTML(el)
template = getOuterHTML(el)
如果经过上述过程template存在的话
compileToFunctions
把模板 template 编译生成 render 以及 staticRenderFns
把模板 template 编译生成 render 以及 staticRenderFns
createCompiler()
createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult
template: string,
options: CompilerOptions
): CompiledResult
baseCompile()
优化语法树
optimize(ast, options)
optimize(ast, options)
生成代码
generate(ast, options)
generate(ast, options)
得到生成的render函数
options.render = render
options.staticRenderFns = staticRenderFns
options.render = render
options.staticRenderFns = staticRenderFns
收藏
收藏
0 条评论
下一页