JSX 简介:https://zh-hans.reactjs.org/docs/introducing-jsx.html

比较

template

学习成本低 大量内置指令简化开发 组件作用域 CSS

  • 模版语法(HTML 的扩展)
  • 数据绑定使用 Mustache 语法(双大括号)

    1. <span>{{ msg }}</span>

    jsx|tsx

    灵活

  • JavaScript 的语法扩展

  • 数据绑定使用单引号

    扩展

    引入 Vue 官方文档(https://cn.vuejs.org/v2/guide/comparison.html#JSX-vs-Templates

    更抽象一点来看,我们可以把组件区分为两类:一类是偏视图表现的 **(presentational)**,一类则是偏逻辑的 **(logical)**。我们推荐在前者中使用模板,在后者中使用 **JSX** 或渲染函数。这两类组件的比例会根据应用类型的不同有所变化,但整体来说我们发现表现类的组件远远多于逻辑类组件。

常用

v-for 与 v-if

jsx|tsx 没有 v-forv-if,分别条件运算符 (?:) 替代v-ifarray.map 替代 v-for

  1. render() {
  2. return (
  3. // v-if
  4. {this.type && <div>true</div>}
  5. // v-if / v-else
  6. {this.type ? <div>true</div> : <div>false</div>}
  7. // v-for
  8. {this.options.map(item => {
  9. <div>{item.title}</div>
  10. })}
  11. )
  12. }

指令

v-model v-model_trim domPropsInnerHTML 等价于 v-html domPropsTextContent等价于 v-text

  1. render() {
  2. return (
  3. <div><input v-model={this.keyValue} /></div>
  4. <div><input v-model_trim={this.keyValue} /></div>
  5. <div><p domPropsInnerHTML={this.html} /></div>
  6. <div><p domPropsTextContent={this.text} /></div>
  7. )
  8. }

事件

遵循一个规则或者如下:事件绑定需要在事件名称前端加上 on 前缀,原生事件添加 nativeOn

@click 等价于 on-click 等价于 onClick 等价于 vOn:click vOn:keyup_enter_native @click.native 等价于 nativeOnClick

注意: 传递参数不能直接使用 **onClick={this.search('params')}**,这会导致 **jsx|tsx** 每次都会 **render** 会自执行一次方法(重则会死循环) 处理: 应该使用 **bind**,或箭头函数来处理传参 **onClick={()=> this.search('params')}**

  1. render() {
  2. return (
  3. <div><input v-model={this.keyValue} on-input={this.inputText} /></div>
  4. <div><input v-model={this.keyValue} vOn:click_stop_prevent={this.inputText} /></div>
  5. <div><input v-model={this.keyValue} vOn:keyup_enter_native={() => this.search()} /></div>
  6. <div><el-button nativeOnClick={this.handleClick}>Native click</el-button></div>
  7. )
  8. }

监听.sync修饰符的事件

https://github.com/vuejs/babel-plugin-transform-vue-jsx/blob/master/example/example.js

  1. // jsx | tsx
  2. <el-dialog
  3. visible={this.dialogVisible}
  4. {...{ on: { 'update:visible': console.log } }}
  5. before-close={() => (this.dialogVisible = false)}
  6. />
  7. // jsx 发现这样写也ok
  8. // 在 vue2.x 版本使用 tsx 这种方式打包会报错
  9. <el-dialog
  10. visible:sync={this.dialogVisible}
  11. before-close={() => (this.dialogVisible = false)}
  12. />
  13. // template
  14. // 在 vue2.x 版本使用 tsx 这种方式打包会报错
  15. <el-dialog
  16. :visible.sync="dialogVisible"
  17. :before-close="handleClose"
  18. />

class绑定

注:与 reactjsx 绑定的有区别,react 中使用 classNamevue 中使用 class

  1. <button
  2. onClick={handleClick}
  3. disabled={disabled}
  4. type={'button'}
  5. v-loading={loading}
  6. class={[
  7. 'ws-btn',
  8. 'ws-button',
  9. type ? `ws-button--${type}` : '',
  10. {
  11. 'is-disabled': disabled,
  12. 'is-circle': circle,
  13. 'is-block': block
  14. }
  15. ]}
  16. >
  17. {defaultSlots}
  18. </button>

自定义组件

导入直接使用,不需要在 components 属性声明

  1. import HelloWolrd from './HelloWorld';
  2. export default {
  3. name: 'App',
  4. render() {
  5. return <HelloWorld msg='Welcome to Your Vue.js App' />;
  6. }
  7. };

新版本

所有形如 on-update:*prop 都有一个对应的 onUpdate* 属性可供使用。

由于 JSX 自身的规定,on-update:*onUpdate:* 不是合法的 prop 名称(如果你发现我的代码没有这样写,那一定是幻觉,请偷偷给我提醒下),如下所示

  1. <d-select @update:value="..." />
  2. // 在 JSX 中可以写为
  3. <DSelect onUpdateValue={...} />


样式模块化

样式文件必须已 .module 文件名结尾才能使用模块的方式 vue 单文件使用 scoped 可以实现模块化:<style scoped>

Button.tsx

  1. import Vue, { VNode } from 'vue';
  2. // @ts-ignore
  3. import ButtonModule from './Button.module.less';
  4. export default Vue.extend({
  5. name: 'Button',
  6. props: {
  7. type: String,
  8. loading: Boolean,
  9. disabled: Boolean,
  10. circle: Boolean,
  11. block: Boolean
  12. },
  13. methods: {
  14. handleClick(event: MouseEvent) {
  15. if (this.loading) {
  16. event.preventDefault();
  17. } else if (!this.disabled) {
  18. this.$emit('click', event);
  19. }
  20. }
  21. },
  22. render(): VNode {
  23. const { type, loading, disabled, circle, block, $slots, handleClick } = this;
  24. const loadingNode = () => {
  25. return loading && <van-loading color='inherit' size='15px' />;
  26. };
  27. const defaultSlots = () => {
  28. return (
  29. $slots?.default && (
  30. <span class={loading ? ButtonModule['button--loading-text'] : null}>{$slots.default}</span>
  31. )
  32. );
  33. };
  34. return (
  35. <button
  36. onClick={handleClick}
  37. disabled={disabled}
  38. type={'button'}
  39. class={[
  40. ButtonModule['btn'],
  41. ButtonModule['button'],
  42. type ? ButtonModule[`button--${type}`] : '',
  43. disabled ? ButtonModule['is-disabled'] : '',
  44. loading ? ButtonModule['is-loading'] : '',
  45. circle ? ButtonModule['is-circle'] : '',
  46. block ? ButtonModule['is-block'] : '',
  47. ]}
  48. >
  49. {loadingNode()}
  50. {defaultSlots()}
  51. </button>
  52. );
  53. }
  54. });

Button.module.less

  1. @theme-color: #294ba3;
  2. @theme-color-active: #26479a;
  3. @white: #fff;
  4. @secondary-font-color: #545454;
  5. .button {
  6. display: inline-block;
  7. height: 0.88rem;
  8. line-height: 0.88rem;
  9. white-space: nowrap;
  10. cursor: pointer;
  11. background: @white;
  12. border: 1px solid #ddd;
  13. color: @secondary-font-color;
  14. -webkit-appearance: none;
  15. text-align: center;
  16. box-sizing: border-box;
  17. outline: none;
  18. margin: 0;
  19. transition: 0.1s;
  20. font-weight: 500;
  21. user-select: none;
  22. padding: 0 0.24rem;
  23. font-size: 15px;
  24. border-radius: 0.12rem;
  25. &.is-circle {
  26. height: 28px;
  27. line-height: 28px;
  28. border-radius: 0.28rem;
  29. }
  30. &.is-loading {
  31. display: flex;
  32. justify-content: center;
  33. align-items: center;
  34. .van-loading {
  35. display: flex;
  36. color: inherit;
  37. font-size: inherit;
  38. margin-right: 0.5em;
  39. }
  40. }
  41. &.is-active,
  42. &:active {
  43. background: #fafafa;
  44. }
  45. &.is-disabled,
  46. &.is-disabled:active,
  47. &.is-disabled:focus,
  48. &.is-disabled:hover {
  49. cursor: not-allowed;
  50. color: #ccc;
  51. background-color: @white;
  52. }
  53. &--primary {
  54. border-color: @theme-color;
  55. background: @theme-color;
  56. color: @white;
  57. &.is-disabled,
  58. &.is-disabled:active,
  59. &.is-disabled:focus,
  60. &.is-disabled:hover {
  61. cursor: not-allowed;
  62. color: @white;
  63. background-color: #ddd;
  64. border-color: #ddd;
  65. }
  66. &.is-active,
  67. &:active {
  68. background: @theme-color-active;
  69. }
  70. }
  71. &--border {
  72. color: #294ba3;
  73. background: @white;
  74. border-color: #3975c6;
  75. &.is-disabled,
  76. &.is-disabled:active,
  77. &.is-disabled:focus,
  78. &.is-disabled:hover {
  79. cursor: not-allowed;
  80. color: #ccc;
  81. background-color: @white;
  82. border-color: @white;
  83. }
  84. &.is-disabled,
  85. &.is-disabled:active,
  86. &.is-disabled:focus,
  87. &.is-disabled:hover {
  88. cursor: not-allowed;
  89. color: #ccc;
  90. background-color: @white;
  91. border-color: @white;
  92. }
  93. }
  94. &--text {
  95. height: 0.88rem;
  96. background: transparent;
  97. border: none;
  98. }
  99. & + .button {
  100. margin-left: 0.08rem;
  101. }
  102. &--loading-text {
  103. opacity: 0.2;
  104. }
  105. }
  106. .is-block {
  107. width: 100%;
  108. display: block;
  109. }

效果如下

命名规则以 XXX.module 名称为主

image.png

coding

vue2:https://github.com/WuChenDi/Front-End/tree/master/05-Vue/vue2-jsx

参考:

vue2

https://github.com/vuejs/jsx#readme

Babel Plugin JSX for Vue 3.0

https://github.com/vuejs/jsx-next#readme

vite

https://github.com/vitejs/vite/tree/main/packages/plugin-vue-jsx#readme

注:

vue jsx 2X 版本不支持空标签 <></> 的写法,3X 支持
React 中可以使用空标签 <></><react.Fragment></react.Fragment> 来实现包裹元素,其实空标签本质就只是 react.Fragment 的一个语法糖