data + computed + watch 思想实现

// main.js
import Vue from '../modules/vue/index'
const App = {
  el:'#app',
  data(){
    return{
      message: 'Hello Vue!',
      a:1,
      b:2
    }
  },
  template: `
    <div>
      <h1>{{ message }}</h1>
    </div>
  `,
  computed:{
    total(){
      console.log('computed');
      return this.a + this.b;
    }
  },
  watch:{
    total(newValue,oldValue){
      console.log('total watch',newValue,oldValue);
    },
    a(newValue,oldValue){
      console.log('a watch',newValue,oldValue);
    },
    b(newValue,oldValue){
      console.log('b watch',newValue,oldValue);
    },
  }
}
const vm = new Vue(App)
console.log(vm);
console.log(vm.total);
console.log(vm.total);
console.log(vm.total);

vm.a = 100;
console.log(vm.total);
console.log(vm.total);
console.log(vm.total);

vm.b = 200;

console.log(vm.total);
console.log(vm.total);
console.log(vm.total);
// const vm = Vue.createApp(App).mount('#app')
// modules/vue/index.js

import Computed from "./computed";
import { reactive } from "./reactive";
import Watcher from "./watcher";

class Vue {
  constructor(optinos){
    const {data,computed,watch,el} = optinos;
    this.$data = data();
    this.$el = el;
    this._init(this,computed,watch);
  }
  _init(vm,computed,watch){
    this.initData(vm);
    const computedIns = this.initComputed(vm,computed);
    const watcherIns = this.initWatcher(vm,watch);

    this.$computed = computedIns.update.bind(computedIns);
    this.$watch = watcherIns.invoke.bind(watcherIns);
  }
  initData(vm){
    // 响应式数据
    reactive(vm,(key,value)=>{
      // console.log('get',key,value);
    },(key,newValue,oldValue)=>{
      // console.log('set',key,newValue,oldValue);
      if(newValue === oldValue) return;
      this.$computed(key,(key,newValue,oldValue)=>{
        this.$watch(key,newValue,oldValue);
      });
      this.$watch(key,newValue,oldValue);
    });
  }
  initComputed(vm,computed){
    // 枚举computed -> 增加 computedData
    // 返回实例 -> 实例里有update -> 更新computedData的value
    // const computedIns = new Computed();
    const computedIns = new Computed();
    for (const key in computed) {
      computedIns.addComputed(vm,computed,key)
    }

    return computedIns;
  }
  initWatcher(vm,watch){
    // 枚举watcher -> 增加侦听器
    const watcherIns = new Watcher();
    for (const key in watch) {
      watcherIns.addWatcher(vm,watch,key)
    }

    return watcherIns;
  }
}

export default Vue;

/**
 * data -> data() -> vm.$data -> reactive -> vm.xxx
 *         get vm[key] -> vm.$data[key]
 *         set vm[key] -> vm.$data[key] = newValue
 *                    ? -> updateComputedProp -> value
 *                    ? -> updateWatchProp -> callback
 * 
 * computed -> props -> {
 *    value: -> get -> value
 *    get: -> methods
 *    dep: -> [a,b]
 * }
 * 
 * watch -> props -> fn -> data set -> call fn
 */
// modules/vue/reactive.js

/**
 * 
 * @param {*} vm 
 * @param {*} __get__ 获取数据后支持回调函数
 * @param {*} __set__ 设置数据后支持回调函数
 */
export function reactive(vm,__get__,__set__){
  const _data = vm.$data;
  for (const key in _data) {
    Object.defineProperty(vm,key,{
      get:function(){
        __get__(key,_data[key])
        return _data[key];
      },
      set:function(newValue){
        const oldValue = _data[key];
        _data[key] = newValue;
        __set__(key,newValue,oldValue)
      }
    })
  }
}
// modules/vue/computed.js

class Computed{
  constructor(){
    this.computedData = []
    /**
     * total(){
        console.log('computed');
        return this.a + this.b;
      }
      // computedData
      {
        key:total,
        value:3,
        get:total fn,
        dep:['a','b']
      }
    */
  }
  addComputed(vm,computed,key){
    /**
     * {key:total,value:3,get:total fn,dep:['a','b']}
     */
    const descriptor = Object.getOwnPropertyDescriptor(computed,key),
          descriptorFn = descriptor.value.get?descriptor.value.get:descriptor.value,
          value = descriptorFn.call(vm),
          get = descriptorFn.bind(vm),
          dep = this.collectDep(descriptorFn);
    // 收集computed
    this._addComputedProp({key,value,get,dep});
    // console.log(this.computedData);

    // 把computed计算结果挂载到vm实例上
    const dataItem = this.computedData.find(item=>item.key == key);
    Object.defineProperty(vm,key,{
      get:function(){
        return dataItem.value
      },
      set:function(newValue){
        // 无论computed设置什么值,都要执行get函数,确保结果的依赖是正确的
        dataItem.value = dataItem.get();
      }
    })

  }
  /**
   * 收集computed
   * @param {*} computedProp 
   */
  _addComputedProp(computedProp){
    this.computedData.push(computedProp)
  }
  /**
   * 获取computed所依赖的数据
   * @param {*} fn 
   * @returns 
   */
  collectDep(fn){
    const matched = fn.toString().match(/this\.(.+?)/g);
    return matched.map(item=>item.split('.')[1])
  }
  /**
   * 当依赖改变的时候,要重新计算computed结果
   * @param {*} key 
   * @param {*} cb 
   */
  update(key,cb){
    this.computedData.map(item=>{
      // 该计算属性所依赖的数据
      const dep = item.dep;
      // 如果data数据改变,改变的数据是该计算属性中的依赖,那么就要重新计算computed
      const _key = dep.find(el=>el == key);
      if(_key){
        let oldValue=item.value;
        item.value = item.get(); // 重新计算computed
        cb && cb(item.key,item.value,oldValue) // 回调函数的作用主要是执行watch中对computed的监听
      }
    })
  }
}

export default Computed;
// modules/vue/watcher.js

class Watcher{
  /**
   * addWatcher(vm,watch,key)
   * 
   * this.watchers ->watch
   * {
   *  key:?
   * fn:key fn
   * }
   */
  constructor(){
    /**
     * {
     *  key:
     * fn
     * }
     */
    // 收集watchx项
    this.watchers = [];
  }
  addWatcher(vm,watch,key){
    this._addWatcherProp({
      key,
      fn:watch[key].bind(vm)
    })
    // console.log(this.watchers,'watchers');
  }
  _addWatcherProp(watchProp){
    this.watchers.push(watchProp)
  }
  /**
   * 执行watch项的方法
   * @param {*} key 
   * @param {*} newValue 
   * @param {*} oldValue 
   */
  invoke(key,newValue,oldValue){
    this.watchers.map(item=>{
      if(item.key === key){
        item.fn(newValue,oldValue);
      }
    })
  }
}

export default Watcher;
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

️不倒翁

你的鼓励就是我前进的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值