首頁  >  文章  >  web前端  >  手把手教你如何使用Vite+React進行元件開發(實作)

手把手教你如何使用Vite+React進行元件開發(實作)

青灯夜游
青灯夜游轉載
2021-07-30 14:21:183545瀏覽

手把手教你如何使用Vite+React進行元件開發(實作)

在先前的文章中介紹了借助  TypeScript AST 語法樹解析,對React 元件Props 類型定義及註解提取,自動產生元件對應截圖、用法、參數說明、README、Demo等。在社群中取得了比較好的反響,同時應用在團隊中也取得了較為不錯的結果,現在內部組件系統中已經累計使用該方案沉澱 1000 的 React 元件。

手把手教你如何使用Vite+React進行元件開發(實作)

之前我們是藉助了webpack TypeScript 做了一套用於開發React 元件的腳手架套件,當開發者要元件開發時,即可直接使用腳手架初始化對應項目結構進行開發。

雖然主路徑上確實解決了組件開發中所遇到的組件無圖無真相、組件參數文檔缺失、組件用法文檔缺失、組件Demo 缺失、組件無法索引、組件產物不規範等內部元件管理和沈澱上的問題,但Webpack 的方案始終還是會讓元件開發多一層編譯,當一個元件庫沉澱超過300 時,引入依賴不斷增長,還是會帶來元件編譯上的負荷導致開發者開發體驗下降。

一  Vite 帶來的曙光

#Vite 為前端帶來的絕對是革命性的變化,這麼說毫不誇張。

或許應該說是Vite 背後整合的esbuild 、 Browser es modules、HMR、Pre-Bundling 等這些社區中關於JS 編譯發展的先進工具和思路,在Vite 這樣的整合推動下,給前端開發帶來了革命性變化。

我很早就說過,任何一個框架或函式庫的出現最有價值的一定不是它的程式碼本身,而是這些程式碼背後所帶來的新思路、新啟發。所以我在寫文章的時候,也很注重能把我思考最後執行的整個過程講清楚。

Vite 為什麼快,主要是esbuild 進行pre-bundles dependencies 瀏覽器native ESM 動態編譯,這裡我不做過多贅述,詳細參考:Vite: The Problems

手把手教你如何使用Vite+React進行元件開發(實作)

#在這個想法的背景下,回到我們元件開發的場景再看會發現以下幾個問題高度吻合:

  • 元件庫開發,實際上不需要編譯全部組件。

  • 元件開發,編譯預覽頁面主要給開發者使用,瀏覽器相容可控。

  • HMR(熱更新)能力在 Vite 加持下更加顯得立竿見影,是以往組件開發和調試花費時間最多的地方。

  • Vite 中一切原始碼模組動態編譯,也就是  TypeScript 類型定義和 JS 註解也可以做到動態編譯,大幅縮小編譯範圍。

那麼,以往像 StoryBook 和之前我們用來提取 tsx 元件類型定義的思路將可以做一個比較大的改變。

之前為了取得元件入參的類型資料會在Wwebpack 層面做插件用於動態分析export 的tsx 元件,在該元件下動態加入一段 __docgenInfo 的靜態屬性變量,將從AST 分析得到的類型資料和註解資訊注入進組件JS Bundle,從而進一步處理為動態參數設定:

TypeScript 對元件Props 的定義

手把手教你如何使用Vite+React進行元件開發(實作)

分析注入到JS Bundle中的內容

手把手教你如何使用Vite+React進行元件開發(實作)

分析轉換後實現的參數交互設定

手把手教你如何使用Vite+React進行元件開發(實作)

所以對於元件來說,實際上獲取這一份類型定義的元資料對於元件本身來說是冗餘的,不論這個元件中的這部分元資料有沒有被用到,都會在Webpack 編譯過程中解析提取並注入到元件Bundle 中,這顯然是很低效的。

在Vite 的思路中,完全可以在使用到組件元數據時,再獲取其元數據信息,比如加載一個React 組件為:

import ReactComponent from './component1.tsx'

那麼加載其元數據即:

import ComponentTypeInfo from './component1.tsx.type.json'; 

