我说我是做外包的肯定没人信,给你看看外包程序员瞎几把搞的热情!

前排提醒:vue-jsx和react-jsx相差甚远,前者基本就是玩具生产链。很多想利用JS语言特性的骚套路基本上都不好实现,而且感觉奇奇怪怪的。所以你的生产环境,千万不要乱试vue-jsx这种搭配,够简单就无所谓了…

开局搭环境

要什么环境,vue-cli就行了,又不是什么大项目大团队需要约定webpack

装插件

1
2
3
4
5
6
npm install\
babel-plugin-syntax-jsx\
babel-plugin-transform-vue-jsx\
babel-helper-vue-jsx-merge-props\
babel-preset-env\
--save-dev

.babelrc主要是这个

1
2
3
{
"plugins": ["transform-vue-jsx"]
}

因为其他的东西基本上都会有,主要是要添加这个东西。至此。

正文

没错的这文章本来是属于实践的又不是教程。还是要讲一下自己的体验的。

渲染元素

感想最大的就是这个,Vue在使用jsx的时候很迷,最明显的感觉怪怪的地方有两个:

  1. 它并不是哪个位置的方法都会有createElement函数的。
  2. classes形式的组件找不到render函数!

其实关于vue与jsx,tsx我是做了很多次尝试

首先第一种,常规的就是一个object对象,然后通过Vue.component()方法构建。这种可以说是我现在看来最没坑的一种形式了。平常的话还是像*.vue里的script标签一样,直接export打头开写,<template />渲染部分移入了render()函数。

1
2
3
4
5
export default {
render(/* h / createElement */) {
return <h1> Hello World </h1>
}
}

这种情况的结构与单文件的代码结构是相差无几的,不过换了种渲染方式而已,而且render()函数可以做一些与渲染相关的子方法,比如条件渲染之类的。所以总的来说:

优点:

  1. 顺手,完全单文件中的script标签
  2. 没了

缺点:

  1. 这么搞肯定容易眼花啊!
  2. 不好维护,不直观

第二种,如果是这种结构的tsx的话,与jsx同理。

第三种,class形式的组件,需要vue-class-component插件。然后可以写出类似于这样的东西

1
2
3
4
5
6
7
8
9
10
<template>
<h1>Hello World</h1>
</template>

<script>
@Component
export default class MainClass extends Vue {

}
</script>

乍一看你还以为这是Angular呢!Vue在这方面真的是取长补短的典范。对这种的话,就比传统的方式要好多了,首先全局变量或者data()函数返回值变得特别清晰;再者是原来methods存放的函数可以以class形式的函数来写,那computeds怎么办?直接用get/set关键词,太方便了有没有!

好了关键的来了…

1
2
3
4
5
6
7
8
9
@Component
export default class MainClass extends Vue {

get render() {
return <h1> Hello World </h1>
}
}

// render or template not defind.

这是什么鬼,可能是我操作不当,但是不加get的话不会注入createElement,但是这又是个render()函数,本来不需要加什么前缀的。但是不加有感觉会不会把render()函数识别成是传统写法中存在于methods的函数。

官方在自动注入这方面有一个栗子的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Vue.component('jsx-example', {
render () { // h will be injected
return <div id="foo">bar</div>
},
myMethod: function () { // h will not be injected
return <div id="foo">bar</div>
},
someOtherMethod: () => { // h will not be injected
return <div id="foo">bar</div>
}
})

@Component
class App extends Vue {
get computed () { // h will be injected
return <div id="foo">bar</div>
}
}

其中h就是createElement的别称,而且在jsx的话一般都是不用写的。所以这个函数注入的就有点迷了,不过根据亲测,在methods定义的函数是有h的,可能最后也会在render函数里调用吧。

后来我尝试两三个星期之后,我就放弃了classes形式的jsx写法,等过几天又有经历的时候再继续尝试。目前自己在用的两套写法:例如Hyper那种的classes形式的vue+ts,传统方式的jsx。

还有就是,对于vue来说,有时候要时常关心h函数有没有被注入,这是一件挺不愉快的事情的。比如说你写了一个渲染函数,然后运行的时候发现惨了凉了函数刚好在渲染范围之外(比如把函数直接分离出去,并不存在于render函数中),这是不运行的,那怎么做?

1
function example(h, args) {}

就是这样的!必须在render函数中调用而且h必须是第一个参数!不然不是报错就是不渲染!

函数式组件

可能是解决刚刚提到的“经常找不到自动注入函数”的困扰吧。多了一个这么个东西。

1
2
3
4
5
export default {
functional: true,

render() {}
}

一个关键词functional声明这个位置是个函数式组件。函数式可以直接看做一个能主动注入h的函数,因为它没有状态的,甚至是个RBQ,用完就丢(死无全尸,在组件树是看不到的,依托父组件生存)。同时最大的特点也就是可复用。想想react一个正常语法函数就能搞定的事情…

有利有弊,这种组件因为没状态,用过就丢,所以它需要的数据全靠传递。也就是单向数据,从父组件 -> props -> 处理 -> 输出 -> 销毁一条龙。很简单就是把它看做是render里面的一个函数就行,本意也是如此。

所以这种组件就两个接收参数:负责渲染的h,负责上下文context。各种所需的乱七八糟都在context中,比如全程最重要的props。而且多数组件的参数都有调整:

  • props:提供 props 的对象
  • children: VNode 子节点的数组
  • slots: slots 对象
  • data:传递给组件的 data 对象
  • parent:对父组件的引用
  • listeners: (2.3.0+) 一个包含了组件上所注册的 v-on 侦听器的对象。这只是一个指向 data.on 的别名。
  • injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。

JSX的一些小区别

因为我还没怎么体验过react,所以就只有一个关于dom书写的区别。在react-jsx中,class可是会被识别成关键词的,所以做样式的class要写成className,但在vue-jsx是不需要的。可能一开始这么做没想过后面竟然还有class-component的需求吧。

vue现在大多奇技淫巧都是基于社区的各种babel插件,并不像react两个官方库皇帝级别支持,也不像angular自从诞生时期就声明“劳资要用TypeScript作为开发语言,你不用也得用ES6形式!”

vue-jsx这种写法我现在也会用,搞点小的就直接这么写,而且这么做实际上思路会清晰一点——像在写一个应用而不是写一个网页,你只需要把dom相关的字段脑里想成一个字符串或者一个子函数就行了。(只是老遇到一些乱七八糟的问题)

angularangular.js至今还是两种东西,不要乱了)