别催了~ 正在从服务器偷取页面 . . .

vue2生命周期


生命周期

生命周期4个阶段

Vue2官方文档上的图

分成4个阶段:初始化阶段、模板编译阶段、挂载阶段、卸载阶段
初始化阶段:
从new Vue()到created之间的阶段,此阶段主要是在Vue.js实例上初始化一些属性、事件以及响应数据,例如props、methods、data、computed、watch、provide和inject等等。

模板编译阶段:
在created钩子函数到beforeMount钩子函数之间的阶段是模板编译阶段,此阶段主要目的是将模板编译为渲染函数,只存在于完整版中。(原因在于vm.$mount运行时版的实现已经默认存在渲染函数)

挂载阶段:
beforeMount钩子函数到mounted钩子函数之间是挂载阶段,此阶段Vue.js会将其实例挂载到DOM元素上,即将模板渲染到指定的DOM元素中。而且在挂载的过程中,开启Watcher来持续追踪依赖的变化。(在vm.$mount源码分析那里有提到)

卸载阶段:
调用vm.$destroy方法后,Vue.js的生命周期进入卸载阶段

初始化阶段

new Vue()

new Vue( )被调用时,首先会进行一些初始化操作,然后进入模板编译阶段,最后进入挂载阶段

//目录 src/core/instance/index.js
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

_init方法

//目录 src/core/instance/init.js
export function initMixin (Vue: Class<Component>) {
  //挂载到Vue.prototype上
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    
    // merge options  
    /*
    将当前用户传递的options选项与当前构造函数的options选项及其父级实例构造函数的options属性,
    合成一个新的options并赋值给$options属性
    */
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        /*
        resolveConstructorOptions
        获取当前实例中构造函数的options选项及其所有父级的构造函数的options,有父级时因为当前实例
        可能是一个子组件
        */
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

initLifecycle

初始化实例属性需要初始化内部使用属性(vm._watcher等,以 _开头的),也初始化供外部使用的属性(vm.$parent等,以$开头的)

export function initLifecycle (vm: Component) {
  const options = vm.$options

  // locate first non-abstract parent 
  /*
  vm.$parent 需要找到第一个非抽象类型的父级,如果当前组件不是抽象组件而且存在父级,就通过while
  自底向上循环,直到找到第一个非抽象类的父级
  */
  let parent = options.parent
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }

  vm.$parent = parent
  //vm.$root 是自顶向下将根组件的$root依次传递给每一个子组件
  vm.$root = parent ? parent.$root : vm
  //vm.$children是子组件主动添加到父组件中的,如第14行
  vm.$children = []
  vm.$refs = {}

  vm._watcher = null
  vm._inactive = null
  vm._directInactive = false
  vm._isMounted = false
  vm._isDestroyed = false
  vm._isBeingDestroyed = false
}

initInjections/initProvide

用法:inject和provide选项需要一起使用,它们允许祖先组件向其所有子孙后代注入依赖,并在其上下游关系成立的时间里始终生效。
示例:

//provide选项应该是一个对象或返回一个对象的函数。该对象包含可注入器子孙的属性
//inject选项应该是一个字符串数组或对象,对象的key是本地绑定名,value是一个key或对象,用来在可用的注入内容中搜索

//示例一
var Provider={
  provide:{
    foo:'bar'
  }
}

var Child={
  inject:['foo'],
  created(){
  console.log(this.foo)//"bar"
  }
}

//示例二 可以使用ES2015 Symbol作为key,但是这只在原生支持Symbol和Reflect.ownKeys的环境下可工作
const s=Symbol()

const Provider={
   provide(){
     return {
     [s]:'foo'
     }
   }
}

const Child={
   inject:{s}
}

//示例三 可以在data/props中访问注入的值
const Child={
   inject:['foo'],
   props:{
     bar:this.foo
   }
 }
}

//default 默认值
const Child={
     inject:{
      foo:{ default:'foo' }
     }
}
/*
  从_init代码可以看出initInjections是data/props之前初始化,initProvide是在data/props之后初始
  化,这样做的目的是让用户可以在data/props中使用inject所注入的内容
*/

原理:
provide

export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    //内容注入到_provided属性中,如果是函数则执行函数,否则直接赋值
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

