Vue3 知名組件庫 NaiveUI 源碼揭秘

2022-01-08 三元同學
源碼解讀  讀懂開源  精選好文

大家好,我是皮湯。最近程式設計師巴士學習交流群裡有小夥伴想要了解一下如何看源碼,正好最近有一點心得感悟,之前也寫過一篇實際跑通 NaiveUI 源碼的文章:尤大都推薦的組件庫是如何開發出來的?[1] 源碼的經驗,來給大家分享一下。

心理認知要到位

首先要認識到,看源碼是一個開始比較枯燥、同時時間跨度相對比較長的一個過程。所以看源碼的第一步是找到自己想要了解領域、或者自己所在業務領域高度相關的項目,並且在這個領域比較出名,且維護活躍。

打個比方,皮湯我因為是一名前端,而前端這個領域有很多新興的內容,如 Unbundled 方案 Vite,新興框架 Svelte,新彙編語言 WebAssembly,CSS 工程化方案 TailwindCSS,組件庫如抖音很火的開源庫 Semi Design[2]、或者社區比較火的 Vue3 組件庫 NaiveUI 等。

而皮湯我一直對組件庫、CSS 方向比較痴迷,且是組內最近負責前端工程化 CSS 方面基建的負責人之一,所以讓我去研究一個組件庫的源碼,如 NaiveUI,那麼我是很有興趣、動力和理由的,而這也是驅動你啃下一個源碼的核心驅動力之一。

理解高潮 MVP

其次我們看源碼要有一定的技巧,複雜如 React[3],可以算作一個簡單的作業系統了,如果你上來通過比較簡單粗暴的從代碼入口開始,一路打斷點了解源碼,那你再怎麼堅持也會想吐的。

那這裡的技巧是什麼呢?就像我們網際網路創業一樣,如果你有一個大而全的點子,但是你的第一步肯定不是找一個空曠的屋子,備好半年的糧食,準備幾臺電腦,然後高強度開發幾個月,然後祈求一問世就驚豔世人。一個是這種情況非常少見,二個是你也得有堅持幾個月的資本和耐心。

而在創業領域一個比較知名且流傳深遠的技巧就是 MVP,即最小可行性產品,你需要先做出一個非常小的,剛好能夠使用以及能夠測試你想法的產品,然後快速推向市場,接收用戶反饋,接著跟進用戶反饋,不斷迭代產品,滿足用戶需求,直至達到 PMF(產品與市場匹配),這個時候你基本上就可以找投資,進行規模化,然後就是融資、去納斯達克敲鐘。

所以對應到我們看源碼這個領域,第二個需要注意的,就是你需要找到一個開源項目能跑起來的最小 MVP,去除其他繁雜的依賴,最最核心的流程與機制。這個能夠幫助你理解項目核心的 MVP,我稱之為 高潮 MVP -- 即如果你能夠跑通一個項目這樣的 MVP,那麼你內心會異常幸福,感覺自己成就慢慢,興奮達到了高潮,而接下來其他內容、分支基本上就是復用這套 MVP,往上面添磚加瓦,補齊一些兼容細節等。

開始之前

那麼對於 NaiveUI 來說,它的高潮 MVP 是什麼呢?

我們首先打開它的官網:

點開開始使用:

作為一個組件庫來說,它一般需要談論自己的價值觀、設計原則、定製方式,ICON 圖標相關的內容,但是這對於你搞懂這份源碼其實沒有多大幫助,所以需要略去這些幹擾項。

讓我們再來看看它的源碼倉庫:

確保這個庫使用何種語言編寫,這樣你在看源碼之前可以先衡量你當前的知識儲備是否能夠支撐你看懂這份源碼,當然如果你沒有對應的支持儲備,但是又堅持想要看這份源碼,那麼你首先應該考慮根據它使用的語言,提前進行語言學習儲備。

看透本質

讓我們回到 NaiveUI 的官網:

可以看到,對於一個 「組件庫」 來說,實際上最最基礎的其實就是 「組件」,而組成 「組件」 的背後則需要一系列更加基礎的元素,如顏色、間距、邊框、背景、字體等。

那麼我們的目標是不是很明確了呢?把一個 「按鈕」 組件拿下,理解能夠完整使用到這樣一個按鈕背後所需要的所有必須的流程、知識、細節,那麼針對其他的組件,基本上 90 % 的邏輯可以復用,只需要理解剩餘 10% 特定功能需求就可以搞懂。

