🧭第20節:項目實戰:通用 UI 組件庫搭建

📖引言

本節將帶領大家完成一個完整的項目實戰——從零開始搭建一個包含 Button、Input、Select、Table、Modal 等核心組件的通用 UI 組件庫,並將其發佈到 NPM 上。項目完成後,我們還將總結 10 大高頻面試題,幫助你在面試中脱穎而出。


🎯1. 核心內容概覽

🔧1.1 項目結構設計

  • 清晰的目錄結構。
  • 模塊化開發與代碼組織。

🛠️1.2 核心組件實現

  • Button:基礎按鈕組件。
  • Input:輸入框組件。
  • Select:下拉選擇組件。
  • Table:表格組件。
  • Modal:彈窗組件。

🚀1.3 文檔與發佈流程

  • 使用 Storybook 展示組件。
  • 將組件庫發佈到 NPM。

💡1.4 面試題總結

  • 提煉組件庫開發中的常見問題。

⚡2. 重難點分析

✨2.1 重點

  1. 組件庫的設計與實現:確保組件的可複用性和易用性。
  2. 文檔與展示:通過 Storybook 提供清晰的組件文檔。
  3. 發佈流程:掌握從本地開發到 NPM 發佈的完整流程。

❗ 2.2 難點

  1. 跨框架兼容性:支持 Vue 和 React 的使用場景。
  2. 性能優化:避免因組件複雜度導致的性能問題。
  3. 版本管理與依賴衝突:確保組件庫的穩定性。

🛠️3. 項目實戰:通用 UI 組件庫搭建

🔧3.1 項目結構設計

🌟 目錄結構
my-ui-library/
├── src/
│   ├── components/       # 核心組件
│   │   ├── Button.vue
│   │   ├── Input.vue
│   │   ├── Select.vue
│   │   ├── Table.vue
│   │   └── Modal.vue
│   ├── index.js          # 入口文件
├── stories/              # Storybook 故事文件
├── package.json          # 包配置
├── vite.config.js        # Vite 打包配置
└── README.md             # 項目説明

🛠️3.2 核心組件實現

✅ Button 組件
<template>
  <button :class="['btn', type]" @click="handleClick">
    <slot></slot>
  </button>
</template>

<script>
export default {
  props: {
    type: {
      type: String,
      default: 'primary',
    },
  },
  methods: {
    handleClick() {
      this.$emit('click');
    },
  },
};
</script>

<style>
.btn {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.btn.primary {
  background-color: #4CAF50;
  color: white;
}
.btn.secondary {
  background-color: #FFC107;
  color: black;
}
</style>
✅ Input 組件
<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
    :placeholder="placeholder"
  />
</template>

<script>
export default {
  props: {
    modelValue: String,
    placeholder: String,
  },
};
</script>

<style>
input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
  outline: none;
}
</style>
✅ Select 組件
<template>
  <select :value="modelValue" @change="$emit('update:modelValue', $event.target.value)">
    <option v-for="option in options" :key="option.value" :value="option.value">
      {{ option.label }}
    </option>
  </select>
</template>

<script>
export default {
  props: {
    modelValue: String,
    options: Array,
  },
};
</script>

<style>
select {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
</style>
✅ Table 組件
<template>
  <table>
    <thead>
      <tr>
        <th v-for="column in columns" :key="column">{{ column }}</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(row, index) in data" :key="index">
        <td v-for="column in columns" :key="column">{{ row[column] }}</td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    columns: Array,
    data: Array,
  },
};
</script>

<style>
table {
  width: 100%;
  border-collapse: collapse;
}
th, td {
  border: 1px solid #ddd;
  padding: 8px;
}
th {
  background-color: #f4f4f4;
}
</style>
✅ Modal 組件
<template>
  <div v-if="visible" class="modal">
    <div class="modal-content">
      <span class="close" @click="handleClose">×</span>
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    visible: Boolean,
  },
  methods: {
    handleClose() {
      this.$emit('update:visible', false);
    },
  },
};
</script>

<style>
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
}
.modal-content {
  background-color: white;
  padding: 20px;
  border-radius: 4px;
  position: relative;
}
.close {
  position: absolute;
  right: 10px;
  top: 5px;
  cursor: pointer;
}
</style>

🚀3.3 文檔與發佈流程

🌟 使用 Storybook 展示組件

stories/Button.stories.js 中:

import Button from '../src/components/Button.vue';

export default {
  title: 'Example/Button',
  component: Button,
};

const Template = (args) => ({
  components: { Button },
  setup() {
    return { args };
  },
  template: '<Button v-bind="args">Click Me</Button>',
});

export const Primary = Template.bind({});
Primary.args = {
  type: 'primary',
};
🌟 發佈到 NPM
npm publish

💡4. 10 大高頻面試題

4.1 基礎知識

✅1. 什麼是組件庫?為什麼需要組件庫?

  • 組件庫是封裝了一系列可複用的 UI 組件,用於提升開發效率和代碼一致性。

✅2. 如何確保組件的可複用性?

  • 通過 Props、Slots 和 Events 實現靈活的輸入輸出。

✅3. React 和 Vue 在組件開發上的主要區別是什麼?

  • React 使用 JSX,Vue 使用模板語法;狀態管理方式不同。

4.2 性能優化

✅4. 如何優化組件的渲染性能?

  • 使用 memokeep-alive 減少不必要的渲染。

✅5. 懶加載組件的作用是什麼?如何實現?

  • 減少初始加載時間;通過動態導入實現。

4.3 工程化

✅6. 如何將組件庫發佈到 NPM?

  • 配置 package.json,使用 npm publish

✅7. 什麼是 Storybook?它的作用是什麼?

  • Storybook 是一個交互式組件展示工具,用於開發和測試組件。

4.4 高級問題

✅8. 如何處理組件庫的版本管理?

  • 使用語義化版本號(Semantic Versioning)。

✅9. 如何解決組件庫中的樣式衝突問題?

  • 使用 CSS Modules 或 Scoped CSS。

✅10. 如何為組件庫編寫單元測試?
- 使用 Jest 或 Vue Test Utils 編寫測試用例。


🔮5. 總結與展望

通過本節的學習,你應該已經掌握了以下技能:

  1. 如何從零搭建一個通用 UI 組件庫。
  2. 如何使用 Storybook 展示和測試組件。
  3. 如何將組件庫發佈到 NPM 並維護。

🚀未來,隨着前端技術的不斷髮展,組件庫的開發和維護將更加智能化和高效化。希望你能將這些知識運用到實際項目中,打造出更專業、更高效的組件庫!