Vite知识体系

一、浅谈构建工具

1、前端工程的痛点

(1)前端项目的组成部分(核心要素--文件资源)
①逻辑代码:js、ts、jsx
②样式代码:css、scss、less
③静态资源:jpg、png、webp

(2)四个方向的归纳
①模块化:ESM、CommonJS、UMD
把项目拆分为不同的模块,然后进行分别的开发和维护【分而治之】
②资源编译:高级语法的编译
高级语法浏览器是不认识的,所以需要编译成浏览器可以识别的形式
③产物质量:代码体积、代码性能

  • 体积:线上的代码一般都比较大,容易影响性能和用户体验,需要压缩,将未使用到的模块在构建产物当中剔除掉,优化产物体积
  • 兼容性:对于需要兼容移动端的项目,大部分情况需要兼容到安卓4.4、ios9(也就是需要兼容低端的浏览器,否则容易出现使用的高级语法,浏览器无法识别,出现白屏事故) ④开发效率:热更新

2、前端构建工具的意义

(1)模块化方案
①提供模块加载方案
②兼容不同模块规范

(2)语法转译
①高级语法转译,如Sass、TypeScript
②资源加载,如图片、字体、worker

(3)产物质量
产物压缩、无用代码删除、语法降级

(4)开发效率
热更新

二、Vite概要介绍

1、Vite概览

(1)定位:新一代前端构建工具

(2)两大组成部分
①No-bundle开发服务(NodeJs的devserver),源文件无需打包(与传统工具最大的不同)
②生产环境基于Rollup的Bundler,将所有代码进行打包

(3)核心特征
①高性能,dev启动速度和热更新速度非常快
②简单易用,开发者体验好

2、业界案例

(1)Rollup -> Vite

  • 启动时间:2分15秒->1.7秒
  • 更新时间:23秒->1秒以内

(2)Webpack -> Vite

  • 启动时间:2分36秒->6秒
  • 热更新:13秒->1秒以内

3、当下问题

(1)开发体验问题
缓慢的启动->项目编译等待成本高
缓慢的热更新->修改代码后不能实时更新

(2)瓶颈
bundle打包带来的性能开销
JavaScript语言的性能瓶颈

4、两大行业趋势

(1)全球浏览器对原生ESM的普遍支持(目前占比92%以上)

(2)基于原生语言(Go、Rust)编写前端编译工具链
如Go语言编写的Esbuild、Rust 编写的SWC

5、浏览器原生ESM支持

(1)两大要素

  • script标签增加type="module”属性
  • ESM模块导入导出语法

(2)代码例子
浏览器在识别到import后,会发起请求,到foo.js下执行内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test esm</title>
</head>
<body>
    <script type="module">
        import { foo } from './foo.js'
        console.log(foo);
    </script>
</body>
</html>
export const foo = "foo";

6、基于原生ESM的开发服务优势

(1)无需打包项目源代码

(2)天然的按需加载

(3)可以利用文件级的浏览器缓存
当一个文件变更时,不会导致整个bundle失效,只会导致当前的请求缓存失效,达到更细密度的浏览器缓存

7、基于Esbuild的编译性能优化

(1)Esbuild:基于Golang开发的前端工具,具备如下能力
①打包器Bundler:对应webpack的工作
②编译器Transformer
③压缩器Minifier

(2)性能极高,在Vite 中被深度使用

8、内置的web构建能力

(1)Vite开箱即用的功能等价于webpack、webpack-dev-server、css-loader、style-loader、less-loader、sass-loader、postcss-loader、file-loader、MiniCssExtractPlugin、HTMLWebpackPlugin

(2)可以看出:vite是对常见web开发的需求进行一个封装

三、Vite上手实战

1、项目初始化

(1)安装pnpm

#提前安装 pnpm
npm i -g pnpm
#初始化命令
pnpm create vite
#安装依赖
pnpm install
#启动项目
pnpm run dev

 2、使用Sass/Scss & CSS Modules

(1)安装sass

pnpm install sass -D

(2)例子
①新建组件:文件夹下=》新建component文件夹=》新建Header文件夹=》新建index.tsx文件

②编写index.tsx文件

import React from "react";

export function Header(){
    return <div>Header</div>
}

③在App.tsx文件引入,使用

import { Header } from './components/Header'
// 在App()中使用
<Header></Header>

