Рукописный исходный код Vue2.0 (9) - принцип свойства прослушивания

Vue.js
Рукописный исходный код Vue2.0 (9) - принцип свойства прослушивания

предисловие

В этой статье в основном написан исходный код Vue2.0 от руки.слушать свойство

В предыдущей статье мы в основном представили VueПринцип компонентаУглубленное понимание характеристик разработки компонентов Vue В этой статье будет представлен принцип использования большого количества свойств прослушивания в нашем ежедневном развитии бизнеса.

Для людей:

1. Хотите иметь глубокое понимание исходного кода vue для лучшего ежедневного развития бизнеса

2. Хотите владеть исходным кодом vue framework в резюме (больше не боитесь вопросов интервьюера о серийных убийцах, ха-ха)

3. Учащиеся, у которых нет времени смотреть официальный исходный код или им трудно понять с первого взгляда исходный код


текст

<script>
  // Vue实例化
  let vm = new Vue({
    el: "#app",
    data() {
      return {
        aa: 1,
        bb: 2,
      };
    },
    template: `<div id="a">hello 这是我自己写的Vue{{name}}</div>`,
    methods: {
      doSomething() {},
    },
    watch: {
      aa(newVal, oldVal) {
        console.log(newVal);
      },
      // aa: {
      //   handle(newVal, oldVal) {
      //     console.log(newVal);
      //   },
      //   deep: true
      // },
      // aa: 'doSomething',
      // aa: [{
      //   handle(newVal, oldVal) {
      //     console.log(newVal);
      //   },
      //   deep: true
      // }]
    },
  });
  setTimeout(() => {
    vm.aa = 1111;
  }, 1000);
</script>

Есть много способов записать свойство прослушивания, которое может быть записано в виде строк, функций, массивов и объектов.Вы можете добавить некоторые опции в запись объекта, чтобы улучшить функцию.Особенность свойства прослушивания заключается в том, что вы можете выполнить пользовательский метод, переданный пользователем после изменения значения прослушивания.

1. Инициализация свойств прослушивания

// src/state.js

// 统一初始化数据的方法
export function initState(vm) {
  // 获取传入的数据对象
  const opts = vm.$options;
  if (opts.watch) {
    //侦听属性初始化
    initWatch(vm);
  }
}

// 初始化watch
function initWatch(vm) {
  let watch = vm.$options.watch;
  for (let k in watch) {
    const handler = watch[k]; //用户自定义watch的写法可能是数组 对象 函数 字符串
    if (Array.isArray(handler)) {
      // 如果是数组就遍历进行创建
      handler.forEach((handle) => {
        createWatcher(vm, k, handle);
      });
    } else {
      createWatcher(vm, k, handler);
    }
  }
}
// 创建watcher的核心
function createWatcher(vm, exprOrFn, handler, options = {}) {
  if (typeof handler === "object") {
    options = handler; //保存用户传入的对象
    handler = handler.handler; //这个代表真正用户传入的函数
  }
  if (typeof handler === "string") {
    //   代表传入的是定义好的methods方法
    handler = vm[handler];
  }
  //   调用vm.$watch创建用户watcher
  return vm.$watch(exprOrFn, handler, options);
}

initWatch инициализирует Watch для обработки массива createWatcher обрабатывает совместимость Watch, включая строки, функции, массивы и объекты Наконец, вызовите $watch и передайте обработанные параметры для создания пользовательского Watcher

2.$watch

//  src/state.js
import Watcher from "./observer/watcher";
Vue.prototype.$watch = function (exprOrFn, cb, options) {
  const vm = this;
  //  user: true 这里表示是一个用户watcher
  let watcher = new Watcher(vm, exprOrFn, cb, { ...options, user: true });
  // 如果有immediate属性 代表需要立即执行回调
  if (options.immediate) {
    cb(); //如果立刻执行
  }
};

Метод прототипа $watch — это основной метод для создания пользовательских часов.Передайте определяемые пользователем параметры и user:true конструктору Watcher.

3. Преобразование наблюдателя

// src/observer/watcher.js

