webpack江湖小白成长记

使用Webpack进行前端项目文件的编译、打包与压缩。

注意:本文所使用的webpack版本为2.2.1,与webpack1.0+版本的配置文件写法有所不同
本文demo的GitHub地址:https://github.com/yrq110/webpack-demo

LEVEL 0 - 利其器

  1. 新建项目

    1
    2
    mkdir webpack-demo
    cd webpack-demo
  2. 初始化npm

    1
    npm init
  3. 安装webpack

    1
    npm i webpack --save
  4. 项目的目录结构如下

LEVEL 1 - 初学乍练

  1. index.js中添加如下代码:

    1
    2
    var p = document.getElementById('wp');
    p.innerHTML = 'Hello webpack!';
  2. index.html中添加如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>Hello Webpack</title>
    </head>
    <body>
    <p id="wp"></p>
    <script type="text/javascript" src="../dist/bundle.js"></script>
    </body>
    </html>
  3. webpack.config.js文件(webpack的配置文件)中添加如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const webpackConfig = {
    //1.入口
    entry : './src/index.js', //入口文件
    //2.输出
    output : {
    path: './dist', //输出路径
    filename : 'bundle.js', //输出文件名
    }
    };
    module.exports = webpackConfig;
  4. 执行webpack命令进行打包

    1
    webpack

应有如下输出

1
2
3
4
5
6
7
MacBook:webpack-demo yrq$ webpack
Hash: aa10bdffcf9fb2f57989
Version: webpack 2.2.1
Time: 68ms
Asset Size Chunks Chunk Names
bundle.js 2.88 kB 0 [emitted] main
[0] ./src/index.js 255 bytes {0} [built]

此时在dist文件夹下会有打包好的bundle.js文件。

在浏览器中打开index.html文件应会看到”Hello webpack!”字样。

LEVEL 1-1 添加ES6编译

  1. 首先安装babel的加载器与相关插件

    1
    npm i babel-loader babel-core babel-preset-es2015 babel-preset-react --save
  2. webpack.config.js修改成如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    const webpackConfig = {
    //1.入口
    entry : './src/index.js', //入口文件
    //2.输出
    output : {
    path: './dist', //输出路径
    filename : 'bundle.js', //输出文件名
    },
    //3.加载器
    module: {
    rules: [
    //3.1 编译es6
    {
    test: /\.(js|jsx)$/, //文件后缀、类型
    exclude: /node_modules/, //排除这个目录的文件
    loader: 'babel-loader', //使用的加载器
    options: {
    presets: ['react', "es2015"], //插件
    },
    }]
    }
    };
    module.exports = webpackConfig;
  3. 在index.js中添加如下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Person {
    constructor(name) {
    this.name = name;
    }
    }
    class Friend extends Person{
    constructor(name) {
    super(name);
    }
    callName() {
    alert(this.name);
    }
    }
    var friend = new Friend('yrq110');
    friend.callName();
  4. 执行webpack命令进行打包

    1
    webpack

在浏览器中打开index.html文件应会出现弹窗。

LEVEL 1-2 打包css文件

  1. index.css中添加如下代码

    1
    2
    3
    p {
    background: red;
    }
  2. index.js的开头添加如下一行

    1
    import './index.css'
  3. 安装css加载器

    1
    npm i style-loader css-loader --save
  4. webpack.config.js修改成如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    const webpackConfig = {
    //1.入口
    entry : './src/index.js', //入口文件
    //2.输出
    output : {
    path: './dist', //输出路径
    filename : 'bundle.js', //输出文件名
    },
    //3.加载器
    module: {
    rules: [
    //3.1 编译打包es6
    {
    test: /\.(js|jsx)$/, //文件后缀、类型
    exclude: /node_modules/, //排除这个目录的文件
    loader: 'babel-loader', //使用的加载器
    options: {
    presets: ['react', "es2015"], //插件
    },
    },
    //3.2 打包css
    {
    test: /\.css$/,
    use: ['style-loader','css-loader'],
    }]
    }
    };
    module.exports = webpackConfig;
  5. 执行webpack命令进行打包

    1
    webpack

在浏览器中打开index.html文件会看到p标签背景变成了红色。

LEVEL 1-3 使用Plugin

一般来说需要把所有静态文件输出到一个目录下。现在我们原始的html文件还在src目录下,而打包后的js在dist,需要相同目录下复制一份html,使用HtmlWebpackPlugin来解决这个问题。

  1. 安装HtmlWebpackPlugin

    1
    npm i html-webpack-plugin --save
  2. index.html中引入bundle.js的行删掉

  3. webpack.config.js修改成如下所示

    配置文件中使用require进行包的导入,不要使用es6的import语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const webpackConfig = {
//1.入口
entry : './src/index.js', //入口文件
//2.输出
output : {
path: './dist', //输出路径
filename : 'bundle.js', //输出文件名
},
//3.加载器
module: {
rules: [
//3.1 编译打包es6
{
test: /\.(js|jsx)$/, //文件后缀、类型
exclude: /node_modules/, //排除这个目录的文件
loader: 'babel-loader', //使用的加载器
options: {
presets: ['react', "es2015"], //插件
},
},
//3.2 打包css
{
test: /\.css$/,
use: ['style-loader','css-loader'],
}]
},
//4.插件
plugins: [
new webpack.optimize.UglifyJsPlugin(), //压缩js文件
new HtmlWebpackPlugin({template: 'src/index.html'}) //设置HtmlWebpackPlugin插件及html模板路径
],
};
module.exports = webpackConfig;
  1. 执行webpack命令进行打包

    1
    webpack
  2. 会看到dist目录下产生了一个index.html文件,内容如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>Hello Webpack</title>
    </head>
    <body>
    <p id="wp"></p>
    <script type="text/javascript" src="bundle.js"></script></body>
    </html>

会发现webpack自动添加了js文件的引入。

EXTRA MISSION - path模块

使用path模块重写路径字符串
将LEVEL 1-1中的配置文件使用path模块重写路径

1
2
3
4
5
6
7
8
9
10
11
12
const path = require('path');
const webpackConfig = {
//1.入口
entry : path.resolve(__dirname, 'src/index'), //入口文件
//2.输出
output : {
path: path.resolve(__dirname, 'dist'), //输出路径
filename : 'bundle.js', //输出文件名
}
};
module.exports = webpackConfig;

LEVEL 2 - 略知一二

LEVEL 2-1 独立打包

有时我们需要将css样式文件单独打包出来,需要用到extract-text-webpack-plugin插件

  1. 安装Extract Text Plugin

    1
    npm install extract-text-webpack-plugin --save
  2. webpack.config.js中添加相关语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    const ExtractTextPlugin = require("extract-text-webpack-plugin");
    ...
    const webpackConfig = {
    ...
    module: {
    rules: [
    ...
    {
    test: /\.css$/,
    use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
    })
    }
    ]
    },
    plugins: [
    ...
    new ExtractTextPlugin("index.css"),
    ],
    };
    module.exports = webpackConfig;
  3. 进行打包后发现dist目录下有打包好的index.css样式文件

LEVEL 2-2 图片打包

大多数情况下都需要图片打包,对于不同的图片引入方式的打包方法有所不同。

打包CSS中的图片

  1. 如下修改index.cssindex.html

    1
    2
    3
    4
    5
    6
    ...
    #img {
    height: 200px;
    width: 200px;
    background:url(dawn.png) no-repeat left top;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    <!DOCTYPE html>
    <html>
    ...
    <body>
    ...
    <div id="img"></div>
    </body>
    </html>

    目录中添加一个图片,这里是dawn.png。打包后打开index.html会看到一个图片。

  2. 安装url-loader

    1
    npm install url-loader --save
  3. webpack.config.js中添加rule

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ...
    const webpackConfig = {
    ...
    module: {
    rules: [
    ...
    {
          test: /\.(png|jpg)$/,
          loader: 'url-loader?limit=8192&name=images/[name].[ext]'
        }
    ]
    },
    ...
    };

    其中limit字段表示所打包图片大小小于8192字节会被转换成base64引用,name字段为相对于output.path的保存路径,可以设置打包后的文件名与扩展名,若为了方便缓存也可以添加[hash]参数。

  4. 打包后的dist目录如下:

打包HTML中的图片

  1. 如下修改index.html

    1
    2
    3
    4
    5
    6
    7
    8
    <!DOCTYPE html>
    <html>
    ...
    <body>
    ...
    <img src="dawn.png" alt="dawn"/>
    </body>
    </html>

    添加一个img元素,引用图片路径与上一步中的相同。

  2. 安装html-loader

    1
    npm install html-loader --save
  3. webpack.config.js中添加rule

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ...
    const webpackConfig = {
    ...
    module: {
    rules: [
    ...
    {
          test: /\.html$/,
          use: 'html-loader'
        }
    ]
    },
    ...
    };
  4. 打包后打开index.html会看到两张图片,第一张是打包原始css文件中的图片,第二张是打包原始html中的图片。

LEVEL 2-3 打包多个资源文件

有时需要引入多个资源文件,只需修改entry入口文件配置即可。

  1. 在src目录下新建一个other.js

    1
    2
    // other.js
    alert("Hello webpack~");
  2. webpack.config.js中添加入口文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ...
    const webpackConfig = {
    entry :
    {
    ...
    other: path.resolve(__dirname, 'src/other')
    },
    ...
    };
  3. 打包后的dist目录如下:

打开index.html会看到两个弹出提示窗.

LEVEL 2-4 Webpack Dev Server

每次修改完保存再执行命令未免有些麻烦,使用Webpack Dev Server可以在每次保存后自动刷新页面,实时显示修改后的效果。

  1. 安装webpack-dev-server

    1
    npm install webpack-dev-server --save
  2. 命令行启动

    1
    webpack-dev-server --content-base ./dist

    设定webpack-dev-server当前的资源目录为./dist,默认为当前目录下。

  3. 配置写入webpack.config.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ...
    const webpackConfig = {
    ...
    devServer: {
    contentBase: path.join(__dirname, "dist"),
    compress: true,
    port: 9000
    }
    };
    ...

    直接执行webpack-dev-server即可在设置的资源目录与端口下启动。

LEVEL 3 - 登堂入室

LEVEL 3-1 配置

通过前面的使用我们知道webpack需要一个配置对象,下面的代码中包含了常用的可用配置选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
const path = require('path');
module.exports = {
// 1.设置应用启动与webpack打包的路径
entry: "./app/entry", // string | object | array
// entry: ["./app/entry1", "./app/entry2"],
// entry: {
// a: "./app/entry-a",
// b: ["./app/entry-b1", "./app/entry-b2"]
// },
// 2.设置webpack的输出
output: {
// 目标输出路径,必须是一个绝对路径(使用Node.js中的path模块)
path: path.resolve(__dirname, "dist"), // string
// 输入块(entry chunks)的文件名模板
filename: "bundle.js", // string
// 与HTML页面关联的输出路径url
publicPath: "/assets/", // string
// 导出库的名称
library: "MyLibrary", // string
// 导出库类型
libraryTarget: "umd", // universal module definition
},
// 3.设置使用模块
module: {
// 模块的规则(设置loader、parser等)
rules: [
{
// 这里是匹配的条件,每个条件都接受一个表达式或字符串
// 必须同时匹配test和include
// 必须排除exclude
// 最佳实践:
// - 在test中使用正则表达式匹配文件名
// - 在include和exclude中使用绝对路径数组
// - 避免使用exclude,优先使用include
test: /\.jsx?$/,
include: [
path.resolve(__dirname, "app")
],
exclude: [
path.resolve(__dirname, "app/demo-files")
],
// issuer条件设置(import的根路径)
issuer: { test, include, exclude },
// rules的标记,可以被重写 (高级设置)
enforce: "pre",
enforce: "post",
// 所执行的加载器,会在相关的上下文中被处理
// 在webpack2中-loader后缀不再是可选的
// 可以看看[webpack 1升级指南](https://webpack.js.org/guides/migrating/)
loader: "babel-loader",
// 加载器设置
options: {
presets: ["es2015"]
},
},
{
test: "\.html$",
use: [
// 执行多个加载器与设置
"htmllint-loader",
{
loader: "html-loader",
options: {
/* ... */
}
}
]
}
],
},
// 4. 处理module请求的设置
// (不会处理加载器的属性)
resolve: {
// 寻找module的路径
modules: [
"node_modules",
path.resolve(__dirname, "app")
],
// 使用的扩展名
extensions: [".js", ".json", ".jsx", ".css"],
},
performance: {
hints: "warning", // enum
maxAssetSize: 200000, // int (in bytes),
maxEntrypointSize: 400000, // int (in bytes)
assetFilter: function(assetFilename) {
return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
}
},
// 会在浏览器的开发工具中添加元信息,有利于调试
// 使用source-map会牺牲构建速度以得到更详细的信息
devtool: "source-map", // enum
// webpack的主目录
context: __dirname, // string (必须是绝对路径!)
// bundle的运行环境
// 会改变chunk的加载方式与可用module
target: "web", // enum
// 不要打包这些module,不过在运行时环境中需要调用它们
externals: ["react", /^@angular\//],
// 设置如何显示bundle信息
stats: "errors-only",
devServer: {
proxy: { // 后端开发服务器的代理URL
'/api': 'http://localhost:3000'
},
contentBase: path.join(__dirname, 'public'), // boolean | string | array, 静态文件路径
compress: true, // 启用gzip压缩
historyApiFallback: true, // 若为true则对404状态返回index.html,使用object设置多个路径
hot: true, // 模块热替换,取决于HotModuleReplacementPlugin
https: false, // 若为true则是自签名方式,使用object设置证书优先级
noInfo: true, // 在热加载时仅显示错误与警告信息
// ...
},
// 添加的插件列表
plugins: [
// ...
],
}

LEVEL 3-2 加载器

webpack使用加载器(loaders)处理文件,允许你打包各种各样静态资源而不仅仅是JS文件,使用loadername来调用这些加载器。

文件

JSON

  • json-loader 加载JSON文件(默认包含)
  • json5-loader 加载并编译JSON 5文件
  • cson-loader 加载并编译CSON文件

编译(Transpiling)

模板

  • html-loader 将HTML导出为字符串,需要静态资源的引用
  • pug-loader 加载Pug模板并返回函数
  • jade-loader 加载Jade模板并返回函数
  • markdown-loader 将Markdown编译为HTML
  • posthtml-loader 使用PostHTML加载并转换HTML文件
  • handlebars-loader 将Handlebars编译为HTML

样式

  • style-loader 将模块作为DOM的样式导出
  • css-loader 加载CSS,处理引用文件并返回CSS
  • less-loader 加载并编译LESS文件
  • sass-loader 加载并编译SASS/SCSS文件
  • stylus-loader 加载并编译Stylus文件
  • postcss-loader 使用PostCSS加载并转换CSS/SSS文件

测试

  • mocha-loader 使用mocha进行测试(浏览器/NodeJS)
  • eslint-loader 使用ESLint进行代码检测的预加载器
  • jshint-loader 使用JSHint进行代码检测的预加载器
  • jscs-loader 使用JSCS检测代码风格的预加载器
  • coverjs-loader 使用CoverJS进行测试覆盖的预加载器

框架

  • vue-loader 加载并编译Vue组件
  • angular2-template-loader 加载并编译 Angular组件

LEVEL 3-3 插件

一些常用的插件列表

名称 描述
CommonsChunkPlugin 生成entry中共用模块的chunk并分别打包,比如vendor.bundle.js && app.bundle.js
ComponentWebpackPlugin 使用webpack组件
CompressionWebpackPlugin 对资源进行压缩
DefinePlugin 设置编译时的全局常量,有利于配置开发/发布的不同行为
EnvironmentPlugin DefinePlugin中process.env键的简易用法
ExtractTextWebpackPlugin 分离出打包文件中的样式部分(app.bundle.css)
HtmlWebpackPlugin 生成引用打包文件的HTML(index.html)
I18nWebpackPlugin 在打包文件中添加i18n国际化支持
LimitChunkCountPlugin 设置最小/最大限制来调整与控制生成的分块
NormalModuleReplacementPlugin 替换匹配正则的资源

LEVEL 3-4 开发

参考

文章目录
  1. 1. LEVEL 0 - 利其器
  2. 2. LEVEL 1 - 初学乍练
    1. 2.1. LEVEL 1-1 添加ES6编译
    2. 2.2. LEVEL 1-2 打包css文件
    3. 2.3. LEVEL 1-3 使用Plugin
    4. 2.4. EXTRA MISSION - path模块
  3. 3. LEVEL 2 - 略知一二
    1. 3.1. LEVEL 2-1 独立打包
    2. 3.2. LEVEL 2-2 图片打包
      1. 3.2.1. 打包CSS中的图片
      2. 3.2.2. 打包HTML中的图片
    3. 3.3. LEVEL 2-3 打包多个资源文件
    4. 3.4. LEVEL 2-4 Webpack Dev Server
  4. 4. LEVEL 3 - 登堂入室
    1. 4.1. LEVEL 3-1 配置
    2. 4.2. LEVEL 3-2 加载器
      1. 4.2.1. 文件
      2. 4.2.2. JSON
      3. 4.2.3. 编译(Transpiling)
      4. 4.2.4. 模板
      5. 4.2.5. 样式
      6. 4.2.6. 测试
      7. 4.2.7. 框架
    3. 4.3. LEVEL 3-3 插件
    4. 4.4. LEVEL 3-4 开发
  5. 5. 参考
|