在webpack源码中主要依赖于compiler和compilation两个核心对象实现。 compiler对象是一个全局单例,他负责把控整个webpack打包的构建流程,每次都会重新生成一个新的compilation对象,负责此次更新的构建过程 compilation对象是每一次构建的上下文对象,它包含了当次构建所需要的所有信息,每次热更新和重新构建 每个模块文件在通过Loader解析完成之后,会通过acorn库生成模块代码的AST语法树,通过语法树就可以分析这个模块是否还有依赖的模块,进而继续循环执行下一个模块的编译解析。 sourceMap是一项将编译、打包、压缩后的代码映射回源代码的技术,由于打包压缩后的代码并没有阅读性可言,一旦在开发中报错或者遇到问题,直接在混淆代码中debug问题会带来非常糟糕的体验,sourceMap可以帮助我们快速定位到源代码的位置,提高我们的开发效率。sourceMap其实并不是Webpack特有的功能,而是Webpack支持sourceMap Webpack最后打包出来的成果是一份Javascript代码,实际上在Webpack内部默认也只能够处理JS模块代码,在打包过程中,会默认把所有遇到的文件都当作 JavaScript代码进行解析,因此当项目存在非JS类型文件时,我们需要先对其进行必要的转换,才能继续执行打包任务,这也是Loader机制。 Loader负责文件转换,那么Plugin负责功能扩展。Loader和Plugin作为Webpack的两个重要组成部分,承担着不同的职责。 webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的任务,其中compiler暴露了和 Webpack整个生命周期相关的钩子(compiler-hooks),而compilation则暴露了与模块和依赖有关的粒度更小的事件钩子(Compilation Hooks)。 Webpack的事件机制基于Tapable事件流方案 Plugin的开发和开发Loader一样,需要遵循一些开发上的规范和原则:webpack的作用
模块打包运行原理
sourceMap
Loader
Plugin
webpack配置文件
// 自动清空dist目录
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
// js 或者 css 文件可以自动引入到 Html 中
const HtmlWebpackPlugin = require("html-webpack-plugin")
// CSS 文件的形式引入到页面上
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
// require("webpack-dev-server")
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const TerserPlugin = require('terser-webpack-plugin');
const path = require("path")
console.log("process.env.NODE_ENV=", process.env.NODE_ENV) //打印环境变量
// 路径处理方法
function resolve(dir) {
return path.join(__dirname, dir);
}
const config = {
// mode:"development", //模式
entry: "./src/index.js", //打包入口地址
output: {
filename: "bundle.js", //输出文件名
path: path.join(__dirname, 'dist') //输出文件目录
},
// 从输出的 bundle 中排除依赖
// externals: {
// jquery: 'jQuery',
// },
// 解析自定义 loader
// resolveLoader: {
// modules: ['node_modules',resolve('loader')]
// },
// 压缩 JS
optimization: {
minimize: true, // 开启最小化
minimizer: [
// ...
new TerserPlugin({})
]
},
resolve: {
// 配置别名
alias: {
'@': path.join(__dirname, 'src')
},
// extensions:['',""]
// webpack 优先 src 目录下查找需要解析的文件
modules: [resolve('src'), 'node_modules']
},
module: {
// 不需要解析依赖的第三方大型类库
// noParse:"",
rules: [ //转换规则
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的css/sass/scss文件
// test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
// "style-loader",
"css-loader",
// "postcss-loader",
"sass-loader"
] //use:对应loader的名称
},
{
test: /\.(jpe?g|png|gif)$/i, // 匹配图片文件
use: [
// webpack5
// {
// test: /\.(jpe?g|png|gif)$/i,
// type: 'asset',
// generator: {
// // 输出文件位置以及文件名
// // [ext] 自带 "." 这个与 url-loader 配置不同
// filename: "[name][hash:8][ext]"
// },
// parser: {
// dataUrlCondition: {
// maxSize: 50 * 1024 //超过50kb不转 base64
// }
// }
// }
// 'file-loader', // 使用 file-loader
// {
// loader: 'file-loader',
// options: {
// name: '[name][hash:8].[ext]'
// }
// },
{
loader: 'url-loader',
options: {
name: '[name][hash:8].[ext]',
// 文件小于 50k 会转换为 base64,大于则拷贝文件
limit: 50 * 1024
}
}
]
},
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
module: false, // Tree-shaking
cacheDirectory: true // 启用缓存
}
}
]
}
]
},
// 反向定位到源码
devtool: 'source-map',
plugins: [ //配置插件
new HtmlWebpackPlugin({
template: "./src/index.html"
}),
new CleanWebpackPlugin(), // 引入插件
new MiniCssExtractPlugin({
filename: "[name].[hash:8].css"
}),
new BundleAnalyzerPlugin({
analyzerMode: 'disabled', // 不启动展示打包报告的http服务器
generateStatsFile: true, // 是否生成stats.json文件
})
],
devServer: {
contentBase: path.resolve(__dirname, "public"), //静态文件目录
compress: true, //是否启动压缩gzip
port: 8080, // 端口号
// open:true 是否自动打开浏览器
}
}
module.exports = (env, argv) => {
console.log("argv.mode=", argv.mode) //打印mode(模式)值
// 这里可以通过不同的模式修改config配置
return config
}