👏👏👏厉害了 Vue Vine !Vue 组件还能这样写!!!前段时间闲来无事,刷刷视频,偶然间看到了关于 Vue
前言
哈喽大家好!我是 嘟老板。前段时间闲来无事,刷刷视频,偶然间看到了关于 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 体验的开发者量身打造的。
如您对文章内容有任何疑问或想深入讨论,欢迎评论区留下您的问题和见解。
技术简而不凡,创新生生不息。我是 嘟老板,咱们下期再会。
往期干货
转载自:https://juejin.cn/post/7410616997007245352