inject

export function initInjections (vm: Component) {
  //resolveInject是通过用户配置的inject,自底向上搜索可用的的注入内容,并将搜索结果返回
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    //通知defineReactive函数不要将内容转换成响应式
    toggleObserving(false)
    Object.keys(result).forEach(key => {
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        defineReactive(vm, key, result[key], () => {
          warn(
            `Avoid mutating an injected value directly since the changes will be ` +
            `overwritten whenever the provided component re-renders. ` +
            `injection being mutated: "${key}"`,
            vm
          )
        })
      } else {
        defineReactive(vm, key, result[key])
      }
    })
    toggleObserving(true)
  }
}
export function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    /*
      如果浏览器原生支持Symbol,使用Reflect.ownKeys读出inject中所有key,如果不支持使用Object.keys
      获取key。
      原因是Reflect.ownKeys可以读取Symbol类型的属性,而且也可以读出包括不可枚举的属性,要用filter
      过滤
      Object.keys不可读取Symbol类型的属性
    */
    const result = Object.create(null)
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)

    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // #6574 in case the inject object is observed...
      if (key === '__ob__') continue
      /*
        from属性时provide原属性
        Vue.js在实例化的第一步是规格化用户传入的数据(就是在添加$options属性时),即使inject传递的
        内容是数组也会被规格化成对象并存放在from属性中
        {
          inject:[foo]
        }
        规格化后是
        {
         inject:{
            foo:{
              from:'foo'
            }
         }
        }
      */
      const provideKey = inject[key].from
      let source = vm
      while (source) {
        //whlie循环自底向上搜索
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      //如果搜索不到,有默认值则使用默认值
      if (!source) {
        if ('default' in inject[key]) {
          const provideDefault = inject[key].default
          //默认值支持函数,若为函数则执行它
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== 'production') {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result
  }
}

initState

initState是初始化一些状态,包括props、methods、data、computed、watch

export function initState (vm: Component) {
  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 {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}
/*
 初始化各种状态的顺序是有讲究的
 先初始化props,后初始化data,就可以在data中使用props中的数据
 先初始化props、data,所以watch可以观察到里面的数据
*/

initProps
第一步,规格化props,将props规格化为对象格式。

/*
 在_init方法中,调用了mergeOptions,在mergeOptions函数中,调用了normalizeProps(child, vm)、normalizeInject(child, vm)
  、normalizeDirectives(child)规格化数据
*/
export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  ...
  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)
  ...
}
/**
 * Ensure all props option syntax are normalized into the
 * Object-based format.
 */
//对props进行规格化处理,规格化之后的props为对象的格式
function normalizeProps (options: Object, vm: ?Component) {
  const props = options.props
  //没有props退出
  if (!props) return
  const res = {}
  let i, val, name
  if (Array.isArray(props)) {
    i = props.length
    while (i--) {
      val = props[i]
      if (typeof val === 'string') {
        //camelize 将val驼峰化
        name = camelize(val)
        res[name] = { type: null }
      } else if (process.env.NODE_ENV !== 'production') {
        warn('props must be strings when using array syntax.')
      }
    }
  } //不是数组类型,调用isPlainObject函数检查它是否为对象类型
  else if (isPlainObject(props)) {
    //for...in...遍历props(for...in遍历的是键)
    for (const key in props) {
      val = props[key]
      name = camelize(key)
      res[name] = isPlainObject(val)
        ? val
        : { type: val }
    }
  } else if (process.env.NODE_ENV !== 'production') {
    warn(
      `Invalid value for option "props": expected an Array or an Object, ` +
      `but got ${toRawType(props)}.`,
      vm
    )
  }
  options.props = res
}

第二步,初始化props
规格化之后的props从其父组件传入的props数据中或从使用new创建实例时传入的propsData参数中,筛选出需要的数据保存在vm._props中,然后在vm上设置一个代理,实现通过vm.x访问vm._props.x