(3)叠加sass、css-module,编写样式
①Header文件夹下=》新建index.module.scss
vite会默认把.module.scss文件当作css-module

.header {
  color: red;
}

②在index.tsx中引入、使用

import React from "react";
import styles from "./index.module.scss";

export function Header() {
  return <div className={styles.header}>Header</div>;
}

③css-module作用

className的值并不是字符串,而是引入一个json字段,而这个字段变成了_header_gxlcr_1,由header+哈希值
这样的好处就是,当在其它组件用了同样的className,会出现不同组件的样式污染,使用css-module可以做到组件的样式隔离

3、使用静态资源

(1)静态资源在vite可以直接引入使用

这个路径会给vite的devserver发起svg请求,然后devserver会把文件内容返回给浏览器,进行图片的呈现

(2)除了常见的图片格式, Vite也内置了对于JSON、Worker、WASM资源的加载支持 官方文档

4、HMR

可以保存组件的局部状态

当改变index下的内容时,App下的count值会保留

5、生产环境Tree Shaking

(1)把代码中没有的内容删除

(2)优化原理
①基于ESM 的import/export语句依赖关系(依赖关系是可以静态确定的),与运行时状态无关
相对于NodeJs的规范就不能,因为require可能是运行时的结果,就无法进行一些静态分析,当需要强制删除时,可能会删掉一些不应该删掉的内容,是有风险的
因此Tree Shaking只能应用于ESM
②在构建阶段将未使用到的代码进行删除
③Tree Shaking在Vite中无需配置,默认开启

(3)代码例子

//main.ts
import { add } from './util';
console.log(add(1,2));
//util.ts
export const add = (a: number, b: number): number => a + b;
export const multi = (a: number, b: number): number => a * b;

 在util.ts中编写了两个方法add(),multi(),在main.ts使用时,只使用了add()方法,则在最后打包的产物中不会有multi()

(4)实践操作 ①在src文件夹下=》新建util.ts文件

export const add = (a: number, b: number): number => a + b;
export const multi = (a: number, b: number): number => a * b;

②在index.tsx文件引入,使用

import React from "react";
import styles from "./index.module.scss";
import { add } from "../../util";
export function Header() {
  return <div className={styles.header}>Header{add(1, 2)}</div>;
}

(5)调试观察
①停止项目,查看package.json中的build,发现会先进行tsc的编译,就是进行相关类型的检查,然后编译vite build
②在vite.config.js中
在生产环境,会默认进行压缩,这边要调试,暂时将压缩的功能关闭

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  // 关闭压缩功能
  build: {
    minify: false,
  }
})

③进行生产环境的打包

pnpm run build

④查看打包产物dist文件夹下的文件

通过图片中的文件搜索,可以找到add被打包进去,而multi是没有的

(6)总结
Vite最直观的印象就是
①响应迅速
②开箱即用

四、Vite整体架构

1、开发环境下的依赖预打包

(1)node_modules
①非常不可控,一个依赖文件往往会有很多其它延伸的依赖文件
②代码产物的格式是不规范的,有可能是CommonJS,在浏览器上无法运行
因此vite进行依赖预打包

(2)预打包步骤
①在dev服务启动前,扫描代码中用到的依赖
②对依赖代码采用Esbuild进行预打包
③打包完,将业务中的import语句进行改写,指定依赖为预构建产物路径

// 改写前
import React from "react";
// 改写后
import React from "/node_modules/.vite/react.js";

2、单文件编译

(1)用Esbuild编译TS/JSX

(2)Esbuild优势:编译速度提升10-100倍

(3)Esbuild局限性
①不支持类型检查
②不支持语法降级到ES5

3、代码压缩

(1)生产环境中非常重要的阶段,同时是非常耗时的阶段

(2)Esbuild 作为默认压缩工具,替换传统的Terser、Uglify.js 等压缩工具

4、插件机制

(1) 开发阶段->模拟Rollup 插件机制 生产环境->直接使用Rollup

(2)注意:并不是所有的rollup插件都能兼容vite
vite官网之插件兼容性icon-default.png?t=N6B9https://vite-rollup-plugins.patak.dev/

五、Vite进阶路线

1、深入双引擎

vite底层非常依赖的两个构建引擎

(1)官方文档

Esbuildicon-default.png?t=N6B9https://esbuild.github.io/

Rollupicon-default.png?t=N6B9https://rollupjs.org/

