如何使用webpack构建多页面应用,这是一个我一直在想和解决的问题。网上也给出了不少的例子,不少想法。猛一看,以为有那么点儿意思,但仔细看也就那样。css
使用webpack这个构建工具,可使咱们少考虑不少的问题。html
咱们常见的单页面应用只有一个页面,它考虑问题,解决问题围绕着中心化
去解决,所以不少麻烦都迎刃而解。若是你使用过vue.js,那么想必你必定用过vue-router,vuex,它们就是典型的中心化管理模式,固然还有不少,这里不一一列举了。vue
而多页面应用,咱们不能再按照中心化
模式的路走了,由于行不通,这也是不少人认为多页面应用很差作,或者干脆认为webapck只能作单页面应用,而不能作多页面应用的缘由。webpack
因此,我要说明的第一点儿是:不要用作单页面应用的思惟来作多页面应用。git
单页面的模块共享,实际上是代码块在同一个页面的不一样位置的重复出现;而多页面应用的代码块儿共享须要实现的不只是同一个页面的共享,还要作到跨页面的共享。github
因此,第一个要解决的问题是:不一样页面的代码块共享如何实现?web
单页面的路由管理,实际上是根据用户的触发条件来实现不一样的代码块的显隐;而多页面应用的路由管理则否则,它实现的是页面的跳转。vue-router
因此,第二个要解决的问题是:所页面应用的导航该如何作?vuex
单页面的状态管理,很受开发者喜爱。单页面是一个页面,因此页面中的数据状态的管理操做起来还算驾轻就熟,那么多页面应用的呢,显然依靠它自身很难实现。json
因此,第三个要解决的问题是:多页面应用的状态管理如何作?
注:这个问题问的其实有点儿傻,若是你作的是dom操做的多页面儿应用,就不用作状态管理了。若是你仍是使用想vue.js这样的库,你就须要考虑要不要再用作多页面的状态管理了,由于此法儿就是为单页面应用作的,多页面儿行不通。
入口(entry):
webpack对入口不只能够定义单个文件,也能够定义多个文件。
熟悉当页面应用开发的对于下面的代码应该不会陌生吧?
module.exports = {
entry: './src/index.js',
···
}
复制代码
我第一次接触真正的单页面应用项目使用的就是angualrjs,使用的构建工具使webapck+gulp
,其中的webpack.config.js
中的看到的入口文件代码就是它。
后来,接触到的是数组形式,代码以下:
module.exports = {
entry: ['./src/index.js', 'bootstrap']
···
}
复制代码
这样,将bootstrap和入口文件一块儿引用,就能够在任何一个代码块中使用boostrap。
再后来,接触到的是对象形式,代码以下:
module.exports = {
main: './src/index.js'
···
}
复制代码
这样作的目的是为了给输出的文件指定特定的名字。
再后来,就是作多页面应用,就须要用到以下的代码:
module.exports = {
entry: {
index: './src/index.js',
aboutUs: './src/aboutus.js',
contactUs: './src/contactus.js'
}
}
复制代码
为了引入第三方库,咱们能够像以下这样作:
module.exports = {
entry: {
index: ['./src/index.js', 'loadsh'],
aboutUs: './src/aboutus.js',
contactUs: ['./src/contactus.js', 'lodash']
}
}
复制代码
但为了共享模块代码,咱们须要像下面这这样作:
const CommonsChunkPlugin = require('webpack').optimization.CommonsChunkPlugin
module.exports = {
entry: {
index: ['./src/index.js', './src/utils/load.js', 'loadsh'],
aboutUs: ['./src/aboutus.js', 'loadsh'],
contactUs: ['./src/contactus.js','./src/utils/load.js', 'lodash']
},
plugins: [
new CommonsChunkPlugin({
name: "commons",
filename: "commons.js",
chunks: ["index", "aboutUs", "contactUs"]
})
]
}
复制代码
这样型就会造成以下所示的项目目录结构:
├── src │ ├── common // 公用的模块 │ │ ├── a.js │ │ ├── b.js │ │ ├── c.js │ │ ├── d.js │ ├── uttils // 工具 │ │ ├── load.js // 工具代码load.js │ ├── index.js // 主模块index.js (包含a.js, b.js, c.js, d.js) │ ├── aboutUs.js // 主模块aboutus.js (包含a.js, b.js) │ ├── contactUs.js // 主模块contactus.js (包含a.js, c.js) ├── webpack.config.js // css js 和图片资源 ├── package.json ├── yarn.lock
可是这个内置插件的局限性比较大。正如上面所使用的那样,它只会提取chunks
选项所匹配的模块共有的代码块。就如同上面代码表示的那样,它只会提取pindex, aboutUs, contactUs
共有的代码块loadsh
,而不会提取index, contactUs
共有的代码块load.js
。
固然,通常的第三方库,咱们也不这样使用,而是像下面这样使用:
const CommonsChunkPlugin = require('webpack').optimization.CommonsChunkPlugin
module.exports = {
entry: {
index: ['./src/index.js', './src/utils/load.js'],
aboutUs: ['./src/aboutus.js'],
contactUs: ['./src/contactus.js','./src/utils/load.js'],
vendors: ['lodash']
},
externals: {
commonjs: "lodash",
root: "_"
},
plugins: [
new CommonsChunkPlugin({
name: "commons",
filename: "commons.js",
chunks: ["index", "aboutUs", "contactUs"]
})
]
}
复制代码
对于web应用最终的目的是:匹配生成不一样的html页面。
这里咱们要使用的就是html-webpack-plugin
。
首先,须要安装html-webpack-plugin
:
yarn add --dev html-webpack-plugin
复制代码
而后引入插件,并配置以下:
...
const HtmlWebapckPlugin = require('html-webpack-plugin');
...
plugins: [
...
new HtmlWebapckPlugin({
filename: 'index.html',
chunks: ['vendors', 'commons', 'index']
}),
new HtmlWebapckPlugin({
filename: 'aboutUs.html',
chunks: ['vendors', 'commons', 'aboutUs']
}),
new HtmlWebapckPlugin({
filename: 'contactUs.html',
chunks: ['commons', 'contactUs']
})
],
...
复制代码
这样一个基于webpack3.x的多页面框架就有了基本的样子。
而使用webpack4.x则彻底不一样,它移除了内置的CommonsChunkPlugin
插件,引入了SplitChunksPlugin
插件,这个插件知足了咱们的须要,弥补了CommonsChunkPlugin
的不足。
若是你想要解决以前的不足,去提取index, contacUs
共有的模块,操做起来会很简单。正如上面的所列举的那样,咱们有三个入口点index, aboutUs, contactUs
,SplitChunksPlugin
插件会首先获取这三个入口点共有的代码块,而后创建一个文件,紧接着获取每两个入口点的共有代码块,而后将每一个入口点独有的代码块单独造成一个文件。若是你使用了第三方库,就像上面咱们使用的loadsh
,它会将第三方入口代码块单独打包为一个文件。
配置文件webpack.config.js
须要增长以下的代码:
···
optimization: {
splitChunks: {
chunks: 'all',
maxInitialRequests: 20,
maxAsyncRequests: 20,
minSize: 40
}
}
···
复制代码
由于SplitChunksPlugin
能够提取任意的入口点之间的共同代码,因此,咱们就不须要使用vendors
入口节点了。那么,为匹配生成不一样的页面代码能够修改为以下:
const HtmlWebapckPlugin = require('html-webpack-plugin')
···
plugins: [
new HtmlWebapckPlugin({
filename: 'index.html',
chunks: ['index']
}),
new HtmlWebapckPlugin({
filename: 'aboutUs.html',
chunks: ['aboutUs']
}),
new HtmlWebapckPlugin({
filename: 'contactUs.html',
chunks: ['contactUs']
}),
]
···
复制代码
能够发现结果愈来愈接近咱们所想。可是这里仍是存在一个问题,第三方库loadsh
由于在入口点index, aboutUs
中被分别引入,可是构建的结果却输出了两个第三方库文件,这不是咱们想要的。这个问题怎么解决呢,由于html-webpack-plugin
插件的chunks
选项,支持多入口节点,因此,咱们能够再单首创建一个第三方库的入口节点vendors
。配置代码修改以下:
...
entry: {
index: ['./src/index.js', './src/utils/load.js'],
aboutUs: ['./src/aboutUs.js'],
contactUs: ['./src/contactUs.js','./src/utils/load.js'],
vendors: ['loadsh']
},
...
plugins: [
new HtmlWebapckPlugin({
filename: 'index.html',
chunks: ['index', 'vendors']
}),
new HtmlWebapckPlugin({
filename: 'aboutUs.html',
chunks: ['aboutUs', 'vendors']
}),
new HtmlWebapckPlugin({
filename: 'contactUs.html',
chunks: ['contactUs']
}),
],
...
复制代码
注意:若是不一样的入口点儿之间有依赖关系,如上面的index
和vendors
之间,由于index
依赖于vendors
,因此vendors
要置于index
以前。
这篇文章,说到这里基本上已经结束了。固然,webpack多页面应用的知识点尚未讲完,这些内容会放在后续的文章中详解。