function initProps (vm: Component, propsOptions: Object) {
  //propsData保存父组件传入或用户通过propsData传入的真实props数据
  const propsData = vm.$options.propsData || {}
  //props是指向vm._props的指针
  const props = vm._props = {}
  /*
  keys是指向vm.$options._propKeys的指针,其作用是缓存props对象中的key,
  将来更新props时,只需要遍历
  */
  const keys = vm.$options._propKeys = []
  //判断当前组件是否是根组件
  const isRoot = !vm.$parent
  // root instance props should be converted
  if (!isRoot) {
    //不是根组件不需要将props数据转换为响应式
    toggleObserving(false)
  }
  for (const key in propsOptions) {
    keys.push(key)
    const value = validateProp(key, propsOptions, propsData, vm)
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      const hyphenatedKey = hyphenate(key)
      if (isReservedAttribute(hyphenatedKey) ||
          config.isReservedAttr(hyphenatedKey)) {
        warn(
          `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
          vm
        )
      }
      defineReactive(props, key, value, () => {
        if (!isRoot && !isUpdatingChildComponent) {
          warn(
            `Avoid mutating a prop directly since the value will be ` +
            `overwritten whenever the parent component re-renders. ` +
            `Instead, use a data or computed property based on the prop's ` +
            `value. Prop being mutated: "${key}"`,
            vm
          )
        }
      })
    } else {
      //将数据设置到vm._props中
      defineReactive(props, key, value)
    }
    // static props are already proxied on the component's prototype
    // during Vue.extend(). We only need to proxy props defined at
    // instantiation here.
    if (!(key in vm)) {
      proxy(vm, `_props`, key)
    }
  }
  toggleObserving(true)
}

validateProp是获取props内容的

export function validateProp (
  key: string,
  propOptions: Object,//子级组件用户设置的props选项
  propsData: Object,//父组件或用户提供的props数据
  vm?: Component
): any {
  const prop = propOptions[key]
  //absent表示当前的key在用户提供的props选项中是否存在
  const absent = !hasOwn(propsData, key)
  let value = propsData[key]
  // 处理prop是否为布尔值情况
  const booleanIndex = getTypeIndex(Boolean, prop.type)
  if (booleanIndex > -1) {
   //key在用户提供的props中不存在,而且也没有设默认值,value为false
    if (absent && !hasOwn(prop, 'default')) {
      value = false
    } 
    //key存在但是为空字符串或者value和key相等,hyphenate将key驼峰转换
    else if (value === '' || value === hyphenate(key)) {
      // only cast empty string / same name to boolean if
      // boolean has higher priority
      const stringIndex = getTypeIndex(String, prop.type)
      if (stringIndex < 0 || booleanIndex < stringIndex) {
        value = true
      }
    }
  }
  //value不存在,如果有默认值则使用默认值,并转换为响应式数据
  if (value === undefined) {
    //获取默认值
    value = getPropDefaultValue(vm, prop, key)
    // since the default value is a fresh copy,
    // make sure to observe it.
    const prevShouldObserve = shouldObserve
    toggleObserving(true)
    observe(value)
    toggleObserving(prevShouldObserve)
  }
  if (
    process.env.NODE_ENV !== 'production' &&
    // skip validation for weex recycle-list child component props
    !(__WEEX__ && isObject(value) && ('@binding' in value))
  ) {
    //判断prop是否有效,prop验证失败时会产生警告
    assertProp(prop, key, value, vm, absent)
  }
  return value
}

initMethods
initMethods只需循环选项中的methods对象,并将每个属性依次挂载到vm上即可

function initMethods (vm: Component, methods: Object) {
  const props = vm.$options.props
  for (const key in methods) {
    if (process.env.NODE_ENV !== 'production') {
      //方法不合法
      if (typeof methods[key] !== 'function') {
        warn(
          `Method "${key}" has type "${typeof methods[key]}" in the component definition. ` +
          `Did you reference the function correctly?`,
          vm
        )
      }
      //方法已经在props中声明过了
      if (props && hasOwn(props, key)) {
        warn(
          `Method "${key}" has already been defined as a prop.`,
          vm
        )
      }
      //方法已经在vm中
      if ((key in vm) && isReserved(key)) {
        warn(
          `Method "${key}" conflicts with an existing Vue instance method. ` +
          `Avoid defining component methods that start with _ or $.`
        )
      }
    }
    //将方法挂载到vm中
    vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
  }
}