類似下面這張冰山圖:

冰山之下就屬於那 90%,我們基於一個看似簡單的 「按鈕」 組件,來梳理整個組件庫的核心流程,就可以幫助我們快速、精準的搞懂整份源碼,所以我們的高潮 MVP 就是搞懂一個 「按鈕」 組件的全流程。

了解上下文

理解我們的高潮 MVP 目標是什麼了之後,接下來就是帶著這個目標先詳細讀一下文檔中關於 Button 的所有相關說明,可以看到這個按鈕包含如下內容:

通過右側的目錄,了解到一個按鈕首先會有基礎內容,包含 default、primary、info、success、warning 和 error 這幾類,然後需要處理:

上述表明了這個 Button 可以達到的效果,可以完成的操作,了解之後,接著可以了解按鈕相關的使用 API,通過 API 以及可以達到的效果,我們大致可以理解這個按鈕接收的輸入和輸出有哪些。

一個一個使用 Button 的例子長什麼樣子:

<template>

  <n-space>

    <n-button>Default</n-button>

    <n-button type="primary">Primary</n-button>

    <n-button type="info">Info</n-button>

    <n-button type="success">Success</n-button>

    <n-button type="warning">Warning</n-button>

    <n-button type="error">Error</n-button>

  </n-space>

</template>

了解如何開啟項目

通常開源項目比較方便的一點是它會有詳細的文檔,同時它非常渴望有貢獻者加入,所以會有完善的 貢獻指南,比如 NaiveUI 的貢獻指南[4]如下:

通過貢獻指南,你能夠了解如何安裝依賴、處理一些啟動項目的問題,能夠把項目跑起來進行調試,這通常是你了解整個代碼運行過程的初次體驗。

理解目標項目的項目結構

通常你到這個步驟時,你應該需要知道如下內容:

你理解了你目標內容作為一個功能特性,它的輸入和輸出是什麼

對應到 NaiveUI 我們的這三點分別如下:

高潮 MVP:跑通一個 Button 並能夠使用,保有和現有 Button 一樣的特性,接收一樣的輸入,產生一樣的輸出Button 包含邊框、尺寸、顏色、狀態、事件等相關的內容,輸入這些參數,產出對應條件下的輸出項目的技術棧是 Vue3、TypeScript,構建工具是 Vite,同時使用了 CSS BEM 框架 CSS Render[5],同時包管理工具使用 pnpm

理解這三點之後,接下來我們就需要對照著源碼來理解一下整份文件目錄,了解各個目錄之前的依賴關係,見下圖。

我們可以先了解一下大致每個文件夾是幹什麼的:

src:這個是主要放組件庫相關的組件代碼,以及導出一些國際化、樣式、主題定製相關的內容,一般是一個開源項目的核心開發目錄scripts:一些運行代碼、構建、發版相關的腳本邏輯theme :則為 NaiveUI 內置的默認主題,類似這種組件庫一般都允許用戶自定義主題,整個 NaiveUI 各個組件在使用各種 UI 屬性時都是遵從這套主題進行設置的,也就是可以修改 theme 裡面的內容,或者自己完全自定義一套主題.github 、.husky 等都是一些配置,無需過多關注,可以直接加入到你的 MVP 模板工程裡playground 是用於此時代碼在各種環境下運行的支持代碼,如 SSR 等demo 則是引入 src 相關內容用於展示組件實際效果的網站例子,實際上對於 NaiveUI 也就是我們之前看到的文檔官網其他的如 build 、design-notes 等是構建產物,或者一些主題設計的筆記等,基本上不屬於本次源碼需要閱讀的部門,看興趣的同學可以看看

然後就是一些用於各種工程化配置的文件如:

babel.config.js :Babel 相關jest.config.js :Jest 測試相關postcss.config.js :處理 CSS 相關tsconfig.xx.json 處理 TypeScript 相關vite.config.js :Vite 構建工具相關的配置

以及一些和項目強相關,用於了解整個想法發展上下文的 CHANGELOG.xx.md ,還有我們之前提到的用於跑通代碼的 CONTRIBUTING 貢獻指南。

有點看懵了。🤯

創建你的高潮 MVP 項目

了解了整個 NaiveUI 的項目目錄結構之後,我們就可以著手創建我們的高潮 MVP 項目了,但在這之前我們可以再進行一波簡化,即我們有些內容可以不要:

