怎么在Vue中写jsx语法,以及render函数

4,374 阅读1分钟

前言

最近遇到一个项目,是对element-ui进行了二次封装,做了一些自己的组件库,其中很多实现都是render函数配合template模板实现的,还有就是表单这块是一块比较复杂的业务逻辑,里面用到了jsx语法,我也抽时间研究了jsx在vue中怎么使用,所以记录下自己写的demo,后面好进行查漏补缺。

主要内容

demo的主要结构如下

image.png

  • Hello.vue
# Hello.vue
<script>
export default {
  name: "Hello",
  data() {
    return {
      str: "hello组件",
    };
  },
  props: {
    address: {
      type: String,
      default() {
        return "这是地址";
      },
    },
  },
  render() {
    return (
      <div>
        <span>{this.str}</span>
        <h1>具名插槽</h1>
        {/* 相当于声明了一个test的具名插槽 */}
        {this.$slots.test}
        <h1>作用域插槽</h1>
        {/* 相当于声明了一个person的具名插槽,并传值,即作用域插槽 */}
        {this.$scopedSlots.person({ name: 'john', age: 65 })}
      </div>
    );
  },
};
</script>

<style scoped></style>
  • world.js
# world.js
export default {
  data() {
    return {
      msg: "world组件",
      detail: {
        address: "杭州",
        salary: "10k",
      },
    };
  },
  /* eslint-disable */
  render() {
    return (
      <div>
        <div>{this.msg}</div>
        <div>{this.$slots.demo}</div>
        <div>{this.$scopedSlots.data(this.detail)}</div>
      </div>
    );
  },
};
  • demo.js
# demo.js
export default {
  props: {
    value: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  data() {
    return {
      msg: "demo2",
    };
  },
  render() {
    const { msg, value } = this;
    return (
      <div>
        <input type="text" v-model={msg} />
        <input type="text" v-model={value.name} />
        <input type="text" v-model={value.address} />
      </div>
    );
  },
};
  • App.vue
<!--<template>
  <div id="app">
    <div>{{ myProp }}</div>
    <slot>默认插槽</slot>
    <slot name="otherSlot" v-bind:msg="msg">其他插槽</slot>
    <div ref="hahaha" v-if="isShow">{{ msg }}</div>
    <div>
      <button @click="change">点击</button>
    </div>
    <Hello>
      <template v-slot:test>
        <div>插槽</div>
        <div>插槽</div>
      </template>
      <template v-slot:person="{ name, age }">
        我叫{{ name }},今年{{ age }}岁
      </template>
    </Hello>
    <world>
      <template v-slot:demo> 这是world中的demo </template>
      <template v-slot:data="detail">
        {{ detail.address }}
        {{ detail.salary }}
      </template>
    </world>
  </div>
</template>-->

<script>
import Hello from "./components/Hello";
import World from "./components/world";
import Demo from "./components/demo";
export default {
  name: "App",
  components: { Hello, World, Demo },
  props: {
    myProp: {
      type: String,
    },
  },
  data() {
    return {
      isShow: false,
      msg: "what this is",
      $str: "这是以$符开头的key",
      demo2: {
        name: "demo",
        address: "demo2 address",
      },
    };
  },
  created() {},
  methods: {
    test() {
      console.log("this is test method");
    },
    change() {
      this.isShow = true;
      this.msg = "值被我改变了";
    },
  },
  render() {
    const { msg, isShow, demo2 } = this;
    return (
      <div id="app">
        <div>{msg}</div> <br />
        <slot>默认插槽</slot>
        <slot name="otherSlot" msg={msg}>
          其他插槽
        </slot>
        {/* jsx中没有v-if和v-for,但是可以通过三元表达式和map实现,具体可以看vue文档 */}
        {isShow ? <div>{msg}</div> : <div>show false</div>}
        <div>
          {/* 如果没有从this中结构出来,就必须指定this */}
          <button onClick={this.change}>点击</button>
        </div>
        {/* 子组件中如果声明了插槽,在jsx中必须这么使用 */}
        <Hello
          slots={{
            test: () => {
              return <div>test插槽</div>;
            },
          }}
          scopedSlots={{
            person: (props) => {
              return (
                <div>
                  我叫{props.name},今年{props.age}岁
                </div>
              );
            },
          }}
        />
        <world
          slots={{
            demo: () => {
              return <div>这是world中的demo</div>;
            },
          }}
          // 这里也可以结构赋值使用,具体可以看vue文档
          scopedSlots={{
            data: ({ address, salary }) => {
              return (
                <div>
                  {address}
                  {salary}
                </div>
              );
            },
          }}
        />
        {/* 测试v-mode双向绑定 */}
        <demo v-model={demo2} />
      </div>
    );
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

总结

注意点:

  1. render函数,如果render函数使用的不是ES6的语法,而是写key:value的形式,那么你就必须要写h变量,vue中指的就是createElement,或者你可以不写h变量,但是必须声明一个变量const h = this.$createElement,否则程序就会报错
  2. 如果使用了ES6的语法,就不要写h变量了,要写也可以,但是eslint校验会报错提示'h' is defined but never used,这时候只有禁用使用/* eslint disbale */来禁用eslinit校验了 例子:
# ES6
render() {
  return (
    <div>
      <div>{this.msg}</div>
      <div>{this.$slots.demo}</div>
      <div>{this.$scopedSlots.data(this.detail)}</div>
    </div>
  );
},
# ES5
render: function(h) {
  return (
    <div>
      <div>{this.msg}</div>
      <div>{this.$slots.demo}</div>
      <div>{this.$scopedSlots.data(this.detail)}</div>
    </div>
  );
},
# 或者
render: function() {
  const h = this.$createElement
  return (
    <div>
      <div>{this.msg}</div>
      <div>{this.$slots.demo}</div>
      <div>{this.$scopedSlots.data(this.detail)}</div>
    </div>
  );
},
  1. jsx语法的话props传递就不要使用什么v-bind,直接使用key={variable},jsx语法中不管是传递值还是显示值都是一个花括号{},如果值是一个对象,形式就是{ {} },具体可以看vue文档和# babel-plugin-transform-vue-jsx文档
  2. 注意具名插槽和作用域插槽的使用

引用

vue文档
babel-plugin-transform-vue-jsx