// or 
const ComponentTypeInfoPromise = import('./component1.tsx.type.json');

透過Vite 中Rollup 的插件能力載入.type.json 檔案類型,從而做到對應元件元資料的解析。同時藉助 Rollup 本身對於編譯依賴收集和 HMR 的能力,做到組件類型變化的熱更新。

二  設計想法

以上是看到Vite 的模組載入思路,得到的一些靈感和啟發,從而做出的一個初步設想。

但如果真的要做这样一个基于 Vite 的 React 、 Rax 组件开发套件,除了组件入参元数据的获取以外,当然还有其他需要解决的问题,首当其冲的就是对于 .md 的文件解析。

1  组件 Usage

参照 dumi 及 Icework 所提供的组件开发思路,组件 Usage 完全可以以 Markdown 写文档的形式写到任何一个 .md 文件中,由编译器动态解析其中关于 jsx、tsx、css、scss、less 的代码区块,并且把它当做一段可执行的 script 编译后,运行在页面中。

这样既是在写文档,又可以运行调试组件不同入参下组件表现情况,组件有多少中Case,可以写在不同的区块中交由用户自己选择查看,这个设计思路真是让人拍案叫绝!

最后,如果能结合上述提到 Vite 的 esbuild 动态加载和 HMR 能力,那么整个组件开发体验将会再一次得到质的飞跃。

所以针对 Markdown 文件需要做一个 Vite 插件来执行对 .md 的文件解析和加载,预期要实现的能力如下:

import { content, modules } from "./component1/README.md";

// content README.md 的原文内容
// modules 通过解析获得的`jsx`,`tsx`,`css`,`scss`,`less` 运行模块

预期设想效果,请点击放大查看:

手把手教你如何使用Vite+React進行元件開發(實作)

2  组件 Runtime

一个常规的组件库目录应该是什么样的?不论是在一个单独的组件仓库,还是在一个已有的业务项目中,其实组件的目录结构大同小异,大致如下:

components
├── component1
│   ├── README.md 
│   ├── index.scss
│   └── index.tsx
├── component2
│   ├── README.md
│   ├── index.scss
│   └── index.tsx

在我们的设想中你可以在任意一个项目中启动组件开发模式,在运行 vite-comp 之后就可以看到一个专门针对组件开发的界面,在上面已经帮你解析并渲染出来了在 README.md 中编写的组件 Usage,以及在 index.tsx 定义的 interface,只需要访问不同的文件路径,即可查看对应组件的表现形态。

同时,最后可以帮你可以将这个界面上的全部内容编译打包,截图发布到 NPM 上,别人看到这个组件将会清晰看到其组件入参,用法,截图等,甚至可以打开 Demo 地址,修改组件参数来查看组件不同状态下的表现形态。

如果要实现这样的效果,则需要一套组件运行的 Runtime 进行支持,这样才可以协调 React 组件、README.md、TypeScript 类型定义串联成我们所需要的组件调试+文档一体的组件开发页面。