.github 、.husky 、playground 、scripts 這種我們可以不要,我們只需要測試最基礎的環境,以及在開發時可以跑通即可theme 這種只是整個 NaiveUI 遵循的設計體系,在其他部分會遵循這個體系,但是不會直接引用,所以我們也可以不要這樣我們只剩下 demo 和 src ,而更近一步,我們可以把 demo 做到 src 裡面,整個 src 我們將其職責變為高潮 MVP 網站入口,然後原剩下的 src 下面的代碼則用於導入到 src 入口文件裡面使用TypeScript 相關的,我們後續可以迭代,不用引入不必要的複雜度以及類型體操ESLint 和 Prettier 等我們也可以不需要,依賴於編輯器默認的格式化就可,當然引入這兩個到我們初始的高潮 MVP 項目裡也不礙事

經過簡化之後,我們的高潮 MVP 項目就只需要如下幾個文件了:

構建項目和提供開發伺服器的 Vite 相關內容:vite.config.js用於提供語法轉譯的 babel.config.js用於跑通項目的主要代碼 src 以及 index.html 入口模板

目錄結構如下:

.

├── babel.config.js

├── index.html

├── node_modules

├── package.json

├── public

├── src

├── vite.config.js

└── yarn.lock

很精簡,沒有多餘繁雜的內容對吧?同時也非常易懂。

這些剩下要創建的文件內容,從 NaiveUI 的工程目錄裡面 Copy 過來,然後安裝對應的依賴即可。

跑通流程

當我們根據源碼庫創建了我們的高潮 MVP 項目之後,現在應該可以跑起來了,只不過內容只是一個簡單的 Button,因為為了快速跑起來項目,我們的入口文件 src/App.vue 會如下:

<template>

  <t-button>hello tuture</t-button>

</template>



<script>

import { defineComponent } from "vue";

import { TButton } from "./components";



export default defineComponent({

  name: "App",

  components: {

    TButton,

  },

});

</script>

而對應的 src/components/TButton.vue 如下:

<template>

  <button>{$slots.default}</button>

</template>



<script>

import { defineComponent } from "vue";

import { TButton } from "./components";



export default defineComponent({

  name: "Button"

});

</script>

接下來我們就嘗試一遍了解 NaiveUI 的代碼,一遍將這些主幹代碼遷移到我們的高潮 MVP 項目中來,然後確保遷移過程中能夠持續跑起來,雖然我們可能會遇到有時候一個依賴需要大量的前置依賴,所以需要遷移一大段代碼才能將項目跑起來。

找到核心入口

我們要完成一個 Button 的所有前置依賴,只需要去到 NaiveUI 對應的工程目錄文件裡面,找到 Button 對應的代碼,如下:

其實解析一下組件文件的代碼,就是下面幾部分:

組件裡面處理 props 傳入與使用、自身狀態的定義與使用

而上圖代碼中的所有和 TS 定義相關的內容我們都是不需要的,所以可以刪除 ButtonProps 、NativeButtonProps 、MergedProps 、XButton 這些類型定義相關的內容。

而導入部分涉及到類型定義相關的我們也可以刪除掉:

import type { ThemeProps } from '../../_mixins'

import type { BaseWaveRef } from '../../_internal'

import type { ExtractPublicPropTypes, MaybeArray } from '../../_utils'

import type { ButtonTheme } from '../styles'

import type { Type, Size } from './interface'

刪除完這些無關的代碼之後,我們的代碼還剩下那些內容呢?

導入依賴部分:

import {
  h,
  ref,
  computed,
  inject,
  nextTick,
  defineComponent,
  PropType,
  renderSlot,
  CSSProperties,
  ButtonHTMLAttributes
} from 'vue'
import { useMemo } from 'vooks'
import { createHoverColor, createPressedColor } from '../../_utils/color/index'
import { useConfig, useFormItem, useTheme } from '../../_mixins'
import {
  NFadeInExpandTransition,
  NIconSwitchTransition,
  NBaseLoading,
  NBaseWave
} from '../../_internal'
import { call, createKey } from '../../_utils'
import { buttonLight } from '../styles'
import { buttonGroupInjectionKey } from './ButtonGroup'
import style from './styles/button.cssr'
import useRtl from '../../_mixins/use-rtl'

