前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >⚠️ Vue 3 TSX

⚠️ Vue 3 TSX

作者头像
Innei
发布2021-12-28 10:58:02
7550
发布2021-12-28 10:58:02
举报
文章被收录于专栏:静之森

本篇基于 Vue 3.0.7, vite 2.1.2 编写,由于 Vue 与 vite 改动较大,以最新版本为准,本文仅供参考。

写本篇文章主要是为了记录在正式使用 Vue 3 + vite 2 投入开发中遇到的一些问题,不适合没有任何 Vue 开发经验的同学阅读。本文中将会运用到 Vue 3 的 Composition api,vue-router@next。

首先,我的项目是基于 vite 2 架基的,同时使用了 PostCSS + Tailwind 2 CSS。UI Framework 使用了国外的 PrimeVue。初始化的过程不再讲述了。

Router 与 TSX

首先是,Vue Router 的使用。和 Vue 2 的 Router 并没有什么比较大的区别。

不同的是,Vue Router 3 使用 VueRouter 的默认导出来创建一个实例,而 Vue Router 4 使用 createRouter 来创建实例。与 Vue 一致,Vue Router 也摒弃了 class 的写法,全面转向函数式编程(Functional Programming)。(注:Vue 2 使用 Vue Router 3, Vue 3 使用 Vue Router 4)

tsx

代码语言:javascript
复制
1// Vue router 3
2import VueRouter from 'vue-router'
3const router = new VueRouter({
4  routes,
5})
6// Vue router 4
7import {
8  createRouter,
9  createWebHashHistory,
10} from 'vue-router'
11const router = createRouter({
12  history: createWebHashHistory(),
13  routes,
14})

COPY

Vue Router 支持给每个路由(Route)添加一个 Meta 对象,存储在 Meta 对象中的值会在当前 Router 实例中取得。同时,Vue 3 原生支持了 JSX(大概只是比上代好一点点???),为此,我们也可以像 React 那样操作。

比如,我使用 Routes 来构建一个侧边菜单,当然为了简单,侧边菜单至多只有两层。

tsx

代码语言:javascript
复制
1interface MenuModel {
2  title: string
3  path: string
4  icon: any
5  subItems?: Array<MenuModel>
6  hasParent: boolean
7  fullPath: string
8  query?: any
9}

COPY

以上是我的菜单需要的结构,而如何将 icon 存储下来,在 render 函数直接使用呢。如果是 React,我们可以这样写。

代码语言:javascript
复制
1icon: JSX.Element

COPY

然后直接使用 {menu.icon} 就行了。在 Vue3 中,如果使用 JSX,同样可以这样操作。在 Routes 中稍加修改。在 Meta 中,直接传入一个 JSX.Element

tsx

代码语言:javascript
复制
1import dashboardFilled from '@iconify-icons/ant-design/dashboard-filled'
2import { InlineIcon as Icon } from '@iconify/vue'
3const routeForMenu: Array<RouteRecordNormalized> = [
4  {
5    path: '/dashboard',
6    component: DashBoardView,
7    name: 'dashboard',
8    meta: { title: '仪表盘', icon: <Icon icon={dashboardFilled} /> },
9  },
10  // ...
11]

COPY

同样的在 Sidebar.tsx 中。

tsx

代码语言:javascript
复制
1export const Sidebar = defineComponent({
2  setup() {
3    return () => <div>
4      // ....
5    	{menu.icon}
6    </div>
7  }
8})

COPY

当然这种用法只限于 JSX/TSX。使用 Vue 的模板的话,就会渲染一个 VNode 对象了。

Vue Template
Vue Template

Vue Template

因为 JSX.Element 只是一个 Object,在 Vue 模板中只会判断 components 注册了没有,而不会关心这个 Object 是不是 VNode。而 JSX 中则会去判断是 VNode 则 render。

如果想在 Vue 模板中使用外部的 JSX,那么就需要去 components 注册一下就行了。

tsx

代码语言:javascript
复制
1// icon.tsx
2export const Icon = <FontAwesomeIcon icon={faAlignJustify} />

COPY

vue

代码语言:javascript
复制
1<template>
2  <Icon />
3</template>
4
5<script lang="ts">
6import { Icon } from './icon'
7import { defineComponent } from 'vue'
8
9export default defineComponent({
10  components: { Icon },
11  
12})
13</script>

COPY

上面的是没注册的,下面的是注册的
上面的是没注册的,下面的是注册的

上面的是没注册的,下面的是注册的

Setup 与 TSX

