前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深度使用 Vue Vine 四天之后,谈谈我的使用体验

深度使用 Vue Vine 四天之后,谈谈我的使用体验

作者头像
用户6901603
发布2024-07-12 14:04:21
1700
发布2024-07-12 14:04:21
举报
文章被收录于专栏:不知非攻

当我在 Vue Conf 大会中看到 Vue Vine 这种新的开发方式之后,我非常的激动。因为我确实非常喜欢这种语法。因此在周日当天,我就通过自己的摸索跑通了一个 demo,并写了一篇文章跟大家介绍它。

又一次变革,Vue 更彻底的拥抱了函数式

这篇文章传播有点广泛,甚至还被 Vue Vine 作者看到。当然对于这种新的开发方式,从评论区中能够感受到,许多人并不是那么欢迎它的出现。所以我决定更加深入的使用它之后,再重新写一篇文章,结合它与 React 的差异,跟大家分享一下深入使用之后的真实感受。

在这几天时间里,我使用 Vue Vine,写了一个常用技术点覆盖面还算齐全的小网站。目前长这个样子。当然这个只是我本地的一个 demo,主要用于我自己学习和练习使用。

接下来,我就以完成的这个网站为例,给大家介绍 Vue Vine 的深度使用体验。也进一步跟大家分享为什么我会如此喜欢它。在这个项目中,我做的事情主要包括:

  • 0、一些常用的学习案例,counter/todoList/tab
  • 1、自定义组件
  • 2、集成 vue router 并已正常使用
  • 3、集成 pinia 并已正常使用
  • 4、集成 tailwindcss
  • 5、自定义 hook
  • 6、一个文件中定义多个组件
  • 7、普通接口请求
  • 8、列表请求
  • 9、分页列表请求
  • 10、其他三方工具的兼容尝试,例如 mdx

一、并不顺利的体验过程

在完成项目的过程中我遇到了很多问题,因此这几天我与 Vue Vine 的开发团队在 issue 上进行了大量的沟通。Vue Vine 的版本也从 v0.1.5 发到了 v0.1.8。很显然,他们确实有非常认真的在对待这个方案。对 issue 的反馈比较及时,调整也比较快。

从最开始的 Vue-vine 插件因为崩溃问题完全不能用,到现在我感觉可以勉强支撑起日常开发,只过去了几天的时间。

由于开发团队需要专门针对 .vine.ts 的文件后缀做兼容处理,因此可能除了代码编译之外,vue-vine 插件的开发也是一个比较大的工作量。可能目前依然存在一些开发体验不够好的情况

例如,不支持如下写法

代码语言:javascript
复制
export default function Button() {}

仅支持这种写法

代码语言:javascript
复制
function Button() {}

export default Button

或者目前在 vine 模板中,还不支持给目标代码添加注释的快捷键等一些细节问题。

但是相信未来很快就会得到解决。

二、配置一些独特的 snippet

我们先来简单看一下,在 vue vine 中,声明一个组件的方式,与声明一个函数的方式一模一样,只不过返回的内容必须是 vine 模板。

代码语言:javascript
复制
import {ref} from 'vue'

function HelloWorld() {
  const count = ref(0)

  return vine`
    <button @click="count++">
      counter++
    </button>
    <div>{{count}}</div>
  `
}

export default HelloWorld

有的同学不太喜欢写 return vine,因此,我们可以在 ts 的 snippet 配置文件中,新增如下字段

代码语言:javascript
复制
"return vine": {
    "prefix": "vine",
    "body": [
  "return vine`",
  "  $1",
  "`"
],
  "description": "return vine"
},

这样,就可以有如下快捷输入

道友们可以用同样的方式定义其他更多的快捷指令。我们还可以通过如下方式在 settings.json 中配置 snippet 提示的顺序

代码语言:javascript
复制
"editor.snippetSuggestions": "inline",

三、自定义组件

在我的付费小册《React19》中,我已经自定义好了一个 Button 组件。

一模一样的功能,一模一样的入参,一模一样的样式,我使用 vue-vine 重新封装之后的完整代码如下

代码语言:javascript
复制
import {twMerge} from 'tailwind-merge'
import clsx from 'clsx'

