深入理解Vue的computed实现原理及其实现方式__Vue.js__GitHub__前端__MVVM
发布于 3 年前 作者 banyungong 1121 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

继上一篇:深入理解Vue的watch实现原理及其实现方式 继续讲解

Vue的computed实现相对于watch和data来说比较难以理解,要真正的理解computed的工作方式,你需要深入理解Vue的双向数据绑定原理和实现方式。

如果你还不是很理解推荐你先看此文章:

彻底搞懂Vue针对数组和双向绑定(MVVM)的处理方式


首先来看一波Vue中computed的使用方式:

var vm = new Vue({
  data: { a: 1 },
  computed: {
    // 仅读取
    aDouble: function () {
      return this.a * 2
    },
    // 读取和设置
    aPlus: {
      get: function () {
        return this.a + 1
      },
      set: function (v) {
        this.a = v - 1
      }
    }
  }
})
vm.aPlus   // => 2
vm.aPlus = 3
vm.a       // => 2
vm.aDouble // => 4

计算属性的主要应用场景是代替模板内的表达式,或者data值的任何复杂逻辑都应该使用computed来计算,它有两大优势:

1、逻辑清晰,方便于管理

2、计算值会被缓存,依赖的data值改变时才会从新计算

此文我们需要核心理解的是:

1、computed是如何初始化,初始化之后干了些什么

2、为何触发data值改变时computed会从新计算

3、computed值为什么说是被缓存的呢,如何做的


如果以上三个问题你都已知,你可以忽略下文了,若未知或一知半解,请抱着学习的态度看看别人的理解。

备注:以下只是我的个人理解,并不保证绝对的正确性,若有问题欢迎指正

以下大部分代码摘自Vue源码。


如果你看到了这里,就当做你已经深入理解了Vue的MVVM原理及其实现方式。相关Vue的MVVM实现直接取自上一篇文章。

Dep代码的实现:

//标识当前的Dep id
let uidep = 0
class Dep{
	constructor () {
		this.id = uidep++
		// 存放所有的监听watcher
    	this.subs = []
  	}
//添加一个观察者对象
addSub (Watcher) {
	this.subs.push(Watcher)
}
//依赖收集
<span class="hljs-function"><span class="hljs-title">depend</span></span> () {
	//Dep.target 作用只有需要的才会收集依赖
    <span class="hljs-keyword">if</span> (Dep.target) {
      Dep.target.addDep(this)
    }
}

// 调用依赖收集的Watcher更新
<span class="hljs-function"><span class="hljs-title">notify</span></span> () {
    const subs = this.subs.slice()
    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = 0, l = subs.length; i &lt; l; i++) {
      subs[i].update()
    }
}

}

Dep.target = null const targetStack = []

// 为Dep.target 赋值 function pushTarget (Watcher) { if (Dep.target) targetStack.push(Dep.target) Dep.target = Watcher } function popTarget () { Dep.target = targetStack.pop() }


Watcher代码的实现:

//去重 防止重复收集
let uid = 0
class Watcher{
constructor(vm,expOrFn,cb,options){
//传进来的对象 例如Vue
this.vm = vm
if (options) {
this.deep = !!options.deep
this.user = !!options.user
this.lazy = !!options.lazy
}else{
this.deep = this.user = this.lazy = false
}
this.dirty = this.lazy
//在Vue中cb是更新视图的核心,调用diff并更新视图的过程
this.cb = cb
this.id = ++uid
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
if (typeof expOrFn === ‘function’) {
//data依赖收集走此处
this.getter = expOrFn
} else {
//watch依赖走此处
this.getter = this.parsePath(expOrFn)
}
//设置Dep.target的值,依赖收集时的watcher对象
this.value = this.lazy ? undefined : this.get()
}

<span class="hljs-function"><span class="hljs-title">get</span></span>(){
	//设置Dep.target值,用以依赖收集
    pushTarget(this)
    const vm = this.vm
    //此处会进行依赖收集 会调用data数据的 get
    <span class="hljs-built_in">let</span> value = this.getter.call(vm, vm)
    popTarget()
    <span class="hljs-built_in">return</span> value
}

//添加依赖
addDep (dep) {
	//去重
	const id = dep.id
    <span class="hljs-keyword">if</span> (!this.newDepIds.has(id)) {
      	this.newDepIds.add(id)
      	this.newDeps.push(dep)
      	<span class="hljs-keyword">if</span> (!this.depIds.has(id)) {
      		//收集watcher 每次data数据 <span class="hljs-built_in">set</span>
      		//时会遍历收集的watcher依赖进行相应视图更新或执行watch监听函数等操作
        	dep.addSub(this)
      	}
    }
}

//更新
<span class="hljs-function"><span class="hljs-title">update</span></span> () {
	<span class="hljs-keyword">if</span> (this.lazy) {
  		this.dirty = <span class="hljs-literal">true</span>
	}<span class="hljs-keyword">else</span>{
		this.run()
	}
}

//更新视图
<span class="hljs-function"><span class="hljs-title">run</span></span>(){
	console.log(`这里会去执行Vue的diff相关方法,进而更新数据`)
	const value = this.get()
	const oldValue = this.value
    this.value = value
	<span class="hljs-keyword">if</span> (this.user) {
		//watch 监听走此处
        this.cb.call(this.vm, value, oldValue)
    }<span class="hljs-keyword">else</span>{
    	//data 监听走此处
    	//这里只做简单的console.log 处理,在Vue中会调用diff过程从而更新视图
		this.cb.call(this.vm, value, oldValue)
    }
}

//如果计算熟悉依赖的data值发生变化时会调用
//案例中 当data.name值发生变化时会执行此方法
<span class="hljs-function"><span class="hljs-title">evaluate</span></span> () {
    this.value = this.get()
    this.dirty = <span class="hljs-literal">false</span>
}
//收集依赖
<span class="hljs-function"><span class="hljs-title">depend</span></span> () {
    <span class="hljs-built_in">let</span> i = this.deps.length
    <span class="hljs-keyword">while</span> (i--) {
      this.deps[i].depend()
    }
}

// 此方法获得每个watch中key在data中对应的value值
//使用split(<span class="hljs-string">'.'</span>)是为了得到 像<span class="hljs-string">'a.b.c'</span> 这样的监听值
parsePath (path){
	const bailRE = /[^w.$]/
  <span class="hljs-keyword">if</span> (bailRE.test(path)) <span class="hljs-built_in">return</span>
  	const segments = path.split(<span class="hljs-string">'.'</span>)
  	<span class="hljs-built_in">return</span> <span class="hljs-keyword">function</span> (obj) {
	    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = 0; i &lt; segments.length; i++) {
	      	<span class="hljs-keyword">if</span> (!obj) <span class="hljs-built_in">return</span>
	      	//此处为了兼容我的代码做了一点修改	 
	        //此处使用新获得的值覆盖传入的值 因此能够处理 <span class="hljs-string">'a.b.c'</span>这样的监听方式
	        <span class="hljs-keyword">if</span>(i==0){
	        	obj = obj.data[segments[i]]
	        }<span class="hljs-keyword">else</span>{
	        	obj = obj[segments[i]]
	        }
	    }
	    <span class="hljs-built_in">return</span> obj
	 }
}

}

在Watcher中对于computed来说核心注意点是以下方法:

//如果计算熟悉依赖的data值发生变化时会调用
//案例中 当data.name值发生变化时会执行此方法
evaluate () {
this.value = this.get()
this.dirty = false
}

当computed中用到的data值发生变化时,视图更新调用computed值时会从新执行,获得新的计算属性值。


Observer代码实现