組件聲明部分:

const Button = defineComponent({
  name: 'Button',
  props: buttonProps,
  setup(props) {
    // 定義組件狀態
    const selfRef = ref<HTMLElement | null>(null)
    const waveRef = ref<BaseWaveRef | null>(null)
    const enterPressedRef = ref(false)
    
    // 使用 Props 或注入全局狀態
    const NButtonGroup = inject(buttonGroupInjectionKey, {})
    const { mergedSizeRef } = useFormItem(...)
    const mergedFocusableRef = computed(() => {...})
    
    // 定義組件事件處理
    const handleMouseDown = (e: MouseEvent): void => {...}
    const handleClick = (e: MouseEvent): void => {...}
    const handleKeyUp = (e: KeyboardEvent): void => {...}
    const handleKeyDown = (e: KeyboardEvent): void => {...}
    const handleBlur = (): void => {...}
    
    // 處理組件的主題,獲取該 Button 組件在整個全局設計系統中的對應樣式
    const { mergedClsPrefixRef, NConfigProvider } = useConfig(props)
    const themeRef = useTheme(...)
    const rtlEnabledRef = useRtl(...)
    
     // 將自身狀態、全局狀態相關的主題樣式、各個 CSS 屬性的值、事件相關的內容處理之後返回給模板使用
     return {
      selfRef,
      waveRef,
      mergedClsPrefix: mergedClsPrefixRef,
      mergedFocusable: mergedFocusableRef,
      mergedSize: mergedSizeRef,
      showBorder: showBorderRef,
      enterPressed: enterPressedRef,
      rtlEnabled: rtlEnabledRef,
      handleMouseDown,
      handleKeyDown,
      handleBlur,
      handleKeyUp,
      handleClick,
      customColorCssVars: computed(() => {...}),
      cssVars: computed(() => {...})
    }
   },
   render() {
   // 處理各種組件相關的樣式渲染、事件處理相關的內容,這裡的樣式渲染對應著在文檔裡提到的 Button 可以呈現的狀態和能處理的操作
    const { $slots, mergedClsPrefix, tag: Component } = this
    return (
      <Component
        ref="selfRef"
        class={[
          `${mergedClsPrefix}-button`,
          `${mergedClsPrefix}-button--${this.type}-type`,
          {
            [`${mergedClsPrefix}-button--rtl`]: this.rtlEnabled,
            [`${mergedClsPrefix}-button--disabled`]: this.disabled,
            [`${mergedClsPrefix}-button--block`]: this.block,
            [`${mergedClsPrefix}-button--pressed`]: this.enterPressed,
            [`${mergedClsPrefix}-button--dashed`]: !this.text && this.dashed,
            [`${mergedClsPrefix}-button--color`]: this.color,
            [`${mergedClsPrefix}-button--ghost`]: this.ghost // required for button group border collapse
          }
        ]}
        tabindex={this.mergedFocusable ? 0 : -1}
        type={this.attrType}
        style={this.cssVars as CSSProperties}
        disabled={this.disabled}
        onClick={this.handleClick}
        onBlur={this.handleBlur}
        onMousedown={this.handleMouseDown}
        onKeyup={this.handleKeyUp}
        onKeydown={this.handleKeyDown}
      >
        {$slots.default && this.iconPlacement === 'right' ? (
          <div class={`${mergedClsPrefix}-button__content`}>{$slots}</div>
        ) : null}
        <NFadeInExpandTransition></NFadeInExpandTransition>
        {$slots.default && this.iconPlacement === 'left' ? (
          <span class={`${mergedClsPrefix}-button__content`}>{$slots}</span>
        ) : null}
        {!this.text ? (
          <NBaseWave ref="waveRef" clsPrefix={mergedClsPrefix} />
        ) : null}
        {this.showBorder ? ( ...)}
        {this.showBorder ? (...)}
     </Component>
   )
  }
})

進一步簡化代碼

從上述還剩下的代碼,我們可以看到,其實對於理解組件庫來說,我們其實絕大部分內容是在做定製主題,然後如果根據各種傳入的 props,展示不同的主題的工作,所以你會看到 Button 組件裡充斥著大量的 CSS 變量,如 this.color 、this.ghost 、this.text 、this.cssVars ,所以我們的核心就是理解這些主題是如何定製的,包含哪些變量和依賴,這些變量和依賴是如何影響 Button 可以承載不同樣式和功能的。

