初始化vue需要用 new Vue({opation}) 那么new vue() 到底做了什么呢
它会在内部调用一个 _init初始化方法
那么 _init方法做了哪些事情
它会合并opation 把opation合并到vm.$opation上
随后它会进行一系列的初始化操作如:
初始化data
初始化methods
初始化props
初始化watcher
初始化生命周期
....
然后会调用$mount 挂载实例
今天记一下初始化data
new Vue({
el : "#app",
mounted(){
console.log(this.message) //"Hello,Vue"
},
data(){
return {
message : "Hello,Vue"
}
}
})
大家都知道 在生命周期函数和methods中都可以访问到data中的数据,那么为什么可以访问到呢?
其实Vue在内部做了点事情 就是 initData() 方法
它会获取到我们定义的data 然后把它挂载到实例上
let _data = vm.$opation.data
注意 不管是watch,data,methods,都不能有属性名重复的情话 否则就会报错
因为他们都会被挂载到实例上
然后vue会调用proxy方法
proxy方法 顾名思义 做了一个拦截的工作
const sharedPropertyDefinition = {enumerable: true,configurable: true, get: noop, set: noop}
export function proxy(target:Object,sourceKey:string,key:string){
......
sharedPropertyDefinition.get:(){return this[sourceKey][key]},
sharedPropertyDefinition.set:(val){this[sourceKey][key]=val},
Object.definedproperty(target,key,sharedPropertyDefinition)
}
Vue调用
...循环key
proxy(vm, `_data`, key)
这里的target就是vm,vm就是vue实例,_data就是我们定义的data,key就是我们要访问的属性
所以调用this.data.message 就会被Object.definedproperty拦截就相当于我们调用了 vm._data.message
所以就可以获取到。
面试题:new Vue()做了什么
合并配置、初始化生命周期、初始化事件中心、初始化渲染、初始化 data,props,computed,watcher 等等.
mount 再重新定义该方法,首先 它会对el做一些限制,不能挂载到 html,body上 因为el是会覆盖的 如果挂载到body上 那么它就覆盖了body 整个html文档就不对了
首先它会判断有没有定义render函数 如果没有定义再判断有没有定义template 如果定义了就拿到它的innerHTML,如果也没有定义就直接拿到el的innerHTML 所有的vue组件的渲染最终都是render方法 那么这个过程是vue的一个"在线编译"的过程 它是调用compileToFunctions方法实现的 最后调用原先原型上的$mount方法挂载
$mount 实际上会调用mountComponent方法,mountComponent方法核心是先调用vm.render生成vnode 再实例化一个渲染watcher 在watcher的回调中会调用updateComponent 最终调用vm.render更新dom
$mount(el)如果el是字符串 内部会调用document.querySelector(el) 获取元素 如果是个dom就会直接返回el 否则就报错
watcher 在这里起到了两个作用 一个是初始化时执行回调函数 另一个是当vm中监听的数据发生变化时会执行回调函数 就是updateComponent 更新dom
render方法会调用createElement方法 利用createElement方法创建vNode createElement方法其实是对_createElement方法的封装 他可以让参数灵活,在处理完这些参数之后调用真正的vNode方法 _createElement
_createElement方法有 5 个参数,context表示 VNode 的上下文环境,它是Component类型;tag表示标签,它可以是一个字符串,也可以是一个Component;data表示 VNode 的数据,它是一个VNodeData类型 children表示当前 VNode 的子节点,它是任意类型的,它接下来需要被规范为标准的 VNode数组;normalizationType表示子节点规范的类型,类型不同规范的方法也就不一样,它主要是参考render函数是编译生成的还是用户手写的
createElement两个比较重要的流程
children 的规范化和 vNode的创建
children 的格式化
根据normalizationType的不同,调用了normalizeChildren(children)和simpleNormalizeChildren(children)方法
simpleNormalizeChildren方法调用场景是render函数当函数是编译生成的。理论上编译生成的children都已经是 VNode 类型的,但这里有一个例外,就是functional component函数式组件返回的是一个数组而不是一个根节点,所以会通过Array.prototype.concat方法把整个children数组打平,让它的深度只有一层
normalizeChildren方法的调用场景有 2 种,一个场景是render函数是用户手写的,当children只有一个节点的时候,Vue.js 从接口层面允许用户把children写成基础类型用来创建单个简单的文本节点,这种情况会调用createTextVNode创建一个文本节点的 VNode;另一个场景是当编译slot、v-for的时候会产生嵌套数组的情况,会调用normalizeArrayChildren方法
normalizeArrayChildren主要的逻辑就是遍历children,获得单个节点c,然后对c的类型判断,如果是一个数组类型,则递归调用normalizeArrayChildren; 如果是基础类型,则通过createTextVNode方法转换成 VNode 类型