import { isObject } from "../util/index";
export default class Watcher {
  constructor(vm, exprOrFn, cb, options) {
    // this.vm = vm;
    // this.exprOrFn = exprOrFn;
    // this.cb = cb; //回调函数 比如在watcher更新之前可以执行beforeUpdate方法
    // this.options = options; //额外的选项 true代表渲染watcher
    // this.id = id++; // watcher的唯一标识
    // this.deps = []; //存放dep的容器
    // this.depsId = new Set(); //用来去重dep

    this.user = options.user; //标识用户watcher

    // 如果表达式是一个函数
    if (typeof exprOrFn === "function") {
      this.getter = exprOrFn;
    } else {
      this.getter = function () {
        //用户watcher传过来的可能是一个字符串   类似a.a.a.a.b
        let path = exprOrFn.split(".");
        let obj = vm;
        for (let i = 0; i < path.length; i++) {
          obj = obj[path[i]]; //vm.a.a.a.a.b
        }
        return obj;
      };
    }
    // 实例化就进行一次取值操作 进行依赖收集过程
    this.value = this.get();
  }
  //   get() {
  //     pushTarget(this); // 在调用方法之前先把当前watcher实例推到全局Dep.target上
  //     const res = this.getter.call(this.vm); //如果watcher是渲染watcher 那么就相当于执行  vm._update(vm._render()) 这个方法在render函数执行的时候会取值 从而实现依赖收集
  //     popTarget(); // 在调用方法之后把当前watcher实例从全局Dep.target移除
  //     return res;
  //   }
  //   把dep放到deps里面 同时保证同一个dep只被保存到watcher一次  同样的  同一个watcher也只会保存在dep一次
  //   addDep(dep) {
  //     let id = dep.id;
  //     if (!this.depsId.has(id)) {
  //       this.depsId.add(id);
  //       this.deps.push(dep);
  //       //   直接调用dep的addSub方法  把自己--watcher实例添加到dep的subs容器里面
  //       dep.addSub(this);
  //     }
  //   }
  //   这里简单的就执行以下get方法  之后涉及到计算属性就不一样了
  //   update() {
  //     // 计算属性依赖的值发生变化 只需要把dirty置为true  下次访问到了重新计算
  //     if (this.lazy) {
  //       this.dirty = true;
  //     }else{
  //       // 每次watcher进行更新的时候  可以让他们先缓存起来  之后再一起调用
  //       // 异步队列机制
  //       queueWatcher(this);
  //     }
  //   }
  //   depend(){
  //     // 计算属性的watcher存储了依赖项的dep
  //     let i=this.deps.length
  //     while(i--){
  //       this.deps[i].depend() //调用依赖项的dep去收集渲染watcher
  //     }
  //   }
  run() {
    const newVal = this.get(); //新值
    const oldVal = this.value; //老值
    this.value = newVal; //现在的新值将成为下一次变化的老值
    if (this.user) {
      // 如果两次的值不相同  或者值是引用类型 因为引用类型新老值是相等的 他们是指向同一引用地址
      if (newVal !== oldVal || isObject(newVal)) {
        this.cb.call(this.vm, newVal, oldVal);
      }
    } else {
      // 渲染watcher
      this.cb.call(this.vm);
    }
  }
}

В основном мы фокусируемся на неаннотированных местах, здесь есть два основных преобразования.

1. При создании экземпляра, чтобы быть совместимым с методом записи пользовательских часов, метод записи входящей строки будет преобразован в значение, соответствующее экземпляру Vue, и метод get будет вызван для получения и сохранения старого значения один раз. .

2. Метод запуска определяет, что если это часы пользователя, то следует выполнить функцию обратного вызова cb, переданную пользователем, и передать новое значение и старое значение в качестве параметров.

4. Интеллект-карта свойств слушания

Vue2.0源码-侦听属性.png

резюме

На данный момент принцип свойства прослушивания Vue завершен. По сравнению с предыдущим принципом эта статья проще. Фактически, вычисляемое свойство и свойство прослушивания реализованы с помощью Watcher. Если вы не уверены в реализации всего Watcher, вы можете прочитать в этой статье.Рукописный исходный код Vue2.0 (четыре) — принцип обновления рендерингаВ следующей статье принцип вычисления свойств, которые будут сравниваться со свойствами прослушивания.Вы можете посмотреть на карту разума и написать код ядра самостоятельно.Если вы столкнулись с чем-то, что вы не понимаете или у вас есть споры, пожалуйста, оставьте комментарий .

Наконец, если вы найдете эту статью полезной, помнитеКак СанлианБольшое спасибо!

Ссылка на сериал (будет обновлена ​​позже)

Группа передовых рыболовных технологий Brother Shark

Приветствую всех на технических биржахСвязь