如何在Vue应用程序中组织依赖关系

每个熟悉Vue的人都知道Vue应用程序只有一个入口点main.js文件。 除了创建Vue实例外,还存在所有全局依赖项(指令,组件,插件)的导入和一种依赖项注入。 项目越大,依赖项就越多,此外,每个依赖项都有自己的配置。 结果,我们得到了一个包含所有配置的巨大文件。
本文将讨论如何组织全局依赖性以避免这种情况。



为什么要自己写?


许多人可能会认为-如果有Nuxt ,这为什么有必要为您解决? 在我的项目中,我也使用了它,但是在简单的项目中,这可能是多余的。 此外,还没有人取消那些遗留在您身上的遗留代码的项目,就像您的头上积雪一样。 并在那里连接框架-实际上是从头开始。

策划者


这种组织的组织者是Nuxt。 我在Vue的一个大型项目中使用了它。
Nuxt有一个很棒的功能-插件。 每个插件都是一个导出函数的文件。 配置被传递给函数,在创建实例以及整个商店时,也将传递给Vue构造函数。

此外,每个插件都提供了一个非常有用的功能– inject 。 它对Vue的根实例和store对象进行依赖注入。 这意味着在每个组件,每个存储功能中,指定的依赖项将通过this

这可以派上用场吗?


除了main.js明显“失重”这一事实main.js ,您还将有机会在应用程序中的任何位置使用依赖项,而无需不必要的导入。

依赖注入的主要示例是vue-router 。 它并不经常使用-获取当前路由的参数,进行重定向,但这是全局依赖项。 如果它可以在任何组件中派上用场,那么为什么不将其全局化呢? 另外,由于这个原因,它的状态也将被全局存储并在整个应用程序中改变。

另一个例子是vue-wait 。 该插件的开发人员走得更远,不仅将$wait属性添加到Vue实例,还添加到vuex存储。 给定插件的细节,这被证明是非常有用的。 例如,商店具有在多个组件上调用的动作。 在每种情况下,您都需要在某些元素上显示加载程序。 不必在每次操作调用之前和之后调用$wait.start('action')$wait.end('action') ,只需在操作本身中一次调用这些方法即可。 而且这比dispatch('wait/start', 'action' {root: true})更具可读性,也较省时。 就存储而言,这是语法糖。

从文字到代码


项目的基本结构


让我们看看项目现在的样子:
src
- store
- App.vue
- main.js

main.js看起来像这样:
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; new Vue({ render: h => h(App), store }).$mount('#app'); 


我们联系第一个依赖


现在,我们想将axios连接到我们的项目并为其创建某种配置。 我遵循了Nuxt的术语,并在src创建了一个plugins目录。 目录内有index.jsaxios.js

src
- plugins
-- index.js
-- axios.js
- store
- App.vue
- main.js

如上所述,每个插件都必须导出一个函数。 同时,在函数内部,我们要访问存储,然后要访问inject函数。

axios.js
 import axios from 'axios'; export default function (app) { //       – , , interceptors  .. axios.defaults.baseURL = process.env.API_BASE_URL; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['Content-Type'] = 'application/json'; axios.interceptors.request.use(config => { ... return config; }); } 

index.js
 import Vue from 'vue'; import axios from './axios'; export default function (app) { let inject = () => {}; //   inject,        Dependency Injection axios(app, inject); //       Vue    } 


如您所见, index.js文件还导出了该函数。 这样做是为了能够在其中传递app对象。 现在让我们main.js修改main.js并调用此函数。

main.js
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; import initPlugins from './plugins'; //    // ,    Vue,  ,     initPlugins const app = { render: h => h(App), store }; initPlugins(app); new Vue(app).$mount('#app'); //   initPlugins    


结果


在这一阶段,我们已经实现了将插件配置从main.js中删除的单独文件中。

顺便说一句,将app对象传递给我们所有插件的好处是,我们现在可以在每个插件内部访问商店。 您可以通过调用commitdispatch以及访问store.statestore.getters来自由使用它。

如果您喜欢ES6风格,则可以执行以下操作:

axios.js
 import axios from 'axios'; export default function ({store: {dispatch, commit, state, getters}}) { ... } 

第二阶段-依赖注入


我们已经创建了第一个插件,现在我们的项目如下所示:

src
- plugins
-- index.js
-- axios.js
- store
- App.vue
- main.js

由于在大多数确实需要的库中,已经使用Vue.use实现了依赖注入,因此我们将创建自己的简单插件。

例如,尝试重复vue-wait操作。 这是一个相当繁重的库,因此如果要在一对按钮上显示加载程序,最好放弃它。 但是,我无法抗拒它的便利性,并在其项目中重复了其基本功能,包括商店中的语法糖。

等待插件


plugins目录中创建另一个文件wait.js

我已经有一个vuex模块,我也称它为wait 。 他执行三个简单步骤:

-start-将名为action的对象的state属性设置为true
-end-从状态中删除名为action的对象的属性
- is -从状态获取名为action的对象的属性

在这个插件中,我们将使用它。

wait.js
 export default function ({store: {dispatch, getters}}, inject) { const wait = { start: action => dispatch('wait/start', action), end: action => dispatch('wait/end', action), is: action => getters['wait/waiting'](action) }; inject('wait', wait); } 


并连接我们的插件:

index.js
 import Vue from 'vue'; import axios from './axios'; import wait from './wait'; export default function (app) { let inject = () => {}; Injection axios(app, inject); wait(app, inject); } 


进样功能


现在我们实现inject功能。
 //   2 : // name – ,       this.  ,   Vue        Dependency Injection // plugin – ,       this.  ,  ,           let inject = (name, plugin) => { let key = `$${name}`; //      app[key] = plugin; //     app app.store[key] = plugin; //     store //  Vue.prototype Vue.use(() => { if (Vue.prototype.hasOwnProperty(key)) { return; } Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; } }); }); }; 


Vue.prototype的魔力


现在关于魔术。 Vue文档说编写Vue.prototype.$appName = ' ';就足够了Vue.prototype.$appName = ' ';$appName将在this可用。

但是,事实证明事实并非如此。 由于谷歌搜索,没有答案为什么这样的设计不起作用。 因此,我决定与已经实现此功能的插件作者联系。

全局混合


在我们的示例中,我查看了vue-wait插件代码。 他们提供了这样的实现(为清晰起见,清除了源代码):

 Vue.mixin({ beforeCreate() { const { wait, store } = this.$options; let instance = null; instance.init(Vue, store); // inject to store this.$wait = instance; // inject to app } }); 

建议使用全局混合来代替原型。 效果可能基本相同,除了一些细微差别。 但是,鉴于注入是在此处的商店中完成的,因此看起来并不太正确,并且根本与文档不符。

但是,如果原型呢?


用于inject函数inject的原型解决方案背后的思想是从Nuxt借来的。 它看起来比全局mixin正确得多,所以我选择了它。

  Vue.use(() => { // ,        if (Vue.prototype.hasOwnProperty(key)) { return; } //    ,         app  Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; //  ,    this } }); }); 


结果


完成这些操作后,我们将有机会访问this.$wait从任何组件以及商店中的任何方法this.$wait

发生什么事了


项目结构:

src
- plugins
-- index.js
-- axios.js
-- wait.js
- store
- App.vue
- main.js


index.js
 import Vue from 'vue'; import axios from './axios'; import wait from './wait'; export default function (app) { let inject = (name, plugin) => { let key = `$${name}`; app[key] = plugin; app.store[key] = plugin; Vue.use(() => { if (Vue.prototype.hasOwnProperty(key)) { return; } Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; } }); }); }; axios(app, inject); wait(app, inject); } 


wait.js
 export default function ({store: {dispatch, getters}}, inject) { const wait = { start: action => dispatch('wait/start', action), end: action => dispatch('wait/end', action), is: action => getters['wait/waiting'](action) }; inject('wait', wait); } 


axios.js
 import axios from 'axios'; export default function (app) { axios.defaults.baseURL = process.env.API_BASE_URL; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['Content-Type'] = 'application/json'; } 


main.js
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; import initPlugins from './plugins'; const app = { render: h => h(App), store }; initPlugins(app); new Vue(app).$mount('#app'); 

结论


通过这些操作,我们在main.js文件中收到了一个导入和一个函数调用。 现在,可以立即清楚地在哪里找到每个插件和每个全局依赖项的配置。

添加新插件时,您只需要创建一个导出函数的文件,然后将其导入index.js并调用此函数即可。

在我的实践中,这种结构被证明非常方便,而且很容易在项目之间转移。 现在,如果您需要进行依赖注入或配置其他插件,则无需担心。

在评论中分享您对依赖管理的经验。 成功的项目!

Source: https://habr.com/ru/post/zh-CN423013/


All Articles