class Observer{
constructor (value) {
this.value = value
// 增加dep属性(处理数组时可以直接调用)
this.dep = new Dep()
//将Observer实例绑定到data的__ob__属性上面去,后期如果oberve时直接使用,不需要从新Observer,
//处理数组是也可直接获取Observer对象
def(value, ob, this)
if (Array.isArray(value)) {
//这里只测试对象
} else {
//处理对象
this.walk(value)
}
}

walk (obj) {
	const keys = Object.keys(obj)
	<span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = 0; i &lt; keys.length; i++) {
		//此处我做了拦截处理,防止死循环,Vue中在oberve函数中进行的处理
		<span class="hljs-keyword">if</span>(keys[i]==<span class="hljs-string">'__ob__'</span>) <span class="hljs-built_in">return</span>;
  		defineReactive(obj, keys[i], obj[keys[i]])
	}
}

} //数据重复Observer function observe(value){ if(typeof(value) != ‘object’ ) return; let ob = new Observer(value) return ob; } // 把对象属性改为getter/setter,并收集依赖 function defineReactive (obj,key,val) { const dep = new Dep() //处理children let childOb = observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { console.log(调用get获取值,值为<span class="hljs-variable">${val}</span>) const value = val if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() } } return value }, set: function reactiveSetter (newVal) { console.log(调用了<span class="hljs-built_in">set</span>,值为<span class="hljs-variable">${newVal}</span>) const value = val val = newVal //对新值进行observe childOb = observe(newVal) //通知dep调用,循环调用手机的Watcher依赖,进行视图的更新 dep.notify() } }) } //辅助方法 function def (obj, key, val) { Object.defineProperty(obj, key, { value: val, enumerable: true, writable: true, configurable: true }) }


此文的重点Computed代码实现:

//空函数
const noop = ()=>{}
// computed初始化的Watcher传入lazy: true就会触发Watcher中的dirty值为true
const computedWatcherOptions = { lazy: true }
//Object.defineProperty 默认value参数
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
// 初始化computed
class initComputed {
constructor(vm, computed){
//新建存储watcher对象,挂载在vm对象执行
const watchers = vm._computedWatchers = Object.create(null)
//遍历computed
for (const key in computed) {
const userDef = computed[key]
//getter值为computed中key的监听函数或对象的get值
let getter = typeof userDef === ‘function’ ? userDef : userDef.get
//新建computed的 watcher
watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)
if (!(key in vm)) {
/定义计算属性/
this.defineComputed(vm, key, userDef)
}
}
}
//把计算属性的key挂载到vm对象下,并使用Object.defineProperty进行处理
//因此调用vm.somecomputed 就会触发get函数
defineComputed (target, key, userDef) {
if (typeof userDef === ‘function’) {
sharedPropertyDefinition.get = this.createComputedGetter(key)
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get
? userDef.cache !== false
? this.createComputedGetter(key)
: userDef.get
: noop
//如果有设置set方法则直接使用,否则赋值空函数
sharedPropertyDefinition.set = userDef.set
? userDef.set
: noop
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}

//计算属性的getter 获取计算属性的值时会调用
createComputedGetter (key) {
  <span class="hljs-built_in">return</span> <span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">computedGetter</span></span> () {
  	//获取到相应的watcher
    const watcher = this._computedWatchers &amp;&amp; this._computedWatchers[key]
    <span class="hljs-keyword">if</span> (watcher) {
    	//watcher.dirty 参数决定了计算属性值是否需要重新计算,默认值为<span class="hljs-literal">true</span>,即第一次时会调用一次
      	<span class="hljs-keyword">if</span> (watcher.dirty) {
      		/*每次执行之后watcher.dirty会设置为<span class="hljs-literal">false</span>,只要依赖的data值改变时才会触发
      		watcher.dirty为<span class="hljs-literal">true</span>,从而获取值时从新计算*/
        	watcher.evaluate()
      	}
      	//获取依赖
      	<span class="hljs-keyword">if</span> (Dep.target) {
        	watcher.depend()
      	}
      	//返回计算属性的值
      	<span class="hljs-built_in">return</span> watcher.value
    }
  }
}

}


代码已经写完,完整代码如下:

//标识当前的Dep id
let uidep = 0
class Dep{
constructor () {
this.id = uidep++
// 存放所有的监听watcher
this.subs = []
}

//添加一个观察者对象
addSub (Watcher) {
	this.subs.push(Watcher)
}
//依赖收集
<span class="hljs-function"><span class="hljs-title">depend</span></span> () {
	//Dep.target 作用只有需要的才会收集依赖
    <span class="hljs-keyword">if</span> (Dep.target) {
      Dep.target.addDep(this)
    }
}

// 调用依赖收集的Watcher更新
<span class="hljs-function"><span class="hljs-title">notify</span></span> () {
    const subs = this.subs.slice()
    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = 0, l = subs.length; i &lt; l; i++) {
      subs[i].update()
    }
}

}

Dep.target = null const targetStack = []

// 为Dep.target 赋值 function pushTarget (Watcher) { if (Dep.target) targetStack.push(Dep.target) Dep.target = Watcher } function popTarget () { Dep.target = targetStack.pop() } /----------------------------------------Watcher------------------------------------/ //去重 防止重复收集 let uid = 0 class Watcher{ constructor(vm,expOrFn,cb,options){ //传进来的对象 例如Vue this.vm = vm if (options) { this.deep = !!options.deep this.user = !!options.user this.lazy = !!options.lazy }else{ this.deep = this.user = this.lazy = false } this.dirty = this.lazy //在Vue中cb是更新视图的核心,调用diff并更新视图的过程 this.cb = cb this.id = ++uid this.deps = [] this.newDeps = [] this.depIds = new Set() this.newDepIds = new Set() if (typeof expOrFn === ‘function’) { //data依赖收集走此处 this.getter = expOrFn } else { //watch依赖走此处 this.getter = this.parsePath(expOrFn) } //设置Dep.target的值,依赖收集时的watcher对象 this.value = this.lazy ? undefined : this.get() }

<span class="hljs-function"><span class="hljs-title">get</span></span>(){
	//设置Dep.target值,用以依赖收集
    pushTarget(this)
    const vm = this.vm
    //此处会进行依赖收集 会调用data数据的 get
    <span class="hljs-built_in">let</span> value = this.getter.call(vm, vm)
    popTarget()
    <span class="hljs-built_in">return</span> value
}

//添加依赖
addDep (dep) {
	//去重
	const id = dep.id
    <span class="hljs-keyword">if</span> (!this.newDepIds.has(id)) {
      	this.newDepIds.add(id)
      	this.newDeps.push(dep)
      	<span class="hljs-keyword">if</span> (!this.depIds.has(id)) {
      		//收集watcher 每次data数据 <span class="hljs-built_in">set</span>
      		//时会遍历收集的watcher依赖进行相应视图更新或执行watch监听函数等操作
        	dep.addSub(this)
      	}
    }
}

//更新
<span class="hljs-function"><span class="hljs-title">update</span></span> () {
	<span class="hljs-keyword">if</span> (this.lazy) {
  		this.dirty = <span class="hljs-literal">true</span>
	}<span class="hljs-keyword">else</span>{
		this.run()
	}
}

//更新视图
<span class="hljs-function"><span class="hljs-title">run</span></span>(){
	console.log(`这里会去执行Vue的diff相关方法,进而更新数据`)
	const value = this.get()
	const oldValue = this.value
    this.value = value
	<span class="hljs-keyword">if</span> (this.user) {
		//watch 监听走此处
        this.cb.call(this.vm, value, oldValue)
    }<span class="hljs-keyword">else</span>{
    	//data 监听走此处
    	//这里只做简单的console.log 处理,在Vue中会调用diff过程从而更新视图
		this.cb.call(this.vm, value, oldValue)
    }
}

//如果计算熟悉依赖的data值发生变化时会调用
//案例中 当data.name值发生变化时会执行此方法
<span class="hljs-function"><span class="hljs-title">evaluate</span></span> () {
    this.value = this.get()
    this.dirty = <span class="hljs-literal">false</span>
}
//收集依赖
<span class="hljs-function"><span class="hljs-title">depend</span></span> () {
    <span class="hljs-built_in">let</span> i = this.deps.length
    <span class="hljs-keyword">while</span> (i--) {
      this.deps[i].depend()
    }
}

