一、Vue生命周期

浅级别部分

一个Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。

首先四个阶段很好记:

  • 创建 create
  • 挂载 mount
  • 更新 update
  • 销毁 destroy

每个阶段都有xx前(before+)、xx后(+ed)两部分,一共八个生命周期。

beforeCreate 在数据观测和初始化事件还未开始

created 完成一些属性和方法的运算,初始化事件

beforeMount 编译模板,把data里面的数据和模板生成html。但还没有挂载到页面上

mounted 渲染到html到页面,会进行ajax交互(dom渲染)

beforeUpdate 发生在虚拟DOM重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。

updated 组件DOM已经更新,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。

beforeDestroy 此时实例仍然完全可用

destroyed 所有的事件监听器、子实例会被销毁移除。该钩子在服务器端渲染期间不被调用。

1、生命周期中有多个事件钩子,帮助我们控制Vue实例的过程时更容易形成好的逻辑。

2、第一次加载页面触发(前四个) beforeCreate, created, beforeMount, mounted

待添加...

深级别部分 ###(水平不够先越过这里 这里只做记录)

关于create(初始化)
在初始化时,会调用以下代码,生命周期就是通过 callHook 调用的

Vue.prototype._init = function(options) {  initLifecycle(vm)  initEvents(vm)  initRender(vm)  callHook(vm, 'beforeCreate') // 拿不到 props data  initInjections(vm)  initState(vm)  initProvide(vm)  callHook(vm, 'created')}

beforeCreate 调用的时候,是获取不到 props 或者 data 中的数据的,因为这些数据的初始化都在 initState 中。

关于mount(挂载函数)

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

beforeMount 就是在挂载前执行的,然后开始创建 VDOM 并替换成真实 DOM,最后执行 mounted 钩子。这里会有个判断逻辑,如果是外部 new Vue({}) 的话,不会存在 $vnode ,所以直接执行 mounted 钩子了。如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子。

关于update

function flushSchedulerQueue() {  // ...  for (index = 0; index < queue.length; index++) {    watcher = queue[index]    if (watcher.before) {      watcher.before() // 调用 beforeUpdate    }    id = watcher.id    has[id] = null    watcher.run()    // in dev build, 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')    }  }}

上图还有两个生命周期没有说,分别为 activateddeactivated ,这两个钩子函数是 keep-alive 组件独有的。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。

关于destroy

Vue.prototype.$destroy = function() {  // ...  callHook(vm, 'beforeDestroy')  vm._isBeingDestroyed = true  // remove self from parent  const parent = vm.$parent  if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {    remove(parent.$children, vm)  }  // teardown watchers  if (vm._watcher) {    vm._watcher.teardown()  }  let i = vm._watchers.length  while (i--) {    vm._watchers[i].teardown()  }  // remove reference from data ob  // frozen object may not have observer.  if (vm._data.__ob__) {    vm._data.__ob__.vmCount--  }  // call the last hook...  vm._isDestroyed = true  // invoke destroy hooks on current rendered tree  vm.__patch__(vm._vnode, null)  // fire destroyed hook  callHook(vm, 'destroyed')  // turn off all instance listeners.  vm.$off()  // remove __vue__ reference  if (vm.$el) {    vm.$el.__vue__ = null  }  // release circular reference (##6759)  if (vm.$vnode) {    vm.$vnode.parent = null  }}

在执行销毁操作前会调用 beforeDestroy 钩子函数,然后进行一系列的销毁操作,如果有子组件的话,也会递归销毁子组件,所有子组件都销毁完毕后才会执行根组件的 destroyed 钩子函数。

二、Vue的双向绑定原理

数据劫持+发布-订阅者模式

广义双向绑定的描述就是 数据变化更新视图,视图变化更新数据
view->data:通过简单监听事件即可
data->view:通过Object.defineProperty() 来给属性设置 set(设置属性值时触发)和get(读取属性值时触发)函数,当数据改变了就会来触发这个函数,所以我们只要将一些需要更新的方法放在这里面就可以实现data更新view了

Object.defineProperty()可以劫持各个属性的setter,getter,当数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

vue的数据双向绑定 整合ObserverCompileWatcher三者(不能完全理解的部分)

  • Observer来监听自己的model的数据变化
  • Compile来解析编译模板指令
  • watcher搭起observer和Compile之间的通信桥梁

example,使用js实现一个简单的双向绑定(理解)

<body>    <div id="app">    <input type="text" id="txt">    <p id="show"></p></div></body><script type="text/javascript">    var obj = {}    Object.defineProperty(obj, 'txt', {        get: function () {            return obj        },        set: function (newValue) {            document.getElementById('txt').value = newValue            document.getElementById('show').innerHTML = newValue        }    })    document.addEventListener('keyup', function (e) {        obj.txt = e.target.value    })</script>