function walk(data){
Object.keys(data).foreach(key => {
defineReactive(data, key, data[key])
//对象递归调用walk
walk(data[key])
})
}
function defineReactive(obj, key, value){
let oldValue = value;
const watchers = []
Object.defineProperty(obj, key, {
get(){
//收集watcher
watchers.push(currentWatcher)
return oldValue
},
set(){
if(newValue === oldValue) return;
oldValue = newValue;
watchers.forEach(watcher => wathcer.update())//更新视图
}
})
}
image-20210320163559768
// vm指向当前组件,el指向当前dom节点,第三个参数为标签类型,第四个为回调函数
// currentWatcher为全局变量指针
// 普通渲染的watcher
Watcher(vm, el, 'text', () =>{
// 将currentWatcher对象指向当前watcher(vdom节点)供响应式对象的get函数获取
currentWatcher = this;
// 读取显示的内容
el.textContext = eval('vm.data.text')
// 解绑currentWatcher,防止发生错误。
currentWatcher = null
})
//带v-if指令的watcher
Watcher(vm, el, 'text', () =>{
// 将currentWatcher对象指向当前watcher(vdom节点)供响应式对象的get函数获取
currentWatcher = this;
// 实现v-if指令,通过判断变量值决定是否显示该元素,v-show原理类似
el.style.display = eval('Boolean(vm.data.text)') ? 'block' : 'none'
// 解绑currentWatcher,防止发生错误。
currentWatcher = null
})
//template
<template>
<div v-if="text">
{{text}}
</div>
</template>
// vue-loader 编译后的 compile render
// h函数用于生成Vdom节点,第一个参数为当前组件,第二个参数为属性,第三个属性为子节点
render(){
return this.text
? h(
'div',
null,
h(this.text, null,[])
)
: vm.createEmptyVNode()
}
watcher不再与单个dom节点、指令关联,一个component对应一个watcher,极大减少了vue 1 中watcher数量过多导致的内存问题。同时以来vdom diff在渲染时能以最小的代价来更新dom。
Watch(compoent, vm, 'text', () =>{
const newVnode = component.render()
const oldVnode = component.render()
//通过diff算法返回新旧节点的差异
const patches = vm.diff(newVnode, oldVnode)
// 通过patch函数对该组件应用差异
vm.patch(component, patches);
})
// 编译前
<div>
<span class="foo">
Static
</span>
<span>
{{dynmic}}
</span>
</div>
// 编译后
const __static1=h('span',{
class:'foo'
}, 'static')
render(){
return h('div', [
__static1,
h('span', this.dynamic)
])
}
// 编译前
<Comp></Comp>
<div></div>
<span></span>
// Vue2
render(){
return createFragment([
h(Comp, null, null),
h('div', null, [
h('span', null, null)
])
])
}
function h(type, attrs, children){
if(isComponent(type)){
//创建component vnode
return createComponentVNode(type, attrs, children)
}
if(isDomElement(type)){
//创建原生dom vnode
return createElementVNode(type, attrs, children)
}
//创建纯string节点
return createStringVNode(type, attrs, children)
}
// Vue3
render(){
return createFragment([
createComponentVNode(Comp, null, null),
createElmentVNode('div',null, [
createElmentVNode('span', null, null)
])
])
}
//编译前
<template>
<div>
<p class="foo">
{{msg}}
</p>
<comp/
</div>
</template>
//编译后
render(){
return h('div', [
this.ssrString(
`<p class="foo">`
+ this.msg
+ '</p>'
),
h(comp)
])
}
// 编译前
<div @click="count++"></div>
// 编译后
import {getBoundMethod} from 'vue'
function __fn1(){
this.count++
}
render(){
return h('div',{
onClick:getBoundMethod(__fn1,this)
})
}
// Vue1/2中的做法
function walk(data){
Object.keys(data).foreach(key => {
defineReactive(data, key, data[key])
//对象递归调用walk
walk(data[key])
})
}
// Vue3中的做法
function reactive(target){
let observerd = new Proxy(target, {
get(target, key, receiver){
let result = Reflect.get(target, key, receiver)
//只有在取对象子属性的时候才递归
reactive(result)
return result
},
set(target, key, value, receiver) {
let oldValue = target[key]
if (value == oldValue)
return
let result = Reflect.set(target, key, value, receiver)
return result;
}
})
return observerd
}
export default {
data() {
return {
counter: 0
}
},
watch: {
counter(newValue, oldValue) {
console.log('The new counter value is: ' + this.counter)
}
}
}
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
const Foo = {
template:'#modal',
mixins:[Mixin1, Mixin2],
methods:{
click(){
this.sendLog()
}
},
components:{
appChild:Child
}
}