回頭看下整個項目,代碼冗餘非常嚴重,所以接下來我們把代碼重新梳理優化一下
返回閱讀列表點擊 這裏
全局設定
爲了後續查閱和管理的方便,我們對全局的用例進行一個簡單的設定
我們在 src
目錄下創建一個 Global.cs
文件,代碼如下:
export const components = {
'Button': {
name: 'Button', title: 'Button 按鈕'
},
'Card': {
name: 'Card', title: 'Card 卡片'
},
'Dialog': {
name: 'Dialog', title: 'Dialog 對話框'
},
'Input': {
name: 'Input', title: 'Input 輸入框'
},
'Switch': {
name: 'Switch', title: 'Switch 開關'
},
'Table': {
name: 'Table', title: 'Table 表格'
},
'Tabs': {
name: 'Tabs', title: 'Tabs 標籤頁'
},
}
export const guidances = {
'introduction': { path: "introduction", title: "介紹" },
'install': { path: "install", title: "安裝" },
'start': { path: "start", title: "快速上手" }
}
注意
通過這個文件, 我們能夠清楚的知道本項目的主要文檔頁的信息
樣式簡化
每個 src/component
下的組件文檔頁都引用 ./example
下對應的案例, 所以我們考慮將引用放在一起,然後再暴露給組件的文檔頁
接下來我們再新建一個 src/component/contents
文件夾,爲每個組件新建對應的樣例管理文件,並且該文件需要包含如下特性:
- 所有樣例的引入
- 該樣例專屬的參數列表
下面我們以 JeremyInput
組件爲例子說明一下:
首先我們在 contents
文件夾下創建 Input.ts
文件,內容如下:
import JeremyComponent1 from "../examples/Input/Input1.example.vue";
import JeremyComponent2 from "../examples/Input/Input2.example.vue";
import JeremyComponent3 from "../examples/Input/Input3.example.vue";
import JeremyComponent4 from "../examples/Input/Input4.example.vue";
export default {
components: [
JeremyComponent1,
JeremyComponent2,
JeremyComponent3,
JeremyComponent4,
],
attributes: [
{ attr: 'value', desp: '綁定值', type: 'string', values: '字符串', default: '必填' },
{ attr: 'theme', desp: '類型', type: 'string', values: 'input / textarea', default: 'input' },
{ attr: 'rows ', desp: '行高,但當 theme 爲 input 時值恆爲1', type: 'number', values: '正整數', default: '5' },
{ attr: 'color', desp: '外邊框顏色', type: 'string', values: '任意合法顏色值', default: '#8c6fef' }
]
};
另外,我們需要將模板設置成遍歷的方式,顯然,每個組件文檔頁,都具有與 Button
組件文檔頁類似的結構
那麼我們可以把這個結構抽取出來,然後按照需要進行引入
先抽取結構,我選擇在 src/views
下新建 Content.vue
來承載這個結構
組件列表和參數表通過 src/components/contents
下的彙總來引入
引入後,根據字段名的不同,製作哈希表 LabyMap
再要求用戶傳入參數 props
,通過 name
來指定選擇要顯示哪個組件文檔頁,通過 title
來指示現在的組件文檔頁的標題
<template>
<h1>{{ title }}</h1>
<br />
<div
class="container"
v-for="({ ...component }, index) in components"
:key="index"
>
<jeremy-card class="example">
<h2>{{ component.__sourceCodeTitle }}</h2>
<br />
<component :is="component" />
<br />
<br />
<code class="markdown-body">
<pre
v-if="visibility[index]"
v-html="
Prism.highlight(
component.__sourceCode,
Prism.languages.html,
'html'
)
"
></pre>
</code>
<button class="toggle" @click="toggle(index)">
<span class="close" v-if="visibility[index]">
△
<span class="desp">隱藏代碼</span>
</span>
<span class="open" v-else>
▽
<span class="desp">顯示代碼</span>
</span>
</button>
</jeremy-card>
<br />
</div>
<jeremy-table bordered>
<thead>
<tr>
<th v-for="(head, index) in heads" :key="index">{{ head.name }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(attribute, index) in attributes" :key="index">
<td v-for="key in keys" :key="key" v-html="attribute[key]"></td>
</tr>
</tbody>
</jeremy-table>
</template>
<script lang="ts">
import JeremyButtons from "../components/contents/Button";
import JeremyCards from "../components/contents/Card";
import JeremyDialogs from "../components/contents/Dialog";
import JeremyInputs from "../components/contents/Input";
import JeremySwitchs from "../components/contents/Switch";
import JeremyTables from "../components/contents/Table";
import JeremyTabss from "../components/contents/Tabs";
import { ref } from "vue";
import { JeremyCard, JeremyTable } from "jeremy-ui"
import "prismjs";
// import "prismjs/themes/prism.css";
const Prism = (window as any).Prism;
const JeremyMap = {
Button: JeremyButtons,
Card: JeremyCards,
Dialog: JeremyDialogs,
Input: JeremyInputs,
Switch: JeremySwitchs,
Table: JeremyTables,
Tabs: JeremyTabss,
};
export default {
props: {
name: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
},
components: {
JeremyCard,
JeremyTable,
},
setup(props) {
const { name, title } = props;
const heads = [
{ name: "參數", identifier: "attr" },
{ name: "含義", identifier: "desp" },
{ name: "類型", identifier: "type" },
{ name: "可選值", identifier: "values" },
{ name: "默認值", identifier: "default" },
];
const keys = heads.map((item: any) => item.identifier);
const { components, attributes } = JeremyMap[name];
const visibility = ref(components.map((item) => false));
const toggle = (index) => {
visibility.value[index] = !visibility.value[index];
};
return {
title,
Prism,
heads,
keys,
components,
attributes,
visibility,
toggle,
};
},
};
</script>
<style lang="scss" scoped>
$theme-color: #8c6fef;
.container {
&:hover {
> .example > .toggle > * > .desp {
display: inline;
}
}
> .example {
> .toggle {
display: block;
width: 100%;
height: 32px;
border: none;
transition: background-color 250ms;
outline: none;
&:focus {
outline: none;
}
background: white;
cursor: pointer;
&:hover {
background: fade-out($theme-color, 0.95);
}
> * > .desp {
display: none;
}
}
}
}
</style>
我們還可以抽取指南文檔頁的結構到 src/views/Guidance.vue
<template>
<article class="markdown-body" v-html="md"></article>
</template>
<script>
import { ref } from "vue";
export default {
props: {
path: {
type: String,
required: true,
},
},
setup(props) {
const md = ref(null);
import(`../markdown/${props.path}.md`).then(
(res) => (md.value = res.default)
);
return { md };
},
};
</script>
然後修改 router.ts
,通過路由來傳遞參數
import { createWebHistory, createRouter } from 'vue-router'
import Home from './views/Home.vue'
import Document from './views/Document.vue'
import { guidances, components } from './Global'
import Guidance from './views/Guidance.vue'
import Content from './views/Content.vue'
function Route(path, component, props) {
this.path = path
this.component = component
this.props = props
}
const guidancesRoutes = Object.keys(guidances).map(item => {
return new Route(item, Guidance, guidances[item])
})
const componentsRoutes = Object.keys(components).map(item => {
return new Route(item.toLowerCase(), Content, components[item])
})
const history = createWebHistory()
const router = createRouter({
history,
routes: [
{ path: '/', component: Home },
{
path: '/document', component: Document, children: [
{ path: '', redirect: '/document/introduction' },
...guidancesRoutes,
...componentsRoutes
]
}
]
})
export default router
優化路由
這個 router.ts
看起來也還是很混亂,也需要再優化一下
注意到重複的部分出現在 children
字段下,而該字段的值是個數組,每個數組項都是一個包含 3 個字段的對象
那麼我們可以先定義這個對象:
function Route(path, component, props) {
this.path = path
this.component = component
this.props = props
}
再觀察指南的路由,和組件的路由,有如下規律
- 指南的路由
- 其
path
值集合,與Global.ts
中的guidances
對象的keys
是一致的 - 其
props
中傳入的鍵集合,與Global.ts
中的guidances
對象的keys
是一致的
- 其
- 組件的路由
- 其
path
值集合,與Global.ts
中的components
對象的keys.toLowerCase
是一致的 - 其
props
中傳入的鍵集合,與Global.ts
中的components
對象的keys
是一致的
- 其
那麼容易得到指南路由的數組和組件路由的數組
const guidancesRoutes = Object.keys(guidances).map(item => {
return new Route(item, Guidance, guidances[item])
})
const componentsRoutes = Object.keys(components).map(item => {
return new Route(item.toLowerCase(), Content, components[item])
})
然後在路由配置的 children
字段下,使用 spread 語法展開這兩個數組即可
const router = createRouter({
history,
routes: [
{ path: '/', component: Home },
{
path: '/document', component: Document, children: [
{ path: '', redirect: '/document/introduction' },
...guidancesRoutes,
...componentsRoutes
]
}
]
})
OK,現在的代碼,幾乎沒有冗餘了
項目地址 🎁
GitHub: https://github.com/JeremyWu917/jeremy-ui
官網地址 🌍
JeremyUI: https://ui.jeremywu.top
感謝閱讀 ☕