initData
data中的数据保存在vm._data中,然后通过设置代理,可以通过vm.x访问到vm._data.x

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
      //methods有名称为key的方法了,但是data还是会代理到实例中
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    //props上已经存在与key相同的属性了,不会将data代理到实例中
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
      proxy(vm, `_data`, key)
    }
  }
  //将data转换为响应式
  observe(data, true /* asRootData */)
}

代理proxy 其实就是通过定义对应 key 的 getter/setter 来使得它获取到实际上是其他的值

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

initComputed
computed比较常见的一个特性就是它会缓存结果,只有计算依赖的数据或计算结果发生变化才会重新计算。
computed的实现其实就是在 vm 上的一个特殊的 getter,它结合了 Watcher 来实现缓存和依赖收集的功能。
实现的过程

  1. 使用Watcher读取计算属性
  2. 读取计算属性函数中的数据,使用Watcher观察数据的变化(如果计算属性是模板读取,那么使用组件的Watcher观察,如果是用户自定义的watch,那么使用用户自定义地Watcher观察)
  3. 当数据发生变化时,通知计算属性的Watcher和组件的Watcher(重新渲染模板)
  4. 计算属性的Watcher把dirty设置为true
  5. 模板重新读取计算属性的值,因为dirty为true,所以会重新计算一次值
    ```javascript
    const computedWatcherOptions = { lazy: true }

function initComputed (vm: Component, computed: Object) {
//watchers是用来保存所有计算属性的watcher实例
const watchers = vm._computedWatchers = Object.create(null)
//isSSR用于判断当前运行环境是否是SSR(服务端渲染)
const isSSR = isServerRendering()

for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === ‘function’ ? userDef : userDef.get
if (process.env.NODE_ENV !== ‘production’ && getter == null) {
warn(
Getter is missing for computed property "${key}".,
vm
)
}
//在非SSR环境中,为计算属性创建内部观察器
if (!isSSR) {
// create internal watcher for the computed property.
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}

/*
 这里如果vm上已经有一个名为key的属性,那么这个名为key的属性就可能是
 data、props、methods,但是只有data、props会有警告提示,methods并没
 有提示
*/
if (!(key in vm)) {
  defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {
  if (key in vm.$data) {
    warn(`The computed property "${key}" is already defined in data.`, vm)
  } else if (vm.$options.props && key in vm.$options.props) {
    warn(`The computed property "${key}" is already defined as a prop.`, vm)
  } else if (vm.$options.methods && key in vm.$options.methods) {
    warn(`The computed property "${key}" is already defined as a method.`, vm)
  }
}

}
}

> **注意,Object.create(null)创建出来的对象是没有原型的,它不存在__proto__属性**



defineComputed就是将key设置到vm上,主要就是要分服务端环境来处理key的getter
```javascript
const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

export function defineComputed (
  target: any,
  key: string,
  userDef: Object | Function
) {
  //shouldCache用来判断computed是否应该有缓存,非服务端才要缓存
  const shouldCache = !isServerRendering()
  //userDef是函数,则为getter函数
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key) //计算属性getter
      : createGetterInvoker(userDef)//普通getter
    sharedPropertyDefinition.set = noop
  } 
  //否则为对象,将对象的get方法作为getter方法
  else {
    sharedPropertyDefinition.get = userDef.get
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : createGetterInvoker(userDef.get)
      : noop
    sharedPropertyDefinition.set = userDef.set || noop
  }
  if (process.env.NODE_ENV !== 'production' &&
      sharedPropertyDefinition.set === noop) {
    sharedPropertyDefinition.set = function () {
      warn(
        `Computed property "${key}" was assigned to but it has no setter.`,
        this
      )
    }
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

createComputedGetter接受一个key为参数,并返回一个函数为key的getter函数,先获取key对应的watcher,如果watcher存在而且计算属性的返回值也发生了变化,则重新计算得出最新结果

function createComputedGetter (key) {
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      //dirty属性来标志计算属性是否发生了变化
      if (watcher.dirty) {
        watcher.evaluate()
      }
      if (Dep.target) {
        //将读取计算属性的Watcher添加到计算属性所依赖的所有状态的依赖列表
        //其实就是让读取计算属性的那个Watcher持续观察计算属性所依赖的状态的变化
        watcher.depend()
      }
      return watcher.value
    }
  }
}

