vue-formulate解析

1,260 阅读2分钟

vue-formulate

  1. 解决了什么?
    快速创建表单,上手成本低只需要了解两个基本表单类型(fomrmulateInput和formulateForm) vue-formulate

  2. 实现原理清晰,插件包的形式生成实例

原理图

vue-formulate原理图

vue所需知识前置

  1. 实例加载顺序
    // 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')

源码地址

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

源码地址

  1. vue插件包如何编写
    vue通过Vue.use把包注册使用
  install (Vue, options) {
    Vue.prototype.$formulate = this
    this.options = this.defaults
    var plugins = this.defaults.plugins
    if (options && Array.isArray(options.plugins) && options.plugins.length) {
      plugins = plugins.concat(options.plugins)
    }
    plugins.forEach(plugin => (typeof plugin === 'function') ? plugin(this) : null)
    this.extend(options || {})
    for (var componentName in this.options.components) {
      Vue.component(componentName, this.options.components[componentName])
    }
  }
  1. provide & inject 父级抛出方法,子级或更低级想使用可以自己inject,
 // formulatform
  provide () {
    return {
      formulateFormSetter: this.setFieldValue,
      formulateFormRegister: this.register,
      getFormValues: this.getFormValues,
      observeErrors: this.addErrorObserver,
      removeErrorObserver: this.removeErrorObserver
    }
  },
  
  // formulateInput
  inject: {
    formulateFormSetter: { default: undefined },
    formulateFormRegister: { default: undefined },
    getFormValues: { default: () => () => ({}) }
  },
  1. inheritAttrs设置成false
    组件将不会把未被注册的props呈现为普通的HTML属性,$attrs 将所有传入的prop都拿到

  2. model自定义

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

6 $emit

触发当前实例上的事件。附加参数都会传给监听器回调

总结

  • 维护一份实例如何做到子级实例注册到父级身上,还能做到解耦,通过provide & inject去实现。
  • 如何快速复用共享方法,通过注册到vue实例。
  • 如何复用基础方法类型,通过组件的computed 把当前实例this通过function.call的方式快速实现模板复用。
  • 通过promise.all使多次校验进行后再统一修改状态,避免多次渲染
    performValidation () {
      // 把校验函数和参数处理好
      const rules = parseRules(this.validation, this.$formulate.rules(this.parsedValidationRules))
      // 通过promise的all把所有该校验的都一次过校验完后再触发渲染
      this.pendingValidation = Promise.all(
        rules.map(([rule, args]) => {
          var res = rule({
            value: this.context.model,
            getFormValues: this.getFormValues.bind(this),
            name: this.context.name
          }, ...args)
          res = (res instanceof Promise) ? res : Promise.resolve(res)
          return res.then(res => res ? false : this.getValidationMessage(rule, args))
        })
      )
        .then(result => result.filter(result => result))
        .then(errorMessages => { this.validationErrors = errorMessages })
      return this.pendingValidation
    },

感谢以及参考链接

  1. vue-formualte
  2. vue官网
  3. 阶段执行顺序
  4. 少量批注

第一次写作,肯定有很多不足的地方,希望海涵。如果你有不清楚的地方或者认为我有写错的地方,欢迎评论区交流。