Vue.js 和本地 NPM 包的应用程序模块化
Application modularity with Vue.js and local NPM packages
我正在尝试通过 vue-cli-service
在 Vue 中构建模块化应用程序。主应用程序和模块是位于不同文件夹中的独立项目,结构如下所示:
-- app/package.json
/src/**
-- module1/package.json
/src**
-- module2/package.json
/src**
想法是让 Vue 应用程序完全不知道运行时可能存在的应用程序模块,模块本身是用 vue-cli-service build --target lib
在本地 moduleX/dist
文件夹中编译的,用package.json“主要”和“文件”节点。
我的第一个想法(现在只是为了提高开发速度)是将模块作为本地 NPM 包添加到应用程序中,使用观察者构建它们并使用观察者本身为应用程序提供服务,以便对依赖项的任何更改模块会(我认为)自动分发到主应用程序。
因此应用程序的 package.json
包含如下依赖项:
...
"module1": "file:../module1",
"module2": "file:../module2",
...
这个依赖项意味着可以随时删除,或者通常根据我们的需要进行组合,应用程序只需重新编译,一切都应该可以正常工作。
我现在正试图了解如何在应用程序中动态加载和激活模块,因为我不能像这样使用动态导入:
import(/* webpackMode: "eager" */ `module1`).then(src => {
src.default.boot();
resolve();
});
因为基本上我不知道'module1'、'module2'等...
在 OOP 世界中,我只会使用依赖注入检索 类 实现特定接口,但在 JS/TS 中我不确定它是否可行。
有办法做到这一点吗?
In an OOP world I would just use dependency injection retrieving classes implementing a specific interface, but in JS/TS I'm not sure it is viable.
为什么不呢?
除此之外,使用 JS/TS 您不限于使用 类 实现特定接口:您只需要定义模块的接口(即 module.exports
)并在库条目 (vue build lib) 中尊重它。
编辑:阅读评论可能我理解了请求。
每个模块都应该遵循以下接口(在作为 vue 库入口的文件中)
export function isMyAppModule() {
return true;
}
export function myAppInit() {
return { /* what you need to export */ };
}
比在您的应用中:
require("./package.json").dependencies.forEach(name => {
const module = require(name);
if(! module.isMyAppModule || module.isMyAppModule() !== true) return;
const { /* the refs you need */ } = module.myAppInit();
// use your refs as you need
});
与 package.json
杂耍对我来说听起来不是个好主意 - 无法扩展。我会做什么:
- 在
package.json
中保留所有可用的“模块”
- 创建单独的
js
文件(或在 package.json
中拥有自己的道具),其中包含所有可用的配置(例如,针对不同的客户端)
module.exports = {
'default': ['module1', 'module2', 'module3'],
'clientA': ['module1', 'module2', 'module4'],
'clientB': ['module2', 'module3', 'module4']
}
- 进入 VueCLI 构建过程 - 我发现的最佳示例是 here and create js file which will run before each build (or "serve") and using simple template (for example lodash) 生成新的 js 文件,该文件将根据某些 ENV 变量的值启动配置的模块。请参阅以下(伪)代码(记住它在构建期间在节点内运行):
const fs = require('fs')
const _ = require('lodash')
const modulesConfig = require(`your module config js`)
const configurationName = process.env.MY_APP_CONFIGURATION ?? 'default'
const modules = modulesConfig[configurationName]
const template = fs.loadFileSync('name of template file')
const templateCompiled = _.template(template)
const generatedJS = templateCompiled({ `modules`: modules })
fs.writeFileSync('bootModules.js', generatedJS)
- 为
bootModules.js
编写模板。最简单的是:
<% _.forEach(modules , function(module) { %>import '<%= module %>' as <%= module %><% }); %>;
- 将
bootModules.js
导入您的应用程序
- 使用
MY_APP_CONFIGURATION
ENV 变量来切换所需的模块配置 - 不仅在开发期间有效,而且您还可以设置不同的 CI 进程以不同的 MY_APP_CONFIGURATION
值
这样您就可以将所有配置放在一个地方,您无需在每次构建之前更改 package.json
,您可以通过简单的机制在不同的模块配置之间切换,并且每个构建(包)仅包含需要模块....
我正在尝试通过 vue-cli-service
在 Vue 中构建模块化应用程序。主应用程序和模块是位于不同文件夹中的独立项目,结构如下所示:
-- app/package.json
/src/**
-- module1/package.json
/src**
-- module2/package.json
/src**
想法是让 Vue 应用程序完全不知道运行时可能存在的应用程序模块,模块本身是用 vue-cli-service build --target lib
在本地 moduleX/dist
文件夹中编译的,用package.json“主要”和“文件”节点。
我的第一个想法(现在只是为了提高开发速度)是将模块作为本地 NPM 包添加到应用程序中,使用观察者构建它们并使用观察者本身为应用程序提供服务,以便对依赖项的任何更改模块会(我认为)自动分发到主应用程序。
因此应用程序的 package.json
包含如下依赖项:
...
"module1": "file:../module1",
"module2": "file:../module2",
...
这个依赖项意味着可以随时删除,或者通常根据我们的需要进行组合,应用程序只需重新编译,一切都应该可以正常工作。
我现在正试图了解如何在应用程序中动态加载和激活模块,因为我不能像这样使用动态导入:
import(/* webpackMode: "eager" */ `module1`).then(src => {
src.default.boot();
resolve();
});
因为基本上我不知道'module1'、'module2'等...
在 OOP 世界中,我只会使用依赖注入检索 类 实现特定接口,但在 JS/TS 中我不确定它是否可行。
有办法做到这一点吗?
In an OOP world I would just use dependency injection retrieving classes implementing a specific interface, but in JS/TS I'm not sure it is viable.
为什么不呢?
除此之外,使用 JS/TS 您不限于使用 类 实现特定接口:您只需要定义模块的接口(即 module.exports
)并在库条目 (vue build lib) 中尊重它。
编辑:阅读评论可能我理解了请求。
每个模块都应该遵循以下接口(在作为 vue 库入口的文件中)
export function isMyAppModule() {
return true;
}
export function myAppInit() {
return { /* what you need to export */ };
}
比在您的应用中:
require("./package.json").dependencies.forEach(name => {
const module = require(name);
if(! module.isMyAppModule || module.isMyAppModule() !== true) return;
const { /* the refs you need */ } = module.myAppInit();
// use your refs as you need
});
与 package.json
杂耍对我来说听起来不是个好主意 - 无法扩展。我会做什么:
- 在
package.json
中保留所有可用的“模块”
- 创建单独的
js
文件(或在package.json
中拥有自己的道具),其中包含所有可用的配置(例如,针对不同的客户端)
module.exports = {
'default': ['module1', 'module2', 'module3'],
'clientA': ['module1', 'module2', 'module4'],
'clientB': ['module2', 'module3', 'module4']
}
- 进入 VueCLI 构建过程 - 我发现的最佳示例是 here and create js file which will run before each build (or "serve") and using simple template (for example lodash) 生成新的 js 文件,该文件将根据某些 ENV 变量的值启动配置的模块。请参阅以下(伪)代码(记住它在构建期间在节点内运行):
const fs = require('fs')
const _ = require('lodash')
const modulesConfig = require(`your module config js`)
const configurationName = process.env.MY_APP_CONFIGURATION ?? 'default'
const modules = modulesConfig[configurationName]
const template = fs.loadFileSync('name of template file')
const templateCompiled = _.template(template)
const generatedJS = templateCompiled({ `modules`: modules })
fs.writeFileSync('bootModules.js', generatedJS)
- 为
bootModules.js
编写模板。最简单的是:
<% _.forEach(modules , function(module) { %>import '<%= module %>' as <%= module %><% }); %>;
- 将
bootModules.js
导入您的应用程序 - 使用
MY_APP_CONFIGURATION
ENV 变量来切换所需的模块配置 - 不仅在开发期间有效,而且您还可以设置不同的 CI 进程以不同的MY_APP_CONFIGURATION
值
这样您就可以将所有配置放在一个地方,您无需在每次构建之前更改 package.json
,您可以通过简单的机制在不同的模块配置之间切换,并且每个构建(包)仅包含需要模块....