Skip to content
On this page

Table of Contents generated with DocToc

Análises do princípio NextTick

nextTick permiti-nos adiar a callback ser executada depois da próxima atualizada do ciclo do DOM, para obter a atualização.

Antes da versão 2.4, Vue usou micro tarefas, mas prioridade das micro tarefas é bem alta, e em alguns casos, isso deve ser mais rápido que o evento de bubbling, mas se você usar macro tarefas, pode haver alguns problemas de performance na renderização. Então na nova versão, micro tarefas são usadas por padrão, mas macro tarefas serão usadas em casos especiais, como v-on.

Para implementar macro tarefas, você primeiro deve determinar se o setImmediate pode ser usado, se não, abaixe para MessageChannel. Se não novamente, use setTimeout.

js
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  macroTimerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else if (
  typeof MessageChannel !== 'undefined' &&
  (isNative(MessageChannel) ||
    // PhantomJS
    MessageChannel.toString() === '[object MessageChannelConstructor]')
) {
  const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = () => {
    port.postMessage(1)
  }
} else {
  /* istanbul ignore next */
  macroTimerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

nextTick também suporta o uso de Promise, do qual ira determinar se a Promise está implementada.

js
export function nextTick(cb?: Function, ctx?: Object) {
  let _resolve
  // Consolide funções de callback dentro do de um array
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    if (useMacroTask) {
      macroTimerFunc()
    } else {
      microTimerFunc()
    }
  }
    // Determina se a Promisse pode ser usada
    // Atribuir _resolve se possivel
    // Desta maneira a função callback pode ser chamada em forma de promise
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

Análise do Ciclo de Vida

A função do ciclo de vida é a função gancho que o componente vai disparar quando inicializar ou atualizar os dados.

O seguinte código irá ser chamado na inicialização, e o ciclo de vida vai ser chamado pelo callHook

js
Vue.prototype._init = function(options) {
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate') // não consegue receber dados das props
    initInjections(vm) 
    initState(vm)
    initProvide(vm)
    callHook(vm, 'created')
}

Ele pode ser encontrado no código acima quando beforeCreate é chamado, o dado no props ou data não pode ser obtido porque a inicialização desse dado está no initState.

No próximo, a função montadora vai ser chamada

js
export function mountComponent {
    callHook(vm, 'beforeMount')
    // ...
    if (vm.$vnode == null) {
        vm._isMounted = true
        callHook(vm, 'mounted')
    }
}

beforeMount vai ser executado antes de montar uma instância, então comece a criar o VDOM e substituir ele com o DOM real, e finalmente chame o gancho mounted. E há um julgamente lógico aqui, que se ele for um new Vue({}) externo, $vnode não existe, então o gancho mounted será executado diretamente. Se existe um componente filho, ele vai ser montado recursivamente, apenas quando todos os componentes filhos forem montados, o gancho de montar o componente raíz vai ser executado.

Próximo, isso vem para a função gancho que vai ser chamada quando os dados forem atualizados.

js
function flushSchedulerQueue() {
  // ...
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before() // chama `beforeUpdate`
    }
    id = watcher.id
    has[id] = null
    watcher.run()
    // no dev build, verifca e para check and stop circular updates.
    if (process.env.NODE_ENV !== 'production' && has[id] != null) {
      circular[id] = (circular[id] || 0) + 1
      if (circular[id] > MAX_UPDATE_COUNT) {
        warn(
          'You may have an infinite update loop ' +
            (watcher.user
              ? `in watcher with expression "${watcher.expression}"`
              : `in a component render function.`),
          watcher.vm
        )
        break
      }
    }
  }
  callUpdatedHooks(updatedQueue)
}

function callUpdatedHooks(queue) {
  let i = queue.length
  while (i--) {
    const watcher = queue[i]
    const vm = watcher.vm
    if (vm._watcher === watcher && vm._isMounted) {
      callHook(vm, 'updated')
    }
  }
}

Existem duas funções do ciclo de vida que não são mencionada no diagrama acima, activated e deactivated, e apenas o componente kee-alive possui esses dois ciclos. Componente encapsulado com keep-alive não serão destruídos durante o switch, mas sera cacheado em memória e executado a função gancho deactivated, e executar a função actived depois de coincidir o cache e a renderização.

Finalmente, vamos olhar a função gancho usada para destruir o componente.

js
Vue.prototype.$destroy = function() {
  // ...
  callHook(vm, 'beforeDestroy')
  vm._isBeingDestroyed = true
  // remove-se mesmo a partir do pai
  const parent = vm.$parent
  if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
    remove(parent.$children, vm)
  }
  // destroi watchers
  if (vm._watcher) {
    vm._watcher.teardown()
  }
  let i = vm._watchers.length
  while (i--) {
    vm._watchers[i].teardown()
  }
  // remove a referência a partir do dado ob
  // objeto congelados não devem ter um observador.
  if (vm._data.__ob__) {
    vm._data.__ob__.vmCount--
  }
  // chame o último gancho...
  vm._isDestroyed = true
  // invoque ganchos destruídos na árvore atualmente renderizada
  // dispare o gancho destruído
  callHook(vm, 'destroyed')
  // desligo todos os ouvintes da instância.
  vm.$off()
  // remove __vue__ reference
  // remove __vue__ reference
  if (vm.$el) {
    vm.$el.__vue__ = null
  }
  // lance uma referência circular (#6759)
  if (vm.$vnode) {
    vm.$vnode.parent = null
  }
}

A função beforeDestroy será chamada antes da operação de destruir ser desempenhada, e então uma série de operações de destruição são desempenhadas. Se existe um componente filho, eles serão destruidos recursivamente, e apenas quando todos os componente filhos são destruídos, o gancho destroyed do componente raíz será executado.