Vite: 静态资源在生产环境部署

静态资源在生产环境的处理


1 )通用的 cdn 前缀

  • 在打包的时候,我们希望本地的图片打包出来的引用使用某个域的前缀
  • 这样,我们可以把打包产物部署到特定的cdn中
  • 在 vite.config.ts 中配置 base
    const isProduction = process.env.NODE_ENV === 'production';
    // 填入项目的 CDN 域名地址
    const CDN_URL = 'https://xxx.cdn.com';
    // 具体配置
    {
     base: isProduction ? CDN_URL: '/'
    }
    
  • 配置 .env.development 和 .env.production 分别是 NODE_ENV=developmentNODE_ENV=production
  • $ pnpm run build打包后,可见
    <link rel="icon" type="image/svg+xml" href="https://xxx.cdn.com/assets/vite-DcBtz0py.svg" />
    <script type="module" crossorigin src="https://xxx.cdn.com/assets/index-xyjhVKfF.js"></script>
    <link rel="stylesheet" crossorigin href="https://xxx.cdn.com/assets/index-DiwrgTda.css">
    
  • 看到,这里 图片,js, css 都被打上了cdn的前缀

2 ) 图片托管到其他地址

  • 新增 .env 文件并配置: VITE_IMG_BASE_URL=https://image-cdn.com
  • 进入 src/vite-env.d.ts 增加类型声明
    /// <reference types="vite/client" />
    
    interface ImportMetaEnv {
      readonly VITE_APP_TITLE: string;
      // 自定义的环境变量
      readonly VITE_IMG_BASE_URL: string;
    }
    
    interface ImportMeta {
      readonly env: ImportMetaEnv;
    }
    
  • 如果某个环境变量要在 Vite 中通过 import.meta.env 访问,那么它必须
    VITE_ 开头,如 VITE_IMG_BASE_URL
  • 接下来我们在组件中来使用这个环境变量:
    <img src={new URL('./logo.png', import.meta.env.VITE_IMG_BASE_URL).href} />
  • 在 开发环境 启动项目或者 生产环境 打包后可以看到环境变量已经被替换,地址能
    够正常显示
  • 在打包后的js文件中可见是 src: new URL("./logo.png", "https://image-cdn.com").href

3 ) 打包成单文件或作为base64文件内联

  • 在 Vite 中,所有的静态资源都有两种构建方式
    • 一种是打包成一个单文件
    • 另一种是通过 base64 编码的格式内嵌到代码中
  • 这两种方案到底应该如何来选择呢?
    • 对于比较小的资源,适合内联到代码中,一方面对 代码体积 的影响很小
    • 另一方面可以减少不必要的网络请求, 优化网络性能
    • 而对于比较大的资源,就推荐单独打包成一个文件,而不是内联了
    • 否则可能导致上 MB 的 base64 字符串内嵌到代码中
    • 导致代码体积瞬间庞大,页面加载性能直线下降
  • Vite 中内置的优化方案是下面这样的
    • 如果静态资源体积 >= 4KB,则提取成单独的文件
    • 如果静态资源体积 < 4KB,则作为 base64 格式的字符串内联
    • 上述的 4 KB 即为提取成单文件的临界值
    • 当然,这个临界值你可以通过 build.assetsInlineLimit 自行配置,如下代码所示
      // vite.config.ts
      {
         build: {
            // 8 KB
            assetsInlineLimit: 8 * 1024
         }
      }
      
      • 注意, svg 格式的文件不受这个临时值的影响,始终会打包成单独的文件
      • 因为它和普通格式的图片不一样,需要动态设置一些属性

4 ) 图片的压缩

  • 图片资源的体积往往是项目产物体积的大头,如果能尽可能精简图片的体积
  • 那么对项目整体打包产物体积的优化将会是非常明显的
  • 在 JavaScript 领域有一个非常知名的图片压缩库 imagemin,作为一个底层的压缩工具
  • 前端的项目中经常基于它来进行图片压缩
  • 比如 Webpack 中大名鼎鼎的 image-webpack-loader
  • 社区当中也已经有了开箱即用的 Vite 插件—— vite-plugin-imagemin ,首先让我们来安装它
    • $ 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
    				 }
    			    ]
    			 }
    		})
        ]
    }
    
  • 打包后 $ pnpm run build 可以验证图片压缩非常明显

