你好,我是 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 )

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

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

个人博客:https://kagol.github.io/blogs

小助手微信:opentiny-official

公众号:OpenTiny

Vue Vine:带给你全新的 Vue 书写体验!的更多相关文章

  1. Vue自带的过滤器

    gitHub地址:https://github.com/lily1010/vue_learn/tree/master/lesson05 一 过滤器写法 {{ message | Filter}} 二 ...

  2. 一天带你入门到放弃vue.js(三)

    自定义指令 在上面学习了自定义组件接下来看一下自定义指令 自己新建的标签赋予特殊功能的是组件,而指定是在标签上使用类似于属性,以v-name开头,v-on,v-if...是系统指令! v-是表示这是v ...

  3. 一天带你入门到放弃vue.js(二)

    接下来我们继续学习一天带你入门到放弃系列vue.js(二),如有问题请留言讨论! v-if index.html <div id="app"> <p v-if=& ...

  4. 一天带你入门到放弃vue.js(一)

    写在前面的话! 每个新的框架入手都会进行一些列的扯犊子!这里不多说那么多!简简单单说一下vue吧! Vue.js是目前三大框架(angular,vue,react)之一,是渐进式js框架,据说是摒弃了 ...

  5. 10.vue router 带参数跳转

    vue router 带参数跳转 发送:this.$router.push({path:'/news',query:{id:row.id}}) 接收:var id=this.$route.query. ...

  6. 一篇文章带你了解网页框架——Vue简单入门

    一篇文章带你了解网页框架--Vue简单入门 这篇文章将会介绍我们前端入门级别的框架--Vue的简单使用 如果你以后想从事后端程序员,又想要稍微了解前端框架知识,那么这篇文章或许可以给你带来帮助 温馨提 ...

  7. [vue]vue v-on事件绑定(原生修饰符+vue自带事件修饰符)

    preventDefault阻止默认行为和stopPropagation终止传递 event.preventDefault() 链接本来点了可以跳转, 如果注册preventDefault事件,则点了 ...

  8. 5分钟带你入门vuex(vue状态管理)

    如果你之前使用过vue.js,你一定知道在vue中各个组件之间传值的痛苦,在vue中我们可以使用vuex来保存我们需要管理的状态值,值一旦被修改,所有引用该值的地方就会自动更新,那么接下来我们就来学习 ...

  9. 前端MVC Vue2学习总结(二)——Vue的实例、生命周期与Vue脚手架(vue-cli)

    一.Vue的实例 1.1.创建一个 Vue 的实例 每个 Vue 应用都是通过 Vue 函数创建一个新的 Vue 实例开始的: var vm = new Vue({ // 选项 }) 虽然没有完全遵循 ...

  10. Vue学习【第一篇】:Vue初识与指令

    什么是Vue 什么是Vue Vue.js是一个渐进式JavaScript框架它是构建用户界面的JavaScript框架(让它自动生成js,css,html等) 渐进式:vue从小到控制页面中的一个变量 ...

随机推荐

  1. Spring扩展———自定义bean组件注解

    引言 Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制. Java 语言中的类.方法.变量.参数和包等都可以被标注.和 Javadoc 不同,Java ...

  2. 什么是浅拷贝和深拷贝,如何用 js 代码实现?

    〇.简介和对比 简介 浅拷贝:只复制原始对象的第一层属性值.   如果属性值是值类型,将直接复制值,本值和副本变更互不影响:   如果是引用数据类型,则复制内存地址,因此原始对象和新对象的属性指向相同 ...

  3. CAP 8.2 版本发布通告

    前言 今天我们很高兴宣布 CAP 发布 8.2 版本正式版,我们在这个版本中主要致力于对订阅着并行执行的特性提供支持,同时添加了对在订阅者中对消息头的控制行为. 下面,具体看一下我们新版本的功能吧. ...

  4. Css实现浏览滚动条效果

    Css实现浏览滚动条效果 前言 也是有大半个月没有更新文章了,大部分时间都在玩,然后就是入职的事.今天就更新一个小知识,刷抖音的时候看到的,感觉还不错. 属性介绍 关键属性animation-time ...

  5. [FLET] 01 可以拖动的方块

    from typing import List import flet from flet import ( Container, Draggable, DragTarget, Page, Row, ...

  6. TOPSIS模型原理以及代码实现

    TOPSIS 法是一种常用的组内综合评价方法,能充分利用原始数据的信息,其结果能精确地反映各评价方案之间的差距.下面我们来介绍具体步骤与代码实现 目录 问题提出 第一步:数据输入 1.如何从excel ...

  7. Mac Idea中获取application.properties的值,中文乱码

    设置idea配置 将Properties Files (*.properties)下的Default encoding for properties files设置为UTF-8,将Transparen ...

  8. yb课堂 vue里面的状态管理vuex 《四十》

    文档:https://vuex.vuejs.org/zh/ 在store/下index.js import Vue from 'vue' import Vuex from 'vuex' Vue.use ...

  9. Mysql的Innodb和MyISAM引擎的区别

    区别项 Innodb MyISAM  事务  支持  不支持 锁粒度  行锁,适合高并发 表锁,不适合高并发  是否默认  默认  非默认  支持外键  支持外键  不支持  适合场景  读写均衡,写 ...

  10. Redis巡检检查 redis-check-aof

    一.AOF1.AOF  是什么以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,R ...