webpack实战
查看所有文档页面: 全栈开发,获取更多信息。快马加鞭,加班加点,终于把这个文档整理出来了,顺便深入地学习一番,巩固知识,就是太累人,影响睡眠时间和质量。极客就是想要把事情做到极致,开始了就必须到达终点。
原文链接:webpack实战,原文广告模态框遮挡,阅读体验不好,所以整理成本文,方便查找。
本章教你如何用 Webpack 去解决实际项目中常见的场景。
按照不同场景划分成以下几类:
-
使用新语言来开发项目:
- 使用 ES6 语言
- 使用 TypeScript 语言
- 使用 Flow 检查器
- 使用 SCSS 语言
- 使用 PostCSS
-
使用新框架来开发项目:
- 使用 React 框架
- 使用 Vue 框架
- 使用 Angular2 框架
-
用 Webpack 构建单页应用:
- 为单页应用生成 HTML
- 管理多个单页应用
-
用 Webpack 构建不同运行环境的项目:
- 构建同构应用
- 构建 Electron 应用
- 构建 Npm 模块
- 构建离线应用
-
Webpack 结合其它工具搭配使用,各取所长:
- 搭配 Npm Script
- 检查代码
- 通过 Node.js API 启动 Webpack
- 使用 Webpack Dev Middleware
-
用 Webpack 加载特殊类型的资源:
- 加载图片
- 加载SVG
- 加载 Source Map
使用 TypeScript 语言
由于本文不推荐使用TypeScript,ES6就足够完成大部分任务。原文链接:使用 TypeScript 语言
使用 Angular2 框架
Angular2不在我的技术栈范围,所以这一章不加入,有兴趣的查看原文:使用 Angular2 框架
使用ES6语言
通常我们需要把采用 ES6 编写的代码转换成目前已经支持良好的 ES5 代码,这包含2件事:
- 把新的 ES6 语法用 ES5 实现,例如 ES6 的
class
语法用 ES5 的prototype
实现。 - 给新的 API 注入 polyfill ,例如使用新的
fetch
API 时注入对应的 polyfill 后才能让低端浏览器正常运行。
Babel
Babel 可以方便的完成以上2件事。
Babel 是一个 JavaScript 编译器,能将 ES6 代码转为 ES5 代码,让你使用最新的语言特性而不用担心兼容性问题,并且可以通过插件机制根据需求灵活的扩展。
在 Babel 执行编译的过程中,会从项目根目录下的 .babelrc
文件读取配置。.babelrc
是一个 JSON 格式的文件,内容大致如下:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"es2015",
{
"modules": false
}
],
"stage-2",
"react"
]
}
Plugins
plugins
属性告诉 Babel 要使用哪些插件,插件可以控制如何转换代码。
以上配置文件里的 transform-runtime
对应的插件全名叫做 babel-plugin-transform-runtime
,即在前面加上了 babel-plugin-
,要让 Babel 正常运行我们必须先安装它:
npm i -D babel-plugin-transform-runtime
babel-plugin-transform-runtime
是 Babel 官方提供的一个插件,作用是减少冗余代码。
Babel 在把 ES6 代码转换成 ES5 代码时通常需要一些 ES5 写的辅助函数来完成新语法的实现,例如在转换 class extent
语法时会在转换后的 ES5 代码里注入 _extent
辅助函数用于实现继承:
function _extent(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
}
这会导致每个使用了 class extent
语法的文件都被注入重复的 _extent
辅助函数代码,babel-plugin-transform-runtime
的作用在于不把辅助函数内容注入到文件里,而是注入一条导入语句:
var _extent = require('babel-runtime/helpers/_extent');
这样能减小 Babel 编译出来的代码的文件大小。
同时需要注意的是由于 babel-plugin-transform-runtime
注入了 require('babel-runtime/helpers/_extent')
语句到编译后的代码里,需要安装 babel-runtime
依赖到你的项目后,代码才能正常运行。 也就是说 babel-plugin-transform-runtime
和 babel-runtime
需要配套使用,使用了 babel-plugin-transform-runtime
后一定需要 babel-runtime
。
Presets
presets
属性告诉 Babel 要转换的源码使用了哪些新的语法特性,一个 Presets
对一组新语法特性提供支持,多个 Presets
可以叠加。
Presets
其实是一组 Plugins 的集合,每一个 Plugin 完成一个新语法的转换工作。Presets 是按照 ECMAScript 草案来组织的,通常可以分为以下三大类:
-
已经被写入 ECMAScript 标准里的特性,由于之前每年都有新特性被加入到标准里;
- env 包含当前所有 ECMAScript 标准里的最新特性。
-
被社区提出来的但还未被写入 ECMAScript 标准里特性,这其中又分为以下四种:
-
stage0
只是一个美好激进的想法,有 Babel 插件实现了对这些特性的支持,但是不确定是否会被定为标准; -
stage1
值得被纳入标准的特性; -
stage2
该特性规范已经被起草,将会被纳入标准里; -
stage3
该特性规范已经定稿,各大浏览器厂商和 Node.js 社区开始着手实现; -
stage4
在接下来的一年将会加入到标准里去。
-
- 为了支持一些特定应用场景下的语法,和 ECMAScript 标准没有关系,例如
babel-preset-react
是为了支持 React 开发中的 JSX 语法。
在实际应用中,你需要根据项目源码所使用的语法去安装对应的 Plugins 或 Presets。
接入 Babel
由于 Babel 所做的事情是转换代码,所以应该通过 Loader 去接入 Babel,Webpack 配置如下:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
},
]
},
// 输出 source-map 方便直接调试 ES6 源码
devtool: 'source-map'
};
配置命中了项目目录下所有的 JavaScript 文件,通过 babel-loader
去调用 Babel 完成转换工作。 在重新执行构建前,需要先安装新引入的依赖:
# Webpack 接入 Babel 必须依赖的模块
npm i -D babel-core babel-loader
# 根据你的需求选择不同的 Plugins 或 Presets
npm i -D babel-preset-env
使用SCSS语言
SCSS 可以让你用更灵活的方式写 CSS。 它是一种 CSS 预处理器,语法和 CSS 相似,但加入了变量、逻辑等编程元素,代码类似这样:
$blue: #1875e7;
div {
color: $blue;
}
SCSS 又叫 SASS,区别在于 SASS 语法类似 Ruby,而 SCSS 语法类似 CSS,对于熟悉 CSS 的前端工程师来说会更喜欢 SCSS。
采用 SCSS 去写 CSS 的好处在于可以方便地管理代码,抽离公共的部分,通过逻辑写出更灵活的代码。 和 SCSS 类似的 CSS 预处理器还有 LESS 等。
使用 SCSS 可以提升编码效率,但是必须把 SCSS 源代码编译成可以直接在浏览器环境下运行的 CSS 代码。
node-sass
核心模块是由 C++ 编写,再用 Node.js 封装了一层,以供给其它 Node.js 调用。 node-sass
还支持通过命令行调用,先安装它到全局:
npm i -g node-sass
再执行编译命令:
# 把 main.scss 源文件编译成 main.css
node-sass main.scss main.css
你就能在源码同目录下看到编译后的 main.css
文件。
接入 Webpack
Webpack 接入 sass-loader
相关配置如下:
module.exports = {
module: {
rules: [
{
// 增加对 SCSS 文件的支持
test: /\.scss/,
// SCSS 文件的处理顺序为先 sass-loader 再 css-loader 再 style-loader
use: ['style-loader', 'css-loader', 'sass-loader'],
},
]
},
};
以上配置通过正则 /\.scss/
匹配所有以 .scss
为后缀的 SCSS 文件,再分别使用3个 Loader 去处理。具体处理流程如下:
- 通过
sass-loader
把 SCSS 源码转换为 CSS 代码,再把 CSS 代码交给css-loader
去处理。 -
css-loader
会找出 CSS 代码中的@import
和url()
这样的导入语句,告诉 Webpack 依赖这些资源。同时还支持 CSS Modules、压缩 CSS 等功能。处理完后再把结果交给style-loader
去处理。 -
style-loader
会把 CSS 代码转换成字符串后,注入到 JavaScript 代码中去,通过 JavaScript 去给 DOM 增加样式。如果你想把 CSS 代码提取到一个单独的文件而不是和 JavaScript 混在一起,可以使用1-5 使用Plugin 中介绍过的 ExtractTextPlugin。
由于接入 sass-loader
,项目需要安装这些新的依赖:
# 安装 Webpack Loader 依赖
npm i -D sass-loader css-loader style-loader
# sass-loader 依赖 node-sass
npm i -D node-sass
使用Flow检查器
Flow 是一个 Facebook 开源的 JavaScript 静态类型检测器,它是 JavaScript 语言的超集。
你所需要做的就是在需要的地方加上类型检查,例如在两个由不同人开发的模块对接的接口出加上静态类型检查,能在编译阶段就指出部分模块使用不当的问题。 同时 Flow 也能通过类型推断检查出 JavaScript 代码中潜在的 Bug。
Flow 使用效果如下:
// @flow
// 静态类型检查
function square1(n: number): number {
return n * n;
}
square1('2'); // Error: square1 需要传入 number 作为参数
// 类型推断检查
function square2(n) {
return n * n; // Error: 传入的 string 类型不能做乘法运算
}
square2('2');
需要注意的时代码中的第一行
// @flow
告诉 Flow 检查器这个文件需要被检查。
使用 Flow
Flow 检测器由高性能跨平台的 OCaml 语言编写,它的可执行文件可以通过:
npm i -D flow-bin
安装,安装完成后通过先配置 Npm Script:
"scripts": {
"flow": "flow"
}
再通过 npm run flow
去调用 Flow 执行代码检查。
除此之外你还可以通过:
npm i -g flow-bin
把 Flow 安装到全局后,再直接通过 flow
命令去执行代码检查。
安装成功后,在项目根目录下执行 Flow 后,Flow 会遍历出所有需要检查的文件并对其进行检查,输出错误结果到控制台。
采用了 Flow 静态类型语法的 JavaScript 是无法直接在目前已有的 JavaScript 引擎中运行的,要让代码可以运行需要把这些静态类型语法去掉。
// 采用 Flow 的源代码
function foo(one: any, two: number, three?): string {}
// 去掉静态类型语法后输出代码
function foo(one, two, three) {}
有两种方式可以做到这点:
-
flow-remove-types
可单独使用,速度快。 -
babel-preset-flow
与 Babel 集成。
集成 Webpack
由于使用了 Flow 项目一般都会使用 ES6 语法,所以把 Flow 集成到使用 Webpack 构建的项目里最方便的方法是借助 Babel。
- 安装
npm i -D babel-preset-flow
依赖到项目。 -
修改
.babelrc
配置文件,加入 Flow Preset:"presets": [ ...[], "flow" ]
往源码里加入静态类型后重新构建项目,你会发现采用了 Flow 的源码还是能正常在浏览器中运行。
要明确构建的目的只是为了去除源码中的 Flow 静态类型语法,而代码检查和构建无关。 许多编辑器已经整合 Flow,可以实时在代码中高亮指出 Flow 检查出的问题。
使用PostCSS
PostCSS 是一个 CSS 处理工具,和 SCSS 不同的地方在于它通过插件机制可以灵活的扩展其支持的特性,而不是像 SCSS 那样语法是固定的。 PostCSS 的用处非常多,包括给 CSS 自动加前缀、使用下一代 CSS 语法等,目前越来越多的人开始用它,它很可能会成为 CSS 预处理器的最终赢家。
PostCSS 和 CSS 的关系就像 Babel 和 JavaScript 的关系,它们解除了语法上的禁锢,通过插件机制来扩展语言本身,用工程化手段给语言带来了更多的可能性。PostCSS 和 SCSS 的关系就像 Babel 和 TypeScript 的关系,PostCSS 更加灵活、可扩张性强,而 SCSS 内置了大量功能而不能扩展。
给 CSS 自动加前缀,增加各浏览器的兼容性:
/*输入*/
h1 {
display: flex;
}
/*输出*/
h1 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
使用下一代 CSS 语法:
/*输入*/
:root {
--red: #d33;
}
h1 {
color: var(--red);
}
/*输出*/
h1 {
color: #d33;
}
PostCSS 全部采用 JavaScript 编写,运行在 Node.js 之上,即提供了给 JavaScript 代码调用的模块,也提供了可执行的文件。
在 PostCSS 启动时,会从目录下的 postcss.config.js
文件中读取所需配置,所以需要新建该文件,文件内容大致如下:
module.exports = {
plugins: [
// 需要使用的插件列表
require('postcss-cssnext')
]
}
其中的 postcss-cssnext
插件可以让你使用下一代 CSS 语法编写代码,再通过 PostCSS 转换成目前的浏览器可识别的 CSS,并且该插件还包含给 CSS 自动加前缀的功能。
目前 Chrome 等现代浏览器已经能完全支持cssnext
中的所有语法,也就是说按照cssnext
语法写的 CSS 在不经过转换的情况下也能在浏览器中直接运行。
接入 Webpack
虽然使用 PostCSS 后文件后缀还是 .css
但这些文件必须先交给 postcss-loader
处理一遍后再交给 css-loader
。
接入 PostCSS 相关的 Webpack 配置如下:
module.exports = {
module: {
rules: [
{
// 使用 PostCSS 处理 CSS 文件
test: /\.css/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
]
},
};
接入 PostCSS 给项目带来了新的依赖需要安装,如下:
# 安装 Webpack Loader 依赖
npm i -D postcss-loader css-loader style-loader
# 根据你使用的特性安装对应的 PostCSS 插件依赖
npm i -D postcss-cssnext
使用React框架
React 语法特征
使用了 React 项目的代码特征有 JSX 和 Class 语法,例如:
class Button extends Component {
render() {
return Hello,Webpack
}
}
在使用了 React 的项目里 JSX 和 Class 语法并不是必须的,但使用新语法写出的代码看上去更优雅。
其中 JSX 语法是无法在任何现有的 JavaScript 引擎中运行的,所以在构建过程中需要把源码转换成可以运行的代码,例如:
// 原 JSX 语法代码
return Hello,Webpack
// 被转换成正常的 JavaScript 代码
return React.createElement('h1', null, 'Hello,Webpack')
React 与 Babel
要在使用 Babel 的项目中接入 React 框架是很简单的,只需要加入 React 所依赖的 Presets babel-preset-react
。
通过以下命令:
# 安装 React 基础依赖
npm i -D react react-dom
# 安装 babel 完成语法转换所需依赖
npm i -D babel-preset-react
安装新的依赖后,再修改 .babelrc
配置文件加入 React Presets
"presets": [
"react"
],
就完成了一切准备工作。
再修改 main.js
文件如下:
import * as React from 'react';
import { Component } from 'react';
import { render } from 'react-dom';
class Button extends Component {
render() {
return Hello,Webpack
}
}
render(, window.document.getElementById('app'));
重新执行构建打开网页你将会发现由 React 渲染出来的 Hello,Webpack
。
React 与 TypeScript
TypeScript 相比于 Babel 的优点在于它原生支持 JSX 语法,你不需要重新安装新的依赖,只需修改一行配置。 但 TypeScript 的不同在于:
- 使用了 JSX 语法的文件后缀必须是
tsx
。 - 由于 React 不是采用 TypeScript 编写的,需要安装
react
和react-dom
对应的 TypeScript 接口描述模块@types/react
和@types/react-dom
后才能通过编译。
修改 TypeScript 编译器配置文件 tsconfig.json
增加对 JSX 语法的支持,如下:
{
"compilerOptions": {
"jsx": "react" // 开启 jsx ,支持 React
}
}
由于 main.js
文件中存在 JSX 语法,再把 main.js
文件重命名为 main.tsx
,同时修改文件内容为在上面 React 与 Babel 里所采用的 React 代码。 同时为了让 Webpack 对项目里的 ts
与 tsx
原文件都采用 awesome-typescript-loader
去转换, 需要注意的是 Webpack Loader 配置的 test
选项需要匹配到 tsx
类型的文件,并且 extensions
中也要加上 .tsx
,配置如下:
module.exports = {
// TS 执行入口文件
entry: './main',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
},
resolve: {
// 先尝试 ts,tsx 后缀的 TypeScript 源码文件
extensions: ['.ts', '.tsx', '.js',]
},
module: {
rules: [
{
// 同时匹配 ts,tsx 后缀的 TypeScript 源码文件
test: /\.tsx?$/,
loader: 'awesome-typescript-loader'
}
]
},
devtool: 'source-map',// 输出 Source Map 方便在浏览器里调试 TypeScript 代码
};
通过npm i react react-dom @types/react @types/react-dom
安装新的依赖后重启构建,重新打开网页你将会发现由 React 渲染出来的 Hello,Webpack
。
使用Vue框架
Vue是一个渐进式的 MVVM 框架,相比于 React、Angular 它更灵活轻量。 它不会强制性地内置一些功能和语法,你可以根据自己的需要一点点地添加功能。 虽然采用 Vue 的项目能用可直接运行在浏览器环境里的代码编写,但为了方便编码大多数项目都会采用 Vue 官方的单文件组件的写法去编写项目。
Vue 的单文件组件通过一个类似 HTML 文件的 .vue
文件就能描述清楚一个组件所需的模版、样式、逻辑。
main.js
入口文件:
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App)
});
入口文件创建一个 Vue 的根实例,在 ID 为 app
的 DOM 节点上渲染出上面定义的 App 组件。
接入 Webpack
目前最成熟和流行的开发 Vue 项目的方式是采用 ES6 加 Babel 转换,这和基本的采用 ES6 开发的项目很相似,差别在于要解析 .vue
格式的单文件组件。 好在 Vue 官方提供了对应的 vue-loader
可以非常方便的完成单文件组件的转换。
修改 Webpack 相关配置如下:
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader'],
},
]
}
安装新引入的依赖:
# Vue 框架运行需要的库
npm i -S vue
# 构建所需的依赖
npm i -D vue-loader css-loader vue-template-compiler
在这些依赖中,它们的作用分别是:
-
vue-loader
:解析和转换.vue
文件,提取出其中的逻辑代码script
、样式代码style
、以及 HTML 模版template
,再分别把它们交给对应的 Loader 去处理。 -
css-loader
:加载由vue-loader
提取出的 CSS 代码。 -
vue-template-compiler
:把vue-loader
提取出的 HTML 模版编译成对应的可执行的 JavaScript 代码,这和 React 中的 JSX 语法被编译成 JavaScript 代码类似。预先编译好 HTML 模版相对于在浏览器中再去编译 HTML 模版的好处在于性能更好。
使用 TypeScript 编写 Vue 应用
从 Vue 2.5.0+ 版本开始,提供了对 TypeScript 的良好支持,使用 TypeScript 编写 Vue 是一个很好的选择,因为 TypeScript 能检查出一些潜在的错误。
新增 tsconfig.json
配置文件,内容如下:
{
"compilerOptions": {
// 构建出 ES5 版本的 JavaScript,与 Vue 的浏览器支持保持一致
"target": "es5",
// 开启严格模式,这可以对 `this` 上的数据属性进行更严格的推断
"strict": true,
// TypeScript 编译器输出的 JavaScript 采用 es2015 模块化,使 Tree Shaking 生效
"module": "es2015",
"moduleResolution": "node"
}
}
修改 App.vue
脚本部分内容如下:
注意 script
标签中的 lang="ts"
是为了指明代码的语法是 TypeScript。
修改 main.ts 执行入口文件为如下:
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App)
});
由于 TypeScript 不认识 .vue
结尾的文件,为了让其支持 import App from './App.vue'
导入语句,还需要以下文件 vue-shims.d.ts
去定义 .vue
的类型:
// 告诉 TypeScript 编译器 .vue 文件其实是一个 Vue
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
Webpack 配置需要修改两个地方,如下:
const path = require('path');
module.exports = {
resolve: {
// 增加对 TypeScript 的 .ts 和 .vue 文件的支持
extensions: ['.ts', '.js', '.vue', '.json'],
},
module: {
rules: [
// 加载 .ts 文件
{
test: /\.ts$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
// 让 tsc 把 vue 文件当成一个 TypeScript 模块去处理,以解决 moudle not found 的问题,tsc 本身不会处理 .vue 结尾的文件
appendTsSuffixTo: [/\.vue$/],
}
},
]
},
};
除此之外还需要安装新引入的依赖:npm i -D ts-loader typescript
为单页应用生成HTML
引入问题
在使用 React 框架中,是用最简单的 Hello,Webpack
作为例子让大家理解, 这个例子里因为只输出了一个 bundle.js
文件,所以手写了一个 index.html
文件去引入这个 bundle.js
,才能让应用在浏览器中运行起来。
在实际项目中远比这复杂,一个页面常常有很多资源要加载。接下来举一个实战中的例子,要求如下:
- 项目采用 ES6 语言加 React 框架。
- 给页面加入 Google Analytics,这部分代码需要内嵌进 HEAD 标签里去。
- 给页面加入 Disqus 用户评论,这部分代码需要异步加载以提升首屏加载速度。
- 压缩和分离 JavaScript 和 CSS 代码,提升加载速度。
在开始前先来看看该应用最终发布到线上的代码。
可以看到部分代码被内嵌进了 HTML 的 HEAD 标签中,部分文件的文件名称被打上根据文件内容算出的 Hash 值,并且加载这些文件的 URL 地址也被正常的注入到了 HTML 中。
解决方案
推荐一个用于方便地解决以上问题的 Webpack 插件 web-webpack-plugin。 该插件已经被社区上许多人使用和验证,解决了大家的痛点获得了很多好评,下面具体介绍如何用它来解决上面的问题。
首先,修改 Webpack 配置。
以上配置中,大多数都是按照前面已经讲过的内容增加的配置,例如:
- 增加对 CSS 文件的支持,提取出 Chunk 中的 CSS 代码到单独的文件中,压缩 CSS 文件;
- 定义
NODE_ENV
环境变量为production
,以去除源码中只有开发时才需要的部分; - 给输出的文件名称加上 Hash 值;
- 压缩输出的 JavaScript 代码。
但最核心的部分在于 plugins
里的:
new WebPlugin({
template: './template.html', // HTML 模版文件所在的文件路径
filename: 'index.html' // 输出的 HTML 的文件名称
})
其中 template: './template.html'
所指的模版文件 template.html
的内容是:
该文件描述了哪些资源需要被以何种方式加入到输出的 HTML 文件中。
以 为例,按照正常引入 CSS 文件一样的语法来引入 Webpack 生产的代码。
href
属性中的 app?_inline
可以分为两部分,前面的 app
表示 CSS 代码来自名叫 app
的 Chunk 中,后面的 _inline
表示这些代码需要被内嵌到这个标签所在的位置。
同样的 表示 JavaScript 代码来自相对于当前模版文件
template.html
的本地文件 ./google_analytics.js
, 而且文件中的 JavaScript 代码也需要被内嵌到这个标签所在的位置。
也就是说资源链接 URL 字符串里问号前面的部分表示资源内容来自哪里,后面的 querystring
表示这些资源注入的方式。
除了 _inline
表示内嵌外,还支持以下属性:
-
_dist
只有在生产环境下才引入该资源; -
_dev
只有在开发环境下才引入该资源; -
_ie
只有IE浏览器才需要引入的资源,通过[if IE]>resource 注释实现。
这些属性之间可以搭配使用,互不冲突。例如 app?_inline&_dist
表示只在生产环境下才引入该资源,并且需要内嵌到 HTML 里去。
WebPlugin
插件还支持一些其它更高级的用法,详情可以访问该项目主页阅读文档。
管理多个单页应用
引入问题
在开始前先来看看该应用最终发布到线上的代码。
构建出的目录结构为:
dist
├── common_029086ff.js
├── common_7cc98ad0.css
├── index.html
├── index_04c08fbf.css
├── index_b3d3761c.js
├── login.html
├── login_0a3feca9.js
└── login_e31e214b.css
如果按照上节的思路,可能需要为每个单页应用配置一段如下代码:
new WebPlugin({
template: './template.html', // HTML 模版文件所在的文件路径
filename: 'login.html' // 输出的 HTML 的文件名称
})
并且把页面对应的入口加入到 enrty
配置项中,就像这样:
entry: {
index: './pages/index/index.js',// 页面 index.html 的入口文件
login: './pages/login/index.js',// 页面 login.html 的入口文件
}
当有新页面加入时就需要修改 Webpack 配置文件,新插入一段以上代码,这会导致构建代码难以维护而且易错。
解决方案
项目源码目录结构如下:
├── pages
│ ├── index
│ │ ├── index.css // 该页面单独需要的 CSS 样式
│ │ └── index.js // 该页面的入口文件
│ └── login
│ ├── index.css
│ └── index.js
├── common.css // 所有页面都需要的公共 CSS 样式
├── google_analytics.js
├── template.html
└── webpack.config.js
从目录结构中可以看成出下几点要求:
- 所有单页应用的代码都需要放到一个目录下,例如都放在
pages
目录下; - 一个单页应用一个单独的文件夹,例如最后生成的
index.html
相关的代码都在index
目录下,login.html
同理; - 每个单页应用的目录下都有一个
index.js
文件作为入口执行文件。
虽然
AutoWebPlugin
强制性的规定了项目部分的目录结构,但从实战经验来看这是一种优雅的目录规范,合理的拆分了代码,又能让新人快速的看懂项目结构,也方便日后的维护。
Webpack 配置文件修改如下:
See the Pen webpack管理多个单页应用 by whjin (@whjin) on CodePen.