使用webpack配置MPA
为什么需要使用 webpack 构建多页应用呢?因为某些项目使用 SPA 不太合适(大多是 SEO 的原因),或者您在做项目时有其他的需求。
如果你有如下需求:
- 使用 ES6 进行开发
- 期望使用面向对象开发(class)
- 自动压缩合并 CSS 和 JS 文件
- 使用 ESLint 进行代码检查
- 自动生成 HTML 文件
- 自动抽取 CSS 文件 …
有了这些需求,基本上就必须使用 webpack
了。
安装依赖
首先是项目中需要使用的依赖安装。
- 安装 webpack 和 webpack-dev-server
npm install webpack webpack-dev-server --save-dev |
- 安装 webpack-merge
npm install webpack-merge --save-dev |
该插件用来对 webpack 配置进行合并操作。
- 安装 babel 相关插件
npm install babel-core babel-loader babel-preset-env --save-dev |
这系列插件用来对 ES6 语法进行转换。
- 安装样式处理相关插件
npm install css-loader style-loader postcss-loader autoprefixer --save-dev |
这系列插件用来处理 CSS 样式,其中 autoprefixer 是 postcss 的一个插件,用来自动给 CSS 样式添加前缀。
- 安装 file-loader
该插件将在导入图片、字体等文件时发挥作用。PS.您也可以安装 url-loader 以实现相同的作用:
npm install file-loader --save-dev npm install url-loader --save-dev |
- 安装 ESLint 相关的插件
npm install eslint eslint-loader --save-dev |
这些插件用来对 JavaScript 代码进行检查。
- 安装 html-webpack-plugin 插件
npm install html-webpack-plugin --save-dev |
该插件用来自动生成 HTML 文件。
- 安装 extract-text-webpack-plugin 插件
npm install extract-text-webpack-plugin --save-dev |
该插件用来将 CSS 抽取到独立的文件。
- 安装 clean-webpack-plugin 插件
npm install clean-webpack-plugin --save-dev |
该插件用来对 dist 文件夹进行清理工作,每次打包时先清理之前的 dist 文件夹。
下面是这些安装了的所有依赖:
... "devDependencies" : {
"autoprefixer" : "^7.1.3" , "babel-core" : "^6.26.0" , "babel-loader" : "^7.1.2" , "babel-preset-env" : "^1.6.0" , "clean-webpack-plugin" : "^0.1.16" , "css-loader" : "^0.28.7" , "eslint" : "^4.6.1" , "eslint-loader" : "^1.9.0" , "extract-text-webpack-plugin" : "^3.0.0" , "file-loader" : "^0.11.2" , "html-webpack-plugin" : "^2.30.1" , "postcss-loader" : "^2.0.6" , "style-loader" : "^0.18.2" , "url-loader" : "^0.5.9" , "webpack" : "^3.5.5" , "webpack-dev-server" : "^2.7.1" , "webpack-merge" : "^4.1.0" }, ... |
配置文件划分
使用 webpack 进行项目构建时,我们有不同的目的,因此最好将配置文件进行拆分,以适应不同的工作:
├─config │ config.js │ webpack.config.base.js │ webpack.config.dev.js │ webpack.config.lint.js │ webpack.config.prod.js │ webpack.config.js |
下面是一些配置的说明:
config.js:一些全局的配置,比如 HTML 文件的路径、publicPath 等 webpack.config.base.js:最基础的配置文件 webpack.config.dev.js:开发环境配置文件 webpack.config.lint.js:使用 ESLint 代码检查时的配置文件 webpack.config.prod.js:生产环境配置文件 webpack.config.js:主配置文件,根据环境变量引用相应的环境的配置 |
这些配置文件之间是通过 webpack-merge 这个插件进行合并的。
配置多页应用的关键点
如何使用 webpack 配置多页面应用呢?实现多页面应用的关键点在哪里呢?首先需要简单看一下多页应用和单页应用功能的区别。
单页应用的特点:
只有一个入口页面(index.html)
这个单页页面(index.html)中需要引入打包后的所有 JavaScript 文件
所有的页面内容完全由 JavaScript 生成
单页应用有自己的路由系统,服务器端没有和路由对应的文件
···
多页应用的特点:
每个版块对应一个页面
每个页面需要对公共的 JavaScript 进行引入
每个页面还需要引入和其自身对应的 JavaScript 文件
由于对应了多个页面,因此不是所有页面内容都是由 JavaScript 生成的
没有自己的路由系统,服务器端有对应的静态文件
···
抛开生成页面内容和路由系统,我们可以看到单页应用和多页应用最大的区别就是:
单页应用需要在入口页面引入所有的 JavaScript 文件
多页应用需要在每个页面中引入公共的 JavaScript 文件以及其自身的 JavaScript 文件
由于 CSS 文件是可以由 extract-text-webpack-plugin 这个插件自动提取并插入到 HTML 页面的,因此我们只需要关心如何在 HTML 页面中引入 JavaScript 文件了。
webpack 在打包时,会将入口文件中的 JavaScript 文件打包到某个目标文件中,在不考虑代码分割提取的情况下,一个入口文件会打包为一个目标文件,多个入口文件会打包为多个对应的目标文件。
因此,我们可以将每个多页页面中的特有的 JavaScript 文件作为入口文件,在打包时将对应打包成不同的 bundle 文件(结果文件),如果你想要的话,还可以在打包时进行代码分割处理,将公用代码抽取成一个文件,然后在 HTML 中引入这些 JavaScript 文件就好了。
总结一下,使用 webpack 配置多页应用的关键点在于:
将每个页面中特有的 JavaScript 文件作为入口文件进行打包
在打包后,每个页面中都需要引入这些打包后的文件
您可以在打包时进行公用代码提取,然后在 HTML 文件中引入
说了这么多,其实就是利用了 webpack 多入口文件进行打包。
自动生成 HTML 页面
在使用 webpack 对 JavaScript 文件进行打包时,通常需要在打包的文件名中加一个 hash 字符串用来防止缓存,当我们修改了 JavaScript 代码后,打包后的文件名也会发生变化。此时如果手动在 HTML 中引用这些 JavaScript 文件,是非常麻烦的。
因此,我们期望能自动生成 HTML 文件,并自动引用打包后的 JavaScript 文件。所谓自动生成 HTML 文件,可以理解为将源代码的 HTML 复制到目标文件夹中,同时自动引用打包后的 JavaScript 文件。
要完成这项操作,就需要使用前面安装的 html-webpack-plugin 这个插件。
html-webpack-plugin 插件的使用
首先,在我的项目中,有这么一些 HTML 页面,将它们放在 html 文件夹中:
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2017/9/5 18:04 1071 company_intro.html -a---- 2017/9/5 18:04 988 contact_us.html -a---- 2017/9/5 18:04 1131 cooperate.html -a---- 2017/9/5 18:04 1244 enterprise_culture.html -a---- 2017/9/5 18:04 1011 hornors.html -a---- 2017/9/5 18:04 1365 index.html -a---- 2017/9/5 18:04 1769 investment.html -a---- 2017/9/5 18:04 1005 join_us.html -a---- 2017/9/5 18:04 1037 news_center.html -a---- 2017/9/5 18:04 987 news_item.html -a---- 2017/9/5 18:04 1134 operate.html -a---- 2017/9/5 18:04 1255 product.html -a---- 2017/9/5 18:04 1132 schools.html |
然后,把这些 HTML 文件名(不要后缀)都写在 config.js 文件中,以供取用:
module.exports = {
HTMLDirs:[ "index" , "company_intro" , "enterprise_culture" , "hornors" , "news_center" , "news_item" , "product" , "schools" , "operate" , "cooperate" , "join_us" , "contact_us" , "investment" ], } |
HTMLDirs 是一个数组,其中保存了项目中会用到的所有 HTML 页面。
接下来,每个 HTML 页面都对应一份 JavaScript 代码,因此在 js 文件夹中建立对应的 JavaScript 文件:
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2017/9/5 18:04 2686 company_intro.js -a---- 2017/9/5 18:04 594 contact_us.js -a---- 2017/9/5 18:04 1725 cooperate.js -a---- 2017/9/8 16:54 3505 enterprise_culture.js -a---- 2017/9/5 18:04 2208 hornors.js -a---- 2017/9/8 16:54 4491 index.js -a---- 2017/9/5 18:04 3180 investment.js -a---- 2017/9/5 18:04 1327 join_us.js -a---- 2017/9/8 16:55 3689 news_center.js -a---- 2017/9/5 18:04 1972 news_item.js -a---- 2017/9/5 18:04 2728 operate.js -a---- 2017/9/5 18:04 2664 product.js -a---- 2017/9/5 18:04 2476 schools.js |
这两项是必须的,只有提供了每个页面的 HTML 文件和对应的 JavaScript 文件,才能构建多页面应用。
同时,可能每个页面都有自己的样式,因此您也可以在 css 文件夹中建立一些样式文件:
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2017/9/5 18:04 419 company_intro.css -a---- 2017/9/5 18:04 167 contact_us.css -a---- 2017/9/5 18:04 214 cooperate.css -a---- 2017/9/5 18:04 926 enterprise_culture.css -a---- 2017/9/5 18:04 255 hornors.css -a---- 2017/9/5 18:04 693 investment.css -a---- 2017/9/5 18:04 136 join_us.css -a---- 2017/9/5 18:04 541 news_center.css -a---- 2017/9/5 18:04 623 news_item.css -a---- 2017/9/5 18:04 342 operate.css -a---- 2017/9/5 18:04 236 product.css -a---- 2017/9/5 18:04 213 schools.css |
关于建立样式这一项,不是必须的。
最后,我们就可以使用 html-webpack-plugin 这个插件来自动生成 HTML 文件了,html-webpack-plugin 插件的用法如下:
// 引入插件 const HTMLWebpackPlugin = require( "html-webpack-plugin" ); // 引入多页面文件列表 const { HTMLDirs } = require( "./config" ); // 通过 html-webpack-plugin 生成的 HTML 集合 let HTMLPlugins = []; // 入口文件集合 let Entries = {} // 生成多页面的集合 HTMLDirs.forEach((page) => {
const htmlPlugin = new HTMLWebpackPlugin({
filename: `${page}.html`, template: path.resolve(__dirname, `../app/html/${page}.html`), chunks: [page, 'commons' ], }); HTMLPlugins.push(htmlPlugin); Entries[page] = path.resolve(__dirname, `../app/js/${page}.js`); }) |
</