(2)推荐学习顺序
①先了解基本使用,动手尝试各项常用配置
②然后学习其插件开发

2、插件开发

(1)为什么需要插件开发
①抽离核心逻辑,将devserver中server端的能力抽离出来,把构建相关的能力封装为一个个的插件,达到解耦的效果,构建和devserver逻辑是分开的,更容易维护的
②易于拓展,社区能够给vite贡献一些插件

(2)一些vite插件的钩子函数
在不同阶段,插入自定义的逻辑

(3)插件示例
①开发vite插件
②配置文件,引入插件

const fileRegex = /\.(my-file-ext)$/

export default function myPlugin(){
  return {
    name: 'transform-file',

    transform(src, id){
      if(fileRegex.test(id)){
        return {
          code: compileFileToJS(src),
          map: null //如果可行将提供source map
        }
      }
    }
  }
}
// vite.config.js
import plugin from './myPlugin'

export default defineConfig({
  plugins:[plugin()]
})

(4)插件开发参考资料
vite官方开发文档
②复杂度较低的插件:json加载插件
③复杂度中等的插件:Esbuild接入插件
④复杂度较高的插件:官方React插件
先看文档,过一遍插件钩子的功能然后多学习其它插件的实现,掌握套路

3、代码分割(拆包)

(1)问题
①无法进行并发请求:以前的启动过程只产出一个bundle,即一个产物文件
②缓存复用率低:当一个文件改动,整个产物全部失效

(2)拆包
意味着改动某一个文件,不影响整个产物,达到更好的缓存复用的效果,从而提升页面加载速度,间接提升用户体验

(3)配置rollup的代码分割
构建选项
Configuration Oprions

4、JS编译工具(Babel)

(1)出现原因
①JavaScript语法标准繁多,浏览器支持程度不一
②开发者需要用到高级语法

(2)参考资料
babel官方站点icon-default.png?t=N6B9https://babeljs.io/docs/
babel插件手册icon-default.png?t=N6B9https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md

(3)Babel实现原理
Parse:将代码解析成AST(抽象语法树),每个单词都是语法树的节点
Transform:AST转换成一些低级语法的AST
Generator:生成器,将低级语法的AST转换成代码
从而达到语法降级的效果

 5、语法安全降级

(1)以Promise语法为例,IE11没有支持
IE11没有内置Promise语法,很可能会导致代码报错,浏览器页面白屏

(2)解决方案
①上层解决方案:@vitejs/plugin-legacy
②底层原理

  • 借助Babel进行语法自动降级
  • 提前注入Polyfill实现,如core-js、regenerator-runtime

(3)参考资料
@babel/preset-env文档icon-default.png?t=N6B9https://babeljs.io/docs/babel-preset-env
Vite官方降级插件文档icon-default.png?t=N6B9https://github.com/vitejs/vite/tree/main/packages/plugin-legacy

6、服务端渲染(SSR)

一种常见的渲染模式,用于提升首屏性能和SEO优化
(1)构建阶段

(2)代码执行阶段 

 

(3)参考资料
Vite-SSR文档
使用Vite搭建SSR工程

7、深入了解底层标准

(1)重点特性
CS规范、ESM规范、HTTP 2.0特性

(2)参考资料

HACKSicon-default.png?t=N6B9https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/

ESM & CJSicon-default.png?t=N6B9https://antfu.me/posts/publish-esm-and-cjs

8、社区生态

(1)Github 40k+ star (可参考webpack 61.3 K, rollup 21.8 K),并且目前还在持续维护

(2)官方提供插件
@vitejs/plugin-vue,提供 Vue 3支持
@vitejs/plugin-vue-jsx,提供 Vue 3JSX支持
@vitejs/plugin-react,提供 React支持
@vitejs/plugin-legacy,提供低版本浏览器降级支持

(3)海量社区插件
github.com/vitejs/awes…icon-default.png?t=N6B9https://github.com/vitejs/awesome-vite

(4)vite框架内置
Nuxt、SvelteKit、Astro、Vitepress

六、总结

1、通过本节学习,认识vite的基础原理,实现简单操作,同时建立了从基础学习到进阶学习的方向 2、本节印象最深的是生产环境Tree Shaking 在大型项目代码构建下,属于关键的一步,学习了Tree Shaking原理步骤,对项目构建也有很大帮助

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

五秒法则

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

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

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

打赏作者

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

抵扣说明:

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

余额充值