function Button(props: {
  class?: string,
  primary?: boolean,
  danger?: boolean,
  sm?: boolean,
  lg?: boolean,
  signal?: boolean,
  success?: boolean,
}) {
  const {class: className, primary, danger, sm, lg, signal, success} = props
  const base = 'rounded-md border border-transparent font-medium cursor-pointer transition relative'

  // type
  const normal = 'bg-gray-100 hover:bg-gray-200'

  // size
  const md = 'text-xs py-2 px-4'

  const cls = twMerge(clsx(base, normal, md, {
    // type
    ['bg-blue-500 text-white hover:bg-blue-600']: primary,
    ['bg-red-500 text-white hover:bg-red-600']: danger,
    ['bg-green-500 text-white hover:bg-green-600']: success,
    ['text-sky-500 bg-white border border-sky-300 hover:bg-sky-50']: signal,

    // size
    ['text-xs py-1.5 px-3']: sm,
    ['text-lg py-2 px-6']: lg,
  }, className))

  return vine`
    <button :class="cls">
      <slot />
      <span v-if="signal" class="absolute flex h-3 w-3 right-[-5px] top-[-5px]">
        <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"></span>
        <span class="relative inline-flex rounded-full h-3 w-3 bg-sky-500"></span>
      </span>
    </button> 
  `
}

export default Button

与 React 相比,中间的逻辑几乎一模一样,我们主要关心一下返回的差别。

代码语言:javascript
复制
return (
  <button className={cls} {...other}>
    {props.children}
    {signal && (
      <span className="absolute flex h-3 w-3 right-[-5px] top-[-5px]">
        <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"></span>
        <span className="relative inline-flex rounded-full h-3 w-3 bg-sky-500"></span>
      </span>
    )}
  </button>
)

很明显, vue-vine 会更简洁一些。这里的差别主要有

1、Vue 支持属性透传到内部元素的父节点,因此我们可以不用像 React 那样使用 {...other} 展开所有的其他属性。

2、 可以使用 slot 插槽取代 {props.children}

3、 样式名在 React 中需要写成 className,这是历史遗留的问题,在 Vue 中可以直接写成本来的样子 class

4、 可以使用指令 v-if 代替如下逻辑判断

代码语言:javascript
复制
{signal && (<span>...</span>)}

很显然,从结果上来看,vue-vine 更加的简洁。但是这其中包含了许多的小的知识点,站在新人的角度上来说,理解成本会偏高一些。而 React 则只遵循一个概念,那就是把 jsx 当成表达式使用,并在这个标准之下写出来的代码理解起来一致性会更强一些,灵活性也会更高一点。

许多 Vue 的三方 UI 库依然使用 JSX 来封装,实际上就是看中了 JSX 理念下的灵活性


四、异步编程

先来看一下我实现的功能的演示效果。我支持了初始化加载列表和点击按钮更新列表的能力。

抛开底层机制不谈,vue-vine 在开发方式上基本上与 React 保持了一致的开发体验。因此,异步编程的逻辑上也基本上是一致.

初始化请求的逻辑,都在组件首次渲染完成之后执行

代码语言:javascript
复制
// vue-vine
onMounted(() => {
  api().then(res => {
    loading.value = false
    data.value = res
  })
})
代码语言:javascript
复制
// react
useEffect(() => {
  // api 请求
}, [])

更新的逻辑都在点击事件或者其他交互事件的回调中执行。

但是 React 19 在这个基础之上更进一步,新提出了 use + Suspense 的使用方式。代码的整体简洁度又提高了一个档次。

代码语言:javascript
复制
// React 19
export default function Demo01() {
  const [promise, update] = useState(getMessage)

  function __handler() {
    update(getMessage())
  }

  return (
    <>
      <div className='text-right mb-4'>
        <Button onClick={__handler}>更新数据</Button>
      </div>
      <Suspense fallback={<Skeleton />}>
        <Content promise={promise} />
      </Suspense>
    </>
  )
}

但是在 Vue-vine 中不支持这套机制,那应该怎么办呢?

好在几年前,我曾经在公众号发表过一篇付费文章 React 哲学,文章中提到的开关思维,可以让 vue-vine 的代码实现结果拥有不亚于 React use 的简洁性。

代码如下,注意观察细节

