[VUE] — Principio del oyente del reloj

Directorio de artículos de la serie

[VUE]—Principio del algoritmo diff


prefacio

En el próximo período de tiempo, estudiaré los principios relacionados con vue en profundidad, vamos.

Primero, el tiempo de funcionamiento del reloj.

Después beforeCreate, createdantes, será initState, en initStateserá llamado initWatch.

function initState(vm) {
    
    
    vm._watchers=[];
    const opts = vm.$options;
    if(opts.props) initProps(vm, opts.props);
    if(opts.methods) initMethods(vm, opts.methods);
    if (opts.data) {
    
    
        initData(vm);
    } else {
    
    
        observer(vm._data={
    
    }, true);
    }
    if (opts.computed) initMethods(vm, opts.computed);
    if (opts.watch && opts.watch !== nativeWatch) {
    
    
			// 初始化watch
			initWatch(vm, opts.watch);
		}
}

2. Análisis del código fuente

2.1 reloj de inicio

function initWatch (vm: Component, watch: Object) {
    
    
  // 1.遍历watch
  for (const key in watch) {
    
    
    const handler = watch[key]
    if (Array.isArray(handler)) {
    
    
      for (let i = 0; i < handler.length; i++) {
    
    
        createWatcher(vm, key, handler[i])
      }
    } else {
    
    
    // 2.创建watcher
      createWatcher(vm, key, handler)
    }
  }
}

Este código simplemente itera y procesa cada uno watchusando createWater.

2.2 crear Observador

createWatcherLa función recibe cuatro parámetros. Dentro de la función, en realidad elimina la función de devolución de llamada cby los parámetros optionsdel objeto pasado por el usuario, y luego llama al $watchmétodo de una manera convencional y pasa los parámetros eliminados.

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
    
    
  //监听属性的值是一个对象,包含handler,deep,immediate
  if (isPlainObject(handler)) {
    
    
    options = handler
    handler = handler.handler
  }
  //如果回调函数是一个字符串,从VM中获取
  if (typeof handler === 'string') {
    
    
    handler = vm[handler]
  }
  //expOrFn是key, options是watch的全部选项
  return vm.$watch(expOrFn, handler, options)
}
  1. Obtener la devolución de llamada del oyente
  2. transferirvm.$watch

isPlainObjectEl método consiste en determinar handlersi se trata de un tipo de objeto Object;

//获取值得原始类型字符串
const _toString = Object.prototype.toString

export function isPlainObject (obj: any): boolean {
    
    
  return _toString.call(obj) === '[object Object]'
}

2.3 $reloj

Primero, juzgará si la función de devolución de llamada entrante es un objeto. Si lo es, significa que el usuario ha pasado la función de devolución de llamada del segundo parámetro y el tercer parámetro optionsjuntos, y luego createWatcherse llama a la función en este momento:

Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    
    
    const vm: Component = this
    if (isPlainObject(cb)) {
    
    
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {
    
    }
    options.user = true
    // 每一个watch都配发一个watcher
    const watcher = new Watcher(vm, expOrFn, cb, options)
    // 如果immediate为true,立即执行监听回调
    if (options.immediate) {
    
    
      try {
    
    
        cb.call(vm, watcher.value)
      } catch (error) {
    
    
        handleError(error, vm, `callback for immediate watcher "${
      
      watcher.expression}"`)
      }
    }
    return function unwatchFn () {
    
    
      watcher.teardown()
    }
  }

vm.$watchhizo dos cosas principales

  1. watchasignación para cada uno watcher;
  2. immediateDeterminar si ejecutar la devolución de llamada del oyente inmediatamente de acuerdo con la configuración

2.4 observador

export default class Watcher {
    
    
  vm: Component;
  expression: string;
  cb: Function;
  id: number;
  deep: boolean;
  user: boolean;
  lazy: boolean;
  sync: boolean;
  dirty: boolean;
  active: boolean;
  deps: Array<Dep>;
  newDeps: Array<Dep>;
  depIds: SimpleSet;
  newDepIds: SimpleSet;
  before: ?Function;
  getter: Function;
  value: any;

  constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    
    
