Webpack是一款项目打包工具,将多个模块打包成一个模块,将ESM规范转换为旧的JS语法。
npm init -y
webpack
、webpack-cli
npm i -D webpack webpack-cli
src
目录,然后编写代码npx webpack
进行打包也就是文件打包的起点。
用法:entry: string | [string]
webpack.config.js
module.exports = {
entry: {
app: '.src/app.js',
},
}
这是单入口写法,可扩展性不大。使用对象语法虽然繁琐一些,但是可扩展性强。
用法:entry: {
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js',
},
};
属性名 | 描述 |
---|---|
dependOn | 当前入口所依赖的入口。它们必须在该入口被加载前被加载。 |
filename | 指定要输出的文件名称。 |
import | 启动时需加载的模块。 |
library | 指定 library 选项,为当前 entry 构建一个 library。 |
runtime | 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。 |
publicPath | 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。 |
runtime
和 dependOn
不应在同一个入口上同时使用。
module.exports = {
entry: {
a2: './a',
b2: {
runtime: 'x2',
dependOn: 'a2',
import: './b',
},
},
};
确保 runtime
不能指向已存在的入口名称。
module.exports = {
entry: {
a1: './a',
b1: {
runtime: 'a1',
import: './b',
},
},
};
另外 dependOn
不能是循环引用的。
module.exports = {
entry: {
a3: {
import: './a',
dependOn: 'b3',
},
b3: {
import: './b',
dependOn: 'a3',
},
},
};
webpack.config.js
配置 2 个单独的入口点,一个是主应用程序,一个是第三方库。这样就可以在 vendor.js
中存入未做修改的必要 library 或文件(例如 Bootstrap, jQuery, 图片等),然后将它们打包在一起成为单独的 chunk。内容哈希保持不变,这使浏览器可以独立地缓存它们,从而减少了加载时间。
module.exports = {
entry: {
main: './src/app.js',
vendor: './src/vendor.js',
},
};
webpack.prod.js
配置生产环境下的打包文件名
module.exports = {
output: {
filename: '[name].[contenthash].bundle.js',
},
};
webpack.dev.js
配置开发环境下的打包文件名
module.exports = {
output: {
filename: '[name].bundle.js',
},
};
webpack.config.js
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js',
},
};
通过配置output
选项,告知webpack如何向硬盘写入编译文件。注意,即使可以存在多个entry
起点,也只能配置一项output
.
用法:
webpack.config.js
module.exports = {
output: {
filename: 'bundle.js',
},
};
此配置将一个单独的 bundle.js
文件输出到 dist
目录中。
使用占位符来确保每个文件具有唯一的名称。
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js',
},
output: {
filename: '[name].js',
path: __dirname + '/dist',
},
};
// 写入文件到硬盘:./dist/app.js, ./dist/search.js
如果资源使用CDN和hash
module.exports = {
//...
output: {
path: '/home/proj/cdn/assets/[fullhash]',
publicPath: 'https://cdn.example.com/assets/[fullhash]/',
},
};
如果在编译时,不知道最终输出文件的 publicPath
是什么地址,则可以将其留空,并且在运行时通过入口起点文件中的 __webpack_public_path__
动态设置。
__webpack_public_path__ = myRuntimePublicPath;
// 应用程序入口的其余部分
用于对模块的源代码进行转换, loader 可以使你在 import
或 “load(加载)” 模块时预处理文件。比如可以将TypeScript
转换为JavaScript
,将内联图像转换为data URL
。
import
语句中显式指定 loader。module.rules
允许在webpack中配置多个loader。loder从右到左或者从下到上进行取值和执行。如下示例,从sass-loader
开始执行,然后继续执行css-loader
,最后执行style-loader
。
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true,
},
},
{ loader: 'sass-loader' },
],
},
],
},
};
可以在 import
语句或任何 与 “import” 方法同等的引用方式 中指定 loader。使用 !
将资源中的 loader 分开。每个部分都会相对于当前目录解析。
import Styles from 'style-loader!css-loader?modules!./styles.css';
通过为内联 import
语句添加前缀,可以覆盖配置中的所有 loader
, preLoader
和 postLoader
:
使用 !
前缀,将禁用所有已配置的 normal loader
(普通 loader)
import Styles from '!style-loader!css-loader?modules!./styles.css';
使用 !!
前缀,将禁用所有已配置的 loader(preLoader, loader, postLoader
)
import Styles from '!!style-loader!css-loader?modules!./styles.css';
使用 -!
前缀,将禁用所有已配置的 preLoader
和 loader
,但是不禁用 postLoaders
import Styles from '-!style-loader!css-loader?modules!./styles.css';
options
对象配置。package.json
的 main
来将一个 npm 模块导出为 loader,还可以在 module.rules 中使用 loader
字段直接引用一个模块。loader 遵循标准模块解析规则。多数情况下,loader 将从模块路径加载(通常是从 npm install
, node_modules
进行加载)。
插件用于解决loader无法实现的其他事情。
webpack 插件是一个具有 apply
方法的 JavaScript 对象。apply
方法会被 webpack compiler 调用,并且在 整个 编译生命周期都可以访问 compiler 对象。
ConsoleLogOnBuildWebpackPlugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, (compilation) => {
console.log('webpack 构建正在启动!');
});
}
}
module.exports = ConsoleLogOnBuildWebpackPlugin;
compiler hook 的tap
方法的第一个参数,应该是驼峰式命名的插件名称。建议为此使用一个常量,以便它可以在所有 hook 中重复使用。
由于插件可以携带参数/选项,在进行webpack配置时,需要向plugins
属性传入一个new
实例。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.(js | jsx)$/,
use: 'babel-loader',
},
],
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({ template: './src/index.html' }),
],
};
在使用 Node API 时,还可以通过配置中的 plugins 属性传入插件。
some-node-script.js
const webpack = require('webpack'); // 访问 webpack 运行时(runtime)
const configuration = require('./webpack.config.js');
let compiler = webpack(configuration);
new webpack.ProgressPlugin().apply(compiler);
compiler.run(function (err, stats) {
// ...
});
由于webpack遵循CommonJS模块规范,因此可以在配置中使用:
require(...)
引入其他文件及使用npm下载的工具函数? :
操作符value
使用常量或变量赋值避免如下操作:
--env
)webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
};
除了可以将单个配置导出为 object,function或Promise以外,还可以将其导出为多个配置。
对于多targets
(如AMD
和CommonJS
)构建library
module.exports = [
{
output: {
filename: './dist-amd.js',
libraryTarget: 'amd',
},
name: 'amd',
entry: './app.js',
mode: 'production',
},
{
output: {
filename: './dist-commonjs.js',
libraryTarget: 'commonjs',
},
name: 'commonjs',
entry: './app.js',
mode: 'production',
},
];
只传一个
--config-name
名字标识,webpack将只会构建指定的配置项,比如--config-amd
。
如果某个配置依赖另一个配置的输出,可以使用一个dependencies
列表指定一个依赖列表。
webpack.config.js
module.exports = [
{
name: 'client',
target: 'web',
// ...
},
{
name: 'server',
target: 'node',
dependencies: ['client'],
},
];
如果导出了多个配置,可以在配置中使用parallelism
选项来指定编译的最大并发数。类型:number
,支持版本:5.22.0+
module.exports = [
{
//config-1
},
{
//config-2
},
];
module.exports.parallelism = 1;
TypeScript
先安装必要依赖,比如TypeScript以及其相应的类型声明:
npm i --save-dev typescript ts-node @types/node @types/webpack
# 如果使用版本低于 v4.7.0 的 webpack-dev-server,还需要安装以下依赖
npm i --save-dev @types/webpack-dev-server
示例配置文件如下:
webpack.config.ts
import * as path from 'path';
import * as webpack from 'webpack';
import 'webpack-dev-server';
const config: webpack.Configuration = {
mode: 'production',
entry: './src/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: [name].bundle.js,
},
};
export default config;
该示例需要 typescript 版本在 2.7 及以上,并在 tsconfig.json
文件的 compilerOptions
中添加 esModuleInterop
和 allowSyntheticDefaultImports
两个配置项。
需要确保 tsconfig.json
的 compilerOptions
中 module
选项的值为 commonjs
,否则 webpack 的运行会失败报错,因为 ts-node
不支持 commonjs
以外的其他模块规范。
可以通过三个途径来完成 module 的设置:
直接修改 tsconfig.json
文件
修改 tsconfig.json
并且添加 ts-node
的设置。
使用 tsconfig-paths
第一种方法就是打开 tsconfig.json
文件,找到 compilerOptions
的配置,然后设置 target
和 module
的选项分别为 "ES5"
和 "CommonJs"
(在 target
设置为 es5
时你也可以不显示编写 module
配置)。
其他两种方法查看:https://www.webpackjs.com/configuration/configuration-languages/#typescript
Babel and JSX
安装依赖项:
npm i --save-dev babel-register jsxobj babel-preset-es2015
.babelrc
{
"presets": ["es2015"]
}
webpack.config.babel.js
import jsxobj from 'jsxobj';
// 插件引入示例
const CustomPlugin = (config) => ({
...config,
name: 'custom-plugin',
});
export default (
<webpack target='web' watch mode='production'>
<entry path='src/index.js' />
<resolve>
<alias
{...{
react: 'preact-compat',
'react-dom': 'preact-compat',
}}
/>
</resolve>
<plugins>
<CustomPlugin foo="bar" />
</plugins>
</webpack>
);