// 此方法获得每个watch中key在data中对应的value值
//使用split(<span class="hljs-string">'.'</span>)是为了得到 像<span class="hljs-string">'a.b.c'</span> 这样的监听值
parsePath (path){
	const bailRE = /[^w.$]/
  <span class="hljs-keyword">if</span> (bailRE.test(path)) <span class="hljs-built_in">return</span>
  	const segments = path.split(<span class="hljs-string">'.'</span>)
  	<span class="hljs-built_in">return</span> <span class="hljs-keyword">function</span> (obj) {
	    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = 0; i &lt; segments.length; i++) {
	      	<span class="hljs-keyword">if</span> (!obj) <span class="hljs-built_in">return</span>
	      	//此处为了兼容我的代码做了一点修改	 
	        //此处使用新获得的值覆盖传入的值 因此能够处理 <span class="hljs-string">'a.b.c'</span>这样的监听方式
	        <span class="hljs-keyword">if</span>(i==0){
	        	obj = obj.data[segments[i]]
	        }<span class="hljs-keyword">else</span>{
	        	obj = obj[segments[i]]
	        }
	    }
	    <span class="hljs-built_in">return</span> obj
	 }
}

}

/----------------------------------------Observer------------------------------------/ class Observer{ constructor (value) { this.value = value // 增加dep属性(处理数组时可以直接调用) this.dep = new Dep() //将Observer实例绑定到data的__ob__属性上面去,后期如果oberve时直接使用,不需要从新Observer, //处理数组是也可直接获取Observer对象 def(value, ob, this) if (Array.isArray(value)) { //这里只测试对象 } else { //处理对象 this.walk(value) } }

walk (obj) {
	const keys = Object.keys(obj)
	<span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = 0; i &lt; keys.length; i++) {
		//此处我做了拦截处理,防止死循环,Vue中在oberve函数中进行的处理
		<span class="hljs-keyword">if</span>(keys[i]==<span class="hljs-string">'__ob__'</span>) <span class="hljs-built_in">return</span>;
  		defineReactive(obj, keys[i], obj[keys[i]])
	}
}

} //数据重复Observer function observe(value){ if(typeof(value) != ‘object’ ) return; let ob = new Observer(value) return ob; } // 把对象属性改为getter/setter,并收集依赖 function defineReactive (obj,key,val) { const dep = new Dep() //处理children let childOb = observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { console.log(调用get获取值,值为<span class="hljs-variable">${val}</span>) const value = val if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() } } return value }, set: function reactiveSetter (newVal) { console.log(调用了<span class="hljs-built_in">set</span>,值为<span class="hljs-variable">${newVal}</span>) const value = val val = newVal //对新值进行observe childOb = observe(newVal) //通知dep调用,循环调用手机的Watcher依赖,进行视图的更新 dep.notify() } }) } //辅助方法 function def (obj, key, val) { Object.defineProperty(obj, key, { value: val, enumerable: true, writable: true, configurable: true }) } /----------------------------------------初始化watch------------------------------------/ //空函数 const noop = ()=>{} // computed初始化的Watcher传入lazy: true就会触发Watcher中的dirty值为true const computedWatcherOptions = { lazy: true } //Object.defineProperty 默认value参数 const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } // 初始化computed class initComputed { constructor(vm, computed){ //新建存储watcher对象,挂载在vm对象执行 const watchers = vm._computedWatchers = Object.create(null) //遍历computed for (const key in computed) { const userDef = computed[key] //getter值为computed中key的监听函数或对象的get值 let getter = typeof userDef === ‘function’ ? userDef : userDef.get //新建computed的 watcher watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions) if (!(key in vm)) { /定义计算属性/ this.defineComputed(vm, key, userDef) } } } //把计算属性的key挂载到vm对象下,并使用Object.defineProperty进行处理 //因此调用vm.somecomputed 就会触发get函数 defineComputed (target, key, userDef) { if (typeof userDef === ‘function’) { sharedPropertyDefinition.get = this.createComputedGetter(key) sharedPropertyDefinition.set = noop } else { sharedPropertyDefinition.get = userDef.get ? userDef.cache !== false ? this.createComputedGetter(key) : userDef.get : noop //如果有设置set方法则直接使用,否则赋值空函数 sharedPropertyDefinition.set = userDef.set ? userDef.set : noop } Object.defineProperty(target, key, sharedPropertyDefinition) }