    this.vm = vm
    if (isRenderWatcher) {
    
    
      vm._watcher = this
    }
    vm._watchers.push(this)
    // options
    if (options) {
    
    
      this.deep = !!options.deep
      this.user = !!options.user
      this.lazy = !!options.lazy
      this.sync = !!options.sync
      this.before = options.before
    } else {
    
    
      this.deep = this.user = this.lazy = this.sync = false
    }
    this.cb = cb
    this.id = ++uid // uid for batching
    this.active = true
    this.dirty = this.lazy // for lazy watchers
    this.deps = []
    this.newDeps = []
    this.depIds = new Set()
    this.newDepIds = new Set()
    this.expression = process.env.NODE_ENV !== 'production'
      ? expOrFn.toString()
      : ''
    // parse expression for getter
    if (typeof expOrFn === 'function') {
    
    
      this.getter = expOrFn
    } else {
    
    
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
    
    
        this.getter = noop
        process.env.NODE_ENV !== 'production' && warn(
          `Failed watching path: "${
      
      expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }
    this.value = this.lazy
      ? undefined
      : this.get()
  }
  ...
  //此处只拿出了Watch类的构造方法;如需查看完整代码还请自行查阅
  }

Al crear un nuevo observador, se realizan principalmente las siguientes cosas:

  1. monitor de clave
  2. devolución de llamada del oyente ( watchin cb)
  3. monitor configuradooptions

3. Monitoreo profundo

options¿Cómo implementar la observación profunda cuando el atributodeep en el parámetro de opción es ?true

La llamada observación profunda significa que seremos notificados cuando cambie el objetoobj , y también seremos notificados cuando cambie el obj.aatributo .

No es difícil lograr esta función, sabemos que si queremos que nos notifiquen cuando cambien los datos, solo necesitamos convertirnos en una dependencia de estos datos, porque cuando los datos cambien, todas sus dependencias serán notificadas, entonces, ¿cómo convertirse en una dependencia de datos? , muy simple, solo lea los datos. Es decir, solo necesitamos leer recursivamente todos los valores en el objeto al crear una watcherinstancia , luego la instancia se agregará a la lista de dependencia de todos los valores en el objeto, y luego cuando cualquier valor en el los cambios de objetos serán notificadosobjwatcher .

Cuando watcherse crea una instancia , Watcherse ejecuta un método en la clase getpara leer los datos observados.

export default class Watcher {
    
    
    constructor (/* ... */) {
    
    
        // ...
        this.value = this.get()
    }
    get () {
    
    
        // ...
        // "touch" every property so they are all tracked as
        // dependencies for deep watching
        if (this.deep) {
    
    
            traverse(value)
        }
        return value
    }
}

En un getmétodo, si se pasa deep===true, la función se llamará traverse.

3.1 atravesar

const seenObjects = new Set()
 
export function traverse (val: any) {
    
    
    _traverse(val, seenObjects)
    seenObjects.clear()
}
 
function _traverse (val: any, seen: SimpleSet) {
    
    
    let i, keys
    const isA = Array.isArray(val)
    if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {
    
    
        return
    }
    if (val.__ob__) {
    
    
        const depId = val.__ob__.dep.id
        if (seen.has(depId)) {
    
    
            return
        }
        seen.add(depId)
    }
   
    if (isA) {
    
    
     // 如果是数组,循环遍历
        i = val.length
        while (i--) _traverse(val[i], seen)
    } else {
    
    
    // 对象
        keys = Object.keys(val)
        i = keys.length
        // 递归遍历每一个属性
        while (i--) _traverse(val[keys[i]], seen)
    }
}

Se puede ver que esta función es en realidad un proceso de recorrido recursivo , que recorre recursivamente y lee los valores internos de los datos observados.

Primero juzgue el valtipo de entrada, si no es Array或objecto ha sido congelado, luego regrese directamente y salga del programa.

Luego obténgalo val的dep.idy guárdelo en la colección creada seen, porque la colección tiene un efecto de deduplicación natural en comparación con los datos, para garantizar que los datos almacenados dep.idno se dupliquen y no causen dependencias de colección duplicadas.

A continuación, se juzga que si es una matriz, haga un bucle en la matriz y llame recursivamente a cada elemento de la matriz _traverse; si es un objeto, elimine todos los objetos key, luego realice la operación de lectura y luego recurra al valor interno.

De esta forma, después de leer recursivamente todos los valores en los datos observados, la watcherinstancia se agregará a la lista de dependencias de todos los valores en el objeto, y luego se le notificará cuando cambie algún valor en el objeto.

Para resumir, de hecho, el método poligonal se entiende bien. Lo principal que se debe hacer es:

  1. Al leer, esta propiedad se puede recopilar watch-watcher;
  2. Objetos de nivel profundo, donde cada propiedad también responde, cada propiedad tiene su propio recopilador de dependencias, al leer continuamente cada propiedad, cada propiedad se puede recopilar watch-watcher; de esta manera, no importa qué tan profundo cambie la propiedad en el objeto, se notificará watch-watcher, así que esto completa el monitoreo profundo;
    actualice

Supongo que te gusta

Origin blog.csdn.net/weixin_44761091/article/details/124140188
Recomendado
Clasificación