一、静态和动态导入
默认情况下,我们静态导入的所有模块都会添加到初始捆绑包中。使用默认 ES2015 导入语法 导入的模块将静态导入。import module from 'module'
实际上,我们可以而把那些在需要时展示的组件,通过动态导入来实现
import("b.vue").then() //动态导入,在打包时会自动chunk
如上图,在点击时 emoji 时,才去加载 emoji-picker 组件。
动态路由和动态组件会自动拆分bundle。
二、可见性导入
除了用户交互之外,我们通常还有在初始页面上不可见的组件。一个很好的例子是延迟加载图像,这些图像在视口中不直接可见,但只有在用户向下滚动后才会加载。
三、交互时导入(懒惰式加载)
当用户与需要它的 UI 交互时,延迟加载非关键资源
您的页面可能包含不是立即必需的组件或资源的代码或数据。例如,用户看不到用户界面的一部分,除非他们单击或滚动页面的某些部分。例如模态框、列表的详情页、视频播放器或聊天小部件、第三方资源等等
与其立即加载这些资源,不如在更合适的时刻加载它们,例如:
- 当用户首次单击以与该组件进行交互时
- 将组件滚动到视图中
- 或延迟该组件的加载,直到浏览器空闲(通过 requestIdleCallback API)。
加载资源的不同方法概括如下:
- 立即加载资源(加载脚本的正常方式)
- Lazy (路由)- 当用户导航到路由或组件时加载
- Lazy (交互时) - 当用户单击 UI 时加载(例如显示聊天)
- Lazy (在视口中)- 当用户滚动到组件时加载
- Prefetch - 预拉取
- Preload - 预加载
如: 第三方 UI
如: 视频播放器嵌入
如: 认证
如:聊天小部件
如: 帮助页
四、基于路由的拆分
根据当前路由动态加载组件
{
path: "/home",
component: () => import(/* webpackChunkName: "home"*/ "@/components/layout/Index.vue"),
children: [
//todo
],
},
五、捆绑拆分
将代码拆分为可重用的小块
构建现代 Web 应用程序时,打包器例如,Webpack或rollup获取应用程序的源代码,并将其打包到一个或多个bundle中,
当用户访问网站时, bundle请求并加载,以便将数据显示到用户的屏幕。
bundle越大,则浏览器引擎到达进行第一次呈现调用的行所需的时间越长。在那之前,用户必须盯着空白屏幕相当长一段时间,这可能是.非常令人沮丧!
我们希望尽快向用户显示数据。更大的bundle导致加载时间、处理时间和执行时间增加.如果我们能减小这个捆绑包的大小,以便加快速度,那就太好了。
六、断续器模式
通过预缓存、延迟加载和最小化往返来优化初始负载
当我们想要访问一个网站时,我们首先必须向服务器发出请求才能获得这些资源。入口点指向的文件从服务器返回,这通常是我们应用程序的初始HTML文件!浏览器的 HTML 解析器在开始从服务器接收此数据后立即开始解析此数据。如果解析器发现需要更多资源(如样式表或脚本),则会向服务器发送另一个 HTTP 请求以获取这些资源!
断续器模式侧重于四个主要的性能注意事项:
- 有效地推送关键资源,从而最大限度地减少到服务器的往返量并减少加载时间。
- 尽快渲染初始路由,提升用户体验
- 在后台为经常访问的路由预缓存资源,以最大限度地减少对服务器的请求量并实现更好的脱机体验
- 懒惰地加载不经常请求的路线或资源
七、Tree Shaking
通过消除死代码来减小捆绑包大小
我们可能会将代码添加到我们的捆绑包中,而这些代码在应用程序中的任何地方都没有使用。这段死代码可以消除,以减小捆包的尺寸,并防止不必要地加载更多数据!消除死代码的过程在将其添加到我们的捆绑包之前,称为tree-shaking
八、Preload
在发现关键资源之前通知浏览器(指关键资源加载完后,会接着加载preload的资源。)
方式
webpackPreload: true 方式(Webpack 4.6.0+)
const EmojiPicker = import(/* webpackPreload: true */ "./EmojiPicker");
九、Prefetch
获取和缓存可能很快请求的资源(指关键资源加载完后,不一定会加载prefetch的资源,可能等到具体需要时才加载)
方式
webpackPrefetch: true方式
const EmojiPicker = import(/* webpackPrefetch: true */ "./EmojiPicker");
十、虚拟列表
这是在动态列表中仅呈现可见内容行而不是整个列表的想法。呈现的行只是完整列表的一小部分,当用户滚动时,可见的内容(窗口)会移动。这可以提高渲染性能。
十一、压缩脚本
减少通过网络传输脚本所需的时间
- 1、Gzip 和 Brotli 是两种最常用的被现在浏览器所支持的 压缩js 方式(需要和nginx配置使用)
- 2、Brotli 在相似的压缩级别下提供了更好的压缩比(但是需要https,而gzip在http下也可以)
- 3、如果你使用 webpack来打包你得代码,你可以使用 CompressionPlugin 压缩为gzip 或者 使用 BrotliWebpackPlugin 压缩为brotli,如果使用rollup,则使用rollup-plugin-gzip插件
- 4、Gzip压缩切换到Brotli压缩,文件体积大小会降低15%-25%左右。
- 5、compress(a + b) <= compress(a) + compress(b) - 单个大捆绑包将比多个较小的捆绑包提供更好的压缩
HTTP 数据压缩可以以不同的方式进行分类。其中之一是有损与无损。
有损压缩意味着压缩-解压缩循环会导致文档略有更改,同时保持其可用性。最终用户大多无法察觉到这种变化。有损压缩最常见的示例是图像的 JPEG 压缩。
使用无损压缩,压缩和后续解压缩后恢复的数据将与原始数据精确匹配。PNG 图像是无损压缩的一个示例。无损压缩与文本传输相关,应应用于基于文本的格式,如 HTML、CSS 和 JavaScript。
有多个工具可用于缩小 HTML、CSS 和 JS 资源。Terser 是 ES6+ 中流行的 JavaScript 压缩工具,默认情况下,Webpack 包含一个用于此库的插件,用于创建缩小的构建文件。
也可以使用esbuild压缩,它是一种比terser速度还快的压缩方式。能快10-100倍
静态与动态压缩
缩小有助于显著减小文件大小,但压缩 JS 可以提供更显著的增益。可以通过两种方式实现服务器端压缩。
静态压缩:可以使用静态压缩来预压缩资源,并在生成过程中提前保存它们。常用于打包时压缩。比如webpack或者rollup在打包时,就提前压缩为gzip,然后放到 nginx上,配置 gzip_static:on;这样在http请求时,会获取 gzip的文件,然后浏览器解压缩,显示页面。
动态压缩:通过此过程,当浏览器请求资源时,压缩会动态进行。常用于接口中的数据压缩,在请求接口时,服务器动态压缩数据,然后返回到浏览器。
Gzip 和Brotli算法
Gzip压缩格式已经存在了近30年,是一种基于Deflate算法的无损算法。Deflate算法本身使用LZ77算法和霍夫曼编码的组合。
LZ77 算法标识重复的字符串,并将它们替换为反向引用,反向引用是指向它以前出现的位置的指针,后跟字符串的长度。随后,霍夫曼编码识别常用的引用,并用具有较短位序列的引用替换它们。较长的位序列用于表示不经常使用的引用。
所有主流浏览器都支持 Gzip。
Brotli
2015年,谷歌推出了Brotli算法和Brotli压缩数据格式。与GZip一样,Brotli也是一种基于LZ77算法和霍夫曼编码的无损算法。此外,它还使用二阶上下文建模以类似的速度产生更密集的压缩。上下文建模是一项功能,它允许在同一块中对同一字母表使用多个霍夫曼树。Brotli 还支持更大的窗口大小用于反向引用,并具有静态字典。这些功能有助于提高其作为压缩算法的效率。
Brotli目前受到所有主要服务器和浏览器的支持,并且越来越受欢迎。
比较gzip和brotli
下表显示了不同压缩级别的 Brotli 和 Gzip 压缩率和速度的基准比较。
启用压缩
webpack配置
module.exports = {
//...
plugins: [
//...
new CompressionPlugin()
]
}
Nginx 这样的 HTTP 代理上启用它
server {
listen 8080;
server_name localhost;
# 需要http_gzip_static_module 模块
gzip_static on;
location / {
alias html
index index.html index.htm;
try_files $uri $uri/ /html/index.html;
}
}
浏览器通过请求中的 Accept-Encoding HTTP 标头传达它支持的压缩算法,这表明浏览器支持 Gzip 和brotliAccept-Encoding: gzip, br
服务器将返回 Content-Encoding HTTP 响应标头,以指示响应中使用的压缩算法。例如,Content-Encoding: br