[Vue] 为 Vue 添加(混入)实例属性和钩子(Custom Property and Hooks for Vue )

为 Vue 添加实例属性和钩子(Custom Property and Hooks of Vue )

目标

1、给 Vue 实例添加类似 vue-router 插件的 $router 的属性

2、给 Vue 实例添加钩子(类似)(仅是回调函数,不影响生命周期)

环境

Vue 2.x 。(实际使用 Vue 2.6.10)

需求

最近一个Vue项目,使用 Vuex 管理数据。为了方便,使用了 VueX 的插件 vuex-orm 以及 vuex-orm 的插件  vuex-orm-localforage 。

  • vuex-orm : vuex 的数据访问插件,封装了 atctions 、mutations 、以及 commit dispatch,能够像 使用DAO一样访问 store中的数据。
  •  vuex-orm-localforage : vuex-orm  的持久化插件, 将vuex-orm 的模块数据(state)进行持久化存储和恢复。

需要在 mounted 中 通过 vuex-orm 查询数据 ,但是由于 vuex-orm-localforage 从持久层恢复数据是异步的,mounted 时,数据还未恢复,查询到 undefined,此时 vuex-orm 无法更新查询结果。导致数据永远为 undefined。

改进过程

有几个解决办法。

方法1,通过事件总线分发恢复数据完成事件(restored)

       在数据恢复之后,通过事件总线 分发 restored 事件,Vue实例中监听该事件,更新数据。

缺陷:

  1. 事件只有一次,有些Vue组件是延迟初始化,会错过该事件。 需要增加一个 属性来标识数据是否完成恢复完成。
  2. 如果有进一步的需求,数据恢复失败,那么还要增加另一个属性标识。

造成重复代码,虽然只增加三四行代码,但是如果勉强止步与此,就永远学不到更好的方案。

方法2,数据恢复完成之后再挂载($mount)

这样就能保证,mounted时 能够访问到 所有数据 。原来的代码:

const vue = new Vue({ store,  router,  render: h => h(App)}).$mount('#app')

改为:

const vue = new Vue({ store,  router,  render: h => h(App)})
const promises = restoreData() // 恢复数据返回Promise数组
Promise.all(promises).then(() => vue.$mount('#app'))//恢复成功后挂载

 但是,此方案缺陷更严重。

缺陷:数据量比较大时,恢复严重影响页面加载速度

考虑到我的应用中,vuex-orm无法更新数据的查询都是在子路由中,只有开发环境时,为了方便才会直接打开路由路径。在此方方案,基础上判断是否是生产环境,再决定挂载顺序,也勉强可以接受。

方法3:将恢复数据的 Promise 导出

然后代码中 使用 promise.then 即可。

let restored = Promise.all(restore())
export { restored }

// 使用时调用 restored.then即可

 使用的地方较多,需要不停的 import 。我比较懒,就得想更好的办法改进

方法4:将恢复数据的 Promise 实例添加到 Vue 的属性 。

何不像 vuex 和 vue-router 那的 this.$store 那样, 为 Vue 混入 $restore 。 Vuex vue-router能做到,我们一定也可以。

Vuex 是通过 Vue.mixin 来混入 ,查询一下 Vue.mixin的API ,即刻动手

/**
 * restore init
 */
function initRestore () {
  const options = this.$options
  if (options.restore) {
    this.$restore = options.restore
  } else if (options.parent && options.parent.$restore) {
    this.$restore = options.parent.$restore
  }
}

// 导出 mixin 选项
export default { created: initRestore }

然后new Vue 的时候 传入 restore 即可

const restore = Promise.all(restoring())

const vue = new Vue({ store,  router, restore  render: h => h(App)}).$mount('#app')

注意此处 修改了一下方法名称

方法4+:既然混入了属性,何不混入钩子

混入 restored 钩子,这样使用时,又省了一层箭头函数。

最终代码

/store

// file mixin.js
/**
 * restore init
 */
function initRestore () {
  const options = this.$options

  if (options.restore) {
    this.$restore = options.restore
  } else if (options.parent && options.parent.$restore) {
    this.$restore = options.parent.$restore
  }
}

/**
 * 给 Vue 实例添加 restored 钩子。
 * resotore 完成之后调用(无论成功失败)
 */
function initRestored () {
  const restored = this.$options.restored
  if (restored && (typeof restored === 'function')) {
    if (this.$restore) {
      this.$restore.finally(() => restored.call(this))
    }
  }
}

// 导出 mixin 选项
//export default { created: initRestore, mounted: initRestored }

// 为了示意 省略 导出、导入,直接写在一个文件
Vue.minx({ created: initRestore, mounted: initRestored })

 main.js

const restore = Promise.all(restoring())

const vue = new Vue({ store,  router, restore  render: h => h(App)}).$mount('#app')

/views 或者 /components

export default {
    data(){},
    mounted(){},
    restored(){
        // 像 mounted 一样
    }        
}

 

你可能感兴趣的:(Vue)