说 entry 和 output 之前要先说一下 context,也就是上下文对象。
Webpack 在寻找相对路径的文件时会以 context 为根目录,context 默认为执行启动 Webpack 时所在的当前工作目录。 如果想改变 context 的默认配置,则可以在配置文件里这样设置它:
module.exports = {
context: path.resolve(__dirname, 'app')
}
之所以在这里先介绍 context,是因为 Entry 的路径和其依赖的模块的路径可能采用相对于 context 的路径来描述,context 会影响到这些相对路径所指向的真实文件。
module.exports = {
entry: {
main: './path/to/my/entry/file.js',
},
};
// 简写
module.exports = {
entry: './path/to/my/entry/file.js',
};
所以可以看到,我们使用默认配置的时候,也就是 output 不指定输出的名称,那么打包出的文件是 main.js,就是因为我们入口这里默认是 main。
两种写法
module.exports = {
entry: ['./src/file_1.js', './src/file_2.js']
};
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js',
},
};
对象写法的好处是可扩展性强,可以将其拆分然后用专门的工具(如 webpack-merge)将它们合并起来。
这些 API 很少用到,而且这些主要是用来做代码分割的,而我们做代码分割一般用 optimization.splitChunks 来做,可以看我这篇文章
module.exports = {
//...
entry: {
home: './home.js',
shared: ['react', 'react-dom', 'redux', 'react-redux'],
catalog: {
import: './catalog.js',
filename: 'pages/catalog.js',
dependOn: 'shared',
chunkLoading: false, // Disable chunks that are loaded on demand and put everything in the main chunk.
},
personal: {
import: './personal.js',
filename: 'pages/personal.js',
dependOn: 'shared',
chunkLoading: 'jsonp',
asyncChunks: true, // Create async chunks that are loaded on demand.
layer: 'name of layer', // set the layer for an entry point
},
},
};
使用dependOn,app 这个 chunk 就不会包含 react-vendors 拥有的模块了.
module.exports = {
//...
entry: {
app: { import: './app.js', dependOn: 'react-vendors' },
'react-vendors': ['react', 'react-dom', 'prop-types'],
},
};
dependOn 选项的也可以为字符串数组:
module.exports = {
//...
entry: {
moment: { import: 'moment-mini', runtime: 'runtime' },
reactvendors: { import: ['react', 'react-dom'], runtime: 'runtime' },
testapp: {
import: './wwwroot/component/TestApp.tsx',
dependOn: ['reactvendors', 'moment'],
},
},
};
加载动态入口
// 同步
module.exports = {
//...
entry: () => './demo',
};
// 异步
module.exports = {
//...
entry: () => new Promise((resolve) => resolve(['./demo', './demo2'])),
};
// 异步接口加载
module.exports = {
entry() {
return fetchPathsFromSomeExternalSource(); // 返回一个会被用像 ['src/main-layout.js', 'src/admin-layout.js'] 的东西 resolve 的 promise
},
};
静态资源文件名称,如图片字体图标等
默认: string = '[hash][ext][query]'
可以使用 :[name], [file], [query], [fragment], [base] 与 [path]
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'images/[hash][ext][query]'
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource'
}
]
},
};
说句题外话,一般不会在 output 中改变资源名称,而是在这里
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'images/[hash][ext][query]'
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource'
}
},
{
test: /\.html/,
type: 'asset/resource',
// 这里
generator: {
filename: 'static/[hash][ext][query]'
}
}
]
},
};
输出的 chunk 文件名。
module.exports = {
//...
output: {
chunkFilename: (pathData) => {
return pathData.chunk.name === 'main' ? '[name].js' : '[name]/[name].js';
},
},
};
一般我们也不用这个设置,而是用 optimization.splitChunks 来做,可以看我这篇文章
清除打包后的资源,和 CleanWebpackPlugin 插件作用差不多。
module.exports = {
//...
output: {
clean: true, // 在生成文件之前清空 output 目录
},
};
每个输出 bundle 的名称,这些 bundle 将写入到 output.path 选项指定的目录下。
module.exports = {
//...
output: {
filename: 'bundle.js',
},
};
module.exports = {
//...
output: {
filename: '[name].bundle.js',
},
};
module.exports = {
//...
output: {
filename: (pathData) => {
return pathData.chunk.name === 'main' ? '[name].js' : '[name]/[name].js';
},
},
};
id Chunk 的唯一标识,从0开始
name Chunk 的名称
hash Chunk 的唯一标识的 Hash 值
chunkhash Chunk 内容的 Hash 值
其中 hash 和 chunkhash 的长度是可指定的,[hash:8] 代表取8位 Hash 值,默认是20位。
默认:string = 'self'
当输出为 library 时,尤其是当 libraryTarget 为 'umd’时,此选项将决定使用哪个全局对象来挂载 library。
module.exports = {
// ...
output: {
library: 'myLib',
libraryTarget: 'umd',
filename: 'myLib.js',
globalObject: 'this',
},
};
输出一个库,为你的入口做导出。
module.exports = {
// …
entry: './src/index.js',
output: {
library: 'MyLibrary',
},
};
举例:
使用上面的配置打包该文件
export function hello(name) {
console.log(`hello ${name}`);
}
打包后可以这样使用
<script src="https://example.org/path/to/my-library.js"></script>
<script>
MyLibrary.hello('webpack');
</script>
同上
配置库暴露的方式。
类型默认包括 ‘var’、 ‘module’、 ‘assign’、 ‘assign-properties’、 ‘this’、 ‘window’、 ‘self’、 ‘global’、 ‘commonjs’、 ‘commonjs2’、 ‘commonjs-module’、 ‘commonjs-static’、 ‘amd’、 ‘amd-require’、 ‘umd’、 ‘umd2’、 ‘jsonp’ 以及 ‘system’,除此之外也可以通过插件添加。
举例
module.exports = {
// …
output: {
library: {
name: 'MyLibrary',
type: 'var',
},
},
};
var MyLibrary = _entry_return_;
// 在加载了 `MyLibrary` 的单独脚本中
MyLibrary.doSomething();
module.exports = {
// …
output: {
library: {
name: 'MyLibrary',
type: 'assign',
},
},
};
// 直接赋值给MyLibrary,不管有没有定义,慎用
MyLibrary = _entry_return_;
// 使用assign-properties更安全:如果 MyLibrary 已经存在的话,它将被重用
module.exports = {
// …
output: {
library: {
name: 'MyLibrary',
type: 'this',
},
},
};
this['MyLibrary'] = _entry_return_;
// 在一个单独的脚本中
this.MyLibrary.doSomething();
MyLibrary.doSomething(); // 如果 `this` 为 window 对象
module.exports = {
// …
output: {
library: {
name: 'MyLibrary',
type: 'window',
},
},
};
window['MyLibrary'] = _entry_return_;
window.MyLibrary.doSomething();
module.exports = {
// …
output: {
library: {
name: 'MyLibrary',
type: 'global',
},
},
};
global['MyLibrary'] = _entry_return_;
global.MyLibrary.doSomething();
module.exports = {
// …
output: {
library: {
name: 'MyLibrary',
type: 'commonjs',
},
},
};
exports['MyLibrary'] = _entry_return_;
require('MyLibrary').doSomething();
module.exports = {
// …
// 试验性质的模块要加上这个
experiments: {
outputModule: true,
},
output: {
library: {
// do not specify a `name` here,输出 ES 模块。
type: 'module',
},
},
};
module.exports = {
// …
output: {
library: {
// note there's no `name` here
type: 'commonjs2',
},
},
};
module.exports = _entry_return_;
require('MyLibrary').doSomething();
...还有amd、umd,用的都比较少,我们正常一般不指定,所以我们导出的库支持所有导入方式,或者我们使用esmodule也不错
给打包后的文件中不同的导出方式做注释
module.exports = {
// …
mode: 'development',
output: {
library: {
name: 'MyLibrary',
type: 'umd',
auxiliaryComment: {
root: 'Root Comment',
commonjs: 'CommonJS Comment',
commonjs2: 'CommonJS2 Comment',
amd: 'AMD Comment',
},
},
},
};
默认 string = path.join(process.cwd(), 'dist')
const path = require('path');
module.exports = {
//...
output: {
path: path.resolve(__dirname, 'dist/assets'),
},
};
一般用根目录的 publicPath
module.exports = {
//...
output: {
// One of the below
publicPath: 'auto', // It automatically determines the public path from either `import.meta.url`, `document.currentScript`, `` or `self.location`.
publicPath: 'https://cdn.example.com/assets/', // CDN(总是 HTTPS 协议)
publicPath: '//cdn.example.com/assets/', // CDN(协议相同)
publicPath: '/assets/', // 相对于服务(server-relative)
publicPath: 'assets/', // 相对于 HTML 页面
publicPath: '../assets/', // 相对于 HTML 页面
publicPath: '', // 相对于 HTML 页面(目录相同)
},
};
const getPublicPath = () => {
if (process.env.NODE_ENV === "development") {
return "/project_name";
} else {
return `//cdn.xxx.com/repo/project_name/dist/`;
}
};
publicPath: getPublicPath()