所以上述代碼中,有一些內容其實我們就可以刪掉了:

我們只需要看一個獨立的 Button 是如何運作的,所以 NButtonGroup 部分,按鈕組部分就可以不要了我們也不需要處理一些獨特的適配,如 RTL(從右向左排版)等

所以我們需要近一步刪除這些代碼:

import { buttonGroupInjectionKey } from './ButtonGroup'

import useRtl from '../../_mixins/use-rtl'



const NButtonGroup = inject(buttonGroupInjectionKey, {})

以及其他使用到 buttonGroup 相關的內容。

理解輸入

通過上一步,我們基本上去除了所有無關的內容,達到了我們最終高潮 MVP 項目裡需要的 Button 的所有的、最精簡的內容,也就是說我們核心入口代碼自身和依賴的部分已經確定了,那麼接下來就需要處理全部的輸入,以及刪除這些輸入中相關的依賴與 Button 處理無關的邏輯。

我們可以看到 Button 主要有如下一種輸入:

使用鉤子 useFormItem 、或全局狀態注入 inject(...) 相關的輸入

我們可以看到,import 相關的輸入主要分為兩類:

某些庫,如 vue 的導入:這個我們只需要查詢對應庫的文檔就可了解對於 API 的作用直接依賴於自身項目的其他相對路徑導入:這個我們就需要繼續探究 NaiveUI 源碼庫的其他部分

而鉤子 useFormItem 、或全局狀態注入 inject(...) 相關的輸入則也依賴於 import 裡自身項目的其他相對路徑引入。

我們需要順著如下的這些依賴,進行依賴分析:

import { createHoverColor, createPressedColor } from '../../_utils/color/index'

import { useConfig, useFormItem, useTheme } from '../../_mixins'

import {

  NFadeInExpandTransition,

  NIconSwitchTransition,

  NBaseLoading,

  NBaseWave

} from '../../_internal'

import { call, createKey } from '../../_utils'

import { buttonLight } from '../styles'

import { buttonGroupInjectionKey } from './ButtonGroup'

import style from './styles/button.cssr'

這些依賴裡面有些自己本就是葉子依賴,並無其它依賴,如:

import { createHoverColor, createPressedColor } from "../../_utils/color/index";



// 其中某幾項

import { useFormItem } from "../../_mixins";



// 下面的某幾項

import {

  NFadeInExpandTransition,

  NIconSwitchTransition,

} from "../../_internal";



import { call, createKey, getSlot, flatten } from "../../_utils";

這些葉子依賴可以直接對照著原倉庫建立對應的目錄結構和文件命名,然後把代碼拷貝過來。

對於那些非葉子依賴,我們需要再下一番功夫繼續解析其依賴,重複之前的兩項操作:

刪除 TS 或者其他和 Button 不相干的代碼和依賴

最後就是對照著源碼的目錄結構創建一樣的結構,將處理完無關內容的代碼拷貝過去。

打個比方,對於非葉子依賴 style :

import style from "./styles/button.cssr.js";

我們需要去到對應的文件下,查看其依賴:

import { c, cB, cE, cM, cNotM } from '../../../_utils/cssr'

import fadeInWidthExpandTransition from '../../../_styles/transitions/fade-in-width-expand.cssr'

import iconSwitchTransition from '../../../_styles/transitions/icon-switch.cssr'

發現其依賴了用於進行 BEM 規範定義的 cssr 庫(自建)、以及處理動畫的一些 fadeInWidthExpandTransition 和 iconSwitchTransition 依賴,那麼接著要繼續進入這些依賴,如:

import { c, cB, cE, cM, cNotM } from '../../../_utils/cssr'

它的依賴如下:

 /* eslint-disable @typescript-eslint/restrict-template-expressions */

import CSSRender, { CNode, CProperties } from 'css-render'

import BEMPlugin from '@css-render/plugin-bem'

發現沒有其他再需要繼續遞歸尋找的依賴了,都是引入的第三方庫,那麼就可以去查閱一下對應的第三方庫的文檔,了解 API 的含義即可。

如此往復進行上述的依賴分析,直至收斂,最後我們會得到一個如下的文件組織圖:

