Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

asyncComputed源码解析 #32

Open
huangchucai opened this issue Aug 22, 2017 · 0 comments
Open

asyncComputed源码解析 #32

huangchucai opened this issue Aug 22, 2017 · 0 comments

Comments

@huangchucai
Copy link
Owner

asyncComputed源码解析

思考来源: vue-cli对于异步请求数据的时候,通过methods, created, data这三步,觉得实在是太过于复杂,能不能有一个计算属性,可以直接得到值,从而在模板中使用。

在github上面搜索得到大牛已经实现了 asyncComputed, 别人捷足先登了。楼主决定先看一遍它的源码,之后会对他的源码进行拓展。

1. Vue的install()方法和Vue.use()

1.1开发插件

插件通常会为Vue添加全局功能,插件的范围没有限制。

  • 添加全局方法或者属性
  • 添加全局资源:指令/过滤器/过渡
  • 通过全局mixin方法添加一些组件选项
  • 添加Vue实力方法,通过把它们添加到Vue.prototype上实现

1.1.1定义插件:Vue.js的插件使用的install() 。这个方法的第一个参数是Vue构造器,第二个参数是一个可选的对象:

// myplugin.js
const plugins = {
  install(Vue, myOptions) {
    // 通过this.$options.asyncComputed可以获取Vue实例中绑定的方法
    Vue.config.optionMergeStrategies.asyncComputed = Vue.config.optionMergeStrategies.computed
    Vue.prototype.$myMethods = function() {
      console.log(`这是原型上面的方法,可以听过this.$myMethods调用`);
    }
  }
}

1.1.2使用插件 : Vue通过全局Vue.use(obj||fn) 来安装vue的插件

import myplugin from './myplugin.js'
Vue.use(myplugin)

**1.1.3Vue.use结合install直接使用: **

Vue.use({
  install(Vue,myOptions) {
    Vue.prototype.$myMethods = function() {
      console.log(`这是原型上面的方法,可以听过this.$myMethods调用`);
    }
    Vue.mixin({
      created(){
        console.log(this.$option)
      }
    }) 
  }
})

2. 解析asyncComputed

2.1 通过自定义混合得到asyncComputed

我们通过computed拓展得到asyncComputed,这里通过computed拓展主要是方便记忆和基于computed的缓存机制方便存放数据和状态。

// asyncComputed.js
const AsyncComputed = {
  install(Vue,pluginOptions) {
    pluginOptions = pluginOptions || {}  //这里是额外的选择
    // 下面通过自定义混合得到了asyncComputed, 可以在Vue实例中使用asyncComputed属性
    Vue.config.optionMergeStrategies.asyncComputed = Vue.config.optionMergeStrategies.computed
  }
}

vue config

2.2 asyncComputed的使用和配置

使用 : app.vue

export default {
  ...
  asyncComputed: {
    type() {
      return  axios.get('http://rap.taobao.org/mockjsdata/24170/category/topList').then(res => 			res.data.lists)
    }
  },
}

**配置: ** 我们可以通过Vue.mixin 来混合配置

2.2-1 asyncComputed.js

//asyncComputed.js
// 得到所有的asyncComputed中的函数
function getterFor (fn) {
  // 如果是函数的话,直接返回
  if (typeof fn === 'function') return fn
  // 如果不是函数的话,得到对象的get的值  => 其实也是一个函数
  let getter = fn.get
  
  // 判定fn这个对象是否又watch属性
  if (fn.hasOwnProperty('watch')) {
    getter = function getter () {
      fn.watch.call(this)
      return fn.get.call(this)
    }
  }
  // 最后总是返回一个函数
  return getter
}

// 得到默认的配置
function defaultFor (fn, pluginOptions) {
  // 已经被制定了this是Vue实例
  let defaultValue = null
  // 看是否有默认值
  if ('default' in fn) {
    defaultValue = fn.default
  } else if ('default' in pluginOptions) {
    defaultValue = pluginOptions.default
  }
  // 如果默认值是一个函数的话,执行函数并返回
  if (typeof defaultValue === 'function') {
    return defaultValue.call(this)
  } else {
    return defaultValue
  }
}

