1、响应式处理
let vm = new Vue({
data() {
return {
name:'张三',
age: 18,
info:{ c: 'xxx' }
}
}
})
vue对传入得options挂载到vm上,执行initState(),总的初始化函数,初始化顺序为props、methods、data、computed、watch。
执行proxy方法,将_data上的数据代理到vm上,方便this.xxx访问。
observe(data),核心方法,递归遍历对象,通过defineReactive方法对对象属性的get、set进行拦截
问题 为什么对象数组分开处理
因为对象的属性一般不对太多,递归劫持完全是可行的,但是数组的个数不确定,如果太多(成千上万)每一个都去劫持get、set会影响性能。
所以对象deineProperty劫持,数组用变异方法去处理
遗留问题
对象的新增key 删除key 监听不到
数组只能通过变异方法去操作能触发响应式,arr[1]=xxx 不行,操作length也不行
2、模板渲染
查找options选项是否有el,没有再去$mount挂载,有el则为根节点
查找options是否有render函数,没有再去找template模板,再没有再去获取el的outHtml,最后都会转换成render函数
通过将template转换成render函数,也就是compileToFunctions方法
const render = compileToFunctions(template)
// 把生成的render函数赋值到options的render属性上
options.render = render
// compiler/index.js
const { parse } = require('./parse.js')
const { generate } = require('./codegen.js')
function compileToFunctions (template) {
// 把传进来的template传入parse函数中,并生成抽象语法树(AST)
// 抽象语法数是一个描述dom结构的树结构,包括html,css,js代码
// 使用变量ast接收AST
let ast = parse(template)
// 把上面生成的AST传入generate函数中
// 生成一个render格式的函数代码
// 格式大概是类似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world"))))
// _c代表创建元素,_v代表创建文本,_s代表文Json.stringify--把对象解析成文本
let code = generate(ast)
// 使用with改变this指向,可以方便code里去获取this(也就是Vue实例)里的数据
let renderFn = new Function(`with(this){return ${code}}`)
// 返回这个生成的render函数
return renderFn
}
module.exports = {
compileToFunctions: compileToFunctions
}
let ast = parse(template) 模板转换成ast语法树,
let code = generate(ast) ast转换成render函数所需的语法 返回
3、挂载$mount
mountComponent执行render函数 生成vnode,
vnode就是个对象,包含{ tag,data,children,key,text }之类的属性,
patch(oldVnode, vnode)方法执行,将vnode转换成真实dom,挂载完成。
初次挂载总结
new Vue对options进行初始化,对data进行递归遍历监听get、set,编译template时,触发属性的get方法,get中对有dep对watcher进行收集,在set时候通过dep.notify(),循环watcher进行页面更新,也就触发了对应的响应式,编译完成template生成vnode,通过patch方法将vnode转换成真实dom挂载到页面上,挂载完成。
vue computed原理
首先Watcher有三种类型
渲染Watcher: data里的变量监听,负责通知html重新渲染
computed Watcher:负责通知conputed里的依赖此变量的属性进行修改
user Watcher: watch对象里面定义的key,通知对应的变量函数执行
defineReactive函数中,每个对象都会实例化自己的dep = new Dep(),get时会把watcher收集进dep里,watcher也会收集dep,互相收集是因为computed没有自己的dept,要用他依赖的dept去做更新
lazy:true的配置是conputed的缓存,第一次使用会进行计算值,并且用value去缓存值,dirty改为false,依赖不更新,dirty一直是false,就会一直去取value的值,就是缓存的原理。