This template should help get you started developing with Vue 3 and Typescript in Vite.
VSCode + Vetur. Make sure to enable vetur.experimental.templateInterpolationService
in settings!
<script setup>
is a feature that is currently in RFC stage. To get proper IDE support for the syntax, use Volar instead of Vetur (and disable Vetur).
Since TypeScript cannot handle type information for .vue
imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in .vue
imports (for example to get props validation when using manual h(...)
calls), you can use the following:
Run Volar: Switch TS Plugin on/off
from VSCode command palette.
- Install and add
@vuedx/typescript-plugin-vue
to the plugins section intsconfig.json
- Delete
src/shims-vue.d.ts
as it is no longer needed to provide module info to Typescript - Open
src/main.ts
in VSCode - Open the VSCode command palette
- Search and run "Select TypeScript version" -> "Use workspace version"
最近完成了一个公司内部的网站,开发使用 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 查找。
以 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
的处理交给了 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);
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
的使用主要是抽取了一些可复用的方法,和 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
就默认所有方法都隐式的引入了,导致版本迭代和多人开发之后可能不好去维护。
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
里也有很多定义好的动画效果方案,可以使用这些动画效果来提升用户体验。
为了增加平台的互动性,我们模拟 Github
实现了评论和表情回复功能
想要挑选自己想要使用的 emoji, 可以从这个网站中查找: listemoji