5 )雪碧图减少图片的HTTP的请求

5.1 没有优化前

  • 大量的 HTTP1.1 请求会导致网络解析耗时变长,页面加载性能直接受到影响
  • HTTP2 的多路复用设计可以解决大量 HTTP 的请求导致的网络加载性能问题
  • 因此雪碧图技术在 HTTP2 并没有明显的优化效果
  • 一般来说,如果一个组件内有多个svg的图片
    import Logo1 from '@assets/icons/logo-1.svg';
    import Logo2 from '@assets/icons/logo-2.svg';
    import Logo3 from '@assets/icons/logo-3.svg';
    import Logo4 from '@assets/icons/logo-4.svg';
    import Logo5 from '@assets/icons/logo-5.svg';
    
  • 这样导入不免繁琐,Vite 中提供了 import.meta.glob 的语法糖来解决这种批量导入的问
    题,适合按需加载的场景,如上述的 import 语句可以写成下面这样
    const icons = import.meta.glob('../../assets/icons/logo-*.svg');
    
  • 同样,如果要同步的导入,则可以使用 import.meta.globEager
    const icons = import.meta.globEager('../../assets/icons/logo-*.svg');
    
  • 即便以上两种提供了写法上的方便,但是仍旧会有很多的网络请求,我们想要减少这些请求

5.2 使用雪碧图优化

  • 通过 vite-plugin-svg-icons 来实现这个方案,首先安装一下这个插件
    • $ pnpm i vite-plugin-svg-icons -D
  • 在 vite.config.ts 中配置
    import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
    
    // 下面是具体的配置信息
    {
     	plugins: [
     		// 省略其它插件
     		createSvgIconsPlugin({
     			iconDirs: [path.join(__dirname, 'src/assets/icons')]
     		})
        ]
    }
    
  • 在 src/components 目录下新建 SvgIcon 组件:
    // SvgIcon/index.tsx
    export interface SvgIconProps {
      name?: string;
      prefix: string;
      color: string;
      [key: string]: string;
    }
    
    export default function SvgIcon({
      name,
      prefix = 'icon',
      color = '#333',
      ...props
    }: SvgIconProps) {
      const symbolId = `#${prefix}-${name}`;
      return (
        <svg {...props} aria-hidden="true">
    	   <use href={symbolId} fill={color} />
        </svg>
      );
    }
    
  • 在一个组件中应用 SvgIcon
    const icons = import.meta.globEager('../../assets/icons/logo-*.svg');
    const iconUrls = Object.values(icons).map((mod) => {
    	 // 如 ../../assets/icons/logo-1.svg -> logo-1
    	 const fileName = mod.default.split('/').pop();
    	 const [svgName] = fileName.split('.');
    	 return svgName;
    });
    
    // 渲染 svg 组件
    {iconUrls.map((item) => (
     	<SvgIcon name={item} key={item} width="50" height="50" />
    ))}
    
  • 在 src/main.tsx 文件中添加一行代码 import 'virtual:svg-icons-register';
  • 回到浏览器,雪碧图已经生成,注意,雪碧图包含了所有图标的具体内容
  • 而对于页面每个具体的图标,则通过 use 属性来引,用雪碧图的对应内容:
  • 如此一来,我们就能将所有的 svg 内容都内联到 HTML 中,省去了大量 svg 的网络请求
  • SVG Sprite 示例,参考一个 SVG Sprite 文件,例如 icons.svg
    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">  
      <symbol id="icon-home" viewBox="0 0 24 24">  
        <!-- Home 图标的内容 -->  
        <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>  
      </symbol>  
      <symbol id="icon-search" viewBox="0 0 24 24">  
        <!-- Search 图标的内容 -->  
        <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>  
      </symbol>  
      <!-- 更多图标... -->  
    </svg>
    
  • 使用 元素引用图标, 在你的 HTML 或 Vue 组件中,可以使用 元素来引用 SVG Sprite 中的特定图标:
    <svg class="icon">  
      <use xlink:href="icons.svg#icon-home"></use>  
    </svg>  
      
    <svg class="icon">  
      <use xlink:href="icons.svg#icon-search"></use>  
    </svg>
    
  • 21
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值