文章目录
在开发过程中,我们会把程序分解为功能离散的文件,这些文件就是模块;
一、webpack 模块
1、webpack 支持的模块
ES6 的 import/export/export default 语句
commonJS 的 require/module.exports 语句
AMD 的 define 和 require 语句
CSS 等样式库的 @import 语句
样式里面 url 引入的资源
总体来说 webpack 天生就支持 ECMAScript、CommonJS、AMD、Assets 等模块;同时 webpack 的 loader 也可以使 webpack 可以支持多种语言和预处理语法来编写模块;
2、webpack 模板解析的简易原理
webpack 将 js、css、less、img、html 等文件通过 loader 以及 webpack 内置的一些模块方法解析成模块化文件;
打包编译的解析过程:
1、webpack 执行会返回一个描述 webpack 打包编译整个流程的对象:compiler ;
2、compiler 对象内置了一个打包状态,随着打包的进行状态也会实时的发生变更,同时会触发相应状态的 webpack 钩子函数;
3、每一个 webpack 打包都会创建一个 compiler 对象,它会走完整个生命周期的过程,webpack 所有模块的解析都是 compiler 对象内置模块的解析器(resolvers)去工作的;
4、resolver 的主体功能就是解析模块,它是基于 enhanced-resolve 这个包来实现的,所以在 webpack 中无论使用什么样的模块引用语句,最终都是使用 enhanced-resolve 这个包的 API 来实现模块解析的;
二、模块解析(resolve)
webpack 通过 resolvers 实现模块之间的依赖和引用;resolver 帮助 webpack 从每一个 require/import 语句中找到需要引入的模块代码;当打包的时候 webpack 使用 enhanced-resolve 来解析文件路径;
1、解析路径
webpack 能解析三种文件路径:相对路径、绝对路径、模块路径;
1、绝对路径:相对于项目的根目录作为参考的路径(‘/src/util/test.js’),第一个 / 就代表根目录;
2、相对路径:相对于文件本身作为参考的路径(‘./util/test.js’),./ 表示进入文件;
3、模块路径:引入第三方模块的时候,直接通过模块名(‘lodash’);
2、目录(路径)配置别名
如果我们的目录层级比较深,那么我们在内部的文件中引入外部的文件就会需要多次使用 ‘…/’ 来找到外部的文件;这也操作起来会比较麻烦;所以我们可以给特定文件夹下的目录起个别名:
const path = require('path')
module.exports={
resolve:{
alias:{
"@":path.resolve(__dirname,'./src')
}
}
}
这样 @
就可以直接访问到 src 这个目录下的文件了,此时 @
指向就是 src;
"../../../../../../../../util/tset"
//替换为
"@/util/test"
3、配置文件的扩展名
在引入文件的时候,webpack 会默认帮我们查找一些具有某个文件名的文件,因此我们在引入文件时可以不用带上后缀名;但是如果我们有相同文件名后缀不一样的两个文件(test.js、test.json),在引入时不携带后缀名,webpack 会帮我们找到对应名称的 js 文件,而不是 json 文件;这是因为 webpack 默认的优先级配置;
当然我们也可以修改 webpack 默认的配置:
module.exports = {
extensions:[".json", ".js", ".vue"]
}
这样上面的引入,webpack 会帮我们找到 json 文件;
三、外部扩展(Externals)
1、引入 cdn 文件
有时候为了减小 bundle 文件的体积,我们需要把一些不变的第三方库用 cdn 的形式引入进来;
//index.html
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
这个时候我们想在代码中使用引入的 jquery ,但是三种模块引入方式都不行,webpack 给我们提供了 Externals 的配置属性,让我们配置外部扩展模块:
module.exports = {
plugins:[
new HtmlPlugin({
template:'./index.html'
})
],
externals:{
jquery:'jQuery'
}
}
然后在页面中引入
import $ from 'jquery'
这也就可以正常使用 JQuery 了;
2、改造
如果我们不想自己手动引入 cdn 在 html 文件中,想让 webpack 自动生成一个 html 并且帮我们自动引入 script 标签,并且引入 cdn;我们只需要下面的操作:首先删除 index.html;
module.exports = {
plugins:[
new HtmlPlugin()
],
externalsType:'script',
externals:{
jquery:[
"https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js",
"jQuery"
]
}
}
不需要手动 Html 引入,只需要上面的配置就可以自动生成的 HTML 文件中自动引入 jquery 的 cdn 文件;
四、依赖图(dependency graph)
每当一个文件依赖另一个文件时, webpack 就会认为这两个文件存在依赖关系;这也就让 webpack 可以获取非代码资源(图片,字体)等,并把它们作为依赖提供给应用程序;
当 webpack 开始工作时,它会根据我们写的配置,从入口(entyr)开始,递归构建一个依赖关系图,这个依赖图包含应用程序所有的模块,然后将所有模块打包为 bundle(也就是出口(output)配置项);
1、bundle 分析工具
1、webpack-chart:生成一个可交互的饼图;
2、webpack-visualizer:可视化,并分析你的 bundle ,检查那些模块占用空间,那些是可以重复使用的;
3、webpack-bundle-analyzer:一个 plugin 和 cli 工具,将 bundle 内容展示成一个便捷的、交互式、可缩放的树状图;
4、webpack bundle optimize helper:分析 bundle 并提供可操作的改进措施,以减少 bundle 的大小;
5、bundle-stats:生成 bundle 报告(大小、资源、模块)并比较不同构建之间的结果;
2、webpack-bundle-analyzer
安装
npm i webpack-bundle-analyzer -D
安装成功之后在 webpack.config.js 中引入使用
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
plugins:[
new BundleAnalyzerPlugin()
]
}
然后运行项目,就可以自动打开一个浏览器,里面可以看到下面这种关系图:
stat:统计信息,可以看到每个模块的互相依赖的图,以及每个模块的大小信息;
parsed:解析以后的模块结果,以及模块大小信息;
gzipped:压缩以后的模块,以及模块大小信息;
五、扩展功能
1、PostCSS 与 css 模块
PostCSS :是一个用 JavaScript 工具和插件转换 CSS 代码的工具;比如可以使用 Autoprefixer 插件自动获取浏览器的流行度和能够支持的属性,并根据这些数据自动为 CSS 添加前缀,将最新的 CSS 语法转换成大多数浏览器能够理解的语法;
CSS模块:就是所有的类名都只有局部作用域的 CSS 文件;CSS 模块不是浏览器中的官方标准,是改变类名和选择器的作用域到当前文件的构建过程;
1.1、PostCSS 使用
webpack 中使用 PoseCSS 需要安装 style-loader、css-loader、postcss-loader:
安装:
npm i style-loader css-loader postcss-loader -D
安装之后使用
module.exports={
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader','postcss-loader']
}
]
}
}
这个时候我们可以在浏览器上查看,并没有做浏览器的兼容设置;
我们想要使用具体的功能还需要安装对应的插件:我们在安装一个插件 autoprefixer 来加载样式的前缀;
npm i autoprefixer -D
新增文件 postcss.config.js 来配置 css 的插件;
module.exports = {
plugins:[
require('autoprefixer')
]
}
在 package.json 中约定浏览器的版本:
"browserslist": [
"> 1%", //全球浏览器的使用率要大于1%
"last 2 versions"//每个浏览器最新的两个版本
],
这样浏览器的样式就会出现带有前缀的:
插件会自动帮助我们找到当前浏览器支持的样式,并添加前缀;
如果想在样式里面写嵌套可以安装插件: postcss-nested(这种嵌套样式会被编译成浏览器可以识别的样式)
1.2、CSS 模块给样式类添加唯一的名字
开启 CSS 模块;
module.exports={
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
modules:true
}
},
'postcss-loader'
]
}
]
}
}
这个时候我们的类名就会变成一个 hash 字符串类型的唯一标识;
由于编译后的类名变了,我们在使用的时候也需要换一种方式来使用:在引入css的时候把 css 文件作为一个模块来引入
import styles from ''./app.css"
const div = document.createElement('div')
div.textContent = 'hello webapck'
div.classList.add(styles.box) //使用
document.body.appendChild(div)
1.3、部分开启 CSS 模块
css 文件名统一使用 module 关键字进行识别,在 module 里面增加一个规则来匹配这种类型的文件:xxx.module.css
{
test:new PegExp(`^(.*\\.module).*\\.css`),
use:[
'style-loader',
{
loader:'css-loader',
options:{
modules:true
}
},
'postcss-loader'
]
}
2、Web Works
如果一个 js 运算量比较大,执行时间比较长,我们又不想让它阻塞 js 的循环以及 ui 的渲染,这时候我们考虑使用一下 Web Works ;
Web Works 提供了 js 的后台处理线程的 API,允许我们将复杂的、耗时的 js 逻辑放在浏览器的后台线程里进行处理,让 js 线程不阻塞 ui 线程从渲染;
定义works:
new Worker(scriptURL:string | URL,options?:WorkerOptions)
新建 work.js 文件,放置线程运行的代码
self.onmessage = (message) => {
self.postMessage({//发送
msg:123
})
}
在入口文件中创建一个 web works 来引用刚才的 worker;
const worker = new Worker(new URL('./work.js',import.meta,url))
worker.postMessage({
question:"hello"
})
worker.onmessage = (message) =>{//接收消息
console.log(message)
}
这样,打包的时候 webpack 会帮 Web Works 做一个额外的打包;
3、TypeScript
安装 TS 及相应的 loader
npm i typescript ts-loader -D
安装成功之后配置
module:{
rules:[
{
test:/\.ts$/,
use:'ts-loader',
exclude:/node_modules/
}
]
},
resolve:{
extensions:['.ts', '.js']
}
配置好之后 ,执行命令 初始化 ts
npx tsc --init
将 rootDir 指向当前文件夹 “./src”,outDir 指向 “./dist”;然后就可以正常编译打包 ts 文件了;
注意:我们在使用 TS 加 webpack 的时候,如果引入其他第三方插件、库、工具,都需要引入一个类型文件;想要查询相关类型文件包可以访问:https://www.typescriptlang.org/dt/search?search=