好傢伙,
程式碼出了點bug,暫時只能實現這兩種形式
完整程式碼已開源https://github.com/Fattiger4399/analytic-vue.git
watch有非常多種使用方式,我們要對其進行分類討論處理
//initState.js
if (opts.watch) {
initWatch(vm);
}
initWatch()方法
function initWatch(vm) {
//1 獲取watch
let watch = vm.$options.watch
console.log(watch)
//2 遍歷 { a,b,c}
for (let key in watch) {
//2.1獲取 他的屬性對應的值 (判斷)
let handler = watch[key] //陣列 ,物件 ,字元,函數
if (Array.isArray(handler)) {//陣列 []
handler.forEach(item=>{
createrWatcher(vm,key,item)
})
} else {//物件 ,字元,函數
//3建立一個方法來處理
createrWatcher(vm,key,handler)
}
}
}
createrWatcher()
//格式化處理
//vm 範例
//exprOrfn key
//hendler key對應的值
//options 自定義設定項 vue自己的為空,使用者定義的才有
function createrWatcher(vm,exprOrfn,handler,options){
//3.1 處理handler
if(typeof handler ==='object'){
options = handler; //使用者的設定專案
handler = handler.handler;//這個是一個函數
}
if(typeof handler ==='string'){// 'aa'
handler = vm[handler] //將範例行的方法作為 handler 方法代理和data 一樣
}
//其他是 函數
//watch 最終處理 $watch 這個方法
// console.log(vm,"||vm")
// console.log(exprOrfn,"||exprOrfn")
// console.log(handler,"||handler")
// console.log(options,"||options")
return vm.$watch(vm,exprOrfn,handler,options)
}
原型上掛$watch方法
export function stateMixin(vm) {
console.log(vm,6666)
//列隊 :1就是vue自己的nextTick 2使用者自己的
vm.prototype.$nextTick = function (cb) { //nextTick: 資料更新之後獲取到最新的DOM
// console.log(cb)
nextTick(cb)
},
vm.prototype.$watch =function(Vue,exprOrfn,handler,options={}){ //上面格式化處理
// console.log(exprOrfn,handler,options)
//實現watch 方法 就是new watcher //渲染走 渲染watcher $watch 走 watcher user false
// watch 核心 watcher
let watcher = new Watcher(Vue,exprOrfn,handler,{...options,user:true})
if(options.immediate){
handler.call(Vue) //如果有這個immediate 立即執行
}
}
}
watcher類
class Watcher {
//vm 範例
//exprOrfn vm._updata(vm._render())
constructor(vm, exprOrfn, cb, options) {
// 1.建立類第一步將選項放在範例上
this.vm = vm;
this.exprOrfn = exprOrfn;
this.cb = cb;
this.options = options;
// 2. 每一元件只有一個watcher 他是為標識
this.id = id++
this.user = !!options.user
// 3.判斷表示式是不是一個函數
this.deps = [] //watcher 記錄有多少dep 依賴
this.depsId = new Set()
if (typeof exprOrfn === 'function') {
this.getter = exprOrfn
}else{ //{a,b,c} 字串 變成函數
this.getter =function(){ //屬性 c.c.c
let path = exprOrfn.split('.')
let obj = vm
for(let i = 0;i<path.length;i++){
obj = obj[path[i]]
}
return obj //
}
}
// 4.執行渲染頁面
this.value = this.get() //儲存watch 初始值
}
addDep(dep) {
//去重 判斷一下 如果dep 相同我們是不用去處理的
let id = dep.id
// console.log(dep.id)
if (!this.depsId.has(id)) {
this.deps.push(dep)
this.depsId.add(id)
//同時將watcher 放到 dep中
// console.log(666)
dep.addSub(this)
}
// 現在只需要記住 一個watcher 有多個dep,一個dep 有多個watcher
//為後面的 component
}
run() { //old new
let value = this.get() //new
let oldValue = this.value //old
this.value = value
//執行 hendler (cb) 這個使用者wathcer
if(this.user){
this.cb.call(this.vm,value,oldValue)
}
}
get() {
// Dep.target = watcher
pushTarget(this) //當前的範例新增
const value = this.getter()// 渲染頁面 render() with(wm){_v(msg,_s(name))} ,取值(執行get這個方法) 走劫持方法
popTarget(); //刪除當前的範例 這兩個方法放在 dep 中
return value
}
//問題:要把屬性和watcher 繫結在一起 去html頁面
// (1)是不是頁面中呼叫的屬性要和watcher 關聯起來
//方法
//(1)建立一個dep 模組
updata() { //三次
//注意:不要資料更新後每次都呼叫 get 方法 ,get 方法回重新渲染
//快取
// this.get() //重新渲染
queueWatcher(this)
}
}
<body>
<div id="app">{{a}}</div>
<script src='./dist/vue.js'></script>
<script>
let vm = new Vue({
el: "#app",
data: {
a: 1,
b:[1,2],
c:{c:{c:100}}
},
methods:{
aa(){
console.log(2000)
}
},
//watch 基本使用方式
//1 屬性 :方法(函數)
//2 屬性 :陣列
//3 屬性 :物件
//4 屬性 :字串
watch: {
'c.c.c'(newValue,oldValue){
console.log(newValue,oldValue,'||this isnewValue,oldValue from c.c.c')
},
a:{
handler(){ //
console.log('a資料更新')
},
immediate:true
},
// a:'aa',
}
})
vm.a = 100
// vm.b[1] = 2
console.log(vm.c.c.c=2000)
</script>
</body>