webpack对于前端开发者来说,是再熟悉不过了。现在的前端项目开发,基本上都使用到了webpack。但webpack究竟是什么?为什么要使用webpack?这对于部分开发者来说依旧是十分模糊。下面,我们就来聊聊webpack对于项目开发的意义。
1. 什么是webpack?
对于webpack的概念,官方文档是这样阐述的:
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
听起来还是一头雾水,但实质上,这句话就是告诉我们,webpack是一个JS静态模块打包器(module bundler)。也就是说,webpack的主要作用就是打包,打包的目标是JS程序中的所有静态模块。这里的静态模块就是指项目中的代码和资源文件等,webpack把项目中的每个文件都看作是一个模块。这些模块中,有一个入口文件(index.js),webpack通过这个入口文件,确定各模块间的依赖关系,然后根据依赖关系将所有模块打包成一个或多个bundle文件(bundle.js)。
2. 模块
所谓的模块化,是指将复杂的程序分散成多个功能块。 每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。 精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。在打包时,webpack将项目中的这些功能模块,根据其依赖关系整合一个或多个bundle。
3. loader
loader可以说是webpack功能强大的最好体现。loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。使用不同的loader,可以将不同的外部脚本或语言转换成JS文件。例如,loader 可以将 TypeScript甚至是CSS转换为 JavaScript,这使得你可以直接在模块文件中导入TypeScript和CSS文件。
安装loader的方法很简单,只要调用npm install命令即可:
npm install --save-dev css-loader
npm install --save-dev ts-loader
安装完成后,在webpack.config.js中配置:
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
4. Plugins
Plugins的作用是对webpack进行功能扩展,使得webpack的功能更为丰富。如HtmlWebpackPlugin
使得webpack能够一个简单的html,生成一个自动引用生成的js包的index.html,plugins的安装方法与loader相似:
npm install --save-dev html-webpack-plugin
在webpack.config.js中配置:
const HtmlWebpackPlugin = require('html-webpack-plugin'); //通过 npm 安装
const webpack = require('webpack'); //访问内置的插件
const config = {
plugins: [
// 压缩JS代码,内置插件
new webpack.optimize.UglifyJsPlugin(),
// 生成引用打包js的html文件
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
5. 配置
webpack的参数可通过命令行修改,比如修改入口文件,可输入如下命令:
# {extry file}出填写入口文件的路径,本文中就是上述main.js的路径,
# {destination for bundled file}处填写打包文件的存放路径
# 填写路径的时候不用添加{}
webpack {entry file} {destination for bundled file}
但是webpack的配置参数很多,如果要用命令行配置,那就太麻烦了。一种可替代的方案是,把这些配置参数写入一个配置文件中,这个文件就是webpack.config.js。webpack会根据配置文件的配置参数进行配置,这样大大减少了工作量。因此,webpack.config.js实质上是webpack配置参数的集合:
const path = require('path');
module.exports = {
mode: "production", // "production" | "development" | "none" // Chosen mode tells webpack to use its built-in optimizations accordingly.
entry: "./app/entry", // string | object | array // 这里应用程序开始执行
// webpack 开始打包
output: {
// webpack 如何输出结果的相关选项
path: path.resolve(__dirname, "dist"), // string
// 所有输出文件的目标路径
// 必须是绝对路径(使用 Node.js 的 path 模块)
filename: "bundle.js", // string // 「入口分块(entry chunk)」的文件名模板(出口分块?)
publicPath: "/assets/", // string // 输出解析文件的目录,url 相对于 HTML 页面
library: "MyLibrary", // string,
// 导出库(exported library)的名称
libraryTarget: "umd", // 通用模块定义 // 导出库(exported library)的类型
/* 高级输出配置(点击显示) */ },
module: {
// 关于模块配置
rules: [
// 模块规则(配置 loader、解析器等选项)
{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, "app")
],
exclude: [
path.resolve(__dirname, "app/demo-files")
],
// 这里是匹配条件,每个选项都接收一个正则表达式或字符串
// test 和 include 具有相同的作用,都是必须匹配选项
// exclude 是必不匹配选项(优先于 test 和 include)
// 最佳实践:
// - 只在 test 和 文件名匹配 中使用正则表达式
// - 在 include 和 exclude 中使用绝对路径数组
// - 尽量避免 exclude,更倾向于使用 include
issuer: { test, include, exclude },
// issuer 条件(导入源)
enforce: "pre",
enforce: "post",
// 标识应用这些规则,即使规则覆盖(高级选项)
loader: "babel-loader",
// 应该应用的 loader,它相对上下文解析
// 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
// 查看 webpack 1 升级指南。
options: {
presets: ["es2015"]
},
// loader 的可选项
},
{
test: /\.html$/,
test: "\.html$"
use: [
// 应用多个 loader 和选项
"htmllint-loader",
{
loader: "html-loader",
options: {
/* ... */
}
}
]
},
{ oneOf: [ /* rules */ ] },
// 只使用这些嵌套规则之一
{ rules: [ /* rules */ ] },
// 使用所有这些嵌套规则(合并可用条件)
{ resource: { and: [ /* 条件 */ ] } },
// 仅当所有条件都匹配时才匹配
{ resource: { or: [ /* 条件 */ ] } },
{ resource: [ /* 条件 */ ] },
// 任意条件匹配时匹配(默认为数组)
{ resource: { not: /* 条件 */ } }
// 条件不匹配时匹配
],
/* 高级模块配置(点击展示) */ },
resolve: {
// 解析模块请求的选项
// (不适用于对 loader 解析)
modules: [
"node_modules",
path.resolve(__dirname, "app")
],
// 用于查找模块的目录
extensions: [".js", ".json", ".jsx", ".css"],
// 使用的扩展名
alias: {
// 模块别名列表
"module": "new-module",
// 起别名:"module" -> "new-module" 和 "module/path/file" -> "new-module/path/file"
"only-module$": "new-module",
// 起别名 "only-module" -> "new-module",但不匹配 "only-module/path/file" -> "new-module/path/file"
"module": path.resolve(__dirname, "app/third/module.js"),
// 起别名 "module" -> "./app/third/module.js" 和 "module/file" 会导致错误
// 模块别名相对于当前上下文导入
},
/* 可供选择的别名语法(点击展示) */
/* 高级解析选项(点击展示) */ },
performance: {
hints: "warning", // 枚举 maxAssetSize: 200000, // 整数类型(以字节为单位)
maxEntrypointSize: 400000, // 整数类型(以字节为单位)
assetFilter: function(assetFilename) {
// 提供资源文件名的断言函数
return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
}
},
devtool: "source-map", // enum // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
// 牺牲了构建速度的 `source-map' 是最详细的。
context: __dirname, // string(绝对路径!)
// webpack 的主目录
// entry 和 module.rules.loader 选项
// 相对于此目录解析
target: "web", // 枚举 // 包(bundle)应该运行的环境
// 更改 块加载行为(chunk loading behavior) 和 可用模块(available module)
externals: ["react", /^@angular\//], // 不要遵循/打包这些模块,而是在运行时从环境中请求他们
stats: "errors-only", // 精确控制要显示的 bundle 信息
devServer: {
proxy: { // proxy URLs to backend development server
'/api': 'http://localhost:3000'
},
contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
compress: true, // enable gzip compression
historyApiFallback: true, // true for index.html upon 404, object for multiple paths
hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
https: false, // true for self-signed, object for cert authority
noInfo: true, // only errors & warns on hot reload
// ...
},
plugins: [
// ...
],
// 附加插件列表
/* 高级配置(点击展示) */
resolveLoader: { /* 等同于 resolve */ }
// 独立解析选项的 loader
parallelism: 1, // number
// 限制并行处理模块的数量
profile: true, // boolean
// 捕获时机信息
bail: true, //boolean
// 在第一个错误出错时抛出,而不是无视错误。
cache: false, // boolean
// 禁用/启用缓存
watch: true, // boolean
// 启用观察
watchOptions: {
aggregateTimeout: 1000, // in ms
// 将多个更改聚合到单个重构建(rebuild)
poll: true,
poll: 500, // 间隔单位 ms
// 启用轮询观察模式
// 必须用在不通知更改的文件系统中
// 即 nfs shares(译者注:Network FileSystem,最大的功能就是可以透過網路,讓不同的機器、不同的作業系統、可以彼此分享個別的檔案 ( share file ))
},
node: {
// Polyfills and mocks to run Node.js-
// environment code in non-Node environments.
console: false, // boolean | "mock"
global: true, // boolean | "mock"
process: true, // boolean
__filename: "mock", // boolean | "mock"
__dirname: "mock", // boolean | "mock"
Buffer: true, // boolean | "mock"
setImmediate: true // boolean | "mock" | "empty"
},
recordsPath: path.resolve(__dirname, "build/records.json"),
recordsInputPath: path.resolve(__dirname, "build/records.json"),
recordsOutputPath: path.resolve(__dirname, "build/records.json"),
// TODO
}
此外,webpack还支持多种类型的配置,如开发环境和生产环境,可以分别创建对应的配置文件。要了解这方面的内容,请查看多种配置类型。
上面为webpack较为核心的内容,如果要了解更多,可以读下面这篇博客:
入门 Webpack,看这篇就够了
或者看官方文档:
webpack中文文档