前端工程化
模块化
CJS
CJS
是 CommonJS
的缩写,我们经常在Node
中见到它
关键词
- require:用于模块引入,是一个函数,所以是运行时加载,它可以接收一个
js
表达式, - module:每一个模块都有,主要用于记录当前模块信息
- module.exports:用于模块导出,最终导出的结果,还是以
module.exports
指向的对象为准
特点
- CommonJS模块是运行时加载,而且是同步加载,一般用在服务端,因为服务端读取的是本地资源,速度很快
- 模块可以多次加载,但是实际上只在第一次加载运行,后面都是使用缓存
- 模块输出是值的一个浅拷贝
其它
- 默认情况下
exports
和module.exports
指向是一样的,但是如果使用module.exports = {}
的时候会重新创建一个新对象,这个时候两者的指向就不一样了,但是CJS
总是以module.exports
指向的对象为准,所以建议不要两者混用 - 在浏览器中不能运行,需要其他打包工具打包,在
package.json
中会以main
字段为目标文件路径
AMD
AMD
的是Asynchronous Module Definition
(异步模块定义),AMD
可以理解是CJS
在浏览器端的解决方案,因为如果在浏览器总是同步加载的话可能会出现网络加载资源假死的情况,所以在浏览器是采用了异步加载的方案,因此AMD
就是异步加载的
关键词
- define:用于定义一个模块
- require:用于模块引入
特点
- 体积更小,异步加载,按需加载,可以在浏览器端运行
- 不堵塞
js
线程运行
其他
- 推荐使用
RequireJS
,目前是AMD规范最好的实现者之一
CMD
CMD
规范,全称”Common Module Definition”
,称为 通用模块加载规范
。它与AMD很类似
,一般也是用在浏览器端。浏览器端异步加载库Sea.js
实现的就是CMD
规范
特点
AMD
推崇依赖前置、也就是依赖要提前写好,而CMD
推崇依赖就近的写法,用到了才写AMD
一般来说是加载完立即执行,而CMD
是延迟执行,推崇as lazy as possible
UMD
UMD
代表通用模块定义(Universal Module Definition
),准确来说并不是一个独立的模块标准,而是集合了CJS
、AMD
等一体的打包模式,会自动判断当前可用环境
特点
- 一套代码,多端使用,模块化兼容性好
- 当使用
Rollup/Webpack
之类的打包器时,UMD
通常用作备用模块
ESM
ESM
是ES6 Module
,ES6
标准中的模块规则,一统浏览器和服务端标准的解决方案
关键词
- import:用于模块的引入,只能接收字符串
- export:用于模块的导出
特点
- 浏览器端会异步加载,这一点延续了
AMD
的优势,但是利用了浏览器原生的解析能力,代码体积更小 ESM
会有独立的静态解析阶段,ESM
是编译时执行- 模块内部自动采用严格模式,所以
this
的值为undefined
,模块输出是值的引用,并且是只读的 import/export
提升:import/export
必须位于模块顶级,不能位于作用域内;其次对于模块内的import/export
会提升到模块顶部,这是在编译阶段完成的
其他
在
package.json
中设置"type": "module"
才能开启ESM
模式,会以module
字段为目标文件路径在静态分析阶段的时候,可以
Tree Shaking
,去除死代码,所谓静态分析, 就是在不运行代码的情况下, 对代码进行检测扫描分析
包管理器
npm
这里说的是最早期的npm(v1/v2)
介绍
npm
的理念是:Bring the best of open source to you, your team and your company.
npm(node package manager)
:node.js
的包管理器,主要用于安装、卸载、管理、分享依赖或者框架
早期的缺点
node_modules
是一层一层嵌套的,也就是依赖A
可能还有依赖B
,这样可能会依赖层级太深- 依赖无法被公用,而且存在下载大量重复依赖,占用磁盘空间较大
发展历程
- npm V2:此时期主要是采用简单的递归依赖方法,最后形成高度嵌套的依赖树。然后就会造成如下问题:重复依赖嵌套地狱,空间资源浪费,安装速度过慢,文件路径过长等问题
- npm V3:V3版本作了较大的更新,开始采取扁平化的依赖结构。这样的依赖结构可以很好的解决重复依赖的嵌套地狱问题,但是却出现扁平化依赖算法耗时长这样新的问题
- npm V5:为了解决上面出现的扁平化依赖算法耗时长问题,
npm
引入package-lock.json
机制,package-lock.json 的作用是锁定项目的依赖结构,保证依赖的稳定性,可以有效地减少依赖解析的时间,从而提高了npm的性能。
yarn
yarn最初的出现主要就是解决npm早期的不足之处
优点
- 确定性:采用
yarn.lock
机制,能够锁定安装的版本并提供确定性的依赖关系 - 模块扁平化:将不同版本的依赖包,按照一定的策略,归结为单个版本,避免冗余
- 网络性能更好:
yarn
采用了请求排队的理念,类似于并发池连接,能够更好的利用网络资源 - 离线模式:采用缓存机制实现了离线模式
pnpm
pnpm本质上也是包管理器,这一点跟npm/yarn没有区别,但是具有更加强大的功能
优点
- 包安装速度很快
- 利用软连接和硬连接使得磁盘空间利用非常高效
- 更安全,解决幽灵依赖问题
- 支持
monorepo
前端优化
性能指标
优化,需要确定优化的指标,分析性能优化的首要基础是数据和指标
通常使用Performance API
,可以让我们看到以下指标
Load
代表页面中依赖的(CSS、图片、JS、视频)等所有资源加载完的事件
DOMContentLoaded
代表DOM解析完毕
FP (First Paint)
表示浏览器首次开始绘制像素
FCP(First Contentful Paint)
表示渲染出第一个内容,这个指标就是我们说的白屏时间
FMP(First Meaningful Paint)
首次进行有意义的绘制
LCP(largest contentful Paint)
最大内容绘制,可以用作首屏时间指标
性能优化
网络优化
减少HTTP请求次数
- CSS Sprite:雪碧图(精灵图),将多个图片合成一张图片,合并后的图片的大小要比分开图片大小的总和还小。使用雪碧图,需要使用
CSS
的background-image
和background-position
属性显示所需的图像段 - Font Icon:使用字体图标来代替图标,将多个图标文件合成为字体图标文件不仅可以减少对于图片的
HTTP
请求数量与图标大小,还作为矢量图对于放大缩小等操作不会失真,此外字体图标可以得到CSS
的很好的支持 - Combined files:
Combined files
也就是合并文件,将多个CSS
文件或者JavaScript
文件合并成一个CSS
文件或者JavaScript
文件,可以有效减少HTTP
请求数量,并且可以通过压缩算法减小文件的大小 - Inline images:使用通过使用
data:URL
方案也就是直接放入图片的base64数据
来直接将图像数据嵌入到页面或者CSS
中,虽然这会增加文档或者是CSS
文件的大小,但同样这确实是一个减少HTTP
请求数量的方案
开启HTTP2
- 二进制格式
- 流量控制
- 头部压缩
- 多路复用
- 服务器推送
使用CDN
- 对于一些资源类型的文件放置到
HTML
中并且使用CDN
加速 - 在
webpack
中通过属性externals
配置把类似 echart、element-ui、lodash 等第三方依赖库单独提取出,从而减小打包的体积大小
压缩资源文件
- 开启Gzip压缩:前端项目可以使用
compression-webpack-plugin
插件,使用node
可以使用compression
第三方库,或者由服务端配置Nginx
开启 - 压缩JS:可以使用
UglifyPlugin
- 压缩CSS:可以使用
MiniCssExtractPlugin
orcssnano
- 压缩HTML:可以使用
HtmlWebpackPlugin
- 压缩图片:TinyPNG或者ImageOptim(可以使用工具)
SSR
- 开启服务端渲染,首屏渲染快,
SEO
好 vue
有nuxt.js
,react
有next.js
利用缓存
- 配置
ETag
资源优化
图片资源
使用新一代的图片格式
- 可以考虑
WebP
图片,是由Google
提供的一种较为现代化的图片格式,支持有损压缩和无损压缩,支持alpha
通道和表现动画内容
压缩图片
- 目的还是减小体积
适当调整图片的分辨率
- 对于图片而言,分辨率是影响图片大小的重要因素之一,较高的分辨率将影响页面的加载和解析速度
使用canvas实现本地图片的压缩
- 用于从本地上传到服务器的图片,可以使用
canvas
对原始图片进行剪裁、阉割,减小图片体积
图片懒加载
- 可以使用
getBoundingClientRect().top
去判断,也可以使用IntersectionObserver API
实现图片懒加载
图片预加载
- 可以在特定的场景下使用,比如说我们知道漫画是一张一张连续的图片组成 。我们在看漫画时,也是一张一张看的,但如果当我们看完一张,切换到下一张时再加载图片,那么就会有一段空白的加载时间。而且漫画图片一般比较大,如果网络不是很好,那么加载时间就会比较久。体验就会下降。那么就可以使用预加载
使用CSS3代替图片
- 如果是纯色背景,或者渐变等效果能够使用
CSS3
绘制则尽量使用CSS3
CSS资源
延迟加载非关健CSS
- 比如首页我们只加载使用到的那部分CSS,再等到网络空闲的时机,加载出剩余的CSS
JS资源
使用 async 或 defer 属性
利用webpack分包懒加载
第三方库按需加载
除去重复以及死代码
代码层面优化
避免CSS表达式
CSS
表达式通过expression
方法来接受JavaScript
表达式,是一种动态设置CSS
的强大的方式,但同样也是非常危险的方式,CSS
表达式的问题在于其会进行频繁的计算,CSS
计算的频率要远远超出我们的想象,不仅在页面显示和缩放时会进行计算,在页面滚动或者移动鼠标都会重新计算一次,从而影响到页面的性能
避免重定向
如果一定要重定向的话使用永久重定向会好一些
减少重排重绘
- 不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式。
- 如果需要修改多个css属性,可以先将元素设置为display:none,然后操作完成之后再改变回来,这样就能大大降低回流次数
- 批量修改元素时,可以先让元素脱离文档流,等修改完毕后,再放入文档流
使用防抖与节流
- 可以在按钮、输入框、滚动条等地方使用,根据情况而定
使用事件委托
- 使用事件委托可以减小开销
清理垃圾
- 记得清除定时器等垃圾
感知优化
过渡
Loading
动画,可以用于页面加载、数据加载、提交按钮加载等等- 骨架屏
- 进度条
- 弹窗过渡
- 滚动条平滑滚动
兜底方案
- 对于图片加载失败,可以利用
error
事件捕获到,然后更改此图片的url
为兜底图的url
- 对于列表无数据的时候可以使用一个图片兜底来说明没有数据