写点什么

Vue Vine:带给你全新的 Vue 书写体验!

作者:OpenTiny社区
  • 2024-08-12
    广东
  • 本文字数:2961 字

    阅读完需:约 10 分钟

Vue Vine:带给你全新的 Vue 书写体验!

本文由体验技术团队 Kagol 原创。


上个月和 TinyVue 的小伙伴们一起参加了 VueConf 24 大会,有幸认识沈青川大佬,并了解了他的 Vue Vine 项目,Vue Vine 让你可以在一个文件中通过函数方式定义多个 Vue 组件,同时可以使用所有 Vue 的模板特性。


听起来是不是很酷!


之前我写过 SFC,也写过 JSX 的 Vue 组件,两者各有缺点。


  • SFC 顾名思义单文件组件,只能在一个文件中定义一个组件,如果有几个相关的组件想放一起,对不起,不行!你只能创建一个文件夹,把一堆相关组件一个一个文件放里面。

  • JSX 虽然能通过函数方式定义组件,并且可以在一个文件中定义多个相关的组件,但是没法享受 Vue 模板语法,以及模板编译相关的优化。


Vue Vine 通过把两者的优点集合在一起,创造了一种全新的 Vue 组件书写方式。


我们来一起体验下吧!

搭建 Vue Vine 环境

假设你已经有一个 Vite + Vue3 项目。


只需要以下步骤,就可以搭建 Vue Vine 环境:


  • 安装 vue-vine 依赖:npm i -D vue-vine

  • vite.config.ts 中导入 VineVitePlugin 插件


