【前端】webpack一本通
今日更新完毕,不定期补充,建议关注收藏点赞。
目录
- 简介
- Loader和Plugin的不同?(必会)
- 使用
- webpack默认只能处理js文件 ->引入加载器
- 对JS语法降级,兼容低版本语法
- 合并文件
- 再次打包
- 进阶
- 工作原理
- Webpack 的热更新原理(必会)
- webpack与grunt、gulp的不同?(必会)
- tree shaking
- html-webpack-plugin插件
- webpack的开发服务器:devServer
- 引入
- 使用webpack-dev-server模块:热更新
简介
官网
- webpack作用(自动整合压缩并剔除无用代码)
减少文件数量、缩小代码体积、提高浏览器打开速度- 优点
- 专注于处理模块化的项目,能做到开箱即用,一步到位
- 通过plugin扩展,完整好用又不失灵活
- 通过loaders扩展, 可以让webpack把所有类型的文件都解析打包
- 社区庞大活跃更新,能为大多数场景找到已有的开源扩展
- webpack定义
nodejs第三方模块包, 用于识别翻译压缩打包整合代码
支持所有类型文件的打包
支持less/sass => css
支持ES6/7/8 => ES5
压缩代码, 提高加载速度
webpack基于node, 所以导出遵守CommonJS规范。
webpack是一个打包模块化javascript的工具,在webpack里【一切文件皆模块】,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注构建【模块化】项目
python一切皆对象;
JavaScript 的“几乎一切皆对象” —— 但并不是绝对的。原始类型(Primitive Types)不是对象,它们是 值类型(by value),不可变,不是对象,虽然原始类型不是对象,但当你调用它们的方法时,JS 会临时把它们“包装成对象”;特殊情况:null,这个是历史遗留的 bug ,虽然typeof null 为"object" 但 null 本质上不是对象,也不能调用方法。
- 安装
- 初始化文件夹包环境, 得到package.json文件
- 下载webpack等模块包
- 在package.json自定义命令, 为打包做准备
yarn init
yarn add webpack webpack-cli -D#在配置文件中 配置自定义命令
scripts: {"build": "webpack"
}
Loader和Plugin的不同?(必会)
- 不同的作用
Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。 - 不同的用法
Loader在module.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载器(loader)和使用的参数(options)
Plugin在plugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。
使用
- 默认src/index.js – 打包入口文件
- 引入到入口的文件才会参与打包
- 上述配置文件中的自定义命令,使得:执行package.json里build命令则执行webpack打包命令
- 默认输出dist/main.js的打包结果
- 借助webpack, 把模块和代码打包后,需要将打包结果引入到项目中生效。不希望“手动引入”这么麻烦的,可以安装html-webpack-plugin插件。其使用方法见后。
- 更改webpack打包默认的入口和出口
新建webpack.config.js
填入配置,entry设置为入口文件路径,output对象设置出口路径和文件名
打包观察效果
- 例子:webpack打包代码–jquery实现功能
注意:webpack打包后的js需要引入到html中使用才能生效
步骤:
①:从0准备环境, 初始化包环境, 下载webpack和webpack-cli包, 配置自定义命令build
②:yarn下载jquery, 新建public/index.html,写入内容
③:src/main.js 引入jquery, 编写功能代码
④:执行打包命令
⑤:复制public/index.html到dist/, 然后引入打包后的js, 运行网页观察效果
webpack默认只能处理js文件 ->引入加载器
- 有哪些常见的Loader?他们是解决什么问题的?(必会)
1、 file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
2、 url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
3、 source-map-loader:加载额外的 Source Map 文件,以方便断点调试
4、 image-loader:加载并且压缩图片文件
5、 babel-loader:把 ES6 转换成 ES5
6、 css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
7、 style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
8、 eslint-loader:通过 ESLint 检查 JavaScript 代码 - 处理css文件
css文件引入到入口里,webpack打包css文件依然会报错。如何让webpack能处理css文件:使用下面的两个加载器
css-loader 文档
style-loader文档
css-loader 让webpack能处理css类型文件
style-loader 把css插入到DOM中(插入到head下style标签内)
- 下载加载器
yarn add css-loader style-loader -D
- webpack.config.js配置
module.exports={//其他配置 这里略module:{rules:[{test:/\.css$/i, //i 忽略大小写use:["style-loader","css-loader"] }]}
}
- 打包观察效果:css代码被打包进js文件中,style-loader会把css代码插入到head下style标签内
- 处理less文件
加载器使用less-loader
less-loader加载器作用: 识别less文件
less作用:将less编译为css
yarn add less less-loader -D
//配置module:{rules:[//其他配置略{test:/\.less$/,use:["style-loader","css-loader","less-loader"] }]}
步骤:新建src/less/index.less,把index.less引入到入口处,下载加载器和less来处理less文件,webpack.config.js针对less配置,(打包)转换成css后还需要css-loader和style-loader的处理。
- 处理字体文件、图片文件
webpack5, 使用asset module技术实现字体文件和图片文件处理, 无需配置额外loader,文档: https://webpack.docschina.org/guides/asset-modules/
以前用url-loader和file-loader来处理
现在webpack.config.js – 针对图片文件设置type: “assets“,然后打包,小于8KB文件则转base64打包在js中, 大于8KB, 文件自动命名输出到dist下;字体和图片文件同理。
//配置module:{rules:[//其他配置略{test:/\.(png|jpg|gif|jpeg)$/i,type:'asset'}]}
为什么要区分8kb上下?
图片翻译成了base64, 放到了js文件中
-好处: 浏览器不用发请求了,直接可以读取, 速度快
-坏处: 图片太大,再转base64
就会让图片的体积增大 30% 左右, 得不偿失
- 处理字体图标
- 注意:字体图标 v.s. 字体文件
字体文件 和 字体图标(icon fonts) 是两个相关但不完全一样的东西。 - 字体文件,用于文本显示。常见格式包括:
.ttf(TrueType Font)
.otf(OpenType Font)
.woff / .woff2(Web Open Font Format)
.eot(Embedded OpenType) - 字体图标(Icon Fonts)将图标图形设计成字体的形式,用 CSS 和 HTML 字符来显示图标。
典型例子:Font Awesome、iconfont 阿里巴巴、Material Icons (Google)
它们通常包含以下资源:
字体文件(.woff, .ttf, .eot, .svg)
一个 style.css 或 iconfont.css,映射图标 class 到字体字符,通过字体的形式显示图标,比如<i class="icon-home"></i>
就能显示一个小房子图标。
- 注意:字体图标 v.s. 字体文件
.icon-home:before {content: "\e600";
}
- 如何处理字体图标
- src/assets 下 放入fonts字体相关文件夹
- src/main.js 引入 assets/fonts/iconfont.css
import '@iconfont/iconfont.css';
- src/main.js 创建一个i标签, 使用字体图标标签添加到body上;或者html中使用图标
<i class="icon-home"></i>
- 在webpack.config.js的rules里针对字体图标文件类型设置asset/resource,直接输出到dist下:添加针对字体文件的加载器规则, 使用asset/resource(直接输出文件并配置路径)
const path = require('path');module.exports = {// 其他配置...module: {rules: [// 处理字体图标文件{test: /\.(woff2?|eot|ttf|otf|svg)$/,type: 'asset', //或asset/resource,generator: {filename: 'fonts/[hash][ext][query]', // 输出路径},},
//其他配置略...],},
};
- 打包后运行网页观察效果:在webpack.configjs的rules里针对字体图标文件类型设置asset/resource, 直接输出到dist下
对JS语法降级,兼容低版本语法
babel: 一个javascript编译器, 把高版本js语法降级处理输出兼容的低版本语法
babel-loader: 可以让webpack转译打包的js代码
babel官网: https://www.babeljs.cn/
babel-loader文档: https://webpack.docschina.org/loaders/babel-loader/
- 在src/main.js – 定义箭头函数, 并打印箭头函数变量 (千万不能调用, 为了让webpack打包函数, 看降级效果)
- 下载加载器
yarn add babel-loader @babel/core @babel/preset-env -D
- 配置到webpack.config.js上
module.exports = {// 其他配置...module: {rules: [{test: /\.js$/,exclude:/(node_modules|bower_components)/,use:{loader:'babel-loader',options:{presets:['@babel/preset-env']//预设:转码规则(用bable开发环境本来预设的)}}},
//其他配置略...],},
};
- 打包观察是否降级
合并文件
- 2个js文件 ->打包成1个js文件
新建src下的资源:add.js – 定义求和函数并导出,index.js – 引入add模块并使用函数输出结果
执行yarn build
自定义命令, 进行打包 (确保终端路径在src的父级文件夹)
打包后默认生成dist和dist/main.js, 观察其中代码
再次打包
- 代码增加后, 如何打包呢?
- 确保在src/index.js引入和使用
- 重新执行yarn build打包命令
进阶
- mode 模式
Webpack 的 mode 参数告诉 webpack 使用哪种内置的优化方式。
三种模式:
development 开发模式:优化构建速度,生成未压缩的代码,带有详细的错误信息和注释。
production 生产模式:优化构建结果,如代码压缩、tree shaking,适合上线部署。
none 什么优化都不做,纯原始打包。你需要自己手动配置所有优化。
module.exports = {mode: 'development' | 'production' | 'none'
}
- 把css文件都独立打包提取成一个css文件在dist下
在使用 Webpack 打包时,要将 CSS 提取成独立的文件,可以使用 mini-css-extract-plugin 插件。
安装这些依赖:mini-css-extract-plugin+ css-loader+ style-loader。
在 Webpack 配置文件中进行相应的配置:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');module.exports = {entry: './src/index.js', // 入口文件output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.js', // 输出的 JavaScript 文件},module: {rules: [{test: /\.css$/, // 匹配所有 .css 文件use: [MiniCssExtractPlugin.loader, // 提取 CSS'css-loader', // 解析 CSS],},],},plugins: [new MiniCssExtractPlugin({filename: '[name].css', // 输出的 CSS 文件名}),],mode: 'production', // 使用生产模式
};
运行 Webpack 打包:打包完成后,输出目录(dist/)中会生成一个 bundle.js(包含打包后的 JavaScript)和一个单独的 .css 文件(比如 main.css)。
- 把vue文件让webpack打包使用(百度vue-loader官网)
想要把App.vue的东西显示到index.html
(1): 在public/index.html 准备id叫app的div
(2): yarn add vue - 必须下载vue (和其他加载器和插件-具体参考vue-loader官网)
(3): 需要在main.js中引入[App.vue]模块对象并加入如下代码
import App from './App.vue' // 引入vue文件
import Vue from 'vue' // 引入vue.js对象new Vue({ render: h => h(App) // 渲染函数, 渲染App组件里的标签
}).$mount('#app') // 把vue文件的标签结构 -> 挂载到id为app的标签里
(4): 打包后运行dist/index.html, 观察是否把vue文件里的标签渲染到页面了
工作原理
- yarn build
所有要被打包的资源都要跟入口产生直接/间接的引用关系
- 总结
yarn build后执行webpack命令,找到配置文件、入口和依赖关系,打包代码输出到指定位置 - webpack构建流程:从读取配置到输出文件这个过程尽量说全(必会)
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:- 初始化参数:从配置文件读取与合并参数,得出最终的参数
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,开始执行编译
- 确定入口:根据配置中的 entry 找出所有的入口文件
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果
Webpack 的热更新原理(必会)
webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
HMR的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上 WDS(Webpack Dev Server) 与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该chunk的增量更新。
后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader 和 vue-loader 都是借助这些 API 实现 HMR。
webpack与grunt、gulp的不同?(必会)
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。
grunt和gulp是基于任务和流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
- 从构建思路来说
gulp和grunt需要开发者将整个前端构建过程拆分成多个Task
,并合理控制所有Task
的调用关系。
webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工 - 对于知识背景来说
gulp更像后端开发者的思路,需要对于整个流程了如指掌。 webpack更倾向于前端开发者的思路。
tree shaking
Tree Shaking 是 Webpack 的一项重要优化技术,用于“摇掉”项目中未被使用的代码(通常是未被引用的 ES6 模块导出),从而减小打包体积、提升性能。
Tree shaking 是一种死代码消除(Dead Code Elimination)技术。它的工作原理基于 ES6 的模块静态结构(import/export),可以在编译阶段分析哪些代码是未被使用的,并从最终的 bundle 中删除它们。
- 使用 Tree Shaking 的条件
想让 Webpack 正常进行 tree shaking,需要满足几个前提:
- 使用 ES Module(不是 CommonJS)
//commonJS版本
module.exports = { add, multiply } // 无法 tree shake//ES模块化版本
// utils.js
export function add(a, b) { return a + b }
export function multiply(a, b) { return a * b }// main.js
import { add } from './utils'
console.log(add(2, 3)) // multiply 未使用,将被移除
- mode: ‘production’
Webpack 只在 生产模式 下默认启用 tree shaking 和代码压缩 - 代码没有副作用(side effects)
什么叫代码副作用?副作用,简单说就是:代码在运行时除了导出内容,还会对外部环境产生影响。如果只是定义导出,但并没有什么实质影响,不会修改样式/打印日志等。
Tree shaking 不会删除具有副作用的代码。你可以通过在 package.json 中添加:
{"sideEffects": false //表示你的模块无副作用,Webpack 就可以放心地去摇树了。
}
//如果有些文件有副作用(比如 CSS 引入),可以排除它们
{"sideEffects": ["*.css"]
}
- 如何验证 tree shaking 生效?
打包结果变小(观察输出文件体积)
使用 webpack --mode production --analyze + webpack-bundle-analyzer
检查生产代码里是否真的去掉了未用的代码
html-webpack-plugin插件
webpack打包时自动生成html文件,无需手动引入。文档
- 安装
yarn add html-webpack-plugin -D #下载
#--save-dev 和 -D 是完全等价的
dependencies 是你项目运行时需要用到的依赖(比如 React、Vue 这些核心库)。
devDependencies 是你在开发阶段才需要用的依赖(比如 Webpack、Babel、ESLint、TypeScript 这些工具)。即- D
- 在webpack.config.js中添加配置
const HtmlWebpackPlugin =require('html-webpack-plugin')
module.exports={//其他配置 这里略plugins:[new HtmlWebpackPlugin({template:'./public/index.html' //webpack打包时自动生成html文件})]
}
webpack的开发服务器:devServer
webpack开发服务器, 把代码运行在内存中, 自动更新, 实时返回给浏览器显示。
打包在内存中, 速度快;自动更新打包变化的代码, 实时返回给浏览器显示;
引入
每次修改代码, 重新 yarn build 打包, 才能看到最新的效果, 实际工作中, 打包 yarn build 非常费时 (30s - 60s);
原因:
从0构建依赖
磁盘读取对应的文件到内存, webpack开始加载
再用对应的 loader 进行处理
将处理完的内容, 输出到磁盘指定目录
解决:起一个开发服务器, 缓存一些已经打包过的内容, 只重新打包修改的文件, 最终运行在内存中给浏览器使用
使用webpack-dev-server模块:热更新
webpack-dev-server文档: https://webpack.docschina.org/configuration/dev-server/
- 下载模块包
yarn add webpack-dev-server -D
- 在package.json-配置自定义命令:自定义webpack开发服务器启动命令serve在package.json里
"scripts":{"build":"webpack","serve":"webpack serve"
}
- 启动当前工程里的webpack开发服务器
yarn serve
,webpack-dev-server会给一个地址+端口浏览器,访问即可看到在内存中打包的index.html页面 - 重新编写代码, 观察控制台和浏览器是否自动打包和更新
- 改变webpack-dev-server配置
webpack-dev-server配置文档: https://webpack.docschina.org/configuration/dev-server/#devserverafter
在webpack.config.js中添加如下配置,重启webpack开发服务器即可
都是去文档中找配置项的名字,在webpack.config.js – devServer选项里添加
module.exports={//其他配置略devServer:{port:8888 //自定义端口号}
}