前言
哈喽大家好!我是 嘟老板。前段时间闲来无事,刷刷视频,偶然间看到了关于 Vue Vine
的介绍,感觉还挺有意思的,就花点时间研究了一下,写篇文章分享分享。如果你还不知道 Vue Vine
是啥,建议来看一看。
阅读本文您将收获:
介绍
什么是 Vue Vine
Vue Vine
提供一种新的 Vue
组件定义形式,允许我们在一个文件内定义多个组件,且可继续沿用 <template>
模板的视图定义方式。
Vue Vine
规定以 .vine.ts
为扩展名,称作 VFC
,完美支持 TypeScript
语法。
Vue Vine
目前仅支持Vue3 + vite + TypeScript
环境,可与常规的Vue3
组件结合使用。
如何使用 Vue Vine
基础语法介绍
更多详情见 官网
定义组件
Vue Vine
以函数的形式定义组件,返回一个 vine
标记的模板字符串。
function MyComponent() {
return vine`<div>Hello World</div>`
}
注意:
在vine
模板字符串中禁止使用表达式插值,如:
function MyComponent() { const userName = ref('Vine') // IDE 中无法正常显示模板部分的高亮 return vine`<span>{{ `hello ${userName}` }}</span>` }
props
Vue Vine
提供两种方式定义 props:
- 组件函数定义形参
props
,且为第一个参数,并为其编写TypeScript
对象字面量类型,包含要定义的props
。
import { SomeExternalType } from './path/to/somewhere'
function MyComponent(props: {
foo: SomeExternalType
bar?: number // 可选属性
baz: boolean
}) { ... }
-
vineProp
宏定义。必须指定
prop
的类型,或提供一个默认值用于推导类型。const foo = vineProp<string>()
vineProp
第一个参数是prop
验证器,可选。如验证title prop
是否已#
号开头。const title = vineProp<string>(value => value.startsWith('#'))
vineProp
提供vineProp.optional
用于定义可选prop
。const foo = vineProp.optional<string>()
vineProp
提供vineProp.withDefault
用于定义prop
默认值。const foo = vineProp.withDefault('bar')
宏
vineProp
上面已经讲过了,这里不重复了,说说其他的。
-
vineEmits
与
Vue
的defineEmits
宏用法一致。const emits = vineEmits<{ update: [foo: string, bar: number] }>() emits('update', 'foo', 1)
-
vineExpose
与
Vue
的defineExpose
宏用法一致。vineExpose({ foo: 'foo' })
-
vineSlots
与
Vue
的defineSlots
宏用法一致。
const slots = vineSlots<{
default(props: { msg: string }): any
}>()
-
vineOptions
支持定义以下
Vue
组件选项:name
:组件名称。inheritAttrs
:是否启用组件的默认属性穿透行为,同Vue
的 inheritAttrs 属性。
vineOptions({ name: 'MyComponent', inheritAttrs: false })
-
vineStyle
用于定义组件样式。替代
Vue SFC
的<style>
部分。vineStyle.scoped
用于定义局部样式,同<style scoped></style>
。vineStyle.scoped(` .title { font-size: 20px; font-weight: 700; margin-bottom: 6px; } `)
还可以通过以下方式,指定不同的
css
处理语言。vineStyle(scss` .foo { color: red; .bar { background: yellow; } } `)
测试项目
- vite 创建测试项目
vue-vine-explorer
npm create vite@latest vue-vine-explorer
- 切换至
vue-vine-explorer
目录,安装依赖。
cd vue-vine-explorer
pnpm i
- 安装
Vue Vine
。
pnpm add vue-vine -D
- vite.config.ts 中添加
vite
插件。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { VineVitePlugin as vueVine } from 'vue-vine/vite'
export default defineConfig({
plugins: [vue(), vueVine()],
})
tsconfig.json
中引入macros
类型,避免TS
类型检查报错。
{
"compilerOptions": {
/* ... */
"types": ["vue-vine/macros"]
}
}
- (可选)提升开发体验,建议安装
VSCode
插件 -vue vine
- 创建测试组件
删除模板工程的预置代码,在 components
下新增 test-vine.vine.ts
文件,用来定义 vine
组件
import { computed, ref } from "vue";
export function TestComp1() {
const title = vineProp<string>((value) => value.startsWith("#"));
const author = vineProp.withDefault("Anonymous");
const bgColor = ref("red");
const emits = vineEmits<{
toggleBgColor: [color: string];
}>();
const toggleBgColor = () => {
bgColor.value = bgColor.value === "red" ? "blue" : "red";
emits("toggleBgColor", bgColor.value);
};
vineStyle.scoped(`
.title {
font-size: 20px;
font-weight: 700;
margin-bottom: 6px;
}
.playground {
padding: 6px;
background-color: v-bind(bgColor);
border-radius: 6px;
width: 100%;
text-align: left;
}
`);
return vine`
<header>
<div class="playground">
<div class="title">
{{ title }}
</div>
<div>{{ author }} </div>
<button @click="toggleBgColor()">
点击切换背景色
</button>
</div>
</header>
`;
}
export function TestComp2(props: { text: string }) {
const footerText = computed(() => `footer: ${props.text}`);
vineStyle.scoped(`
.footer {
width: 100%;
height: 65px;
position: fixed;
bottom: 0;
left: 0;
font-size: 20px;
font-weight: 700;
text-align: center;
line-height: 65px;
color: black;
background-color: white;
}
`);
return vine`
<div class="footer">
{{ footerText }}
</div>
`;
}
- 渲染测试组件,验证效果
<script setup lang="ts">
import { TestComp1, TestComp2 } from './components/vine-test.vine'
function handleToggleBgColor(color) {
console.log(`测试组件1切换背景色为${color}`)
}
</script>
<template>
<TestComp1 title="#测试组件1" author="dulaoban" @toggle-bg-color="handleToggleBgColor" />
<TestComp2 text="测试组件2" />
</template>
周边生态
Vue Vine
生态还在持续建设中...,现有的生态包括:
Vite
插件 - vue-vine/vite
在 Vite
配置文件 vite.config.ts
中应用:
import { VineVitePlugin } from 'vue-vine/vite'
export default defineConfig({
plugins: [
// ...其他插件
VineVitePlugin()
],
})
VScode
插件 - vue vine
提供Vue Vine
语法高亮和语言特性。
Eslint parser - @vue-vine/eslint-parser
自定义 Eslint
解析器,帮助 Eslint
识别 Vue Vine
代码结构。
安装:pnpm i -D @vue-vine/eslint-parser
TypeScript
检查器 - vue-vine-tsc
检查 Vue Vine
文件 .vine.ts
类型,与 vue-tsc
兼容,也可以用它来检查 .vue
文件。
安装:pnpm i -D vue-vine-tsc
可在 npm script
中使用:
{
"scripts": {
"build": "vue-vine-tsc -b && vite build",
}
}
TypeScript
声明文件 - vue-vine/macros
帮助使用宏时获得智能提示。
在 TypeScript
配置文件 tsconfig.json
中配置类型:
{
"compilerOptions": {
"types": ["vue-vine/macros"]
}
}
CLI
- create-vue-vine
帮助我们快速创建 Vine
模板工程。
# 未全局安装 create-vue-vine
pnpx create-vue-vine project-name
# 已全局安装 create-vue-vine
create-vue-vine project-name
截至发文,本人测试
CLI
还不可用,估计还在建设中...
实现原理
Vue Vine 项目组成
主要分为以下几部分:
编译器(complier
)
Vue Vine 编译器将用 Vue Vine 语法编写的代码转换为标准的 Vue
组件。包括解析组件结构、属性、事件,并生成相应的渲染函数。
语言服务器(language-server
)
借助语言服务器,Vue Vine 支持代码补全、类型检查和语法高亮等功能。使得编写代码时能够获得更好的开发体验。
Vite
插件(vite-plugin
)
Vue Vine 集成 Vite
插件,便于构建和热重载。能够在开发过程中快速查看更改效果,提高开发效率。
TypeScript
支持
Vue Vine 充分支持 TypeScript,可以利用类型系统来减少错误,提高代码的可维护性。
Eslint
解析器(eslint-parser
)
Vue Vine 借助 Eslint
解析器,可解析代码、检测错误和支持自定义规则,提高代码质量和一致性。
其他,如 CLI、文档、测试等
编译器介绍
Vue Vine 语法的正确运行,离不开编译器。
想必小伙伴们已经了解了 Vue Vine 的核心思路 - 通过编译器,将 Vue Vine 定义的函数组件转换成 Vue
渲染函数,后面的事情就是 Vue
帮忙干了。
Vue Vine 编译器处理过程大致如下:
以以下 app 入口组件为例,简单说明下:
export function App() {
return vine`<router-view></router-view>`
}
看看程序过程中,每一步都是怎样的效果:
- 首先创建必要的上下文对象,包括 编译上下文(
createComplierCtx
) 和 Vine 文件上下文(createVineFileCtx
),上下文对象中包含编译过程中需要的状态和配置等。
其中,编译上下文 对象:
Vine 文件上下文 对象:
其中,
root
属性存储babel
解析后的ast
结构。fileMagicCode
属性存储编译器转换后的源代码字符串信息。vineCompFns
属性存储 Vine 组件函数。styleDefine
属性存储组件内定义的样式。- ...
-
解析
TypeScript
文件,借助babel
的能力(babelParse
),将代码转换为ast
。以下是组件定义函数转为
ast
后的结构:
-
验证(
doValidateVIne
)和分析(doAnalyuzeVine
),验证组件定义函数是否符合Vue Vine
的规范;分析AST
,包括导入、函数组件及相关宏等。 -
转换(
transformFile
),核心逻辑,对.vine.ts
文件进行转换处理,包括导入排序合并、模板编译、组件函数转换,宏定义属性/css 变量/插槽/... 的处理等等,并将Vine
组件函数转换为 IIFE(立即执行函数),以创建独立的作用域,最终将转换后的源代码更新到vineFileCtx.fileMagicCode
。其中模板编译借助了Vue
提供的@vue/compiler-dom
能力,完美继承Vue
模板组件的静态优化性能。
- 编译样式(
compileVineStyle
),借助postcss
及相关插件对组件定义的样式进行编译。
function compileVineStyle(compilerCtx: VineCompilerCtx, params) {
// ...
const result = postcss(postcssPlugins).process(source, postCSSOptions)
// ...
}
- 生成
Vue
渲染函数,由Vue
处理,Vue Vine
输出包含 生成Vue
渲染函数所需的所有组件和逻辑 的源码字符串。
结语
不得不说,Vue Vine
为 Vue
组件定义开了一扇窗,允许我们以另一种可行的方式开发组件。
个人拙见, Vue Vine
短时间内很难大范围应用,毕竟 Vue
原本的 SFC
(单文件组件)深入人心;若偏爱函数式组件,Vue
也支持 render
函数和 JSX
,要说 Vue Vine
的优势在哪,大概就是既支持函数式开发,又能借助 Vue
框架对于 SFC
的优化能力。
每一个框架或者设计的出现,必然有其受众,我想 Vue Vine
可能是为那些想要用 Vue
语法,又想要 React
体验的开发者量身打造的。
如您对文章内容有任何疑问或想深入讨论,欢迎评论区留下您的问题和见解。
技术简而不凡,创新生生不息。我是 嘟老板,咱们下期再会。
往期干货
- 不知道项目的 Eslint 用了哪些配置?@eslint/config-inspector 帮你搞定
- Eslint 迁移至 v9.x 全过程 + 汇总好用的 Eslint 插件
- TypeORM 知多少?来看看 Node 服务端如何基于 TypeORM 封装 BaseService
- 还在考虑 node 服务端如何管理环境变量?来试试 node-config
- 还在纠结 node 服务端缓存怎么做?来试试 Redis
- 还在好奇 node 后端登录流程怎么做?进来聊聊吧
- 搞定 TS 装饰器,让你写 Node 接口更轻松
- 一文带你了解多数企业系统都在用的 RBAC 权限管理策略
- 还不会搭建 Node 服务?一文带你了解如何用 express + ts 搞定后端