字节青训前端笔记 | Webpack入门
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
本节课将重点围绕「 Webpack 」这一核心话题展开。简述前端工程化的常用工具webpack 的原理和使用
webpack的作用
webpack的作用是把很多文件打包整合到一起, 缩小项目体积, 提高加载速度常用的场景是
- 代码压缩
将JS、CSS代码混淆压缩让代码体积更小加载更快
- 编译语法
编写CSS时使用Less、Sass编写 JS 时使用ES6、TypeScript等这些标准目前都无法被浏览器兼容因此需要构建工具编译例如使用Babel编译ES6语法。
- 处理模块化
CSS 和 JS 的模块化语法目前都无法被浏览器兼容。因此开发环境可以使用既定的模块化语法但是需要构建工具将模块化语法编译为浏览器可识别形式。
开始使用 webpack
首先需要使用 node 安装 webpack
npm install webpack
npm install webpack-cli
我们可以通过 webpack 命令将一个文件进行打包webpack 会根据模块的依赖关系进行静态分析相互依赖的各个模块都会被包含到 bundle.js 文件中。Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块。
npx webpack a.js bundle.js
之后创建一个 webpack 的配置文件 webpack.config.js 在其中我们可以进行打包的配置webpack 命令执行后会默认载入当前目录的 webpack.config.js 文件。以下是常用的配置说明
// webpack 中引入其他的模块要使用commonJS模块化require导入因为webpack是nodeJS 开发的不能使用 import导入
var webpack = require('webpack');
const { resolve } = require('path');
module.exports = {
//entry可以是个字符串或数组或者是对象。当entry是个字符串的时候用来定义入口文件
entry: './js/app.js',
//output参数是个对象用于定义构建后的文件的输出。其中包含path和filename
output: {
path: './build',
filename: 'bundle.js'
},
//基础目录绝对路径用于从配置中解析入口点(entry point)和 加载器(loader)。默认使用当前目录但是推荐在配置中传入一个值
context:resolve(__dirname, './'),
//关于模块的加载相关我们就定义在module.loaders中。这里通过正则表达式去匹配不同后缀的文件名然后给它们定义不同的加载器
module: {
{
// 正则: 匹配所有以css结尾的文件
test: /\.css$/,
// 使用的加载器加载器处理顺序从右往左
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader'
]
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader',
'less-loader'
]
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
//配置项目
options: {
limit: 8 * 1024,
// 配置输出的文件名
name: '[name].[ext]',
// 配置静态资源的引用路径
publicPath: "../images/",
// 配置输出的文件目录
outputPath: "images/"
}
}
]
},
// (4) 配置对于高版本js的兼容性处理
{
test: /\.js$/,
// 配置排除项
exclude: /(node_modules)/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
},
//Webpack社区提供了一个便捷的本地开发工具webpack-dev-server,在webpack.config.js文件添加devServer对象是专门用于存放webpack-dev-server配置的,当webpack-dev-server发现工程源文件进行了更新操作就会自动刷新浏览器展示更新后的内容
devServer: {
hot: true,
// 自动打开浏览器
open: true
}
//webpack在构建包的时候会按目录的进行文件的查找resolve属性中的extensions数组中用于配置程序可以自行补全哪些文件后缀如下的配置我们想要加载一个js文件时只要require(‘common’)就可以加载common.js文件了。
resolve:{
extensions:['','.js','.json']
},
//插件用于完成一些 loader 不能完成的工作
plugins: [
new webpack.NoErrorsPlugin()
]
mode: 'development', //生产环境
// mode:'production,'//开发环境
};
webpack的运行流程
webpack是运行流程如下
- 入口处理从编写的 entry 开始启动我们的编译流程
- 依赖解析根据文件的 require 和 import 的情况找到文件依赖的资源
- 资源解析根据module的配置将资源用不同的解析器进行处理转成标准的内容
- 资源合并将转译后的内容合并打包并且在我们 output 指定的文件中输出
使用webpack的详解
webpack的使用就是书写配置文件使用我们要详细的去学如何去配置我们的 webpack.config.js 文件他有多种的属性
- entry 和 output
entry 用于定于一次打包的入口文件也就是从哪个文件开始打包
output 参数是个对象用于定义构建后的文件的输出。其中包含path和filename代表了输出路径和输出文件名称
entry: './js/app.js',
output: {
path: './build',
filename: 'bundle.js'
},
- module
webpack只能匹配优先类型的前端文件比如 .css 。如果我们需要引入其他类型的文件比如 .less 文件我们需要通过加载器来处理它们module就是做这个事情的它通过rules定义一个对象数组每一项通过 test 定义正则表达式去匹配不同后缀的文件名然后通过 use 给它们定义不同的加载器部分加载器可能需要你配置参数你可以在 options 字段进行配置exclude 则可以排除指定目录下的文件使得他们不使用加载器进行加载以下的例子是几种常见的配置css 的解析less 的解析图片的解析babel的配置
module: {
{
// 正则: 匹配所有以css结尾的文件
test: /\.css$/,
// 使用的加载器加载器处理顺序从右往左
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader'
]
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader',
'less-loader'
]
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
//配置项目
options: {
limit: 8 * 1024,
// 配置输出的文件名
name: '[name].[ext]',
// 配置静态资源的引用路径
publicPath: "../images/",
// 配置输出的文件目录
outputPath: "images/"
}
}
]
},
// 配置对于高版本js的兼容性处理
{
test: /\.js$/,
// 配置排除项
exclude: /(node_modules)/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
},
- plugins
有一些 loader 不能完成的工作我们需要导入一些插件来完成这些工作比如自动生成一个html 文件需要一个插件我们这样导入
//在顶部引入
const HTMLWebpackPlugin = require('html-webpack-plugin')
//....省略代码
module.exports = {
//....
plugins: [
new HTMLWebpackPlugin()
]
}
- devServer
devServer 是一个本地开发服务器会自动监听变化自动打包构建自动更新刷新浏览器你只需要下载相关的库并且配置devServer 项就可以使用它其中的 hot 是否开启热更新就是就是你修改代码保存之后浏览器不会刷新只会修改你更改过的依赖代码。
devServer: {
//热更新
hot: true,
// 自动打开浏览器
open: true
}
当然当你配置了热更新之后你需要使用不一样的命令来执行打包
npx webpack server
- watch
这个属性说明了是否开启 webpack 的监听功能设置为 true 后每次按保存键webpack自动为我们打包我们可以通过 watchOptions 来进行 watch 的配置
watch:true
watchOptions:{
poll:1000,//监测修改的时间(ms)
aggregeateTimeout:500, //防止重复按键500毫米内算按键一次
ignored:/node_modules/,//不监测
}
- devtool
配置webpack的调试工具一共有20多种可以选择具体可以查看官网的文档https://webpack.js.org/
合理的配置可以有效的实现Source Map
devtool:"eval-source-map"
- mode
提供 mode 配置选项告知 webpack 使用相应模式的内置优化。
- development会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。
- 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin
区别简单的来说就是production 会移除一些没有依赖的方法、变量和文件并且代码会进行压缩比development的文件小。
- optimization
optimization主要用来自定义一些优化打包策略主要有这些常用的值
splitChunks设置这个值会自动提取所有公共模块到单独bundle中去
minimize表示是不是压缩模块
minimizer属性存放一个数组数组里可以存放用于代码压缩的插件
usedExports 表示是不是只导出那些外部使用了的那些成员
concatenateModules表示是不是合并模块因为打包默认会把很多模块放进一个单独的函数中如果模块很多的话那么输出文件就会有很多这种模块函数
sideEffect这个新特性可以让我们来标识我们的代码有没有副作用。但是有些模块的导入虽然没有使用但是外部可能会继承自他们或者有有些简介的联系sideEffects 就可以标记这些模块告诉javascript哪些没有副作用可以去掉哪些有副作用不可以去掉
optimization: {
splitChunks: {
// 自动提取所有公共模块到单独 bundle
chunks: 'all'
},
// 表示只导出那些外部使用了的那些成员
usedExports: true,
// 压缩模块
minimize:true,
minimizer:[
new TerserPlugin(),
new OptimizeCssAssetsWebpackPlugin()
],
concatenateModules:true,
sideEffect: true
},
理解 Loader
loader 主要的作用的内容转换因为 webpack 只认识 js 代码loader要做的就是把非 js 的资源转换成 js 的资源css 是链式调用的比如 less 的解析需要用到如下三个 loader他们是按照顺序分别执行的
loader 的执行分为 pitch 和 normol 两个阶段前者是从数组第一个向后执行而后者是从最后一个向前执行loader 会先执行 pitch然后获取资源再执行 normal loader但是如果 pitch 有返回值时就不会走之后的 loader并将返回值返回给之前的 loader。
因此数组的第一个 loader 要返回 js 脚本也就是最后要输出给webpack的内容每个 loader 只做一件事为了使 loader 在更多的场景中链式调用每一个 loader 都是一个模块每个 loader 都是无状态的确保 loader 在不同的模块转换之间保存状态。
如果想要学会自己写一个 loader 可以查看 https://mp.weixin.qq.com/s/TPWcB4MfVrTgFtVxsShNFA
以下是常见的 loader:
理解插件
插件架构可以提升工具的拓展性webpack的原理和底层是非常复杂的新人需要了解整个流程成本很高迭代成本很高牵一发动全身并且作为开源项目缺乏成长性参与它很难。插件的精髓是对外开放拓展对修改封闭我只实现核心的功能其他的功能都由插件生态来共建和实现
webpack的实现原理是Webpack 编译阶段会为各个编译对象初始化不同的 Hook 开发者可以在自己编写的 Plugin 中监听到这些 Hook 在打包的某个特定时间段触发对应 Hook 注入特定的逻辑从而实现自己的行为。
webpack上有两个很重要的对象
- compiler 对象中保存着完整的 Webpack 环境配置它通过 CLI 或 者 Node API传递的所有选项创建出一个 compilation 实例。这个对象会在首次启动 Webpack 时创建我们可以通过 compiler 对象上访问到 Webapck 的主环境配置比如 loader 、 plugin 等等配置信息
- compilation 对象代表一次资源的构建compilation 实例能够访问所有的模块和它们的依赖在 compilation 对象中我们可以获取/操作本次编译当前模块资源、编译生成资源、变化的文件以及被跟踪的状态信息同样 compilation 也基于 tapable 拓展了不同时机的 Hook 回调
Plugin 的原型对象上需要存在一个 apply 方法当 webpack 创建 compiler 对象时会调用各个插件实例上的 apply 方法并且传入 compiler 对象作为参数。同时需要指定一个绑定在 compiler 对象上的 Hook 在 Hook 的回调中处理插件自身的逻辑根据 Hook 的种类在完成逻辑后通知 webpack 继续进行
Webpack Plugin 的核心机制就是基于 tapable 产生的发布订阅者模式在不同的周期触发不同的 Hook 从而影响最终的打包结果。
想要详细了解的自行查阅一些资料比如:
https://blog.csdn.net/gogo2027/article/details/127750064?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-127750064-blog-125101031.pc_relevant_3mothn_strategy_recovery&spm=1001.2101.3001.4242.1&utm_relevant_index=3