Vite学习笔记(性能优化篇)

1,266 阅读5分钟

项目常见优化方式分类

1、网络优化 包括 HTTP2、DNS预解析、Preload、Prefetch等手段

2、资源优化 包括 构建产物分析、资源压缩、产物拆包、按需加载等优化方式

3、预渲染优化 服务端渲染(SSR) 、静态站点生成 (SSG)

网络优化

HTTP2

为甚么采用HTTP2来进行网络优化?

1、HTTP1.1存在队头阻塞问题,同一个TCP管道中同一时刻只能处理一个HTTP请求,在当前请求没有处理完时,其它请求处于阻塞状态;

2、 浏览器对于同一个域名下的兵法请求数量都有限制,Chrome中只允许6个请求发送,且不允许用户配置,当请求数量超过6个时,多出的请求只能排队等待发送。

HTTP2的优点?

1、多路复用 . HTTP2将数据分为多个二进制帧,多个请求和响应的数据帧在同一个TCP通道进行传输,解决了之前队头阻塞问题。

2、不存在同一域名并发请求数量限制

3、服务端推送能力 可以让某些资源提前到达浏览器,比如对一个html 的请求,通过HTTP2可以同时将相应的js和css资源推送扫浏览器。

通过插件 vite-plugin-mkcer 在本地服务开启HTTP2

插件原理:HTTP2依赖TLS握手,插件会自动生成TLS证书,支持通过HTTPS的方式启动,vite会自动把HTTPS服务升级为HTTP2

当使用 vite 的proxy配置时,vite会将HTTP2降级为HTTPS,可通过插件

vite-plugin-proxy-middleware

解决该问题

    import { defineConfig } from "vite";
    import react from "@vitejs/plugin-react"; 
    import mkcert from "vite-plugin-mkcert";
    export default defineConfig({ 
        plugins: [react(), mkcert()], 
        server: { 
            // https 选项需要开启 
            https: true, 
        },
    });

vite-plugin-mkcert 插件仅用于开发阶段,在生产阶段需要对线上服务器进行配置,开启HTTP2

DNS解析

浏览器向跨域的浏览器发送请求时,首先会进行DNS解析,将服务器域名解析为对应的IP地址。通过dns-prefeth技术将这一过程提前,降低DNS解析的延迟时间。

<!-- href 为需要预解析的域名 --> 
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">

dns-prefetch会与preconnect 搭配使用,前者用来解析 DNS,而后者用来会建立与服务器的连接,建立 TCP 通道及进行 TLS 握手,进一步降低请求延迟。

<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>

<link rel="dns-prefetch" href="https://fonts.gstatic.com/">

preconnect 的 link 标签一般需要加上 crorssorigin(跨域标识),否则对于一些字体资源 preconnect 会失效

DNS解析

可以通过 Preload方式进行预加载,在资源使用之前进行加载,使资源更早的到达浏览器。

    //声明 `href` 和 `as` 属性,分别表示资源地址和资源类型

    <link rel="preload" href="style.css" as="style">
    <link rel="preload" href="main.js" as="script">
    
    

对于原生ESM模块,浏览器提供了modulepreload来进行预加载

<link rel="modulepreload" href="/src/app.js" />

通过配置vite 一键开启 modulepreload 的 Polyfill

// vite.config.ts 
export default {
    build: {
    polyfillModulePreload: true 
    }
}

Prefetch 浏览器空闲时去预加载其他页面资源 (浏览器兼容性差)


<link rel="prefetch" href="https://B.com/index.js" as="script">

//浏览器会在 A 页面加载完毕之后去加载`B`这个域名下的资源,
//如果用户跳转到了`B`页面中,浏览器会直接使用预加载好的资源,从而提升 B 页面的加载速度

资源优化

1、产物分析报告

首先需要安装 rollup-plugin-visualizer依赖

pnpm i rollup-plugin-visualizer -D

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig({ 
plugins: [
    react(),
    visualizer({ 
    // 打包完成后自动打开浏览器,显示产物体积报告 
    open: true,
    }),
  ],
});

// 执行 pnpm  run  build 后会生成stats.html 文件


image.png

image.png

2、资源压缩

JS压缩


// vite.config.ts 
export default {
    build: { 
    // 类型: boolean | 'esbuild' | 'terser' 
    // 默认为 `esbuild`
    minify: 'esbuild', 
    // 产物目标环境  ,vite默认是modules , 即如下的 browserlist  ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1']
    // 为了线上的稳定性,推荐将 target 参数设置为`ECMA`语法的最低版本es2015或者es6
    target: 'modules', 
    // 如果 minify 为 terser,可以通过下面的参数配置具体行为 // https://terser.org/docs/api-reference#minify-options
    terserOptions: {}
    } 
}

css 压缩

export default{
    build:{
        //设置CSS的目标环境
        cssTarget:''
    }
}

vite 在默认情况下会使用ESbuild 对CSS进行压缩,不需要对 cssTarget进行配置;

但是在需要兼容 安卓微信的webview时,需要将build.cssTarget 设置为 chrome61, 防止 vite 将rgba()颜色转化为#RGBA 十六进制符号的形式,出现样式问题

图片压缩

图片压缩需要安装插件

pnpm i vite-plugin-imagemin -D
//vite.config.ts 
import viteImagemin from 'vite-plugin-imagemin';
{
plugins: [ 
        // 忽略前面的插件
        viteImagemin({ 
        // 无损压缩配置,无损压缩下图片质量不会变差 
        optipng: { optimizationLevel: 7 },
        // 有损压缩配置,有损压缩下图片质量可能会变差 
        pngquant: { quality: [0.8, 0.9], }, 
        // svg 优化 svgo: 
            { 
                plugins: [ 
                    { name: 'removeViewBox' },
                    { name: 'removeEmptyAttrs', active: false } 
                ]
            }
        })
    ]
}

插件已经生效,如下图: image.png

产物拆包

打包的产物为什么需要拆包?

不对产物进行分包,代码会全部打包到同一个chunk文件中,

首屏加载的代码体积过大,即时是当前页面不需要的代码也会进行加载

线上缓存复用率低,改动一行代码会导致整个上次打包的结果缓存失效

vite 内置的拆包能力?

1、CSS 代码分割,实现一个chunk对应一个css文件

2、默认将应用的代码和第三方库的代码分别打包成两份产物,并对于动态import 的模块单独打包成一个chunk

    
    // vite.config.ts 
    { 
        build: {
            rollupOptions: {
                output: { 
                    // manualChunks 配置
                    manualChunks: { 
                    // 将 React 相关库打包成单独的 chunk 中 
                    'react-vendor': ['react', 'react-dom'],
                    // 将 Lodash 库的代码单独打包 
                    'lodash': ['lodash-es'], 
                    // 将组件库的代码打包
                    'library': ['antd', '@arco-design/web-react'],
                    },
                }, 
            } 
        },
    }

打包结果,react为单独chunk ,如下图

image.png