用 jsx 写 vue 组件

前言

我们平常写 vue 的组件时,一般都是用的是模版,这种方式看起来比较简洁,而且 vue 作者也推荐使用这个方式,但是这种方式也有一些它的弊端,例如模版调试麻烦,或者在一些场景下模版描述可能没那么简单和方便。

下面我们要讲的是如何在 vue 里面写 jsx,知道 react 的人应该都知道 jsx,jsx 的一个特性就是非常灵活,虽然有的人觉得 jsx 很丑陋,把逻辑都写到模版的感觉,但萝卜青菜各有所爱,适合自己适合团队的就是最好的。

在使用 jsx 之前我们需要安装一个 babel 插件(babel-plugin-transform-vue-jsx)
安装方式:

1

2

3

4

5

6

7

npm install\

  babel-plugin-syntax-jsx\

  babel-plugin-transform-vue-jsx\

  babel-helper-vue-jsx-merge-props\

  babel-preset-es2015\

  --save-dev

然后再.babelrc 里面添加:

1

2

3

4

5

{

  "presets": ["es2015"],

  "plugins": ["transform-vue-jsx"]

}

接着我们就可以愉快地在 vue 里面编写 jsx 了。
Test.vue

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<script>

export default {

    props: ['onClick', 'isShow'],

    data() {

        return {

            test: 123

        };

    },

    render() {

        return (

            <div class="test" onClick={ this.onClick }>

                { this.test }

                { this.isShow + '' }

            </div>

        );

    }

}

</script>

可以看到我们把 jsx 写在了 render 方法里面,render 方法是 vue2.0 才支持的,用来提供对虚拟 DOM 的支持,也就是说只有 vue2.0 才支持 jsx 语法转换。

这里要注意的一点是 vue 里面编写 jsx 和在 react 里面的 jsx 语法还是有一点不一样的。
一下是一段覆盖大部分语法的 vue jsx 代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

render (h) {

  return (

    <div

      // normal attributes or component props.

      id="foo"

      // DOM properties are prefixed with `domProps`

      domPropsInnerHTML="bar"

      // event listeners are prefixed with `on` or `nativeOn`

      onClick={this.clickHandler}

      nativeOnClick={this.nativeClickHandler}

      // other special top-level properties

      class={{ foo: true, bar: false }}

      style={{ color: 'red', fontSize: '14px' }}

      key="key"

      ref="ref"

      // assign the `ref` is used on elements/components with v-for

      refInFor

      slot="slot">

    </div>

  )

}

可以看到 DOM 属性要加 domProps 前缀,但这里 lass 和 style 却不需要,因为这两个是特殊的模块,而且 react 的 class 用的是 className,vue 却用的 class。事件监听是以 “on” 或者 “nativeOn” 为开始。

实际上 vue2.0 的模版最后都会被编译为 render 方法,所以模版声明的组件和 jsx 声明的组件最后都是一样的。
上面的 jsx 最后会被编译成下面这样:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

render (h) {

  return h('div', {

    // Component props

    props: {

      msg: 'hi'

    },

    // normal HTML attributes

    attrs: {

      id: 'foo'

    },

    // DOM props

    domProps: {

      innerHTML: 'bar'

    },

    // Event handlers are nested under "on", though

    // modifiers such as in v-on:keyup.enter are not

    // supported. You'll have to manually check the

    // keyCode in the handler instead.

    on: {

      click: this.clickHandler

    },

    // For components only. Allows you to listen to

    // native events, rather than events emitted from

    // the component using vm.$emit.

    nativeOn: {

      click: this.nativeClickHandler

    },

    // class is a special module, same API as `v-bind:class`

    class: {

      foo: true,

      bar: false

    },

    // style is also same as `v-bind:style`

    style: {

      color: 'red',

      fontSize: '14px'

    },

    // other special top-level properties

    key: 'key',

    ref: 'ref',

    // assign the `ref` is used on elements/components with v-for

    refInFor: true,

    slot: 'slot'

  })

}

这也意味着两种形式的组件是可以相互引用的。

有时候我们难免会在模版里引入 jsx 编写的 vue 组件或者在 jsx 编写的 vue 组件里引入模版组件,这里还是有些需要注意的事项:
1. 在模版里面引入 jsx 的组件,可以通过 components 引用,另外 props 的编写从驼峰式改为连接符:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<template>

  <div class="wrapper">

    <Test :on-click="clickHandler" :is-show="show"></Test>

  </div>

</template>

<script>

import Test from './Test.vue';

export default {

  name: 'hello',

  components: {

    Test

  },

  data() {

    return {

      msg: 'Welcome to Your Vue.js App',

      show: true

    };

  },

  methods: {

    clickHandler(){

      this.show = !this.show;

    }

  }

};

</script>

2. 在 jsx 里面引入 vue 模版组件,这里没有什么要注意的,除了连接符式的属性要转换成驼峰式,还有一个需要注意的是指令,如果用了 jsx,那么内置的指令都不会生效(除了 v-show),好在内置指令大部分都可以用 jsx 描述。那么自定义指令要怎么用呢?
自定义指令可以使用 “v-name={value}” 语法,如果要支持指令参数和 modifier 可以用 “v-name={{ value, modifier: true }}” 语法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

<script>

import Vue from 'vue';

Vue.directive('my-bold', {

  inserted: function (el) {

    el.style.fontWeight = 900;

  }

})

export default {

    props: ['onClick', 'isShow'],

    data() {

        return {

            test: 123

        };

    },

    methods: {

        afterLeave() {

            console.log('afterLeave')

        }

    },

    render() {

        const directives = [

            { name: 'my-bold', value: 666, modifiers: { abc: true } }

        ];

        return (

            <transition onAfterLeave={this.afterLeave} name="fade">

                <div class="test" onClick={this.onClick} v-show={ this.isShow } v-my-bold>

                    {this.test}

                    {this.isShow + ''}

                </div>

            </transition>

        );

    }

}

</script>

<style>

.fade-enter-active, .fade-leave-active {

  transition: opacity .5s

}

.fade-enter, .fade-leave-to {

  opacity: 0

}

</style>

我们还可以用原生 vnode 的数据格式使用自定义指令:

1

2

3

4

5

6

const directives = [

  { name: 'my-dir', value: 123, modifiers: { abc: true } }

]

return <div {...{ directives }}/>

扩展

如果有人觉得在 vue 组件里面要写 data,props,computed 和 methods 不够优雅,可以参考下这个插件 vue-class-component,它能让你使用 ES6 的 class 和 ES7 的装饰器编写 vue 组件。

相关链接:
babel-plugin-transform-vue-jsx

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

短暂又灿烂的

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值