2.2-2 asyncComputed.js中的 Vue.mixin({})中的beforeCreate()

//asyncComputed.js
const prefix = '_async_computed$'
const AsyncComputed = {
  install(Vue,pluginOptions) {
    pluginOptions = pluginOptions || {}  //这里是额外的选择
    // 下面通过自定义混合得到了asyncComputed, 可以在Vue实例中使用asyncComputed属性
    Vue.config.optionMergeStrategies.asyncComputed = Vue.config.optionMergeStrategies.computed
  }
  Vue.mixin({
      beforeCreate () {
        // 获取每一个vue组件数据
        const optionData = this.$options.data
        // 如果没有配置计算属性,就赋值为一个空对象
        if (!this.$options.computed) {
          this.$options.computed = {}
        }  
        // 循环asyncComputed 对象 
        for (const key in this.$options.asyncComputed || {}) {
          // this.$options.asyncComputed[key] 得到对应的函数
          this.$options.computed[prefix + key] = getterFor(this.$options.asyncComputed[key])
        }

        this.$options.data = function vueAsyncComputedInjectedDataFn () {
          const data = (
            // 如果是一个函数的话直接执行,不是的直接返回数据
            (typeof optionData === 'function')
              ? optionData.call(this)
              : optionData
          ) || {}
          for (const key in this.$options.asyncComputed || {}) {
            // 给data初始化值,把asyncComputed的名字赋值给this.$options.data
            data[key] = null
          }
          return data
        }
      },
    })
}

这里beforeCreate() 钩子函数初始化数据null,并没有得到数据。

optionData: 得到每一个对应实例中的data(函数或者对象)

this.$options.computed[prefix + key] 给实例的computed 添加函数 ;

this.$options.data 给Vue实例化组件添加data方法(一个函数,在created()的时候执行)

2.2-3 asyncComputed.js中的 Vue.mixin({})中的Created()

created () {
  // 处理默认值
  for (const key in this.$options.asyncComputed || {}) {
    // 把对应的函数,额外的选择都传递给函数defaultFor
    this[key] = defaultFor.call(this, this.$options.asyncComputed[key], pluginOptions)
  }

  for (const key in this.$options.asyncComputed || {}) {
    // 给每一个asyncComputed匹配一个id
    let promiseId = 0
    // console.log(this.$watch)
    this.$watch(prefix + key, newPromise => {
      const thisPromise = ++promiseId
      // 如果newPromise或者newPromise.then也不存在,就返回一个promise  
      if (!newPromise || !newPromise.then) {
        newPromise = Promise.resolve(newPromise)
      }
      // 如果已经存在一个promise 
      newPromise.then(value => {
        if (thisPromise !== promiseId) return
        this[key] = value
          }).catch(err => {
        // 错误处理
        if (thisPromise !== promiseId) return

        if (pluginOptions.errorHandler === false) return

        const handler = (pluginOptions.errorHandler === undefined)
          ? console.error.bind(console, 'Error evaluating async computed property:')
        : pluginOptions.errorHandler

        if (pluginOptions.useRawError) {
          handler(err)
        } else {
          handler(err.stack)
        }
      })
    }, { immediate: true })
  }
}
  1. 检查是否存在默认值,如果有的话,给实例添加属性,值是对应的值

    for (const key in this.$options.asyncComputed || {}) {
      // 把对应的函数,额外的选择都传递给函数defaultFor
      this[key] = defaultFor.call(this, this.$options.asyncComputed[key], pluginOptions)
    }
  2. 监听(this.$watch) this.$options.$computed中的所有属性, 如果已经是一个promise,就直接给覆盖beforeCreate()中初始化的this[key]的值。如果不是一个promise函数,则直接执行并返回一个promise函数

    this.$watch(prefix + key, newPromise => {
          const thisPromise = ++promiseId
          // 如果newPromise或者newPromise.then也不存在,就返回一个promise  
          if (!newPromise || !newPromise.then) {
            newPromise = Promise.resolve(newPromise)
          }
          // 如果已经存在一个promise 
          newPromise.then(value => {
            if (thisPromise !== promiseId) return
            this[key] = value
          })
    }

全部代码链接

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant