一张图,看懂webpack
webpack 是一个现代的 JavaScript 应用程序的模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图表(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成少量的 bundle - 通常只有一个,由浏览器加载。
它是高度可配置的,但是,在开始前你需要先理解四个核心概念:入口(entry)、输出(output)、loader、插件(plugins)。
概览
app.js
import bar from './bar'
bar()
bar.js
export default function () {
// ...
}
webpack.config.js
module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js'
}
}
index.html
<html>
<head>
...
</head>
<body>
...
<script src="bundle.js"></script>
</body>
</html>
起步
参考地址: https://doc.webpack-china.org/guides/get-started/
webpack is a tool to build JavaScript modules in your application.
webpack 是一个用来构建我们应用程序中的 JavaScript 模块的工具
创建一个bundle文件
-
在项目中创建
package.json
文件$ npm init -y
-
安装
webpack
到项目中$ npm install --save webpack
-
在
app
目录下创建index.js
文件
app/index.js
function component () { var element = document.createElement('div'); // 需要引入 lodash,下一行才能正常工作 element.innerHTML = _.join(['Hello','webpack'], ' '); return element; } document.body.appendChild(component());
要运行这段代码,在项目根目录下创建
index.html
文件。index.html
<html> <head> <title>webpack 2 demo</title> <script src="https://unpkg.com/lodash@4.16.6"></script> </head> <body> </body> <script src="app/index.js"></script> </html>
在此示例中,
<script>
标签之间存在隐式依赖关系。运行
index.js
会依赖于页面中提前引入的lodash
。之所以说是隐式的是因为index.js
并未显式声明需要引入lodash
,只是假定推测已经存在一个全局变量_
。使用这种方式去管理 JavaScript 项目会有一些问题:
- 如果依赖不存在,或者引入顺序错误,应用程序将无法正常运行。
- 如果依赖被引入但是并没有使用,那样就会存在许多浏览器不得不下载的无用代码。
-
要在
index.js
中打包lodash
依赖,首先我们需要安装lodash
。$ npm install --save lodash
在
app/index.js
最上面添加此行代码,在文件内显示引入import _ from 'lodash'
修改
index.html
文件,删除两行script标签的引入,改为引入dist/bundle.js
<html> <head> <title>webpack 2 demo</title> </head> <body> </body> <script src="dist/bundle.js"></script> </html>
-
使用webpack,对
app/index.js
进行处理$ webpack app/index.js dist/bundle.js
之后会生成一个
dist
文件夹,内部存在bundle.js
文件
刷新网页,效果是一样的!
通过声明模块所需的依赖,webpack 能够利用这些信息去构建依赖图表,然后使用图表生成一个优化过的,会以正确代码顺序被运行的 bundle。并且没有用到的依赖将不会被 bundle 引入。
现在在此文件夹下带上以下参数运行
webpack
,其中index.js
是入口文件,bundle.js
是已打包所需的所有代码的输出文件。说明: 尽管
import
/export
语句在浏览器中还未被支持,你也可以正常的使用,因为 webpack 会将其替换为 ES5 兼容的代码。你可以检查dist/bundle.js
的代码来说服自己放心使用
使用配置好的文件
对于更复杂的配置,我们可以使用一个配置文件,webpack 会按照它来打包代码。创建一个 webpack.config.js
文件后,你可以使用如下的配置设置来表示上述 CLI 命令。
webpack.config.js
var path = require('path');
module.exports = {
entry: './app/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
执行命令,进行压缩打包
$ webpack --config webpack.config.js
同样可以生成bundle.js
文件
通过配置文件可以最灵活地使用 webpack。我们可以通过向配置文件添加 loader 规则(loader rules)、插件(plugins)、解析选项(resolve options)以及许多其他增强功能,来进行打包。
配合npm使用
考虑到用 CLI 这种方式来运行 webpack 不是特别方便,我们可以设置一个快捷方式。像这样调整 package.json
:
package.json
添加如下代码:
"scripts": {
"build": "webpack"
},
通过执行命令,即可使用webpack进行打包压缩等操作
$ npm run build
概念解释
入口(entry)
webpack 将创建所有应用程序的依赖关系图表(dependency graph)。图表的起点被称之为入口起点(entry point)。入口起点告诉 webpack 从哪里开始,并遵循着依赖关系图表知道要打包什么。可以将您应用程序的入口起点认为是根上下文(contextual root)或 app 第一个启动文件。
module.exports = {
entry: './path/to/my/entry/file.js'
};
entry属性告诉我们主文件在哪里
出口(output)
将所有的资源(assets)归拢在一起后,还需要告诉 webpack 在哪里打包应用程序。webpack 的 output
属性描述了如何处理归拢在一起的代码(bundled code)。
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};
通过 output.filename
和 output.path
属性,来告诉 webpack bundle 的名称,以及我们想要生成(emit)到哪里
loader
webpack 的目标是,让 webpack 聚焦于项目中的所有资源(asset),而浏览器不需要关注考虑这些(这并不意味着资源(asset)都必须打包在一起)。webpack 把每个文件(.css, .html, .scss, .jpg, etc.) 都作为模块处理。然而 webpack 只理解 JavaScript。
webpack loader 会将这些文件转换为模块,而转换后的文件会被添加到依赖图表中。
在更高层面,webpack 的配置有两个目标。
- 识别出(identify)应该被对应的 loader 进行转换(transform)的那些文件
- 由于进行过文件转换,所以能够将被转换的文件添加到依赖图表(并且最终添加到 bundle 中)(
use
属性)
const path = require('path');
const config = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{test: /\.(js|jsx)$/, use: 'babel-loader'}
]
}
};
module.exports = config;
以上配置中,对一个单独的 module 对象定义了 rules
属性,里面包含两个必须属性:test
和 use
。这可以告诉 webpack compiler 如下:
“嘿,webpack compiler,当你碰到「在
require()
/import
语句中被解析为 '.js' 或 '.jsx' 的路径」时,在你把它们添加并打包之前,要先使用babel-loader
去转换”。
插件
由于 loader 仅在每个文件的基础上执行转换,而 插件(plugins)
最常用于(但不限于)在打包模块的“compilation”和“chunk”生命周期执行操作和自定义功能(查看更多)。webpack 的插件系统极其强大和可定制化。
想要使用一个插件,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,你需要使用 new
创建实例来调用它。
// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path');
const config = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{test: /\.(js|jsx)$/, use: 'babel-loader'}
]
},
plugins: [ // 创建插件对象,添加到此数组中
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;