import { VineVitePlugin } from 'vue-vine/vite'
export default defineConfig({ plugins: [ // ...其他插件 VineVitePlugin() ],})
复制代码


  • 安装 VSCode 扩展:Vue Vine



  • tsconfig.json 中配置 macro 类型


{  "compilerOptions": {    "types": ["vue-vine/macros"]  }}
复制代码

愉快地体验下 Vue Vine 吧

我们创建一个 MyComponent.vine.ts 文件,写入以下内容:


export function MyComponent() {  return vine`    <div>Hello World</div>  `}
复制代码


然后在 App.vue 中引入这个组件。


<script setup lang="ts">import { MyComponent } from './components/MyComponent.vine'</script>
<template> <MyComponent /></template>
复制代码


可以看到显示了一个 Hello World



再定义一个组件,并引入 TinyVue 的组件试试。


MyComponent.vine.ts 文件,写入以下内容:


+ import { TinyButton, TinyAlert } from '@opentiny/vue'
export function MyComponent() { return vine` <div>Hello World</div> `}
+ export function ComponentDemo() {+ return vine`+ <tiny-button type="primary">确定</tiny-button>+ <tiny-alert description="这是一段描述"></tiny-alert>+ `+ }
复制代码


在 App.vue 中引入这个组件。


<script setup lang="ts">- import { MyComponent } from './components/MyComponent.vine'+ import { MyComponent, ComponentDemo } from './components/MyComponent.vine'</script>
<template> <MyComponent />+ <ComponentDemo /></template>
复制代码


用 Vue Vine 方式写一个简单的分页组件

之前在我的博客写过一篇文章:手把手教你使用 Vue / React / Angular 三大框架开发 Pagination 分页组件


我们现在用 Vue Vine 方式重写一遍。


创建 Pagination.vine.ts 文件,写入以下内容:


import { ref } from 'vue'
// 演示组件 props 定义export function Pagination(props: { defaultCurrent: number, defaultPageSize: number, total: number,}) { // 演示 emit 事件定义 const emit = vineEmits<{ change: [current: number] }>()
// 当前页码 const current = ref(props.defaultCurrent) // 总页码 const totalPage = ref(Math.ceil(props.total / props.defaultPageSize)) // 设置当前页码 const setPage = (page: number) => { if (page < 1) return if (page > totalPage.value) return current.value = page emit('change', current.value) }
return vine` <div class="x-pagination"> <Button class="btn-prev" @click="setPage(current - 1)">&lt;</Button> {{ current }} <Button class="btn-next" @click="setPage(current + 1)">></Button> </div> `}
// 自定义 Button 组件(演示 <slot></slot> 插槽)export function Button() { const emit = vineEmits<{ click: [] }>()
return vine` <button type="button" @click="emit('click')"><slot></slot></button> `}
复制代码


再定义一个 List 列表组件,用来模拟分页数据。


List.vine.ts


import { ref, watch } from 'vue'
export function List(props: { dataSource: { id: number name: string }[]}) { const lists = ref(props.dataSource)
watch(() => props.dataSource, (newVal) => { lists.value = newVal })
return vine` <ul> <li v-for="list in lists" :key="list.id"> {{ list.name }} </li> </ul> `}
复制代码


在 App.vue 中使用 Pagination 和 List 组件。


<script setup lang="ts">+ import { ref } from 'vue'+ import chunk from 'lodash-es/chunk'import { MyComponent, ComponentDemo } from './components/MyComponent.vine'+ import { Pagination } from './Pagination.vine'+ import { List } from './List.vine'++ // 数据源+ const lists = [+   { id: 1, name: 'Curtis' },+   { id: 2, name: 'Cutler' },+   { id: 3, name: 'Cynthia' },+   { id: 4, name: 'Cyril' },+   { id: 5, name: 'Cyrus' },+   { id: 6, name: 'Dagmar' },+   { id: 7, name: 'Dahl' },+   { id: 8, name: 'Dahlia' },+   { id: 9, name: 'Dailey' },+   { id: 10, name: 'Daine' },+ ]++ // 列表当前展示的数据+ const dataList = ref<{+   id: number+   name: string+ }[]>([])++ const defaultCurrent = 1+ const defaultPageSize = 3+ const total = lists.length++ // 设置当前列表数据+ const setList = (current: number, pageSize: number) => {+   dataList.value = chunk(lists, pageSize)[current - 1]+ }++ setList(defaultCurrent, defaultPageSize)++ const onChange = (current: number) => {+   setList(current, defaultPageSize)+ }</script>
<template> <MyComponent /> <ComponentDemo />+ <List :data-source="dataList" />+ <Pagination :default-current="defaultCurrent" :default-page-size="defaultPageSize" :total="total" @change="onChange" /></template>
复制代码


效果如下:



这里有几个需要注意的点:


  • 定义组件 props 的方式,组件函数只有一个唯一的 props 参数,可以定义 props 的类型,和定义 TypeScript 类型一样


export function Pagination(props: {  defaultCurrent: number,  defaultPageSize: number,  total: number,}) { ...}
复制代码


  • 定义 emit 的方式,通过 vineEmits 宏而不是 defineEmits 宏进行定义


const emit = vineEmits<{  change: [current: number]}>()
emit('change', current.value)
复制代码


更多用法参考 Vue Vine 官网:https://vue-vine.dev/


你觉得 Vue Vine 风格写 Vue 组件体验如何呢?欢迎在评论区留言讨论。

联系我们

GitHub:https://github.com/opentiny/tiny-vue(欢迎 Star ⭐)


OpenTiny 官网:https://opentiny.design/tiny-vue


B 站:https://space.bilibili.com/15284299


小助手微信:opentiny-official


公众号:OpenTiny


(温馨提示:OpenTiny CCF开源创新大赛也在持续报名中,欢迎大家一起报名参赛,赢取10W奖金

用户头像

OpenTiny 企业级web前端开发解决方案 2023-06-06 加入

官网:opentiny.design 我们是华为云的 OpenTiny 开源社区,会定期为大家分享一些团队内部成员的技术文章或华为云社区优质博文,涉及领域主要涵盖了前端、后台的技术等。

评论

发布
暂无评论
Vue Vine:带给你全新的 Vue 书写体验!_开源_OpenTiny社区_InfoQ写作社区