在这样的 Runtime 中,同样需要借助 Vite 的模块解析能力,将其 URL 为 **/*/(README|*).html 的请求,转换为一段可访问的组件 Runtime Html 返回给浏览器,从而让浏览器运行真正的组件开发页面。

http://localhost:7000/components/component1/README.html
-> 
/components/component1/README.html 
->
/components/component1/README.md
-> 
Runtime Html

3  组件 Props Interface

正如我上述内容中讲到的,如果利用 Vite 添加一个对 tsx 的组件 props interface 类型解析的能力,也可以做成独立插件用于解析 .tsx.type.json 结尾的文件类型,通过 import 这种类型的文件,从而让编译器动态解析其 tsx 文件中所定义的 TypeScript 类型,并作为模块返回给前端消费。

手把手教你如何使用Vite+React進行元件開發(實作)

其加载过程就可以当做是一个虚拟的模块,可以理解为你可以通过直接 import 一个虚拟的文件地址,获取到对应的 React 组件元信息:

// React Component
import Component from './component1.tsx';
// React Component Props Interface
import ComponentTypeInfo from './component1.tsx.type.json';

// or
const ComponentTypeInfoPromise = import('./component1.tsx.type.json');

由于这种解析能力并不是借助于 esbuild 进行,所以在转换性能上无法和组件主流程编译同步进行。

在请求到该文件类型时,需要考虑在 Vite 的 Serve 模式下,新开线程进行这部分内容编译,由于整个过程是异步行为,不会影响组件主流程渲染进度。当请求返回响应后,再用于渲染组件 Props 定义及侧边栏面板部分。

在热更新过程中,同样需要考虑到 tsx 文件修改范围是否涉及到 TypeScript 类型的更改,如果发现修改导致类型变化时,再触发 HMR 事件进行模块更新。

三  组件 Build

以上都是在讨论组件在 Vite 的 Serve 态(也就是开发态)下的情况,我们上文中大量借助 Vite 利用浏览器 es module 的加载能力,从而做的一些开发态的动态加载能力的扩展。

但是 Vite 在组件最终 Build 过程中是没有 Server 服务启动,当然也不会有浏览器动态加载,所以为了让别人也可以看到我们开发的组件,能够体验我们开发时调试组件的样子,就需要考虑为该组件编译产出一份可以被浏览器运行的 html。

所以在 Vite 插件开发过程中,是需要考虑在 Build 状态下的编译路径的,如果是在 Build 状态下,Vite 将使用 Rollup 的编译能力,那么就需要考虑手动提供所有组件的  rollup.input(entries)。

在插件編寫過程中,一定需要遵循 Rollup 所提供的插件載入生命週期,才能確保 Build 流程和 Serve 流程的模組載入邏輯和編譯邏輯保持一致。

手把手教你如何使用Vite+React進行元件開發(實作)

我一開始在實作的過程中,就是我沒有了解透徹Vite 和Rollup 的關係,在模組解析過程中依賴了大量Vite 的Server 提供的服務端中間件能力。導致在考慮到 Build 態時,才意識到其中的問題,最後幾乎重新寫了先前的載入邏輯。

四  總結

我姑且把這個方案(套件)稱之為vite-comp,其大致的構成就是由Vite 3 Vite Pugins 構成,每個外掛程式互相不耦合,互相職責也不相同,也就是說你可以拿到任一個Vite 外掛去做別的用途,後續會考慮單獨開源,分別是:

  • Markdown,用於解析.md 文件,載入後可取得原文及jsx、tsx 等可運行區塊。

  • TypeScript Interface,用於解析 .tsx 檔案中對於 export 元件的 props 類型定義。

  • Vite Comp Runtime,用於執行元件開發狀態,編譯最終元件文件。

手把手教你如何使用Vite+React進行元件開發(實作)

結合Vite,已經實現了Vite 模式下的React、Rax 元件開發,它相比之前使用Webpack 做的元件開發,已經體現了以下幾個大優勢:

  • 無懼大型元件庫,即使有2000 個元件在同一個專案中,啟動依舊是

  • 高效的元件元資料載入流,專案一切依賴編譯按需進行。

  • 毫秒級熱更新響應,借助 esbuild 幾乎是按下保存的一瞬間,就可以看到改變效果。

預覽體驗:

手把手教你如何使用Vite+React進行元件開發(實作)

#Markdown 元件文件毫秒層級回應

手把手教你如何使用Vite+React進行元件開發(實作)

TypeScript 類型辨識

手把手教你如何使用Vite+React進行元件開發(實作)

Vite 現在還是只是剛起步,這種全新的編譯模式,已經為我帶來了非常多的開發態收益,結合Vite 的玩法未來一定還會層出不窮,比如Midway lambda Vite 的前端一體化方案也是看得讓人拍案叫絕,在這個欣欣向榮的前端大時代,相信不同前端產物都會和Vite 結合出下一段傳奇故事。

我是一個熱愛生活的前端工程師! Yooh!

【相關教學推薦:React影片教學

以上是手把手教你如何使用Vite+React進行元件開發(實作)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:阿里技术。如有侵權,請聯絡admin@php.cn刪除