evaluate()就是执行一下this.get()方法重新计算一下值,然后将dirty设置为false;depend()先遍历this.deps属性(保存了计算属性用到的所有状态的dep实例,每个dep实例保存了它的所有依赖),依次执行dep实例的depend方法,将组件的Watcher依次加入这些dep实例的依赖列表中,实现了组件watcher观察计算属性用到的所有状态的变化。

export default class Watcher {
  constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    ...
    // 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.dirty = this.lazy // for lazy watchers
    this.value = this.lazy
      ? undefined
      : this.get()
  }
  ...
  
  /**
   * Evaluate the value of the watcher.
   * This only gets called for lazy watchers.
   */
  evaluate () {
    this.value = this.get()
    this.dirty = false
  }

  /**
   * Depend on all deps collected by this watcher.
   */
  depend () {
    let i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
  }

}

initWatch
示例:

//watch选项的格式 { [key:string]:string|Function|Object|Array }
var vm=new Vue({
   data:{
     a:1,
     b:2,
     c:3,
     d:4
   },
  watch:{
    a:function(newVal,oldVal){
      
    },
    b:{
      handler:function(newVal,oldVal){}
      deep:true,//深度watcher
      immediate:true//侦听开始后会立即调用
    }
    c:'myMethod'//一个函数名,
    d:[
        function handle1(newVal,oldVal){},
        function handle2(newVal,oldVal){}
    ]
  }
})

原理:
initState初始化时,先判断用户是否设置watch选项并且watch选项不等于浏览器原生的watch(因为Firefox浏览器中Object.prototype上有一个watch方法)

if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }

initWatch的实现其实并不难,就是遍历watch选项,然后分成数组和其他情况处理,如果是数组则遍历数组中的每一项依次处理

function initWatch (vm: Component, watch: Object) {
  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 {
      createWatcher(vm, key, handler)
    }
  }
}

createWatcher就是将handler分成字符串、对象、函数类型来处理

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}

callHook内部原理

作用:callHook的作用时触发用户设置的生命周期钩子,而用户设置的生命周期钩子会在执行new Vue()时,通过参数传递给Vue.js(即可以在Vue.js构造函数中,通过options参数获取到用户设置的生命周期钩子,例如vm.$options.created)
注意:vm.$options.created获取到的是一个数组[fn],数组中包括了钩子函数。
为什么是数组?
因为Vue.mixin方法会将选项写入Vue.option中,影响之后创建的所有Vue.js实例,而Vue.js初始化时会将用户传入的option和构造函数的options合并成一个选项赋值给vm.$options。如果Vue.mixin和用户实例化Vue.js时,设置了同一个生命周期钩子,则触发生命周期时,需要同时执行这两个函数,而转换成数组后,即可在同一个生命周期钩子列表里保存多个生命周期钩子。
源码:

export function callHook (vm: Component, hook: string) {
  // #7573 disable dep collection when invoking lifecycle hooks
  pushTarget()
  const handlers = vm.$options[hook]
  const info = `${hook} hook`
  //取出列表,依次执行
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      invokeWithErrorHandling(handlers[i], vm, null, vm, info)
    }
  }
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook)
  }
  popTarget()
}

指令

过滤器

用法:用来格式化文本。
示例:

<!--双括号-->
{{message|capitalize}}

<!--在v-bind中-->
<div v-bind:id="raw|formatId"></div>

//在组件定义一个本地过滤器,过滤器函数总是将表达式的值作为第一个参数
filters:{
  capitalize:function(val){
     if(!val)return ''
     value=value.toString()
    return value.charAt(0).toUpperCase()+value.slice(1)
  }

}

//也可以在Vue.js示例之前全局定义过滤器
Vue.filter('capitalize',function(value){
   if(!val)return ''
     value=value.toString()
    return value.charAt(0).toUpperCase()+value.slice(1)
})

//过滤器也可以串联
{{message|filterA|filterB}}
//过滤器函数也可以接收参数
{{message|filterA('arg1','arg2')}}

文章作者: John Doe
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 John Doe !
评论
  目录