代码语言:javascript
复制
<!--vue-vine-->
function Dashboard() {
  const {loading, data} = useFetch(fetchUsersApi)

  return vine`
    <div class="w-[700px] mx-auto mt-10">
      <Button @click="loading=true" :disabled="loading">更新列表</Button>
      <Skeleton v-if="loading" class="mt-4" />

      <template v-else v-for="item in data?.results">
        <ListItem  :data="item" />
      </template>
    </div>
  `
}

我们可以把 .value 等操作,通过自定义 hook,封装到底层去,眼不见心不烦。封装好自定义 hook 之后,就把他当成一个共用的,长期的,稳定的公共 api 使用,未来在应用层的页面,则直接在 template 中使用 ref 定义的状态。

这样,我们的应用层页面中,大多数时候就看不见 .value 的使用了。注意看按钮的点击逻辑

很显然,我在 React 哲学中提到的开关思维,非常契合 vue-vine,它比在 React 中使用更简洁,更能大放异彩。


五、分页列表

分页列表是一个比较复杂的逻辑。但是我们依然可以使用开关思维把他的代码处理成非常简单的结果。

注意看我的演示效果,我使用加载更多的按钮充当分页加载的执行时机。

我在底层封装了一个共用方法 usePagination 用于处理状态的定义和接口请求的逻辑。然后在应用层直接使用

你需要注意观察的是,loading 是使用 ref 定义的状态,对应初始化的 UI 变化。incrementing 则对应加载更多时的 UI 变化。在应用层,我们可以直接在点击回调中 @click,修改他们的值,就能轻松的完成完整的逻辑。

代码语言:javascript
复制
function Dashboard() {
  const {loading, incrementing, data} = usePagination(fetchUsersApi)

  return vine`
    <div class="w-full max-w-[500px] mx-auto mt-10">
      <Button @click="loading=true" :disabled="loading">更新列表</Button>
      <Skeleton v-if="loading" class="my-4" />

      <template v-else v-for="item in data?.results">
        <ListItem  :data="item" />
      </template>

      <Skeleton v-if="incrementing" class="my-4" />
      <div v-if="!incrementing" class="flex justify-center">
        <Button @click="incrementing=true" signal>点击加载更多</Button>
      </div>
    </div>
  `
}

六、我们可能不再需要 Pinia 了

当然,vue3 中也可以不需要 pinia,只是 vue-vine 改成的函数式的语法中,这种倾向会更明显,也更自然

这将是 vue-vine 语法变化后,一个比较重要的倾向。当你需要将状态保存在全局时,我们可以很自然的在一个单独的 ts 文件中,定义 ref

例如,我定义一个名为 useCounter.ts 的模块

代码语言:javascript
复制
export const count = ref(0)

export function increment() {
  count.value++
}

然后在需要的组件中引入并使用即可

代码语言:javascript
复制
import {count, increment} from './useCounter'

function Home() {
  return vine`
    <button @click="increment">count++</button>
    <div class="text-green-600">
      {{count}}
    </div>
  `
}

export default Home

此时的 count 是响应性的,所有组件,不管任何层级,都能通过同样的方式引入和使用,他是全局共享的。

因此,我们只需要合理的把 useCounter.ts 放到合适的位置,就可以非常轻松的替代 pinia 的作用。最关键的是,这样的方式非常的简洁,理解成本也非常低。

这也将是 vue-vine 与 React 在应用层面最大的差别。当 react 开发者还在苦苦思索哪一个状态管理库是最佳实践时,vue-vine 开发可以用最简单最直白的方式做到同样的事情。


总结

不管你对于 vue-vine 语法长得那么像 react 是何种看法,但是我们得相信的是,它确实有自己独特的魅力。

vue-vine 目前的完成度也非常高。我尝试过大多数常用的能力和生态都能够成功接入。

深度使用几天之后,我的总体感受就是非常舒服,它和 react 有高度一致的开发体验。因为 vue-vine 彻底拥抱函数式的原因,我也非常认可 vue 往这个方向转变。对于老手来说,他大多数时候比 react 拥有更简洁的代码结果。我相信这种方式一定会得到许多 vue 和 react 开发者的喜爱。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 这波能反杀 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、并不顺利的体验过程
  • 二、配置一些独特的 snippet
  • 三、自定义组件
  • 四、异步编程
  • 五、分页列表
  • 六、我们可能不再需要 Pinia 了
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档