使用 webpack3 配置多页应用(一)

在本系列文章的基础上,我对脚手架配置进行了一些优化整理,基于 Webpack4 和 Vue,下面是文章链接:
基于 Webpack4 + Vue 的多页应用解决方案(一)
基于 Webpack4 + Vue 的多页应用解决方案(二)


为什么需要使用 webpack 构建多页应用呢?因为某些项目使用 SPA 不太合适(大多是 SEO 的原因),或者您在做项目时有其他的需求。

###############
2018 年 11 月补充
彼时我对多页应用和 SEO 之间的关系存在误解,多页应用和 SEO 实际上没有必然的联系。要解决 SEO 的问题,最好通过 SSR(服务端渲染)和 CSR(客户端渲染)的配合,也叫做同构。
这几篇文章中没有介绍同构的知识,因此不必将 SEO 的问题代入。
###############

对我自己而言,前段时间需要对公司官网进行移动端改版,其实官网本身比较简单,PC 端的官网的接口调用和公共函数移动端完全可以使用,最多的改动就是样式了,完全可以使用 ES5 一路复制粘贴下去。但我也想尝试一点新东西,我想通过移动端官网这个项目做一些练手工作,我的需求大概如下:

  • 使用 ES6 进行开发
  • 期望使用面向对象开发(class)
  • 自动压缩合并 CSS 和 JS 文件
  • 使用 ESLint 进行代码检查
  • 自动生成 HTML 文件
  • 自动抽取 CSS 文件
  • ···

有了这些需求,基本上就必须使用 webpack 了,于是又开始了一次采坑之旅。我将尽可能详细的将配置过程写出来,方便您进行参考,也方便自己日后查看。本脚手架是基于 webpack3 配置的。

安装依赖

首先是项目中需要使用的依赖安装。
1.安装 webpack 和 webpack-dev-server

npm install webpack webpack-dev-server --save-dev

2.安装 webpack-merge

npm install webpack-merge --save-dev

该插件用来对 webpack 配置进行合并操作。
3.安装 babel 相关插件

npm install babel-core babel-loader babel-preset-env --save-dev

这系列插件用来对 ES6 语法进行转换。
4.安装样式处理相关插件

npm install css-loader style-loader postcss-loader autoprefixer --save-dev

这系列插件用来处理 CSS 样式,其中 autoprefixer 是 postcss 的一个插件,用来自动给 CSS 样式添加前缀。
5.安装 file-loader

npm install file-loader --save-dev

该插件将在导入图片、字体等文件时发挥作用。
PS.您也可以安装 url-loader 以实现相同的作用:

npm install url-loader --save-dev

6.安装 ESLint 相关的插件

npm install eslint eslint-loader --save-dev

这些插件用来对 JavaScript 代码进行检查。
7.安装 html-webpack-plugin 插件

npm install html-webpack-plugin --save-dev

该插件用来自动生成 HTML 文件。
8.安装 extract-text-webpack-plugin 插件

npm install extract-text-webpack-plugin --save-dev

该插件用来将 CSS 抽取到独立的文件。
9.安装 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`);
})

在上面的代码中,首先引入了所需的插件和变量,然后利用 html-webpack-plugin 循环生成 HTML 页面。
简单说下 HTMLWebpackPlugin 构造函数的几个参数:

  • filename:生成的 HTML 文件名,我这里选择和原始文件名保持一致
  • template:生成 HTML 文件使用的模板,也就是我们之前在 html 文件夹中建立的那些文件
  • chunks:生成 HTML 文件时会自动插入相应的代码片段(也就是 JavaScript 文件),我这里选择插入每个页面对应的 JavaScript 文件,以及最后提取出来的公共文件代码块。

关于 chunks 还需要说明一点,chunks 是一个数组,在生成 HTML 文件时会将数组中的对应的 JavaScript 片段自动插入到 HTML 中,这些片段也就是 webpack 打包时的 output 选项中的 [name]。这里只需要写上 [name] 值就行了,无需使用打包生成的完整名称,因为这会还没开始打包呢,打包后生成的名称咱也不知道。
最后,我们把这些生成 HTML 文件的配置插入到 HTMLPlugins 这个数组中,同时设置 webpack 的入口文件。

总结

本文主要谈到了使用 webpack 构建多页面应用的几个核心问题,虽然就是运用了 webpack 的多入口打包以及自动生成 HTML 的一个插件,但其中的缘由还是需要我们了解的。
本文只是一个粗略的介绍,没有涉及太多配置文件细节的内容,这部分内容会在下一篇文章介绍,此处限于篇幅就不再展开了。

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 写在开头 先说说为什么要写这篇文章, 最初的原因是组里的小朋友们看了webpack文档后, 表情都是这样的: (摘...
    Lefter阅读 5,234评论 4 31
  • 最近在学习 Webpack,网上大多数入门教程都是基于 Webpack 1.x 版本的,我学习 Webpack 的...
    My_Oh_My阅读 8,095评论 40 247
  • GitChat技术杂谈 前言 本文较长,为了节省你的阅读时间,在文前列写作思路如下: 什么是 webpack,它要...
    萧玄辞阅读 12,613评论 7 110
  • 闲的无聊,去新华书店逛了一圈,似乎好久没有这样静下心来看看书了,这里安静的想让人屏住呼吸,看着那么多安静看书...
    杨大壮最坚强阅读 483评论 0 2
  • 一直想坚持记录自己的生活,将每天的所思所想、所做的事都记录下来,时时检视自己生命的痕迹;但同时又对自己有较高的要求...
    淡淡的平静阅读 197评论 0 0