寫在前面
一年多沒寫過博客了,工作、生活逐漸磨平了棱角。
寫代碼容易,寫博客難,堅持寫高水平的技術博客更難。
技術控決定慢慢拾起這份堅持,用作技術學習的階段性總結。
返回閱讀列表點擊 這里
開始
大前端時代,最近在面試前端工程師的過程中,有感而發,技術更新迭代快,學習成本高。瀏覽了各大博客論壇,千差萬別,比較混亂。最終決定參考 Element UI 的設計風格,主題色選擇紫色(受到 MaterialDesignInXamlToolkit 的影響),寫一套基於 Vue3
的 UI
框架庫和對應的官方網站,方便后期在項目中快速使用,也算是對 Vue3
新特性的學習和總結。
先看一下 Jeremy UI 官網 效果吧!目前項目依然處於開發階段,小伙伴們有興趣可以跟着一起練習一下,也歡迎參與到項目中,項目參考地址 GitHub ,歡迎 star
、issue
、fork
和 PR
。
分析官網
界面布局
-
頂部邊欄
-
首頁
-
文檔頁
- 側邊欄
- 內容區域
主題風格
紫色
詳細說明
位置 | 特性 |
---|---|
布局 | 兩側留白,內容在中間 最大寬度 1200px ,小於 1200px 時,寬度 100% 小於 500px 時,變為手機版 |
頂部邊欄 | 左側是通往首頁的跳轉鏈接,右側是通往文檔頁的跳轉鏈接 視口小於 500px 時,只顯示前往首頁的跳轉 |
首頁 | 分為兩部分,視覺效果部分和特點介紹部分 |
首頁視覺 | 紫色極光背景,中間放置兩個跳轉鏈接,一個前往 GitHub 查看代碼,另一個前往文檔頁 |
首頁特點 | 包含 3 個特點,分別是 1. 使用了 vue3 新特性 2. 使用 ts 3. 代碼易讀 |
文檔頁 | 文檔頁實際由頂邊欄和本體構成,本體包含側邊欄和內容區域 |
頂邊欄-文檔頁 | 小於 500px 時,在左側額外顯示彈出側邊欄的按鍵,並使得側邊欄不可見 |
側邊欄 | 默認可見,選中高亮,但視口寬度小於 500px 時,默認不可見 包含兩個部分:指南和組件,各自有子級跳轉 |
內容區域 | 根據側邊欄中選中的鏈接,顯示 md 內容或組件范例內容 |
搭建官網
創建方式有很多,而我這里選擇通過 Vite
進行創建 Vue3
項目,為什么選 Vite
呢?如下是 Vite 官網 的說法:
現實問題
在瀏覽器支持 ES 模塊之前,JavaScript 並沒有提供原生機制讓開發者以模塊化的方式進行開發。這也正是我們對 “打包” 這個概念熟悉的原因:使用工具抓取、處理並將我們的源碼模塊串聯成可以在瀏覽器中運行的文件。
時過境遷,我們見證了諸如 webpack、Rollup 和 Parcel 等工具的變遷,它們極大地改善了前端開發者的開發體驗。
然而,當我們開始構建越來越大型的應用時,需要處理的 JavaScript 代碼量也呈指數級增長。包含數千個模塊的大型項目相當普遍。我們開始遇到性能瓶頸 —— 使用 JavaScript 開發的工具通常需要很長時間(甚至是幾分鍾!)才能啟動開發服務器,即使使用 HMR,文件修改后的效果也需要幾秒鍾才能在瀏覽器中反映出來。如此循環往復,遲鈍的反饋會極大地影響開發者的開發效率和幸福感。
Vite 旨在利用生態系統中的新進展解決上述問題:瀏覽器開始原生支持 ES 模塊,且越來越多 JavaScript 工具使用編譯型語言編寫。
緩慢的服務器啟動
當冷啟動開發服務器時,基於打包器的方式啟動必須優先抓取並構建你的整個應用,然后才能提供服務。
Vite 通過在一開始將應用中的模塊區分為 依賴 和 源碼 兩類,改進了開發服務器啟動時間。
依賴 大多為在開發時不會變動的純 JavaScript。一些較大的依賴(例如有上百個模塊的組件庫)處理的代價也很高。依賴也通常會存在多種模塊化格式(例如 ESM 或者 CommonJS)。
Vite 將會使用 esbuild 預構建依賴。Esbuild 使用 Go 編寫,並且比以 JavaScript 編寫的打包器預構建依賴快 10-100 倍。
源碼 通常包含一些並非直接是 JavaScript 的文件,需要轉換(例如 JSX,CSS 或者 Vue/Svelte 組件),時常會被編輯。同時,並不是所有的源碼都需要同時被加載(例如基於路由拆分的代碼模塊)。
Vite 以 原生 ESM 方式提供源碼。這實際上是讓瀏覽器接管了打包程序的部分工作:Vite 只需要在瀏覽器請求源碼時進行轉換並按需提供源碼。根據情景動態導入代碼,即只在當前屏幕上實際使用時才會被處理。
緩慢的更新
基於打包器啟動時,重建整個包的效率很低。原因顯而易見:因為這樣更新速度會隨着應用體積增長而直線下降。
一些打包器的開發服務器將構建內容存入內存,這樣它們只需要在文件更改時使模塊圖的一部分失活[1],但它也仍需要整個重新構建並重載頁面。這樣代價很高,並且重新加載頁面會消除應用的當前狀態,所以打包器支持了動態模塊熱重載(HMR):允許一個模塊 “熱替換” 它自己,而不會影響頁面其余部分。這大大改進了開發體驗 —— 然而,在實踐中我們發現,即使采用了 HMR 模式,其熱更新速度也會隨着應用規模的增長而顯著下降。
在 Vite 中,HMR 是在原生 ESM 上執行的。當編輯一個文件時,Vite 只需要精確地使已編輯的模塊與其最近的 HMR 邊界之間的鏈失活[1](大多數時候只是模塊本身),使得無論應用大小如何,HMR 始終能保持快速更新。
Vite 同時利用 HTTP 頭來加速整個頁面的重新加載(再次讓瀏覽器為我們做更多事情):源碼模塊的請求會根據
304 Not Modified
進行協商緩存,而依賴模塊請求則會通過Cache-Control: max-age=31536000,immutable
進行強緩存,因此一旦被緩存它們將不需要再次請求。一旦你體驗到 Vite 的神速,你是否願意再忍受像曾經那樣使用打包器開發就要打上一個大大的問號了。
為什么生產環境仍需打包
盡管原生 ESM 現在得到了廣泛支持,但由於嵌套導入會導致額外的網絡往返,在生產環境中發布未打包的 ESM 仍然效率低下(即使使用 HTTP/2)。為了在生產環境中獲得最佳的加載性能,最好還是將代碼進行 tree-shaking、懶加載和 chunk 分割(以獲得更好的緩存)。
要確保開發服務器和生產環境構建之間的最優輸出和行為一致並不容易。所以 Vite 附帶了一套 構建優化 的 構建命令,開箱即用。
為何不用 ESBuild 打包?
雖然
esbuild
快得驚人,並且已經是一個在構建庫方面比較出色的工具,但一些針對構建 應用 的重要功能仍然還在持續開發中 —— 特別是代碼分割和 CSS 處理方面。就目前來說,Rollup 在應用打包方面更加成熟和靈活。盡管如此,當未來這些功能穩定后,我們也不排除使用esbuild
作為生產構建器的可能。
在開始之前我們需要注意兼容性問題:
兼容性注意
Vite 需要 Node.js 版本 >= 12.0.0
使用 npm
創建 jeremy-ui
項目
- 直接創建
mkdir jeremy-ui
cd jeremy-ui
cva .
- 腳手架創建(根據提示信息創建即可,注意選擇
vue
->vue-ts
)
啟動項目
cd jeremy-ui
npm install
npm run dev
正常情況下,你就可以在瀏覽器上( http://localhost:3000/ ) 預覽項目了。
注意:
如果你不是通過腳手架去創建的項目,那么你可能需要將 main.js
改成 main.ts
。另外,你還需要在 src
目錄下添加 shims.d.ts
文件,讓 ts
文件能夠識別 vue
后綴的文件。
shims.d.ts
文件源碼如下:
declare module '*.vue' {
import { ComponentOptions } from 'vue'
const componentOptions: ComponentOptions
export default componentOptions
}
整體布局設計
根據之前的分析,我們能夠得出結論,首先,最大寬度限制需要作用到 app
上,所以相應的樣式應該寫在 app.vue
上,其次,首頁和文檔頁的頂邊欄,其功能為:
位置 | 功能 |
---|---|
首頁 | 禁止彈出菜單 |
文檔頁 | 根據情況顯示或者隱藏菜單 |
可見並不完全相同,也就是說不是同一個組件的復用,所以應該將頂邊欄組件分別嵌入首頁和文檔頁組件,而不是嵌入 app.vue ,
然后,首頁顯然沒有側邊欄,所以首頁的內容直接寫在其模板內即可,而文檔頁需要側邊欄和內容區域,但是文檔頁的側邊欄,實際上與彈出的頂部菜單是相同的內容,所以只需要編寫一次,然后復用為側邊欄和頂部菜單即可。
除此之外,文檔頁的內容區域,還要分別展示 markdown
內容和組件內容,所以內容區域要分成兩種進行編寫。
綜上,首頁大致為上下結構,恆定不變;文檔頁大致為 T
字結構,小於 1200px
時為上下結構,附有彈出菜單的選項。
總體樣式表 index.scss
(可能需要修改 index.css
的后綴)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul,
ol {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
&:hover {
text-decoration: underline;
cursor: pointer;
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: normal;
}
body {
font-size: 16px;
line-height: 1.5;
color: #1d2c40;
font-family: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica,
"Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB",
"Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN",
"Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti",
SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;
}
.icon {
width: 1em; height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
然后修改 main.ts
中的引入
import './index.scss'
最后完成 App.vue
的內容
<template>
<div class="app">
<router-view />
</div>
</template>
<script lang="ts">
export default {
name: "App",
};
</script>
<style lang="scss" scoped>
$max-width: 1200px;
.app {
max-width: $max-width;
margin-left: calc(50vw - 600px);
position: relative;
@media (max-width: $max-width) {
margin-left: 0;
}
}
</style>
依賴包的安裝
請參考 package.json
文件,對應更新一下,然后再項目根目錄下執行 npm install
即可。
{
"name": "jeremy-ui",
"version": "0.0.3",
"files": [
"lib/*"
],
"main": "lib/jeremy.js",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"github-markdown-css": "^4.0.0",
"marked": "^1.2.8",
"prismjs": "^1.23.0",
"vue": "^3.0.4",
"vue-router": "^4.0.4"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.0.4",
"rollup-plugin-esbuild": "^4.7.2",
"rollup-plugin-scss": "^3.0.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-vue": "^6.0.0",
"sass": "^1.44.0",
"vite": "^1.0.0-rc.13"
}
}
路由設計
既然有多個頁面,那肯定就要通過路由進行跳轉。路由顯然是使用 vue-router
,首先安裝它。要注意,默認安裝的 vue-router
是 3.x.x
版本的,只能用於 vue2
,要想用於 vue3
,必須是 4.x.x
版本
通過 npm info vue-router
可以看到最新版( next
版本)是 4.0.4
(如有變化,下載最新版即可),則我們通過 npm install vue-router@4.0.4
安裝適合 vue3
的 vue-router。
Jeremy UI
官網的路由設計如下:
目標 | 路由 |
---|---|
首頁 | / |
文檔頁 | /document |
文檔頁 - 介紹 | /document/introduction |
文檔頁 - 安裝 | /document/install |
文檔頁 - 快速上手 | /document/start |
文檔頁 - [組件名] | /document/[componentName] |
因為目前還沒有試圖,所以我們先把框架寫好,暫時寫一個空的引入即可,router.ts
如下
import { createWebHistory, createRouter } from 'vue-router'
const history = createWebHistory()
const router = createRouter({
history,
routes: [
]
})
export default router
然后再在 main.ts
中引入
import { createApp } from 'vue'
import App from './App.vue'
import './index.scss'
import router from './router'
createApp(App).use(router).mount('#app')
使用 SVG
為了官網的相對美觀,我們決定使用 SVG
,可以使用 icon font 提供的免費圖標,使用教程大致如下
- 選擇喜歡的圖標,加入購物車
- 新建一個項目,或選擇一個已有項目,將購物車里的圖標加入該項目
- 進入”我的項目”,生成新的引用鏈接
- 選擇
Symbol
模式,復制鏈接,粘貼到本地項目的index.html
的新script
標簽中
之后就可以在本地使用如下的方式引用 svg
了
<svg>
<use xlink:href="#icon-Vue"></use>
</svg>
svg
的命名,參考 icon font
上”我的項目”里設置的每個圖標的名稱,另外,如果項目中的圖標有任何變化,必須要重新生成引用鏈接!
項目地址 🎁
GitHub: https://github.com/JeremyWu917/jeremy-ui
官網地址 🌍
JeremyUI: https://ui.jeremywu.top
GitHub 分支說明
分支名稱 | 說明 |
---|---|
main | 合並的 website 分支的 PR |
dev | 開發分支,組件庫的引用來自本地 |
publish | GitHub Pages 發布的分支,組件庫的引用來自 npm |
website | 官網使用的分支,組件庫的引用來自 npm |
小伙伴 clone
之后可以參考 dev
分支,可以向 dev
分支提交 PR 哦!
參考資料
知乎 - 隨安
掘金 - 大威Wayne
Vite 官方 - Vite
阿里矢量圖標庫 - Iconfont