30分钟入门webpack4
1 webpack使用来做什么的
在我们愉快的使用vue,React等框架时,听说过webpack,确很少了解过它,在vue-cli3中和create-react-app等脚手架中往往看不到webpack的身影,在vue-cli3中,它的作者使用了vue.config.js来覆盖它,当在它最里面还是webpack。在create-react-app中你要使用npm run eject才能把webpack的配置暴露出来。vue和React是现在流行的前端框架都在使用webpack。那webpack是用来做什么的了?当然不用我说你的知道,它是用来打包的。官网上是这么说的“本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle”。通俗的说就是将 import 包进行整理,翻译成浏览器能运行的程序。
2 大体流程
- 确定入口:根据配置中的 entry 找出所有的入口文件
- 编译模块:从入口文件出发,调用所有配置的 Loader
对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理; - 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk
转换成一个单独的文件加入到输出列表, - 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
3 从开始哪转换
const config = {
entry: {
app: './src/app.js',
}
};
入口文件就是webpack要开始翻译打包的初始文件,然后根据这个文件找到它的依赖包,并把他的依赖包进行翻译
4 如何转换
如何翻译这件事情就要交给loader了。
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
对应的文件交给对应的loader进行转换,比如上述代码中的根据正则,以.css结尾的文件先交个css-loader转换后再给style-loader转换。更多loader,请查看webpack官方提供的
5 转换过中的webpack进行的动作
在打包期间,webpack要做代码切割,代码压缩等事情,而这些事情loader是无法解决的,所以交给了plugins(插件)。插件也是webpack的支柱功能,webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
在面代码中webpack.optimize.UglifyJsPlugin是用来js代码压缩的,HtmlWebpackPlugin是用来指定模板的,HotModuleReplacementPlugin是用来模块热更新的(开发环境下使用),更多插件请查阅webpack官方提供的
6 输出
转换完成进行输出
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
}
output 位于对象最顶级键(key),包括了一组选项,指示 webpack 如何去输出、以及在哪里输出你的「bundle、asset 和其他你所打包或使用 webpack 载入的任何内容」。
上述代码,path 代表代码输出的目录对应一个绝对路径,filename代表定了每个输出 bundle(打包内容) 的名称,chunkFilename,决定了非入口(non-entry) chunk 文件的名称,更多配置看webpack output配置介绍
7 不完整配置介绍
{
mode: 'development', //知 webpack 使用相应模式的内置优化
context: '', //用于从配置中解析入口起点(entry point)和 loader
devtool: 'cheap-source-map',//此选项控制是否生成,以及如何生成 source map(用于调试定位等)
node: { //这些选项可以配置是否 polyfill 或 mock 某些 Node.js 全局变量和模块
setImmediate: false,
process: 'mock',
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
},
output: { //指示 webpack 如何去输出、以及在哪里输出
path: '\\dist',
filename: 'static/js/[name].js',
publicPath: '',
chunkFilename: 'static/js/[name].js'
},
resolve: { //配置模块如何解析
alias: { //创建 import 或 require 的别名,来确保模块引入变得更简单
'@': '\\src',
vue$: 'vue/dist/vue.runtime.esm.js',
assets: '\\src\\assets',
api: '\\src\\api',
views: '\\src\\views',
components: '\\src\\components'
},
extensions: [ //自动解析确定的扩展 代码中可以省略的后缀名
'.mjs',
'.js',
'.jsx',
'.vue',
'.json',
'.wasm'
],
modules: [ //告诉 webpack 解析模块时应该搜索的目录
'node_modules',
'\\node_modules',
'\\node_modules\\_@vue_cli-service@4.5.4@@vue\\cli-service\\node_modules'
],
plugins: [ //应该使用的额外的解析插件列表
/* config.resolve.plugin('pnp') */
{}
]
},
module: { //这些选项决定了如何处理项目中的不同类型的模块
noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/, //防止 webpack 解析那些任何与给定正则表达式相匹配的文件。忽略的文件
rules: [ //创建模块时,匹配请求的规则数组
{
test: /\.vue$/,
use: [
/* config.module.rule('vue').use('cache-loader') */
{
loader: '\\node_modules\\_cache-loader@4.1.0@cache-loader\\dist\\cjs.js',
options: {
cacheDirectory: '\\node_modules\\.cache\\vue-loader',
cacheIdentifier: '4e8d3dea'
}
},
/* config.module.rule('vue').use('vue-loader') */
{
loader: 'vue-loader',
options: {
compilerOptions: {
whitespace: 'condense',
preserveWhitespace: true
},
cacheDirectory: '\\node_modules\\.cache\\vue-loader',
cacheIdentifier: '4e8d3dea'
}
}
]
},
]
},
optimization: { //优化打包
splitChunks: { //对于动态导入模块,默认使用 webpack v4+ 提供的全新的通用分块策略(common chunk strategy)。
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\\/]node_modules[\\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
},
minimizer: [ //允许你通过提供一个或多个定制过的 TerserPlugin 实例, 覆盖默认压缩工具(minimizer)。
/* config.optimization.minimizer('terser') */
new TerserPlugin(
{
terserOptions: {
compress: {
arrows: false,
collapse_vars: false,
comparisons: false,
computed_props: false,
hoist_funs: false,
hoist_props: false,
hoist_vars: false,
inline: false,
loops: false,
negate_iife: false,
properties: false,
reduce_funcs: false,
reduce_vars: false,
switches: false,
toplevel: false,
typeofs: false,
booleans: true,
if_return: true,
sequences: true,
unused: true,
conditionals: true,
dead_code: true,
evaluate: true
},
mangle: {
safari10: true
}
},
sourceMap: false,
cache: true,
parallel: true,
extractComments: false
}
)
]
},
plugins: [ //plugins 选项用于以各种方式自定义 webpack 构建过程
/* config.plugin('vue-loader') */
new VueLoaderPlugin(),
/* config.plugin('define') */
new DefinePlugin(
{
'process.env': {
NODE_ENV: '"development"',
VUE_APP_ENV: '"development"',
BASE_URL: '""'
}
}
),
/* config.plugin('case-sensitive-paths') */
new CaseSensitivePathsPlugin(),
/* config.plugin('friendly-errors') */
new FriendlyErrorsWebpackPlugin(
{
additionalTransformers: [
function () { /* omitted long function */ }
],
additionalFormatters: [
function () { /* omitted long function */ }
]
}
),
/* config.plugin('html') */
new HtmlWebpackPlugin(
{
title: 'Alex、Hu',
templateParameters: function () { /* omitted long function */ },
template: '\\public\\index.html'
}
),
/* config.plugin('copy') */
new CopyPlugin(
[
{
from: '\\public',
to: '\\dist',
toType: 'dir',
ignore: [
'.DS_Store',
{
glob: 'index.html',
matchBase: false
}
]
}
]
)
],
entry: { //开始应用程序打包过程的一个或多个起点。如果传入数组,则会处理所有条目
app: [
'./src/main.js'
]
}
}