theme: github
总览
最近完成了一个公司内部的网站,开发使用 vue3
和 vite
的全新的 web 框架 vitesse, 作者是 vue 的核心开发成员 antfu.
vitesse
除了包含 vite
启动快, 热更新快,修改配置文件不需要重启等优点外,还加入了一系列便于开发的插件,包含自动按需引入,路由自动生成,layout
系统布局,还有 多语言 i18n
和 markdown
的支持。
除了采用 TypeScript
, 还采用了 vue3 更简洁的 <script setup>
语法。
在样式方面,采用了功能类优先(utility-first)的 windicss, 通过类名来使用内置的 css 样式,可以通过类名解决大部分的样式问题,包含 css 伪类, 暗黑模式变体等, 样式中的计量单位都采用的是经过响应式处理的 rem
。
有了这些深得开发者心的功能,整体的开发体验是比较好的。
开发工具上, chrome 插件市场有新的 beta 版的 Vue.js devtools
,需要卸载掉原来的重装,新插件同时支持 vue2 和 vue3, vscode 插件由原来的 Vetur
切换为 Volar
。
GitHub 上提供了整体框架的 demo,想要上手来试一试 vue3
和 vite
,或者是要搭建自己的网站的的话可以 clone
这个 demo. 地址:vitesse-with-antd, 欢迎 star~
下面会对使用的整体框架进行分析,以及实现的功能点的介绍。
框架分析
按需引入
组件按需引入
组件的按需引用使用了 vite-plugin-components ,会让指定目录下的文件按需加载,默认的路径是 src/components
, 在项目中可直接使用组件,省去了在每个 vue 文件中单独的 import 。
<template>
<div>
<HelloWorld msg="Hello Vue 3.0 + Vite" />
</div>
</template>
<script setup lang="ts">
</script>
会自动转化成这样
<template>
<div>
<HelloWorld msg="Hello Vue 3.0 + Vite" />
</div>
</template>
<script setup lang="ts">
import HelloWorld from './src/components/HelloWorld.vue'
</script>
图标按需引入
vite-plugin-icons 插件支持引入 iconify
中的所有图标,在 vite-plugin-components
中一起使用也可以不用单独 import 就直接使用 iconify
提供的图标, 支持的图标可以访问 icones 查找。
UI 库的按需引入
以 ant-design-vue
为例,同时还支持 UI 组件库的按需引入, 可直接使用 UI 组件, 这里匹配了以 A 开头的组件从 node-modules
中 ant-design-vue
中引入。
以上提到的所有按需引入的功能在 vite 中的配置如下:
import ViteComponents from "vite-plugin-components";
import ViteIcons, { ViteIconsResolver } from "vite-plugin-icons";
export default defineConfig({
plugins: [
// https://github.com/antfu/vite-plugin-components
ViteComponents({
// auto import icons
customComponentResolvers: [
// https://github.com/antfu/vite-plugin-icons
ViteIconsResolver({
componentPrefix: "",
}),
(name: string) => {
if (name.startsWith('A'))
return { importName: name.slice(1), path: 'ant-design-vue' }
},
],
}),
// https://github.com/antfu/vite-plugin-icons
ViteIcons(),
]
})
路由和 layout
路由和 layout
的处理交给了 2 个插件,vite-plugin-vue-layouts 和 vite-plugin-pages
vite-plugin-pages
可以默认将 pages
路径下的 vue 文件自动生成路由,layouts
可以通过每个 pages 目录下 vue 文件中的定义来指定使用哪一个 layout
, 比如下面的定义让当前的路由加载 src/layouts/empty
文件。
<route lang="yaml">
meta:
layout: empty
</route>
关于路由的创建,在以前的 vue-router 3.x
版本中,我们通过 mode
来控制是使用 history
还是 hash
模式的路由,在 4.x 中是通过 createWebHistory
, createWebHashHistory
来区分
import { createRouter, createWebHistory } from "vue-router";
import { setupLayouts } from "layouts-generated";
import generatedRoutes from "pages-generated";
const routes = setupLayouts(generatedRoutes);
const router = createRouter({
history: createWebHistory(),
routes,
});
app.use(router);
TypeScript 相关
web-dev 中全局使用了 TypeScript, 局部使用的类型定义我们可以在单个 vue 文件中单独定义,也可以单独使用 d.ts
文件,一些全局通用的类型定义我们放在了根路径的 types 文件夹下面。
TypeScript的配置文件 tsconfig.json 中支持定义全局类型的路径配置
"typeRoots": ["./node_modules/@types/", "./types"]
在 typeRoots
下的 d.ts
文件中定义的类型为全局类型,可以在全局无需额外引入直接使用这些类型。
但需要注意如果 d.ts
中含有了 导入,导出,全局类型定义就会失效,详情可参考这个 issues。
对于 vue3
中 props
的类型定义,不能直接使用创建时的类型定义,而需要使用 vue3
提供的类型 PropType
来指明构造函数。顺便说明一下 vue3
中是使用 defineProps
来定义 props
。
import type { PropType } from 'vue'
const props = defineProps({
list: {
type: Array as PropType<MaterialConfig[]>,
default: []
}
})
const { list } = toRefs(props)
hooks 的使用
目前对于 hooks
的使用主要是抽取了一些可复用的方法,和 composition-api
的设计思想相符,侧重于实现同一功能模块的逻辑放在一个 hooks
中,一个 hooks
中我们可以 export
出多个变量或者是方法。
参照 vue 给出的图,其实我们可以理解为一个 hooks
实现的逻辑可以代表右图中的一个色块
一个常见的 hooks
的结构如下
import { ref } from 'vue'
export const useMyHooks = (params) => {
const data = ref([])
const functionData = () => {
// Do Sth
}
return {
data,
functionData
}
}
写 hooks
的时候比较容易遇到的问题
(1) hooks
中的逻辑要尽量与外部逻辑解耦,抽取逻辑之后提供的方法要便于使用和减少相同逻辑的重复实现
(2) 每次执行 useMyHooks
其实变量都是会开辟一个单独的存储空间,如果你有一些共享变量请在 useMyHooks
外部声明。
根据目前的使用体验来看,就逻辑复用这一点,其实 hooks
能实现的功能 mixin
应该也能实现。
只是 hooks
在使用时用到的每一个方法时用户有意识的去选择的,比如我需要使用 hooks
提供的常量A,方法B,那么我需要有意识的手动去引入这些我要复用的内容,这样方法和变量的来源也就更清晰。
mixin
的自由度更大,只要是用了这个 mixin
就默认所有方法都隐式的引入了,导致版本迭代和多人开发之后可能不好去维护。
功能点实现
UI 库主题色修改
ant-design-vue
的默认皮肤是蓝色,在设计上我们需要把默认的皮肤色切换成紫色,更符合我们想要的风格。
antd
的官网只提供了在 webpack
中修改主题色 less
变量的方法,在 vite
中也提供了 css
的预处理器,我们可以通过如下配置来实现主题色的修改:
// vite.config.js
export default defineConfig({
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
modifyVars: {
'primary-color': '#7546c9'
},
},
},
},
})
暗夜模式在线切换
在 antd 中修改主题色,只需要修改一个 primary-color
变量,修改暗夜模式则需要改变一系列的变量值,并且需要支持在线切换功能。我们使用到了 vite-plugin-theme
提供的 antdDarkThemePlugin
。
大概实现原理是先拿到 antd
中的 dark.less
变量 -> antdDarkThemePlugin
根据这些变量值生成一个暗夜模式的 css 文件 -> 样式文件插入到 html 中。
用户切换暗黑模式,修改body中的 [data-theme=dark]
, 从而使用暗黑模式的样式。
import { viteThemePlugin, antdDarkThemePlugin } from 'vite-plugin-theme';
import { getLessVars } from 'antd-theme-generator';
const antdDarkVars = getLessVars('./node_modules/ant-design-vue/lib/style/themes/dark.less')
export default defineConfig({
plugins:[
viteThemePlugin({
colorVariables: [''], // 需要给一个初始值才能正常使用功能
}),
antdDarkThemePlugin({
darkModifyVars: {
'primary-color': '#7546c9', // 暗黑模式也是支持紫色的皮肤
...antdDarkVars
}
})
]
})
切换模式的方法我们抽取到了 hooks
中, 使用到了 @vueuse/core
提供的 useDark
, useToggle
, 是为了修改 storage
中的暗黑模式变量,让用户切换操作的模式切换得到缓存,同时 tailwindcss
的暗黑模式变体应该也是从这里取值。
切换时还需要重新加载一下 css
文件,生产模式下模式切换才能生效。
import { useDark, useToggle } from '@vueuse/core';
import { loadDarkThemeCss } from 'vite-plugin-theme/es/client';
export function useToggleDark() {
const isDark = useDark()
const toggleDarkDefault = useToggle(isDark);
const toggleAntd = async () => {
if(isDark.value) {
await loadDarkThemeCss();
document.body.setAttribute('data-theme','dark')
} else {
document.body.setAttribute('data-theme','light')
}
}
toggleAntd() // 初始时先执行一次
const toggleDark = async () => {
toggleDarkDefault();
// 修改antd为暗黑模式
toggleAntd()
};
return { isDark, toggleDark }
}
全局的进入动画效果
windicss
支持自定义类名,在 web-dev 中我们使用 vue-vben-admin 中的实现,定义了 enter-x
, enter-y
, -enter-x
,-enter-y
来定义了一些渐进的动画效果,实现使用了类名的子元素的动画效果。
windicss 的提供插件 @windicss/animations
里也有很多定义好的动画效果方案,可以使用这些动画效果来提升用户体验。
Emoji 的引入
为了增加平台的互动性,我们模拟 Github
实现了评论和表情回复功能
想要挑选自己想要使用的 emoji, 可以从这个网站中查找: listemoji
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: Jenniferyingni 原文链接:https://juejin.im/post/7006266497899167751