你好,我是 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. kooder安装及本地搜索git仓库代码

    kooder安装及本地搜索git仓库代码 需求背景:如果需要从Git代码仓库查询某个配置项做批量的更新替换,如果一个一个找不合适且容易遗漏,需要借助第三方工具来模糊查询来实现. 1.下载及文档地址ht ...

  2. 多个if查询及case when 语句

    concat( if(t2.`flow_flag`=1 and t1.`field_source`= '06','支付宝账号1',''),if(t2.`flow_flag`=0 and t1.`fie ...

  3. window10 yapi安装 swagger配置 及 Error: getaddrinfo ENOTFOUND yapi.demo.qunar.com解决

    node下载https://nodejs.org/download/release/v12.18.3/mongodb下载https://www.mongodb.com/try/download/ent ...

  4. 安装 Nuxt.js 的步骤和注意事项

    title: 安装 Nuxt.js 的步骤和注意事项 date: 2024/6/17 updated: 2024/6/17 author: cmdragon excerpt: Nuxt.js在Vue. ...

  5. 简单的解释下什么是CNAME

    今天在用阿里云的安全防护给接口域名做web应用防火墙,需要配置cname,原来有用到过但是一直没去了解过,只知道怎么用今天搜了一下看看下面是原文,白话文好理解分享一下. 什么是CNAME?先简单的说下 ...

  6. DHorse v1.5.1 发布,基于 k8s 的发布平台

    版本说明 新增特性 支持k8s的v1.30.x版本: 优化特性 优化回滚功能: 修复注册来源的回滚问题: 新增和修改应用时校验应用名: 升级kubernetes-client至v6.13.0: 调整部 ...

  7. 简约-Markdown教程

    ##注意 * 两个元素之间最好有空行 * 利用\来转义 我是一级标题 ==== 我是二级标题 ---- #我是一级标题 ##我是二级标题 ##<center>标题居中显示</cent ...

  8. nginx web服务器应用(虚拟主机 日志 rewrite location https)

    Nginx介绍 Nginx是一个开源的,支持高性能,高并发的www服务和代理服务软件,因具有高并发(特别是静态资源),占用系统资源少等特性,且功能丰富而逐渐流行起来.功能应用上,Nginx不但是一个优 ...

  9. Linux gpio子系统:gpio_direction_output 与 gpio_set_value的区别

    Linux gpio子系统:gpio_direction_output 与 gpio_set_value的区别 背景 最近改驱动程序,看到驱动代码中既有gpio_direction_output也有g ...

  10. 如何免费在 arm 官网上下载合适的手册

    背景 有时候搞底层配置的时候(尤其是uboot),需要查阅文档. 这里介绍如何在arm 官网进行查找下载,这样就可以不用去 CSDN 了. 实际上CSDN上的一些文档就是这样下载下来二次收费的,强烈谴 ...