搭建 vue2 vue-router2 webpack3 多入口工程
《从零搭建 vue2 vue-router2 webpack3 工程》详细介绍了搭建的工程的步骤,本文主要介绍如何在此基础上改造为多入口。
多入口 MPA(multiple entry points)指的是 webpack 配置项 entry 配置为包含多个 key 的数组或者对象,一般用 vue 大部分是做 SPA(one entry point),因为能更好的配合 vue-router。那么有哪些场景会使用多入口呢?为了更加具体的介绍如何配置,下面我设想了两个可能的实际应用场景,如果你有不同的场景,欢迎评论补充。
Github地址:https://github.com/qinshenxue/vue2-vue-router2-webpack2
场景一对应分支 mpa1,关于反馈的「mpa1 的路由不支持 history 模式问题」,增加了分支 mpa1-history-mode-router
场景二对应分支 mpa2
场景一
前台页面和后台管理页面都放在一起,目录结构如下。
mpa1
|---- build
|---- src
|---- admin // 后台管理
| |---- views
| |---- app.vue
| |---- main.js
| |---- router.js
|
|---- web // 移动端
|---- views
|---- app.vue
|---- main.js
|---- router.js
也可以认为就是两个独立的模块,各自都有一套前端路由,只是模块之间存在很多公共的第三方库。对于这种结构,我们希望最终生产构建能够达到以下目标:
- web 和 admin 都公用的模块(比如:vue,vue-router,axios等)打包为 vendor.js,另外提取 vendor.js 中的 webpack 模块加载代码为 manifest.js;
- web 和 admin 各自引入的第三方库分别打包成 web-vendor.js 和 admin-vendor.js;
- web 和 admin 各自业务代码分别打包成 web.js 和 admin.js;
- web 和 admin 各自引入的第三方库的 css 分别打包成 web-vendor.css 和 admin-vendor.css;
- web 和 admin 各自的 css 代码分别打包成 web.css,admin.css;
- 分别生成 web 和 admin 的页面(index.html),页面相应的引入依赖的 css 和 js。
实际就是构建后,output 配置的目录结构如下。
mpa1
|---- src
|---- dist
|---- web
| |---- index.html
|
|---- admin
| |---- index.html
|
|---- assets
|---- js
| |---- manifest.js
| |---- vendor.js
| |---- web-vendor.js
| |---- admin-vendor.js
| |---- web.js
| |---- admin.js
|
|---- css
|---- web-vendor.css
|---- admin-vendor.css
|---- web.css
|---- admin.css
下面开始一步一步调整配置,最终达成上面所有的目标。
添加入口
首先当然要在 webpack.base.config.js 添加 web 和 admin 的入口。
entry: {
web: path.resolve(__dirname, '../src/web/main.js'),
admin: path.resolve(__dirname, '../src/admin/main.js')
}
JS 提取配置
无论是提取各个入口的公共模块(例如 vue、vue-router ),还是提取 web 和 admin 各自引入的第三方库,都只需要用 webpack 自带的插件 webpack.optimize.CommonsChunkPlugin。
先将 web 和 admin 各自引入的第三方模块提取到 vendor。
[
new webpack.optimize.CommonsChunkPlugin({
name: 'web-vendor',
chunks: ['web'],
minChunks: function (module) {
return module.context && module.context.indexOf("node_modules") !== -1;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'admin-vendor',
chunks: ['admin'],
minChunks: function (module) {
return module.context && module.context.indexOf("node_modules") !== -1;
}
})
]
接着将 web-vendor 和 admin-vendor 中公共的模块提取为 vendor。
[
// ...
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
chunks: ['admin-vendor', 'web-vendor']
})
]
最后提取 vendor 中的 webpack 模块加载部分的代码。
[
// ...
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
})
]
CSS 提取配置
CSS 提取采用 extract-text-webpack-plugin 插件。但是配置不需要那么复杂。
[
new ExtractTextPlugin({
allChunks: true,
filename: "css/[name].css?[contenthash:8]"
})
]
那么如何提取 web 和 admin 引入的第三方库的 CSS 呢?其实在上面配置 web-vendor 和 admin-vendor 的时候就完成了。
生成入口首页
生成页面采用 html-webpack-plugin。不同于单入口,需要手动配置 chunks,chunks 配置每个入口依赖的“模块”(chunk)。
[
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, `../dist/web/index.html`),
template: 'index.tpl.html',
chunks: ['manifest', 'vendor', 'web-vendor', 'web'],
inject: true
}),
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, `../dist/admin/index.html`),
template: 'index.tpl.html',
chunks: ['manifest', 'vendor', 'admin-vendor', 'admin'],
inject: true
})
]
以上就是所有的配置,满足了所有我们希望达到的构建目标。可以 git clone 文章开头给出的 github 地址尝试一下,注意要切换到分支 mpa1。
场景二
不用端路由,采用后端路由,页面可能是 php、jsp、aspx,实际开发中这种情况也比较多。就像使用 seajs,requirejs 等模块加载器,每个页面都需要引入自己的依赖项。目录结构如下。
mpa2
|---- build
|---- src
|---- css
|---- images
|---- pages
|---- moduleA
| |---- index.vue
| |---- index.js
|
|---- moduleB
| |---- index.vue
| |---- index.js
|
|---- moduleC
|---- index.vue
|---- index.js
对于这种结构,构建目标和场景一类似,具体如下。
- 各个模块引入的第三方库打包成 vendor.js,另外提取 vendor.js 中的 webpack 模块加载代码为 manifest.js;
- 各个模块自己的 js 分别打包成 moduleA.js、moduleB.js、moduleC.js;
- 各个模块引入的第三方库的 css 打包成 vendor.css;
- 各个模块自己的 css 分别打包成 moduleA.css、moduleB.css、moduleC.css;
- 分别生成各个模块页面(index.html),页面相应的引入依赖的 css 和 js。
添加入口
entry: {
moduleA: resolve('../src/pages/moduleA/index.js'),
moduleB: resolve('../src/pages/moduleB/index.js'),
moduleC: resolve('../src/pages/moduleC/index.js')
}
当你模块特别多的时候,这样写肯定不好维护,可以自己写一个函数来生成。
JS 提取配置
[
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
chunks: ['moduleA', 'moduleB', 'moduleC'],
minChunks: function (module) {
return module.context && module.context.indexOf("node_modules") !== -1;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
})
]
css 提取和场景一相同,就不再说明了。
生成入口首页
把生成页面提取为一个函数,减少配置的工作量。
var isProd = process.env.NODE_ENV === "production";
exports.genHtmlPlugins = function () {
var baseWebpackConfig = require('./webpack.base.config');
var path = require('path')
var plugins = [];
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
plugins.push(
new HtmlWebpackPlugin({
filename: isProd ? path.resolve(__dirname, `../dist/${name}/index.html`) : `${name}/index.html`,
template: 'index.tpl.html',
chunks: isProd ? ['manifest','vendor', name] : [name],
inject: true
}))
})
return plugins
}
上面的函数区分了当前环境,开发环境下,filename 配置为入口名/index.html
,对应访问地址为http://localhost:8090/moduleA/
,开发模式下也不需要用到 CommonsChunkPlugin,因此 chunks 只有一个,而在生产构建时,需要指定 chunks。
最终构建后的目录结构如下。
mpa2
|---- src
|---- dist
|---- moduleA
| |---- index.html
|
|---- moduleB
| |---- index.html
|
|---- moduleC
| |---- index.html
|
|---- assets
|---- js
| |---- manifest.js
| |---- vendor.js
| |---- moduleA.js
| |---- moduleB.js
| |---- moduleC.js
|
|---- css
|---- vendor.css
|---- moduleA.css
|---- moduleB.css
|---- moduleC.css
总结
上述两个场景的配置大同小异,实际上就是通过 CommonsChunkPlugin 和 HtmlWebpackPlugin 这两个插件来完成你想要的目标。