vue源码解析之methods原理

1,509 阅读2分钟

方法执行时机

在beforeCreate之后,created之前,会initState

 	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')

initState中会调用initMethods

  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)
    }
  }

源码分析

initMethods

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
          )
        }
        if (props && hasOwn(props, key)) {
          warn(
            `Method "${key}" has already been defined as a prop.`,
            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[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
    }
}
  1. 首先会对methods中的方法名进行一系列的验证,如果不符合规范,则给出提示;
  2. 除去类型检测,就只需看bind(methods[key], vm),该方法就是将方法绑定到实例上

bind

vue对bind做了兼容性处理

export const bind = Function.prototype.bind
  ? nativeBind
  : polyfillBind

如果浏览器兼容bind则直接调用bind

function nativeBind (fn: Function, ctx: Object): Function {
  return fn.bind(ctx)
}

否则,自定义一个bind方法

/* istanbul ignore next */
function polyfillBind (fn: Function, ctx: Object): Function {
  function boundFn (a) {
    const l = arguments.length
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)
        : fn.call(ctx, a)
      : fn.call(ctx)
  }

  boundFn._length = fn.length
  return boundFn
}

vue系列课程

最近会陆续的对vue进行源码分析,一系列课程如下:

state系列

  1. props原理
  2. methods原理
  3. data原理
  4. computed原理
  5. watch原理

lifecycle系列

  1. 生命周期原理

event系列

  1. event原理

render系列

  1. render原理

inject/provide系列

  1. inject/provide原理

plugins系列

  1. vue-router原理
  2. Vue Router 那些事
  3. Vuex你应该知道的事
  4. vue源码解析之vuex原理
  5. Vue自定义插件

组件系列

  1. keep-alive原理
  2. Vue 单文件组件
  3. Vue组件间通信
  4. vue虚拟列表

指令系列

  1. Vue自定义指令
  2. vue源码解析之Directives原理

算法系列

  1. diff原理
  2. Vue编译器源码分析

异步任务

  1. vue源码解析之NextTick原理

其他

  1. vue单元测试
  2. Vue轮播组件
  3. 你不知道的vue那些事
  4. vue技巧大解密
  5. 面试-高级前端之VUE数据响应式实现