前言
- WebPack 是什么?
WebPack 是什么,WebPack 可以看做是模块打包机:它做的事情是,分析你的项目结构,找到 JavaScript 模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript 等),并将其转换和打包为合适的格式供浏览器使用。
- WebPack 可以用来做什么?
我们都知道,前端技术不断发展,已有的
html
,js
,css
等基础的技术已经不能满足寻(yue)求(lai)捷(yue)径(lan)的程序员群体了,于是jsx
,less
,sass
,以及有利于模块化开发的AMD
,CommonJS
,ES2015 import
等利于完成项目的方案被一个个的提了出来并得以采用,但是这些方案并不能直接被浏览器识别支持,因此与这些方案一同提出的往往会有各种转换工具。
当我们在项目中运用了jsx
,less
,sass
等工具时,往往需要使用babel
以及其他的转换工具将其转换为js
,css
等浏览器识别的样式。当一个项目比较复杂时,这个任务量无疑是庞大的,更不要说当代码体积过大时我们还需要对代码进行压缩,优化,分割,整个项目完成下来耗费的精力和时间想一想都觉得可怕。
于是,webpack 走进了我们的视野。它的理念是一切皆模块,将所有的文件都当做模块处理(需要注意的是,在 webpack 中,文件是模块,但模块不一定是文件)。我们在 webpack 可以使用各种各样的 loader,用以转换各种不同类型的文件,也可以使用各种各样的插件plugin,对我们体积庞大的项目代码进行优化,分割,压缩,提取等。
- WebPack和别的打包工具如Grunt以及Gulp的区别在哪里?
Gulp / Grunt 是一种工具,能够优化前端工作流程。比如自动刷新页面、雪碧图、压缩 css、js、编译 less 等等,但是这些操作都需要在gulp的配置文件中详细设置。
webpack 是预编译的,你在本地直接写 JS,不管是 AMD / CMD / ES6 风格的模块化,它都能认识,不需要另外再在浏览器中加载解释器,它还可以只通过一个文件入口自动寻找其依赖的所有模块,包括文件模块,编译成一个或多个浏览器认识的 JS,还可以通过插件plugin对生成的js代码进行各种优化,这些操作只需要在配置文件中设置用得到的loader或者plugin就可以了,不用告诉webpack具体怎样操作。
两者功能有重合部分,如压缩,合并等,但是各有优势,可以结合使用
综上:
1. gulp/grunt 是工具链,构建工具,自动化,提高效率用。
2. webpack 是文件打包工具,模块化识别,编译模块代码,优化代码方案用。
本文结构:
本文将通过以下十七部分介绍webpack使用方法:
1. webpack环境构建
2. webpack打包ES6文件
3. 引入webpack配置文件
4. webpack-dev-server
5. Source Maps
6. 编译器
7. webpack打包jsx文件
8. webpack打包css文件
9. webpack打包img文件
10. 插件
11. BannerPlugin插件
12. HtmlWebpackPlugin插件
13. HMR插件
14. BundleAnalyzerPlugin插件
15. ExtractTextPlugin插件
16. CommonsChunkPlugin插件
17. UglifyJsPlugin插件
WebPack踩坑之路开始
一、 webpack环境构建
- 首先全局安装 webpack:
在终端中输入
npm install -g webpack
- 我们可以使用 Webstom 编辑器新建一个空项目 webpackdemo,也可以在桌面新建一个空文件夹,名字自定,然后在里面新建一个 app 文件夹和一个 build 文件夹,在 app 文件夹中创建 main.js文件和Greeter.js文件,在 build 文件夹下创建 index.html 文件。
这里的app文件夹里面就是我们平常写的代码目录,在 webpack 中我们会选择一个主js文件main.js,如果需要其他的 js,css,img文件,可以将其作为模块引入到 main.js 中,而 build 文件夹中存放的是我们使用 webpack 将繁杂的 css 和 js 打包过后生成的文件。
- 因为我们是在node环境下打包文件的,因此每一个新的项目都需要一个 package.json 文件,它是一个标准的 npm 说明文件,包含了当前项目依赖的模块,自定义的脚本任务等,创建该文件的方法是进入 webpackdemo 文件夹中,然后在命令窗口输入:
npm init
,接着一路按回车就可以了。
- 我们虽然已经在全局安装了 webpack ,但为了稳妥起见,还是在当前项目中再安装一遍 webpack:
npm install --save-dev webpack
这里的 --save-dev 会将安装的该模块的名称存储在 package.json 文件中的 devDependencies 关键字下,便于我们查看已安装模块信息。
现在我们的项目结构如下图:
二、 webpack打包js文件
- 我们先在 index.html 中写入代码:
// index.html
webpackdom
这里引入的 bundle.js 是通过webpack打包后的可以供浏览器识别的 js 文件(现在还不存在)
- 接着我们在 Greeter.js 中写入一个函数,用途是在页面中打印出来一句话,然后将该函数作为一个模块导出:
// Greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
};
- 接下来编写我们的 main.js 文件,引入Greeter 文件导出的模块,并将该模块添加到页面的div节点中:
// main.js
const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeter());
- 上述文件编写完毕后,我们可以在命令窗口输入webpack打包命令:
//webpack 打包命令
webpack {entry-file} {output for bundled file}
{entry-file} //入口文件路径,即我们所说的主js文件main.js
{output for bundled file} //打包输出的js文件路径
在我们的这个demo中,需要输入的命令是:
webpack app/main.js build/bundle.js
打包成功界面:
上图显示的 bundle.js
即为 main.js
和 Greeter.js
被 webpack
打包成功后生成的文件,打开 index.html ,在浏览器页面会显示如下内容
至此为止,我们成功的用webpack打包了 Greeter.js 文件。
三、 引入webpack配置文件
我们在进行上述操作时会发现,如果 webpack 每次打包都必须输入那行很长的打包命令,无疑是追求高(xiang)效(tou)率(lan)的我们所不能容忍的,我们希望通过更简单的命令来让 webpack 工作。但是我们命令简单了,就需要另建一个文件来告诉 webpack 它需要怎样工作,这个文件就是webpack配置文件。
webpack配置文件作用具体是什么呢?
它是来告诉 webpack ,你要从哪个文件(入口文件)开始,找和这个文件有关系的所有模块,包含引入的第三方模块和文件模块。
找到这些模块后,假如这些模块中用了比如 less, sass, es2015, jsx 等浏览器不能直接识别支持的语法,webpack 会自己用我们在配置文件中设置好的转换工具(loader),将这些语法转换成 CSS3 ES5 等浏览器可以直接识别的语法,然后将这些模块和入口文件一起打包成一个文件。
但是这个文件如果不进行处理,体积会很大。因此在这里 webpack 会引用我们在配置文件中设置的插件(plugin)对生成的文件进行优化,比如分离css,切割代码,分离引入的第三方模块等。
我们先在根目录下创建一个 webpack.config.js ,在里面写入以下代码:
// webpack.config.js
module.exports ={
entry:__dirname + '/app/main.js', //入口文件
output:{
path: __dirname + '/build', //输出文件路径
filename:'bundle.js' //输出文件名
}
}
这样我们的一个最基本最简单的webpack配置文件就创建好了,现在我们可以在命令窗口输入webpack
,然后回车,
出现上图内容即为打包成功。
以上我们已经用了两种打包方式,分别是命令行打包和配置文件打包,在这里继续介绍第三种打包方式:命令窗口输入 npm run xxx
引导任务执行。
首先我们进入 package.json 文件,找到 scripts 属性,在下面添加
"xxx":"webpack" // xxx为个人定义的属性名,可以用 `npm run xxx` 来运行后面的文件
此时 package.json 中的代码如下:
// package.json
{
"name": "webpackdemo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev":"webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^3.6.0"
}
}
这里有个坑:JSON 文件中 不能 添加 注释 !!!
接下来我们运行 npm run dev ,如果你在上面加的属性名是,同样会出现以下结果
以上,我们引入了 webpack.config.js 配置文件并总结出了三种 webpack 打包:
- 命令行打包
webpack {entry-file} {output for bundled file}
- webpack打包
webpack
- npm run xxx 打包
npm run xxx
推荐使用第二种方式
四、 webpack-dev-server
本小节我们将介绍一个本地服务器 webpack-dev-server
。
webpack-dev-server
基于 node.js
构建,可以让浏览器监听我们的代码修改,代码一旦改动并保存,浏览器会立刻自动刷新。
在使用 webpack-dev-server
之前,我们需要先安装它作为项目依赖,在命令窗口输入以下指令:
npm install --save-dev webpack-dev-server
我们若想使用 webpack-dev-server
包括后面要讲的各种 loader
或者 plugin
, 都需要在 webpack.config.js 文件中进行配置:
devServer配置选项 | 功能描述 |
---|---|
contentBase | 默认webpack-dev-server 会为根文件夹提供本地服务器,如果想为另外目录下的文件夹提供服务器,应该在该配置选项中重新设置所在目录 |
prot | 设置默认监听端口,默认为"8080" |
inline | 设置为true 时,当源文件改变时会自动刷新页面 |
historyApiFallback | 在开发单页应用时非常有用,依赖于HTML5 history API ,如果设置为true ,所有的跳转将指向index.html |
接下来更新 webpack.config.js 文件
//webpack.config.js
module.exports ={
entry:__dirname + '/app/main.js',
output:{
path: __dirname + '/build',
filename:'bundle.js'
},
//更新部分代码
devServer: {
contentBase:"./build", //本地服务器加载页面所在目录
historyApiFallback:true, //不跳转
inline:true //实时刷新
}
}
在 package.json
中的 scripts
对象中添加以下命令,用以开启本地服务:
//package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack",
"server": "webpack-dev-server --open"
}
接下来在命令窗口输入 npm run server
启动服务器,正常情况下会自动在浏览器打开 localhost:8080
,若没有自动打开可以在浏览器窗口中手动输入。
然后我们可以在 Greeter.js
中修改一下要输出的内容,保存的同时可以观察到命令窗口中会服务器会自动刷新,同时浏览器窗口会自动刷新html内容。
五、 Source Maps
我们都知道,打包生成的文件其实可读性是很差的。如果我们在本地编写代码时候不小心写错了,然后打包生成的文件肯定是达不到我们想要的效果,而在生成的文件中查错即找bug是不现实的,我们需要直观明显的显示出错位置,这个时候就需要使用Source Maps
来显示出来我们写的代码出错的位置。
同样,我们需要在 webpack.config.js
中进行配置。
devtool选项 | 配置结果 |
---|---|
source-map |
在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map ,但是它会减慢打包速度; |
cheap-module-source-map |
在一个单独的文件中生成一个不带列映射的map ,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便; |
eval-source-map |
使用eval 打包源文件模块,在同一个文件中生成干净的完整的source map 。这个选项可以在不影响构建速度的前提下生成完整的sourcemap ,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项; |
cheap-module-eval-source-map |
这是在打包文件时最快的生成source map 的方法,生成的Source Map 会和打包后的JavaScript 文件同行显示,没有列映射,和eval-source-map 选项具有相似的缺点; |
上述选项由上到下打包速度越来越快,同时负面作用也越来越大,较快的打包速度的后果是对打包后的文件的执行有一定的影响。
中小型的项目推荐使用eval-source-map
,大型项目考虑时间成本时可以使用cheap-module-eval-source-map
。另外,Source Map 只应该在开发阶段使用,生产阶段记得将该配置其去除。
更新webpack.config.js:
//webpack.config.js
module.exports ={
entry:__dirname + '/app/main.js',
output:{
path: __dirname + '/build',
filename:'bundle.js'
},
devServer: {
contentBase:"./build",
historyApiFallback:true,
inline:true
},
//更新代码:
devtool: "eval-source-map" //配置Source Map
}
六、 编译器
本小节我们将认识webpack的可以说最为强大的功能之一:编译器(装载器)Loaders
。
webpack1.x
中是loaders
,在webpack2.x
中将loaders
换为了rules
,我们在网上搜到的大多教程都是基于webpack1.x
的,和现在使用的webpack2.x
有些许出入,不同之处详见:Webpack2 升级指南和特性摘要
听说过webpack的童鞋们肯定都见过loaders
这个单词,那么问题来了,loaders
是用来做什么的?
大家对webpack
可以将那些使用浏览器不能识别支持的各种语法编写的文件打包都有所耳闻,那webpack
这种强大的功能是用什么实现的呢?答案就是Loaders
。webpack
通过各种不同的loader
调用外部的脚本或工具,实现对不同类型文件的转换编译处理,例如将sass
转换为css
,ES6
/ES7
转换为ES5
,React
中用到的jsx
转换为js
等。
同上,我们也需要在配置文件webpack.config.js
中的module
关键字下配置loaders
,即webpack2.x
中的rules
(后面如果再出现rules
即为loaders
)。rules是一个数组,里面存放着需要使用的许多loader
,每个loader
都是一个对象,下面是loader
中常用的几个属性:
-
test
:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须) -
loader
: loader的名称(必须) -
include/exclude
: 手动添加必须处理的文件(文件夹)/屏蔽不需要处理的文件(文件夹)(可选); -
query
: 为loaders提供额外的设置选项(可选)
在配置loader之前,我们将Greeter.js里的问候消息放在一个单独的JSON文件中,通过合适的配置使Greeter.js可以读取JSON文件中的值,各文件修改后的代码如下:
在app文件夹中创建带有问候信息的JSON文件(命名为config.json)
//config.json
{
"greetText":"hello webpack!"
}
更新后的Greeter.js
//Greeter.js
//更新内容:
var config = require('./config.json'); //引入上面创建的 config.json 文件
module.exports = function(){ //将该模块公开,外部其他文件可以调用该模块
var greet = document.createElement('div'); //创建一个div
greet.textContent = config.greetText; //将 config.json 文件中greetText属性中的内容添加到创建的div中
return greet; //将该div块返回出去
}
在
webpack1.x
中,json
文件需要在配置文件中单独使用json-loader
转换处理,而webpack2.x
内置了可以处理json
的模块,不需要再单独配置。
七、 webpack打包ES6文件
我们继续打包文件,不过这次打包的是``类型文件,需要用到Babel
。它是随着ES6
,ES7
,JSX
等js
语法的兴起而产生的,作用就是用来将这些目前尚未被浏览器完全支持的语法转换为可以被浏览器识别支持的ES5
。
Babel
包含了几个模块化的包,核心功能在babel-core
的npm
包中,对于每一个你需要的功能或拓展,你都需要安装单独的包,平时用的最多的是解析ES6
的babel-preset-es2015
包和解析JSX
的babel-preset-react
包。
所以我们可以将上述的几个 npm 包安装一次性的安装下来:
//npm 一次安装多个依赖模块,模块之间需要用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
接下来我们在配置文件中配置babel
:
//webpack.config.js
module.exports ={
entry:__dirname + '/app/main.js',
output:{
path: __dirname + '/build',
filename:'bundle.js'
},
devServer: {
contentBase:"./build",
historyApiFallback:true,
inline:true
},
devtool: "eval-source-map",
//更新内容
module:{
rules:[
{
test:/(\.jsx|\.js)$/, //该正则也可以写作 /\.jsx?$/
use:{
loader: "babel-loader",
options: {
presets:[
"es2015","react"
]
}
},
exclude:/node_modules/
}
]
}
}
现在用上面的配置我们就可以打包出用 ES6
语法编写的代码了。
下面我们修改代码,将上述文件中的代码改为 ES6
语法的代码,不过这里我们会用到 react
,因此先安装React
和React-DOM
,命令如下:
npm install --save-dev react react-dom
然后更新 Greeter.js 代码:
//Greeter.js
//更新内容:
import React, {Component} from 'react'; //引入React组件
import config from './config.json';
class Greeter extends Component { //创建class类
render(){
return(
{config.greetText}
);
}
}
export default Greeter; //返回react组件
更新 main.js 代码:
//main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
render( ,document.getElementById("root"));
更新完毕后我们可以运行打包命令 webpack
,打包成功后再运行 npm run server
启动服务器,服务器会自动在浏览器中打开 html 页面,输出 「 hello webpack! 」:
Babel的配置:
Babel 可以在配置文件中配置,但是它同时也有特别多特别多的设置选项,虽然可以在 webpack.config.js 文件中设置,但是如果有很多设置选项,webpack.config.js 就会显得很臃肿,因此我们可以把 babel 的设置选项提取出来,单独放在一个名为 .babelrc 的文件中。这里的 babel 的设置选项即为下图中选中的部分:
现在我们就可以提取出相关部分,webpack 会自动调用 .babelrc 中的配置选项,修改代码如下:
//webpack.config.js
module.exports = {
entry: __dirname + '/app/main.js',
output: {
path: __dirname + '/build',
filename: 'bundle.js'
},
devServer: {
contentBase: "./build",
historyApiFallback: true,
inline: true
},
devtool: "eval-source-map",
module: {
rules: [{
test: /(\.jsx|\.js)$/, //该正则也可以写作 /\.jsx?$/
use: {
loader: "babel-loader",
//将以下部分代码注掉,该 babel-loader 的options 部分我们将在 .babelrc 中单独设置
// options: {
// presets:[
// "es2015","react"
// ]
// }
},
exclude: /node_modules/
}]
}
}
在该项目的根目录创建 .babelrc 文件
//.babelrc
{
"presets": ["react","es2015"]
}
该文件中 只能 存放 babel-loader 下的设置选项,其他的loader设置不能在这里写
我们可以先打包,再开启本地服务器,查看是否配置成功。
八、 webpack打包css文件
通过前面几节练习,我们也许可能似乎好像貌似大概模模糊糊隐隐约约明白了webpack是干嘛的,该怎么用,所以我们继续打包下一种类型文件:css
打包css文件的话,我们需要使用css-loader
和style-loader
,两者处理任务不同,css-loader
能使用类似 @import
和utl(...)
的方法实现require()
的功能,style-loader
将所有的计算后的样式加入页面中,两者组合在一起使你能够把样式表嵌入到打包后的 js 文件。
首先,安装两个loader
:
npm install --save-dev style-loader css-loader
在配置文件中配置:
//webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /(\.jsx|\.js)$/, //该正则也可以写作 /\.jsx?$/
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
//更新代码:
{
test: /\.css$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}]
}
]
}
}
注意这里对同一个文件引入多个 loader 的方法。
接下来在 app 文件中创建 main.css 的文件,设置一下样式:
//main.css
body {
background-color: pink;
color: salmon;
}
div {
font-size: 30px;
}
本实例中的 webpack 入口唯一,即为 main.js ,所以其他的所有的模块都需要通过 import / require / url 等与之建立关联,为了让 webpack 能找到该
css 文件, 我们把它导入到 main.js 中,如下:
//main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
import './main.css'; //使用 import 引入 css 文件
render( ,document.getElementById("root"));
通常情况下,css 会和 js 打包到同一文件中,并不会打包成一个单独的
css 文件,但是也是可以将其分离的,我们在后面内容将会讲到如何将 css 与 js 分离。
同样,先运行 webpack 打包,再启动本地服务器看是否将 css 文件打包成功,启动服务器命令是 npm run server
。
以上,我们实现了对 css 文件的打包。
CSS module
众所周知,现在使用JavaScript 进行模块化开发已经成了主流,我们可以把复杂的代码转化为一个一个的小的模块,并且这些小模块之间的依赖关系很明确,每个小模块中不掺杂其他内容,配合优化工具,依赖管理和加载管理可以自动完成,省时省力。
与此同时,CSS 也在不断的朝着模块化方向发展。而 webpack 从一开始就对 css 模块化提供了支持,在 css-loader 中进行配置后,接下来做的就是把 modules 传递到需要的地方,然后就可以直接把 css 的类名传递到组件代码中,并且这样做只对当前组件有效,不用担心在不同的模块中使用相同的类名造成冲突。具体代码如下:
//webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /(\.jsx|\.js)$/, //该正则也可以写作 /\.jsx?$/
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
//更新代码:
{
test: /\.css$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader",
//添加以下代码:
options: {
modules:true
}
}]
}
]
}
}
在app文件夹下创建一个 Greeter.css 文件
//Greeter.css
.root {
display: flex;
justify-content: center;
border: 3px solid seagreen;
}
将 .root 导入到 Greeter.js 中:
//Greeter.js
import React, {Component} from 'react';
import config from './config.json';
//更新代码部分:
import styles from './Greeter.css'; //引入模块
class Greeter extends Component {
render(){
return(
//添加类名
{config.greetText}
);
}
}
export default Greeter;
运行
CSS modules是一个很大的主题,有兴趣的童鞋可以去 官方文档 查看了解更多
CSS预处理器
sass 和 less 可以让我们写 css 样式更灵活,我想大家应该已经猜到了要在配置文件中使用相关的 loaders 进行配置,用到的 loaders 即为 less-loader 和 sass-loader 。
另外再介绍一个 css 的处理平台 PostCSS,它可以让你的 CSS 实现更多的功能,可以去阅读 官方文档 了解更多
这里我们用一实例说明 PostCSS 如何为 CSS 代码自动添加适应不同浏览器的CSS前缀,可以简单理解为调兼容
首先安装 postcss-loader
和 autoprefixer
(自动添加前缀插件)
npm install --save-dev postcss-loader autoprefixer
安装完成后,将 webpack.config.js 更新:
//webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /(\.jsx|\.js)$/, //该正则也可以写作 /\.jsx?$/
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
//更新代码:
{
test: /\.css$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules:true
}
},{
loader:"postcss-loader"
}]
}
]
}
}
在根目录创建 postcss.config.js ,添加如下代码:
//postcss.config.js
module.exports = {
plugins:[
require('autoprefixer')
]
}
九、 webpack打包img文件
本小节我们将使用webpack打包两张图片,并将其放到页面内
十、 插件(Plugins)
插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
Plugins 和 Loaders 的区别:
>> loaders 是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个。
>> plugins 并不直接操作单个文件,它直接对整个构建过程起作用,比如切割代码,分离css,压缩代码等。。。
使用插件的方法:
首先通过npm安装插件,然后在配置文件中引入该插件,接着在与 rules 同层级的位置添加一个 plugins 关键字,结构如图, plugins 是一个数组,可以添加多个插件,后面将介绍几种常用插件及其基本配置方法。
十一、 BannerPlugin插件
该插件的作用是给打包后的代码添加一个版权声明,是webpack自带的插件,不用再次安装,配置方法如下:
首先在配置文件中引入 webpack :
const webpack = require('webpack');
接着在 plugins 插件数组中创建该插件,代码如下:
plugins:[
new webpack.BannerPlugin('版权所有,翻版必究')
]
然后运行打包命令,打开打包生成的文件,会在头部显示版权声明,如图:
十二、 HtmlWebpackPlugin插件
该插件的作用是按照一个简单的 index.html 模板文件,在最终路径生成一个可以自动引用打包后的 js 文件的新的 index.html 文件,主要用于每次生成的 js 文件名称都不相同时,例如我们为了优化缓存在输出的 js 文件的名字中加了 hash 值的时候。
首先安装:
npm install --save-dev html-webpack-plugin
这个插件可以自动完成我们之前手动做的一些事情,还可以生成 html 文件,此时我们项目结构中的 build 文件夹就可以让 webpack 自己去创建了,因此就不需要 build 文件夹了,可以先把该文件夹删掉,然后用webpack生成。
如果我们想利用该插件自动生成 html,则需要在 app 文件夹中创建一个 index.tmpl.html 文件,该模板必须包含 html 的必要元素,如 title 等。webpack在编译过程中,会依据此模板生成 html ,自动为该 html 添加它所依赖的 css ,js 等其他文件。 index.tmpl.html 文件模板代码如下:
//index.tmpl.html
Webpack Sample Project
更新webpack的配置文件,代码如下:
//webpack.comfig.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + "/app/main.js", //已多次提及的唯一入口文件
output: {
path: __dirname + "/build", //我们在这里设置输出路径,若该路径不存在,webpack会自动创建
filename: "[name]-[hash].js" //输出文件的文件名,这里[name]是自动匹配的,[hash]是自动生成的
},
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true//实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true
}
},
{
loader: "postcss-loader"
}
]
}
]
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
})
]
};
重新打包,我们会发现自动生成了build文件夹,里面自动生成了一个js文件和一个html文件,运行服务器,观察最终页面效果是不是我们想要的效果
十三、 HMR插件
Hot Module Replacement (HMR) 也是我们常用的一个插件,它允许你在修改组件代码后,自动刷新实时预览修改后的效果。
配置方式:
- 在配置文件中添加 HMR 插件
- 在 devServer 中添加 hot 参数
配置完之后JS还是不能自动热加载的,还需要在JS模块中执行一个webpackk提供的API才能实现热加载。另一种方法是用Babel对React模块进行功能热加载,不过用该种方法需要对babel设置选项进行设置,代码如下:
//webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/build",
filename: "bundle.js"
},
devtool: 'eval-source-map',
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true,
hot: true
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true
}
}, {
loader: "postcss-loader"
}
]
}
]
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'),
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
}),
new webpack.HotModuleReplacementPlugin()//热加载插件
安装 react-transform-hmr
npm install --save-dev babel-plugin-react-transform react-transform-hmr
配置Babel:
//.babelrc
{
"presets": ["react", "es2015"],
"env": {
"development": {
"plugins": [["react-transform", {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}]]
}
}
}
接下来我们来修改下 package.json 文件下的 scripts 关键字下的内容:
如果你使用的是React,那么现在输入npm start
就可以热加载模块了,每次保存都能在浏览器中看到内容。
现在大概讲述一下 webpack-dev-server 和 HMR 大概有什么区别,详情参见webpack-dev-server配置。
- webpack-dev-server 为我们提供了本地服务器,通过对其选项的一些设置,我们可以保存代码后让浏览器自动刷新,显示我们修改后的代码
- HMR 其实是一个插件,可以配合 webpack-dev-server 使用,当对其配置完成后并运行服务器后,我们修改代码,然后保存的同时,浏览器内容并不会全部刷新,而是只更新修改部分内容,比如我们修改了一个边框样式,那么页面中其他的内容样式都不会动,只会刷新边框的样式。
想了解更深入的童鞋可以去看上面发的链接里的内容,也可以自己在网上搜索两者区别。
十四、 BundleAnalyzerPlugin插件
在介绍该插件之前,我们可以先对前面的代码打包,然后看一下打包出来的那个JS文件体积,会发现我们仅仅写了那么几行代码,但是打包出来的js文件都有2M+,此时假设项目已经完成,该上线了,但这种直接打包出来的文件是肯定不能直接推到线上的,因为体积太大。
此时就可以用到该插件了。这个插件作用是分析项目依赖的,配置好该插件之后,运行完打包命令,浏览器会自动打开一个窗口,显示具体依赖的模块,清晰明了直观。
配置:首先安装该模块
npm install webpack-bundle-analyzer --save-dev
在配置文件中引入以及在插件 plugins 数组中实例化出一个插件:
//webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
...
module.exports = {
...
plugins:[
...
new BundleAnalyzerPlugin()
]
}
再次运行打包命令,浏览器自动打开页面如下图:
我们可以清楚地看到生成的 main.js 文件有 2.5Mb,体积很大,我们需要对其进行优化。
十五、 ExtractTextPlugin插件
这时我们最应该做的就是将Source Maps 配置选项缩小代码体积所以我们就需要用插件对生成代码进行优化,ExtractTextPlugin就是其中之一,作用是将编译后的css与js文件分离。
配置方法如下:
- 安装该插件:
npm install --save-dev extract-text-webpack-plugin
- 在配置文件中设置如下:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
...
module.exports={
...
module:{
rules:[
...
{
test: /\.css$/,
use:ExtractTextPlugin.extract({
fallback:"style-loader",
use:{
loader:"css-loader",
options:{
module:true
}
}
})
}
]
},
plugins:[
...
new ExtractTextPlugin("style.css") //括号中的内容为分离出的css文件的名字
]
}
打包后会在目标文件夹生成css文件。
十六、 CommonsChunkPlugin插件
经过以上操作步骤后,打包出的文件的体积依然很大,所以我们继续优化,此处引入 CommonsChunkPlugin插件 。
这个模块的作用是提取出我们用的第三方模块库,例如 react ,react-dom 等,这些体积较大的模块我们可以把它摘出来,另存为一个js文件,然后通过 HtmlWebpackPlugin 插件,html会自动将该js文件引入到html中。
更新配置文件:
//webpack.config.js
...
module.exports = {
//此处我们修改下入口文件
entry:{
app:__dirname + "/app/main.js",
vendors:['react','react-dom']
},
...
plugins:[
...
new webpack.optimize.CommonsChunkPlugin({name:'vendor', filename: 'vendor.js'})
]
}
打包文件,成功提取出vender.js,再观察生成的文件大小,我们主要的js文件体积就缩小了。更多webpack优化方案
十七、 UglifyJsPlugin插件
到这里的时候代码体积其实已经不大了,但是我们为了追求极致,就会用到该插件,将代码深度压缩,经过压缩的js文件体积会变得更小。对代码的压缩混淆,不仅可以保证代码的安全性,还可以降低资源文件的大小,减少网络传输。
该插件是对生成的所有 js 文件进行压缩,具体配置如下:
new webpack.optimize.UglifyJsPlugin(),
&ems p;「」
常用终端命令:
cd .. //返回上层目录
cd firDiector/sonDirector //进入当前目录下的 firDiector
文件夹中的 sonDirector 文件夹
touch file-name.xx //创建filename.xx的文件
mkdir director-name //创建名为director-name的文件夹
npm init //在当前文件夹
npm install webpack -g //全局安装webpack,-g 的位置可在前
npm install --save-dev webpack //在当前目录下安装webpack模块,
--save-dev表示在package.json添加当前
安装模块的简介,推荐添加
npm uninstall webpack //删除webpack模块