目的

性能优化的目的就是如何让页面加载的更快或者说如何让用户感觉到很快,当页面加载速度大于 300ms,用户可以感知到白屏的存在;当页面加载速度大于 3000ms,用户会觉得烦躁以及会关掉页面。所以性能优化在前端是非常重要!
那如何用户感觉到很快有很多的实现方案,比如说骨架屏,舒适的加载动画等等安抚用户情绪的产品设计。
而作为开发者来说,我们可以做哪些事情可以让页面真正的变得更快呢?

性能指标

我们需要查看一些指教,从而来决定我们是否应该去优化以及我们该从那些方向去优化,这很重要!

First Paint (FP)

首次绘制,从白屏到第一个像素点出现的时间。

First Content Paint (FCP)

首次内容绘制,网页首次绘制的内容的时间。

Speed Index

页面加载过程中的视觉显示速度。

Largest Contentful Paint (LCP)

最大的内容绘制完成的时间。

Total Blocking Time (TBT)

页面总阻塞的时间。

查看指标

  • LighthouseLighthouse 是性能评测工具,支持移动端和PC端。

Performance

  1. window.performance.timing;
  2. // PerformanceTiming{ ... }

Performance 接口可以获取到当前页面中性能相关的信息。
Performance.timing 返回 PerformanceTiming 对象,这个对象包含了页面相关的性能信息。

  • rollup-plugin-visualizer / webpack-bundle-analyzer打包的时候生成可视化的依赖分析,分别基于 Rollup 和 Webpack。

    如何优化

    压缩代码

    清除代码注释、空白字符和未使用的代码等等能够有效的减少 HTML 、CSS和JavaScript 的前端加载的时间

    压缩文件

    通过 Gzip 来压缩 HTML 、CSS 和 JavaScript 文件能够有效的较少前端加载的时间。

    实现思路

    压缩代码

    Vite 中默认开启了 Minify,并且我们提供了 terser 和 esbuild 两种打包的方式。
    默认为 esbuild,它比 terser 快 20-40 倍,压缩率只差 1%-2%。
    但使用 terser 会被压缩的更小。
    1. // vite.config.ts
    2. export default defineConfig({
    3. plugins: [vue()],
    4. build: {
    5. minify: "terser",
    6. },
    7. });

    压缩文件

    目前现代浏览器都支持 Gzip 对数据压缩和解压,开启 Gzip 压缩后极大的压缩文件大小,大大缩短了页面的加载时间。
    1. // vite.config.ts
    2. import viteCompression from "vite-plugin-compression";
    3. export default defineConfig({
    4. plugins: [vue(), viteCompression()],
    5. });
    webpack 中可以使用 compression-webpack-plugin 进行 Gzip 压缩文件的打包。
    打包出 Gzip 压缩后需要在服务器开启 Gzip 压缩。

    CDN加速

    无论是通过 Webpack 打包还是 Vite 打包,也会将我们使用的第三方的包进行打包,这样就进一步的提高了静态资源的体积,严重阻塞了页面的加载,白屏时间过长,给用户造成了非常不好的体验,那怎么样才可以解决这个问题呢?
    在 Vite 中基于 Rollup 进行打包,同时我们也为我们提供了自定义 Rollup 的打包配置。
    我们在打包的时候对第三方包进行移除,以达到减少 chunk 文件的体积的目的。
  1. // vite.config.ts
  2. export default defineConfig({
  3. build: {
  4. rollupOptions: {
  5. external: ["vue", "vue-router", "naive-ui", "axios"],
  6. },
  7. },
  8. });

移除了第三方包我们还需要通过 CDN 的方式引入依赖。

  1. <!-- index.html -->
  2. <script src="https://unpkg.com/vue@3.2.25/dist/vue.global.prod.js"></script>
  3. <script src="https://unpkg.com/vue-router@4.0.0/dist/vue-router.global.prod.js"></script>
  4. <script src="https://unpkg.com/naive-ui@2.30.5/dist/index.js"></script>
  5. <script src="https://unpkg.com/axios@0.27.2/dist/axios.min.js"></script>

当然我们在开发模式下不需要引入依赖,我们可以使用 vite-plugin-html 插件动态的添加依赖。

  1. // vite.config.ts
  2. import { createHtmlPlugin } from "vite-plugin-html";
  3. let cdn = {
  4. js: [
  5. "https://unpkg.com/vue@3.2.25/dist/vue.global.prod.js",
  6. "https://unpkg.com/vue-router@4.0.0/dist/vue-router.global.prod.js",
  7. "https://unpkg.com/naive-ui@2.30.5/dist/index.js",
  8. "https://unpkg.com/axios@0.27.2/dist/axios.min.js",
  9. ],
  10. };
  11. export default ({ mode }) => {
  12. // 非生产环境不引入cdn
  13. if (mode === "development") {
  14. cdn = {
  15. js: [],
  16. };
  17. }
  18. return defineConfig({
  19. plugins: [
  20. vue(),
  21. createHtmlPlugin({
  22. inject: {
  23. data: {
  24. injectScript: cdn.js,
  25. },
  26. },
  27. }),
  28. ],
  29. });
  30. };
  1. <html lang="en">
  2. <head>
  3. <meta charset="UTF-8" />
  4. <link rel="icon" href="/favicon.ico" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title><%- title %></title>
  7. <!-- 循环添加CDN -->
  8. <% for (var i in injectScript) { %>
  9. <script src="<%= injectScript[i] %>"></script>
  10. <% } %>
  11. </head>
  12. <body>
  13. <div id="app"></div>
  14. <script type="module" src="/src/main.ts"></script>
  15. </body>
  16. </html>

webpack 中可以使用 htmlWebpackPlugin 来实现。
虽然我们已经通过 CDN 的方式进行引入,但是我们代码内是通过 ESM 的方式进行引入的,而我们不可能手动的去修改整个项目中的所有依赖引入方式,所以我们需要使用 rollup-plugin-external-globals 插件用来构建全局依赖。

  1. // vite.config.ts
  2. import externalGlobals from "rollup-plugin-external-globals";
  3. export default defineConfig({
  4. build: {
  5. rollupOptions: {
  6. external: ["vue", "vue-router", "naive-ui", "axios"],
  7. plugins: [
  8. externalGlobals({
  9. vue: "Vue",
  10. "vue-router": "VueRouter",
  11. "naive-ui": "naive",
  12. axios: "axios",
  13. }),
  14. ],
  15. },
  16. },
  17. });

webpack 中为我们提供了 externals 来实现。
经过以上的操作,我们已经成功的将打包体积从 3.01MB 缩小到 6.78KB。