如何在Vue项目中使用JSX?

SegmentFault

共 16719字,需浏览 34分钟

 · 2021-09-19

作者:ManStruggling

来源:SegmentFault 思否社区

什么是 JSX?

JSX是一种JavaScript和XML的结合,即JavaScript + XML = JSX,JSX源于Facebook,可以在JavaScript里写XML,因为这个特性,所以具备了JavaScript的灵活性,同时又兼具html的语义化和直观性

为什么使用 JSX?

  • jsx对比createElement函数可读性更强;<div></div> VS this.$createElement('div', {...}, [...])

  • vue-cli3.0及以上默认支持jsx语法

  • jsx可以使vue组件更容易导入和管理


首先来横向对比一下<template></template>模板语法、createElement函数、jsx三种渲染方式
栗子一
template模板语法
使用最多,不做解释
<div id="vnode">
    一些文本
    <p class="bar" style="color: red; font-weight: bold;">Only you can stop forest fires</p>
    <span>span text</span>
    <b>b tag</b>
</div>
createElement
createElement函数实际上创建的是Virtual Node,创建VNode树,一旦Dom树的结构复杂,dom节点属性太多,势必会造成可读性差的问题。。。
render(h) {
    return h(
      "div",
      {
        attrs: {
          id: "vnode",
        },
      },
      [
        "一些文本",
        h(
          "p",
          {
            class: {
              bar: true,
            },
            style: {
              color: "red",
              fontWeight: "bold",
            },
          },
          "Only you can stop forest fires"
        ),
        h("span", {}, "span text"),
        h("b", { domProps: { innerText: "b tag" } }),
      ]
    );
  }
JSX
然后jsx就闪亮登场
render() {
    return (
      <div id="vnode">
        一些文本
        <p class="bar" style="color: red; font-weight: bold;">
          Only you can stop forest fires
        </p>
        <span>span text</span>
        <b>b tag</b>
      </div>
    );
  }

可能有的同学觉得,就这,跟template没什么区别呀。
这个案例只涉及到html没有涉及javascript,jsx是把javascript和html放在一起来书写的。所以看下个栗子
栗子二
根据type的值进行条件渲染,A、B、C三个模块,每个模块有自己的对应模块的代码,也有一些公共的模块,假设这些代码有很多行,势必会造成阅读性的降低。有人可能觉得如果这样的话,把公共代码抽离出一个单独的组件不就好了,这样使可以解决问题,如果这里的公共代码仅仅只在这一个文件内使用,别的文件不会对这些公共代码做引用呢,要不要考虑抽离公共组件的必要性呢,jsx就可以完美的解决这些问题。
template
在template
<div>
        <!-- 模块A -->
        <div class="module-a" v-if="type === 0">
            <div>
                ...模块A的代码
            </div>
            <div>
                ...公共代码
            </div>
        </div>
        <!-- 模块B -->
        <div class="module-b" v-else-if="type === 1">
            <div>
                ...模块B的代码
            </div>
            <div>
                ...公共代码
            </div>
        </div>
        <!-- 模块C -->
        <div class="module-c" v-else>
            <div>
                ...模块C的代码
            </div>
            <div>
                ...公共代码
            </div>
        </div>
    </div>

createElement
这里就不做createElement函数的说明了,想必也没有多少人用这个吧。。。。有的话,那你赢了
JSX
template模板代码超过三百行,就很难阅读了,会重复在VM、V层切换,所以在复杂的大型项目中建议用jsx来做render,提高代码的可阅读性
render() {
    // 提取公共模块代码
    const renderCommon = () => <div>...公共代码</div>;
    const renderA = () => (
      <div class="module-a">
        <div>...模块A的代码</div>
        {renderCommon()}
      </div>
    );
    const renderB = () => (
      <div class="module-b">
        <div>...模块B的代码</div>
        {renderCommon()}
      </div>
    );
    const renderC = () => (
      <div class="module-c">
        <div>...模块C的代码</div>
        {renderCommon()}
      </div>
    );
    return (
      <div>
        {this.type === 0 ? renderA() : this.type === 1 ? renderB() : renderC()}
      </div>
    );
  }

Usage

<script>
export default {
  render() {
    // 条件渲染
    const vIfRender = () => {
      let show = false;
      return (
        <div
          id={"test"}
          class={{ "test-wrapper": true }}
          style={{ fontWeight: "bold" }}
        >
          {show ? <div>display</div> : "hidden"}
        </div>
      );
    };
    // v-html渲染
    const vHtmlRender = () => <div domPropsInnerHTML={`<i>i text</i>`}></div>;
    const listRencer = () => (
      <ol>
        {[1, 2, 3, 4, 5].map((item) => (
          <li>{item}</li>
        ))}
      </ol>
    );
    // 事件绑定
    const handleParentClick = () => {
      console.log("trigger parent click");
    };
    const handleClick = (e) => {
      e.stopPropagation();
      console.log("trigger click");
    };
    const eventBindingRender = () => (
      <div onClick={handleParentClick}>
        parent text
        <button domPropsInnerHTML={"点一下试试"} onClick={handleClick}></button>
      </div>
    );
    // 属性绑定
    const inputAttrs = {
      type"number",
      placeholder: "请输入数字",
    };
    const attrBindingRender = () => <input {...{ attrs: inputAttrs }} />;
    // 指令
    const directiveBindingRender = () => (
      <button
        {...{
          directives: [
            {
              name: "permission",
              value: 666,
              modifiers: { foo: true },
            },
          ],
        }}
      >
        权限管理
      </button>
    );
    return (
      <div>
        {
          // v-if 三目运算符
          vIfRender()
        }
        {
          // v-html
          vHtmlRender()
        }
        {
          // 列表渲染
          listRencer()
        }
        {
          // 事件绑定
          eventBindingRender()
        }
        {
          // 属性绑定
          attrBindingRender()
        }
        {
          // 指令
          directiveBindingRender()
        }
      </div>
    );
  },
};
</script>

插槽和作用域插槽

// child.vue
<script>
export default {
    props: {
        config: {
            type: Object,
            required: true
        }
    },
    render() {
        return (
            <div>
                <h3>{this.config.text}</h3>
                {this.$scopedSlots.content({
                    data: this.config.childConfig
                })}
            </div>
        )
    }
}
</script>
<script>
import Child from "./child";
export default {
  render() {
    const config = {
      text: "parent text jsx",
      childConfig: {
        test"children text jsx",
      },
    };
    return (
      <div>
        <Child
          config={config}
          {...{
            scopedSlots: {
              content: ({data}) => {
                return <div>{data.test}</div>;
              },
            },
          }}
        ></Child>
      </div>
    );
  },
};
</script>
组件直接引入使用,无需components注册,方便导入和管理
上述总结使用jsx的一些特殊情况,条件渲染、v-html、列表的渲染、事件的绑定、属性绑定、指令的使用、插槽和作用域插槽,聪明的你肯定可以看出来,使用jsx能像vue3的composition API一样把相同的逻辑代码聚合在一起,避免大型项目开发时,页面逻辑复杂导致代码量上升,option API在template和methods之间来回切换的问题。
第一次撰文,如有疑问,欢迎各位大佬斧正,希望大家能够STAR支持一下小编,希望小编的内容对大家有所帮助~


点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流,扫描下方”二维码“或在“公众号后台回复“ 入群 ”即可加入我们的技术交流群,收获更多的技术文章~

- END -


浏览 6
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报