Vue3.0实践:使用Vue3.0做JSX(TSX)风格的组件开发

论坛 期权论坛 编程之家     
选择匿名的用户   2021-5-30 09:14   38   0

前言

我日常工作都是使用React来做开发,但是我对React一直不是很满意,特别是在推出React Hooks以后。

不可否认React Hooks极大地方便了开发者,但是它又有非常多反直觉的地方,让我难以接受。所以在很长一段时间,我都在尝试寻找React的替代品,我尝试过不少别的前端框架,但都有各种各样的问题或限制。

在看到了Vue 3.0 Composition-API 的设计,确实有眼前一亮的感觉,它既保留了React Hooks的优点,又没有反复声明销毁的问题,而Vue一直都是支持JSX语法的,3.0对TypeScript的支持又非常好,所以我开始尝试用Vue + TSX来做开发。

Vue 3.0已经发布了alpha版本,可以通过以下命令来安装:

npm install vue@next --save

简单示例

先来看看用Vue3.0 + TSX写一个组件是什么什么样子的。

实现一个Input组件:

import { defineComponent } from 'vue';

interface InputProps {
  value: string;
  onChange: (value: string) => void;
}
const Input = defineComponent({
  setup(props: InputProps) {
    const handleChange = (event: KeyboardEvent) => {
      props.onChange(event.target.value);
    }

    return () => (
      <input value={props.value} onInput={handleChange} />
    )
  }
})

可以看到写法和React非常相似,和React不同的是,一些内部方法,例如 handleChange ,不会在每次渲染时重复定义,而是在 setup 这个准备阶段完成,最后返回一个“函数组件”。

这算是解决了React Hooks非常大的一个痛点,比React Hooks那种重复声明的方式要舒服多了。

Vue 3.0对TS做了一些增强,不需要像以前那样必须声明 props ,而是可以通过TS类型声明来完成。

这里的 defineComponent 没有太多实际用途,主要是为了实现让ts类型提示变得友好一点。

Babel插件

为了能让上面那段代码跑起来,还需要有一个Babel插件来转换上文中的JSX,Vue 3.0相比2.x有一些变化,不能再使用原来的vue-jsx插件。

我们都知道JSX(TSX)实际上是语法糖,例如在React中,这样一段代码:

const input = <input value="text" />

实际上会被babel插件转换为下面这行代码:

const input = React.createElement('input', { value: 'text' });

Vue 3.0也提供了一个对应 React.createElement 的方法 h 。但是这个 h 方法又和vue 2.0以及React都有一些不同。

例如这样一段代码:

<div class={['foo', 'bar']} style={{ margin: '10px' }} id="foo" onClick={foo} />

在vue2.0中会转换成这样:

h('div', {
  class: ['foo', 'bar'],
  style: { margin: '10px' }
  attrs: { id: 'foo' },
  on: { click: foo }
})

可以看到vue会将传入的属性做一个分类,会分为 class 、 style 、 attrs 、 on 等不同部分。这样做非常繁琐,也不好处理。

在vue 3.0中跟react更加相似,会转成这样:

h('div', {
  class: ['foo', 'bar'],
  style: { margin: '10px' }
  id: 'foo',
  onClick: foo
})

基本上是传入什么就是什么,没有做额外的处理。

当然和 React.createElement 相比也有一些区别:

  • 子节点不会作为以 children 这个名字在 props 中传入,而是通过 slots 去取,这个下文会做说明。
  • 多个子节点是以数组的形式传入,而不是像React那样作为分开的参数

所以只能自己动手来实现这个插件,我是在 babel-plugin-transform-react-jsx 的基础上修改的,并且自动注入了 h 方法。

实际使用

在上面的工作完成以后,我们可以真正开始做开发了。

渲染子节点

上文说到,子节点不会像React那样作为 children 这个 prop 传递,而是要通过 slots 去取:

例如实现一个Button组件

// button.tsx
import { defineComponent } from 'vue';
import './style.less';

interface ButtonProps {
  type: 'primary' | 'dashed' | 'link'
}
const Button = defineComponent({
  setup(props: ButtonProps, { slots }) {
    return () => (
      <button class={'btn', `btn-${props.type}`}>
        {slots.default()}
      </button>
    )
  }
})

export default Button;-

(Q(
йml(й(utQ(((gvr'
B@S>k>Kv{:vgń(āйй!(r"Gr/vYMc3>jn^Ё!j#IЁ!j77^c(Rr'jj^`ń*{:Gbj[(kvz@k&7rX1)Mcbjk"j(svgG>C6wXń(vbvvy)M`ń((((((zskc2XGB;jb@ń(l((幅(t?7Z~O^&0ZTr>Grj=7nZb"(k|G~Lj>V/'>c2Xbvgj3>r$幅&7幅j2[(rY[Gf*"zCg72;vgj*
k*>C6"ZW[77&3(GB;jń(Ё}}()l(}}(幅(t)?~O^>36?G>C6B8~O"Cr>3**
ng*
7R(fvgG>C6cr'"jGbjXg)Mcjk"jMcrre)Lr'C"3>C6v=)O&3jZkg7X7B >k"(fG"
zsr{:j~vWb['(>Y)Mc*{h*J0rr'2(!tbv{?jjR2cbZj乍A$zsfC"zjtYB;
kb"Gj['(~r/:Zń{ńi)M`QM`;D
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP