- 将子项目打包 UMD 模块,教程
- 在主项目中预先(主项目实例化 Vue 之前)挂载子项目的 routes
- 合并主项目和子项目的路由表
- 实例化主项目 Vue
app-entry/vue.config.js
const webpack = require('webpack');
const APP_NAME = require('./package.json').name;
const PORT = require('./package.json').devPort; // 开发模式下项目的启动端口
const PROXY = require('./config/proxy'); // 开发模式下的 proxy 配置
module.exports = {
baseUrl: './',
configureWebpack: {
// 提取公共依赖
externals: {
vue: 'Vue',
'element-ui': 'ELEMENT',
},
plugins: [
// 定义全局变量
new webpack.DefinePlugin({
'process.env.VUE_APP_NAME': JSON.stringify(APP_NAME),
}),
new InsertScriptPlugin({ files: modules }), // 自动添加子项目入口文件至 index.html
],
},
devServer: {
port: PORT,
proxy: PROXY,
},
};
app-entry/scripts/InsertScriptWebpackPlugin.js
class InsertScriptWebpackPlugin {
constructor(options = {}) {
const { files = [] } = options;
this.files = files;
}
apply(compiler) {
const self = this;
compiler.hooks.compilation.tap(
'InsertScriptWebpackPlugin',
(compilation) => {
if (compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing) {
compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(
'InsertScriptWebpackPlugin',
(htmlPluginData) => {
const {
assets: { js },
} = htmlPluginData;
js.unshift(...self.files); // 优先加载 files 文件
},
);
} else {
console.log('\n');
console.log(
'\x1b[41m%s\x1b[0m',
'Error:',
'`insert-script-webpack-plugin` dependent on `html-webpack-plugin`',
);
}
},
);
}
}
module.exports = InsertScriptWebpackPlugin;
app-entry/public/index.html
<head>
<link href="path/to/element-ui/index.css" rel="stylesheet" />
</head>
<body>
<script src="path/to/vue.min.js"></script>
<script src="path/to/element-ui/index.js"></script>
</body>
app-entry/config/proxy.js
module.exports = {
'/app-typescript/': {
target: 'http://localhost:10241/', // 指向 app-typescript 开发服务
},
'app-javascript/': {
target: 'http://localhost:10242/', // 指向 app-javascript 开发服务
},
};
app-entry/src/main.js
import store from './store'; // store 实例
// 挂载主项目的 store 和 router 实例
Reflect.defineProperty(Vue, '__share_pool__', {
value: {
store,
},
});
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
// 获取子项目的 route-list
const routes = Vue.__share_pool__.routes;
export default new Router({
routes: Object.values(routes).reduce((acc, prev) => acc.concat(prev), [
{
path: '/',
redirect: '/app-typescript',
},
]),
});
以 app-javascript 为例
yarn add mfv-cli-service -D
app-javascript/package.json
{
"scripts": {
"build": "mfv-cli-service build --report --target lib --formats umd-min ./src/main.js"
}
}
这样可以将 app-javascript 构建成一个 UMD 文件,然后在 app-entry 中引用,参考
app-javascript/vue.config.js
const webpack = require('webpack');
const APP_NAME = require('./package.json').name;
const PORT = require('./package.json').devPort; // 开发模式下项目的启动端口
module.exports = {
publicPath: `/${APP_NAME}/`, // 必须为绝对路径;配合 app-entry 中的 proxy 配置,配合生产环境下的 Nginx 配置
configureWebpack: {
// 提取公共依赖
externals: {
vue: 'Vue',
'element-ui': 'ELEMENT',
},
entry: './src/main.js',
output: {
libraryExport: 'default', // https://cli.vuejs.org/zh/guide/build-targets.html#应用
jsonpFunction: `webpackJsonp-${APP_NAME}`, // 解决默认情况下子项目 chunkname 冲突的问题
},
plugins: [
// 定义全局变量,项目中会使用
new webpack.DefinePlugin({
'process.env.VUE_APP_NAME': JSON.stringify(APP_NAME),
}),
],
},
devServer: {
port: PORT,
},
};
app-javascript/src/main.js
import Vue from 'vue';
import routes from './routes';
const sharePool = (Vue.__share_pool__ = Vue.__share_pool__ || {});
const routesPool = (sharePool.routes = sharePool.routes || {});
// 挂载子项目的 routes
routesPool[process.env.VUE_APP_NAME] = routes;
app-javascript/src/routes.js
/* routes-list */
const APP_NAME = process.env.VUE_APP_NAME;
const App = () => import('./App.vue');
const Home = () => import('./views/Home.vue');
const About = () => import('./views/About.vue');
export default [
{
path: `/${APP_NAME}`,
name: APP_NAME,
redirect: { name: `${APP_NAME}.home` },
component: App,
children: [
{
path: 'home',
name: `${APP_NAME}.home`,
component: Home,
},
{
path: 'about',
name: `${APP_NAME}.about`,
component: About,
},
],
},
];
app-javascript/src/base.js
import Vue from 'vue';
import store from './store';
// 子项目异步注册 store module
Vue.__share_pool__.store.registerModule(process.env.VUE_APP_NAME, store);
export default null;
app-javascript/src/store.js
/* store module */
export default {
namespaced: true, // namespaced must be true in module app.
state: {
name: process.env.VUE_APP_NAME,
},
mutations: {},
actions: {},
};
- 方式一:通过 CI 合并主项目和子项目的 dist 目录,然后部署
- 方式二:将子项目当成单独服务对待,独立部署,然后通过 Nginx 反向代理合并主项目和子项目
推荐在服务端为子项目入口文件添加协商缓存。