.
├── App.vue
├── _internal
│   ├── fade-in-expand-transition
│   │   ├── index.js
│   │   └── src
│   │       └── FadeInExpandTransition.jsx
│   ├── icon
│   │   ├── index.js
│   │   └── src
│   │       ├── Icon.jsx
│   │       └── styles
│   │           └── index.cssr.js
│   ├── icon-switch-transition
│   │   ├── index.js
│   │   └── src
│   │       └── IconSwitchTransition.jsx
│   ├── index.js
│   ├── loading
│   │   ├── index.js
│   │   └── src
│   │       ├── Loading.jsx
│   │       └── styles
│   │           └── index.cssr.js
│   └── wave
│       ├── index.js
│       └── src
│           ├── Wave.jsx
│           └── styles
│               └── index.cssr.js
├── _mixins
│   ├── index.js
│   ├── use-config.js
│   ├── use-form-item.js
│   ├── use-style.js
│   └── use-theme.js
├── _styles
│   ├── common
│   │   ├── _common.js
│   │   ├── index.js
│   │   └── light.js
│   ├── global
│   │   └── index.cssr.js
│   └── transitions
│       ├── fade-in-width-expand.cssr.js
│       └── icon-switch.cssr.js
├── _utils
│   ├── color
│   │   └── index.js
│   ├── cssr
│   │   ├── create-key.js
│   │   └── index.js
│   ├── index.js
│   ├── naive
│   │   ├── index.js
│   │   └── warn.js
│   └── vue
│       ├── call.js
│       ├── flatten.js
│       ├── get-slot.js
│       └── index.js
├── assets
│   └── logo.png
├── button
│   ├── src
│   │   ├── Button.jsx
│   │   └── styles
│   │       └── button.cssr.js
│   └── styles
│       ├── _common.js
│       ├── index.js
│       └── light.js
├── components
│   └── Button.jsx
├── config-provider
│   └── src
│       └── ConfigProvider.js
└── main.js

32 directories, 45 files

一個簡單的 Button 竟然要包含 45 個文件,32個目錄來進行支撐,我們基本上可以確定組件庫中 90% 的內容是共通的,只需要理解了一個 Button 需要的所有底層依賴和設計理念,理解這個組件庫只需要再努力一步,了解剩下 10 % 的各組件特殊設計,就可以弄懂整個組件庫的源碼。

上述核心整理的一個 Button 的全部依賴代碼可以進入我的 Github 倉庫查閱:https://github.com/pftom/naive-app。

抽絲剝繭

當我們能夠拿到一個 Button 能夠完美運行背後所需要的所有 「必要」 和 「最簡」 的依賴之後,我們就可以邊運行這個項目,邊通過查閱資料,畫思維導圖理解這份最簡必要代碼了。

我們首先把代碼跑起來,然後逐層理解代碼邏輯,如前置的幾個鉤子函數是幹嘛的:

核心的 useTheme 鉤子是幹嘛的:

用戶自定義相關的鉤子函數又是幹嘛的,它包含哪些 CSS 變量:

Vue3 組件裡面的 setup 返回值有哪些:

最終用於渲染的 render 函數邏輯是幹嘛的:

通過查閱 Vue3 文檔、梳理整個代碼流程,然後了解各個分支是如何運作的,我們就能慢慢理解 Button 組件是如何跑起來的。得益於我們進行了代碼的最精簡化處理,所以整個看代碼的流程雖然會慢一點,但是整體需要理解的內容相比之前我們拿到一整份源碼,幾百上千個文件來一股腦從入口開始打斷點調試會好很多。

寫在最後

相信大家在看皮湯的這篇源碼閱讀文章之前,應該也看過各種大牛的源碼解讀文章,但是相信每個人都有自己比較獨特的看源碼技巧,雖然我這裡是拿如何看懂 NaiveUI 的源碼舉例子,但是相信所有看源碼的過程都是如此,遵循如下步驟:

理解高潮 MVP,又包含定位源碼最小可行性代碼需要的內容,在看源碼之前先梳理結構,確保 MVP 能夠跑起來然後再在最小、最核心的源碼上進行打斷點、畫思維導圖、查閱文檔等方式幫助自己啃下源碼

這是皮湯在看 Vite、NaiveUI 源碼過程中總結出來的經驗,相信能夠為徘徊在看源碼路上卻沒有方法的同學提供一點指引,你完全可以應用這個技巧去看其他的源碼,如 Webpack?qiankun?Ant Design?或者抖音最近發布的 Semi Design。共勉 💪