在 Vue 2 中,data 中的属性以 _$ 打头的会被忽略,从而无法使用响应式流。在 Vue 3 中,data 还是和 Vue 2 一样无法使用,在 setup 函数中亦如此。但是官网文档没写不让用。

tsx

代码语言:javascript
复制
1<template>
2  <div class="w-10 m-auto">
3
4    <p>
5      <p>{{ $$a ?? 0}}</p>
6      <Button @click="$$a++">$$a++</Button>
7    </p>
8  
9    <p>
10      <p>{{ a }}</p>
11      <Button @click="a++">a++</Button>
12    </p>
13  </div>
14</template>
15
16<script lang="ts">
17import { defineComponent, ref } from 'vue'
18import Button from 'primevue/button'
19export default defineComponent({
20  components: { Button },
21  setup() {
22    const $$a = ref(0)
23    const $a = ref(0)
24    const a = ref(0)
25    return {
26      $$a,
27      a,
28    }
29  },
30})
31</script>

COPY

https://cdn.jsdelivr.net/gh/Innei/img-bed@master/1616825107021.png
https://cdn.jsdelivr.net/gh/Innei/img-bed@master/1616825107021.png

但在 TSX 中,你完全可以这样写。

tsx

代码语言:javascript
复制
1export const PlaceHolderView = defineComponent({
2  setup() {
3    const router = useRouter()
4
5    const $$a = ref(0)
6    return () => (
7        <p>
8          {$$a.value}
9          <p>
10            <Button onClick={() => $$a.value++}>$$a++</Button>
11          </p>
12        </p>
13    )
14  },
15})

COPY

Slot 与 TSX

在 Vue 中有个 v-slot 的东西,在 React 中则有个 Children,当然 Vue 的 slots 做的比 React 多得多。而在 React 中除了传递 Children,还可以通过 props 传递 React.reactElement。React 更加灵活。

在 Vue 3 TSX 写法中,v-slots.default 等于 React 的 children。

tsx

代码语言:javascript
复制
1const Children = defineComponent({
2  setup() {
3    return () => <p>Children</p>
4  },
5})
6
7const Parent = defineComponent({
8  setup({}, { slots }) {
9    return () => (
10      <div class="">
11        <p>Parent</p>
12        {slots.default?.()}
13      </div>
14    )
15  },
16})
17
18const RootView = defineComponent({
19  setup() {
20    return () => (
21        <p>
22          <Parent>
23            <Children></Children>
24          </Parent>
25        </p>
26    )
27  },
28})

COPY

https://cdn.jsdelivr.net/gh/Innei/img-bed@master/1616826392910.png
https://cdn.jsdelivr.net/gh/Innei/img-bed@master/1616826392910.png

slots 位于 setup 的第二个参数中,获取当前组件所有的 slots,并且是一个函数,需要 call 一下。

tsx

代码语言:javascript
复制
1<Parent v-slots={{default: () => <Children />}}></Parent>

COPY

也可以用上面的方式传入。

v-slots 对 TSX 的方式不太友好,建议还是使用 React 的方式编写。通过传递 Props 来渲染子组件。

Emit 与 TSX

在 Vue 模板中,我们会用 @ 去监听一个事件。在 React 的 JSX 中用 on 前缀来监听一个事件,如果是自定义事件,一般会定义一个新的函数。而在 Vue 中使用 emit 函数去发起一个事件。但是在 TSX 如何去监听呢。答案也是 on,你甚至可以不用手写一个函数。

tsx

代码语言:javascript
复制
1const Parent = defineComponent({
2  setup({}, { slots, emit }) {
3    onMounted(() => {
4      emit('mount', 1)
5    })
6    return () => (
7      null
8    )
9  },
10})
11
12const RootView = defineComponent({
13  setup({}, ctx) {
14    return () => (
15      <Parent
16        onMount={(val) => {
17          console.log('mount', val)
18        }}
19        >
20      </Parent>
21    )
22  },
23})

COPY

https://cdn.jsdelivr.net/gh/Innei/img-bed@master/1616827452713.png
https://cdn.jsdelivr.net/gh/Innei/img-bed@master/1616827452713.png

显然,onMount 这个 Props 是不存在的,我们也没有定义,但是在 Parent 中 emit 的事件为 mount。就得到了这个 Props。这个过程是发生在编译阶段的,所以在不同的架手架行为可能不同。甚至说随时可能 breaking change,对 ts 的支持也很不友好,充满了红线。所以不建议使用。

以上就是近几天在开发过程中遇到的全部问题了,但是肯定远远不止这些。那么就先告一段落了。

Reference:

vue-jsx-next

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Router 与 TSX
  • Setup 与 TSX
  • Slot 与 TSX
  • Emit 与 TSX
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档