//计算属性的getter 获取计算属性的值时会调用
createComputedGetter (key) {
  <span class="hljs-built_in">return</span> <span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">computedGetter</span></span> () {
  	//获取到相应的watcher
    const watcher = this._computedWatchers &amp;&amp; this._computedWatchers[key]
    <span class="hljs-keyword">if</span> (watcher) {
    	//watcher.dirty 参数决定了计算属性值是否需要重新计算,默认值为<span class="hljs-literal">true</span>,即第一次时会调用一次
      	<span class="hljs-keyword">if</span> (watcher.dirty) {
      		/*每次执行之后watcher.dirty会设置为<span class="hljs-literal">false</span>,只要依赖的data值改变时才会触发
      		watcher.dirty为<span class="hljs-literal">true</span>,从而获取值时从新计算*/
        	watcher.evaluate()
      	}
      	//获取依赖
      	<span class="hljs-keyword">if</span> (Dep.target) {
        	watcher.depend()
      	}
      	//返回计算属性的值
      	<span class="hljs-built_in">return</span> watcher.value
    }
  }
}

}


computed测试:

//1、首先来创建一个Vue构造函数:
function Vue(){
}
//2、设置data和computed的值:
let data={
name:‘Hello’,
}
let computed={
getfullname:function(){
console.log(’-----走了computed 之 getfullname------’)
console.log(‘新的值为:’+data.name + ’ - world’)
return data.name + ’ - world’
}
}
//3、实例化Vue并把data挂载到Vue上
let vue 		= new Vue()
vue.data 		= data
//4、创建Watcher对象
let updateComponent = (vm)=>{
// 收集依赖
data.name

} let watcher1 = new Watcher(vue,updateComponent,()=>{}) //5、初始化Data并收集依赖 observe(data) //6、初始化computed let watcher2 = new initComputed(vue,computed)


在浏览器console中测试:

//首先获得一次getfullname
vue.getfullname

//第二次调用getfullname看看会有什么变化呢 vue.getfullname

分析:调用vue.getfullname第一次会打印 ‘-----走了computed 之 getfullname------’,即计算属性第一次计算了值,第二次调用时,不会再打印值

即直接获取的缓存值,为什么第二次是获得的缓存值呢,因为第二次执行时watcher.dirty=true,就会直接返回watcher.value值。


//为data.name赋值
data.name = 'Hi’

分析:执行data.name时会触发两个Watcher监听函数(为什么是两个Watcher自己去脑补一下额!),一个是全局的watcher,一个是computed的watcher,第一个Watcher会更新视图,第二个Watcher会触发watcher.dirty=true。


//name值变更之后再次执行会是什么结果呢
vue.getfullname

//再执行一次 vue.getfullname

分析:运行vue.getfullname时会执行computedGetter函数,因为watcher.dirty=true因此会从新计算值,因此会打印 ‘-----走了computed 之 getfullname------’,值为’HI world’, 再次执行只会获得计算属性的缓存值。

所有测试代码如下:

/----------------------------------------Vue------------------------------------/
function Vue(){
}
/----------------------------------------测试代码------------------------------------/
// 调用
let data={
name:‘Hello’,
}
let computed={
getfullname:function(){
console.log(’-----走了computed 之 getfullname------’)
console.log(‘新的值为:’+data.name + ’ - world’)
return data.name + ’ - world’
}
}
let vue 		= new Vue()
vue.data 		= data
let updateComponent = (vm)=>{
// 收集依赖
data.name
}
let watcher1 = new Watcher(vue,updateComponent,()=>{})
observe(data)
let watvher2 = new initComputed(vue,computed)

//测试 浏览器console中相继运行一下代码测试 vue.getfullname vue.getfullname data.name=‘Hi’ vue.getfullname vue.getfullname


若有疑问欢迎交流。

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: zane1 原文链接:https://juejin.im/post/6844903606676799501

回到顶部