參考資料[1]

尤大都推薦的組件庫是如何開發出來的?: https://mp.weixin.qq.com/s?__biz=MzkxMjI3OTA3NQ==&mid=2247485296&idx=1&sn=61b6de490f9d437215cadde9502d75df&chksm=c10e143cf6799d2afe13b5aecebc2b6608bd0fab3e61149b80910ce87757ceb2219651ed9a07&token=586597170&lang=zh_CN#rd

[2]

Semi Design: https://github.com/DouyinFE/semi-design

[3]

React: https://github.com/facebook/react

[4]

貢獻指南: https://github.com/TuSimple/naive-ui/blob/main/CONTRIBUTING.md

[5]

CSS Render: https://github.com/07akioni/css-render

相關焦點

  • 【組件庫從0到1】Vite + Vue3 + TSX開發指南
    不少前端小夥伴問過我,說業務代碼寫了2、3年了,應該如何再進一步?村長的回答中大多數會提到兩個關鍵點:加強工程化實踐能力和底層原理源碼知識。工程化能力對於小夥伴們在工作中解決問題有直接影響,如果你想成為小組內的核心程式設計師,拿更高的職位和薪資,能夠給大家搭架子,解決問題,重難點突破是非常關鍵的幾個考察點。
  • 手把手教你搭建基於 Vue3 的前端組件庫
    最終決定參考 Element UI 的設計風格,主題色選擇紫色(受到 MaterialDesignInXamlToolkit 的影響),寫一套基於 Vue3 的 UI 框架庫和對應的官方網站,方便後期在項目中快速使用,也算是對 Vue3 新特性的學習和總結。先看一下 Jeremy UI 官網 效果吧!
  • 從零開始擼一個Vue3.0組件庫-並上傳到NPM上
    前言今日前端早讀課文章由@傲夫靠斯投稿分享。
  • vue-cli3.x的構建第三方庫按需引用,自定義組件的探索
    的問題參考:https://github.com/webpack-contrib/sass-loader(node-sass遷移為Dart-sass)https://blog.csdn.net/mlonly/article/details/88635809(構建組件庫)目的:構建一個能第三方組件庫(element-ui)進行二次封裝,同時能封裝自定義組件,的純js,css,image
  • Vue.js 3.x 源碼解析先導
    聊聊 Vue.js 3.xVue.js 3.x 目前的狀態Vue.js 3.x 目前處於 Pre-Alpha 的狀態,從 Vue 官方的 Roadmap 來看,2019 年 Q3 結束前開源 Vue 3.x 的源碼,Q4 除了繼續完善 Vue.js 核心源碼之外,還要補齊周邊的生態建設:如 vue-router、vuex、vue-cli、Vue Devtools
  • 2020年5個最佳Vue移動端組件庫|UI框架
    小夥伴們平時開發vue,react或是angular項目,都喜歡使用的什麼UI組件庫呢?今天,就來盤點下,幾個熱門優質的Vue.js移動端UI組件庫。1、Mint UI餓了麼開源的移動端UI組件庫,基於vue.js的移動端UI框架,包含豐富的 CSS 和 JS 組件,能夠滿足日常的移動端開發需求。
  • 知名開源項目源碼,該怎麼去讀?
    打個比方,皮湯我因為是一名前端,而前端這個領域有很多新興的內容,如 Unbundled 方案 Vite,新興框架 Svelte,新彙編語言 WebAssembly,CSS 工程化方案 TailwindCSS,組件庫如抖音很火的開源庫 Semi Design[2]、或者社區比較火的 Vue3 組件庫 NaiveUI 等。
  • 基於Vue.js的表格分頁組件
    不了解Vue.js的童鞋可以移步我的上一篇文章《【vue.js學習系列2】淺談Vue.js的幾個特點https://zhuanlan.zhihu.com/p/24041292》了解一下。如需高大上的組件,可以移步Vue官方組件庫:https://github.com/vuejs/awesome-vue#libraries--pluginsBootPage是一款支持靜態數據和伺服器數據的表格分頁組件,支持調整每頁顯示行數和頁碼顯示個數,樣式基於bootstrap,就像這樣:
  • Vue造輪子必備*.vue文件源碼讀取並高亮展示
    前言(需求)就是想讀取 *.vue 文件的源碼並高亮展示到頁面上,又不想用第三方的依賴(其實是找不到)。2.實現思路通過  vue-loader 自定義塊 功能,獲取目標文件的文件路徑,然後通過 fs 讀取源碼,再用 @vue/compiler-core 的 API baseParse將讀取到的內容轉換成 AST 語法抽象樹,然後將 fs 讀取的內容中 抽離出 自定義塊內容 和 需要的源碼,最後再將以上兩個內容重新掛到組件對象上,直接讀取組件相應的欄位就可以
  • 手把手教你搭建基於 Vue3 的前端 UI 組件庫
    Vue3 UI Framework - 首頁官網的頂部邊欄前面已經做完了,今天我們再來做一下官網的首頁創建視圖文件夾
  • 如何從零到一成為 Vue3 組件庫 Naive UI 的貢獻者?
    您將了解到如何參與開源項目bug修復並提交PR成為ContributesNaiveUI 倉庫地址 :NaiveUI[1]🎄如果你正在學習vue3+TS,那麼我建議你可以關注一下NaiveUI,一個很棒的vue3組件庫^_^,文中開源項目便指的它
  • Vue 3源碼及原理,看這篇就夠了
    現在但凡出去面試,關於 Vue.js 技術棧,面試官幾乎必問 Vue 3.0 。不僅會問一些核心特性,還會問原理層面的問題。
  • Vue-Router源碼學習之index.js(vue-router類)
    今天,帶來Vue-Router源碼解析系列的第二篇文章:index.js。正文vue-router類裡面都做了什麼?index.js是vue-router這個類的主構造函數,所以內容上算是比較關鍵的:從圖片中我們可以看出來,這是一個ES6聲明類的方法,vue-router源碼中類的聲明都是使用類ES的語法,constructor (options: RouterOptions
  • 淺析 vue-router 源碼和動態路由權限分配
    但還是堅持啃下來了(當然還沒看完,內容是真的多),下面是我在政採雲(實習)工作閒暇時間閱讀源碼的一些感悟和總結,並帶分析了大三時期使用的 vue-element-admin (https://panjiachen.gitee.io/vue-element-admin-site/zh/guide/#%E5%8A%9F%E8%83%BD) 這個 vuer 無所不知的後臺框架的動態路由權限控制原理
  • 2018 年你需要知道的 Vue.js 組件庫,完善你的應用開發
    也可以將任何 Git 存儲庫中的組件分離出來,並將其快速分享給動態和可固定的組件集合。 從那裡,組件可以被單獨發現並打包給管理者一起使用,或者在任何存儲庫中進行進一步修改。https://www.oschina.net/p/vuetifyVuetifyjs 根據材料設計規格提供 UI 布局。
  • Vue基礎及常用UI組件庫簡介
    /2.5.17-beta.0/vue.min.js"></script><script src="https://cdn.bootcss.com/jquery/3.3.0/jquery.min.js"></script><script type="text/javascript"> new Vue({
  • Vue2.0父子組件間通信
    vue2.0Vue.js是一套構建用戶界面的漸進式框架。與其他重量級框架不同的是,Vue 從根本上採用最小成本、漸進增量的設計。Vue 的核心庫只專注於視圖層,並且很容易與其他第三方庫或現有項目集成。另一方面,當與單文件組件和 Vue 生態系統支持的庫結合使用時,Vue 也完全能夠為複雜的單頁應用程式提供有力驅動。
  • kpc v0.7.8 發布,同時支持 Vue/React/Intact 的前端組件庫
    動機目前市面上已經存在大量組件庫,我們為什麼還要造這個輪子呢?下面我們解釋下這個組件庫開發的動機。
  • Vue 的完整生命周期源碼流程詳解
    $mount()源碼地址:dist/vue.js - 11927行()然後再了解一下去 patch 的入口,有關 Diff 算法源碼的完整流程剖析,可以看我另一篇文章介紹的很詳細了就不搬過來了, 深入淺出虛擬 DOM 和 Diff 算法,及 Vue2 與 Vue3 中的區別源碼地址:src/core/instance/lifecycle.js
  • vue常用框架和組件代碼,抓緊收藏轉發吧
    . element-ui  餓了麼出品的基於Vue2.0的ui框架http://element-cn.eleme.io/#/zh-CN     2. iview  和element-ui差不多的組件庫https://iviewui.com/3. mint-ui 移動端ui組件庫http://mint-ui.github.io/#!