前言
前端技术的一个特点是项目之间会使用很多第三方npm包,在学习时,如果我们只关注其中一个框架,是很难有手感的,我自身就是一个具体的例子,花时间阅读完Vue3文档后,具有灵活运用还有一段距离,这个阶段就需要多看他人成熟项目是怎么编写的,多看具体的实例,本文记录我阅读element-plus-admin这个项目时的细节。
element-plus-admin(https://github.com/hsiangleev/element-plus-admin)使用了Vue中最新的技术栈(Vue3 + Vite + Vue-router 4 + Pinia + element-plus + tailwindcss)构建经典后台,理解其他的代码,方便日后“参考”着开发自己的前端项目。
注意,element-plus-admin项目中并没有使用Vue3最新的写法(因为当时Vue3的一些语法还没推出),但不妨碍我们学习,关于Vue3最新的语法可看:TypeScript+Vue3最新语法糖实践
VsCode Debug Vite构建的Vue3项目
看项目代码,第一件事不是直接看,而是应该将项目运行起来,然后再通过Debug的方式去调试其中的代码,这比单纯的静态分析代码快速靠谱很多。
Vue3官网文档中,只有如何使用VScode Debug Webpack构建的Vue项目的描述,对于Vite构建的项目并没有描述,经过查阅和实践,发现很简单。
点击VSCode中的debug,然后在.vscode目录下生成launch.json文件,其配置如下:
{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-msedge",
"request": "launch",
"name": "element-plus-admin",
"url": "http://localhost:3002",
"webRoot": "${workspaceFolder}"
}
]
}
这里,我使用Edge浏览器来调试,使用Chrome也是可以的(测试过,可以的),默认生成的launch.json,其url配置如果不符合当前项目启动时的url,那么就需要改一下。
调整好launch.json配置后,直接通过debug形式运行项目,此时VSCode会唤醒Edge并尝试访问launch.json中配置的url,这里就是访问http://localhost:3002,为了让其正常访问,你还需要在命令行中启动项目(如:npm run dev、yarn dev之类的),项目启动后,Edge访问成功,VSCode就会基于你下的断点,停到相应的代码处,你就可以愉快的调试项目了,如下图:
项目启动流程
Vite项目会以项目根目录的index.html为项目入口,element-plus-admin也不例外,在index.html中,发现了它是使用了百度统计的代码,估计是统计有多少人使用了它的项目,代码如下:
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script>
var _hmt = _hmt || [];
// 百度统计代码
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?6f30ec463f12087163460a93581d2f3d";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</body>
果断注释掉百度统计相关的代码。
index.html中引入了/src/main.ts,标准的vite项目流程,看到main.ts的代码,主要是引入了各种组件:
const app = createApp(App)
direct(app)
app.use(ElementPlus)
app.use(router)
app.use(pinia)
app.component('SvgIcon', SvgIcon)
// 载入Icon系统 - 全局载入的形式
const ElIconsData = ElIcons as unknown as Array<() => Promise<typeof import('*.vue')>>
for (const iconName in ElIconsData) {
app.component(`ElIcon${iconName}`, ElIconsData[iconName])
}
app.mount('#app')
其中Element-plus是饿了么提供的CSS样式库(支持Vue3),router则是vue-router,用于实现页面的路由功能,而pinia则是新一代的状态管理工具(用于替代vuex)。Element-plus提供了一套Icon样式,要在项目中随意使用,需要将其全局载入,上述代码中,通过for循环的方式,将Icon全局载入。
主题样式的切换
接着看到App.vue中的代码,template通过ElconfigProvider标签包裹,该标签是Element-plus中用于提供国际化的标签,使用该标签包裹,页面中的文字内容便可以自动切换不同的语言,代码如下:
<template>
<!-- 国际化 https://segmentfault.com/a/1190000041239124 -->
<ElConfigProvider :locale='locale'>
<!-- vue-router 渲染标签 -->
<router-view />
</ElConfigProvider>
</template>
:
setup() {
changeThemeDefaultColor()
const { getSetting } = useLayoutStore()
// 重新获取主题色
const f = () => {
let themeArray = theme()
return getSetting.theme >= themeArray.length ? themeArray[0] : themeArray[getSetting.theme]
}
let themeStyle:Ref<ITheme> = ref(f())
// 监控变化
watch(() => getSetting.theme, () => themeStyle.value = f())
watch(() => getSetting.color.primary, () => themeStyle.value = f())
// 省略剩余代码...
上述代码中,通过theme函数获得主题数组,然后基于用户的选择,获得数组中相应主题的配色,完整的主题替换逻辑比较多,这里抽一个细节来剖析,整体原理是一致的。
通过f函数,获得如下结构的对象:
{
tagsActiveColor: '#fff',
tagsActiveBg: color.primary,
mainBg: '#f0f2f5',
sidebarColor: '#fff',
sidebarBg: '#001529',
sidebarChildrenBg: '#000c17',
sidebarActiveColor: '#fff',
sidebarActiveBg: color.primary,
sidebarActiveBorderRightBG: '#1890ff'
},
对象很被ref函数包裹,成为响应式数据,以对象中的tagsActiveColor属性为例,通过全局搜索,可以发现,它在layout-tags-active这个css标签中,代码如下:
.layout-tags-active {
background-color: v-bind(themeStyle.tagsActiveBg);
color: v-bind(themeStyle.tagsActiveColor);
}
而layout-tags-active会被使用在/src/layout/components/tags.vue中,代码如下:
<span