Marinerer / store

转载 && 收藏 && 乱七八糟 :clap:
65 stars 10 forks source link

JSX #44

Open Marinerer opened 2 years ago

Marinerer commented 2 years ago

React JSX

Marinerer commented 2 years ago

Vue JSX

JSX本质上是createElement的语法糖,最终会被编译器转为createElement函数。当在jsx的标签中使用{ ...obj }时, obj将会编译为createElement的第二个参数([参见#Vue Data Object](#Vue Data Object))。

  1. 当不知道某个vue语法怎么用jsx实现时,可以先转换为createElementdata对象,然后使用{...data}写在jsx标签上。

  2. 在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入 const h = this.$createElement,这样就可以去掉 (h) 参数了。

参考:

Syntax

Via. https://github.com/vuejs/jsx

Content

render() {
  return <p>hello</p>
}

with dynamic content:

render() {
  return <p>hello { this.message }</p>
}

when self-closing:

render() {
  return <input />
}

with a component:

import MyComponent from './my-component'
export default {
  render() {
    return <MyComponent>hello</MyComponent>
  },
}

注:

  1. 空标签 在React中可以使用空标签<></>来实现包裹元素,在Vue中使用<template/>是无效的,可以通过遍历来或者将组件放在一个Array内即可。比如 <Layout>{ [<Sidebar />, <MainContent/>] }</Layout>

Attributes/Props

render() {
  return <input type="email" />
}

with a dynamic binding:

render() {
  return <input
    type="email"
    placeholder={this.placeholderText}
  />
}

with the spread operator (object needs to be compatible with Vue Data Object):

render() {
  const inputAttrs = {
    type: 'email',
    placeholder: 'Enter your email'
  }
  return <input {...{ attrs: inputAttrs }} />
}

Slots

  1. 通过 this.$slots 访问静态插槽的内容,每个插槽都是一个 VNode 数组;
  2. 通过 this.$scopedSlots 访问作用域插槽,每个作用域插槽都是一个返回若干 VNode 的函数。
// slots
render() {
  return (<div>{this.$slots.default}</div>)
}

// scoped slots
render() {
  return (<div>{this.$slots.default({ name: 'John' })}</div>)
}
  1. named slots:
render() {
  return (
    <MyComponent>
      <header slot="header">header</header>
      <footer slot="footer">footer</footer>
    </MyComponent>
  )
}
  1. scoped slots:
render() {
  const scopedSlots = {
    header: () => <header>header</header>,
    footer: () => <footer>footer</footer>
  }
  return <MyComponent scopedSlots={scopedSlots} />
}

Directives

v-model

<input vModel={this.newTodoText} />

with a modifier:

<input vModel_trim={this.newTodoText} />
// or
<el-input
  value={this.inputValue}
  on-input={val => this.inputValue = val.trim()}
 />

v-text 和 v-html

// domPropsInnerText 代替 v-text
<div domPropsInnerText={this.content}></div>

// domPropsInnerHTML 代替 v-html
<p domPropsInnerHTML={html} />

实际上,对于domProps,只有innerHTML才需要使用domPropsInnerHTML的写法,其他使用正常写法即可。

v-if 和 v-for

模板中使用的 v-ifv-for ,在渲染函数中用 if/elsemap 来重写。

Event & Key Modifiers

事件处理都采用了箭头函数, 跟react一样, 需要处理this绑定问题,可以使用bind绑定。

  1. 模板中通过 v-on:事件名 绑定事件,jsx 中官方提供 vOn 进行绑定,修饰符通过下划线 split('_') 分割获得。
<input vOn:click={this.newTodoText} />
<input vOn:click_stop_prevent={this.newTodoText} />
  1. JSX中,也可以通过on + 事件名称的大驼峰写法来监听,比如事件icon-click ,在JSX中写为onIconClick
  1. 监听原生事件的规则与普通事件是一样的,只需要将前面的on 替换为 nativeOn
  2. 这种监听事件的方式,事件修饰符只能通过代码去实现。
  1. 参照 data写法
    const data = {
    on: { click: this.clickHandler },
    nativeOn: { mouseenter: this.mouseenterHandler }
    }
    render() {
    return (
    <MyComponent { ...data }></MyComponent>
    )
    }

事件修饰符:

修饰符参见:https://vuejs.org/v2/guide/render-function.html#Event-amp-Key-Modifiers

高阶组件中的 v-on="$listeners"v-bind="$attrs" 可以参照 data写法jsx实现为:

const data = {
  attrs: this.$attrs,
  on: {
     ...this.$listeners,
     click() {},
  }
}

<button { ...data }><button>

自定义指令

自定义指令在JSX里可以通过 directives 使用,比如 element-uiv-loading 指令可以这样用:

在模板里:

<template>
    <div v-loading.fullscreen.lock = "loading">...</div>
</template>

JSX 里:

render() {
  /**
   * modifiers指定修饰符,如果使用某一个修饰符,则指定这个修饰符的值为 true
   * 不使用可以设置为false或者直接删掉
   */
  const directives = [
    {
      name: 'loading',
      value: this.loading,
      modifiers: { fullscreen: true, lock: false }
    }
  ]
  return (
    <div {
        ...{ directives }
        }>加载内容</div>
    )
}

Functional Components

Transpiles arrow functions that return JSX into functional components, when they are either default exports:

export default ({ props }) => <p>hello {props.message}</p>

or PascalCase variable declarations:

const HelloWorld = ({ props }) => <p>hello {props.message}</p>

Vue Data Object

Via. https://cn.vuejs.org/v2/guide/render-function.html

{
  // 与 `v-bind:class` 的 API 相同,
  // 接受一个字符串、对象或字符串和对象组成的数组
  'class': {
    foo: true,
    bar: false
  },
  // 与 `v-bind:style` 的 API 相同,
  // 接受一个字符串、对象,或对象组成的数组
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 普通的 HTML attribute
  attrs: {
    id: 'foo'
  },
  // 组件 prop
  props: {
    myProp: 'bar'
  },
  // DOM property
  domProps: {
    innerHTML: 'baz'
  },
  // 事件监听器在 `on` 内,
  // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
  // 需要在处理函数中手动检查 keyCode。
  on: {
    click: this.clickHandler
  },
  // 仅用于组件,用于监听原生事件,而不是组件内部使用
  // `vm.$emit` 触发的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
  // 赋值,因为 Vue 已经自动为你进行了同步。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // 作用域插槽的格式为
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 如果组件是其它组件的子组件,需为插槽指定名称
  slot: 'name-of-slot',
  // 其它特殊顶层 property
  key: 'myKey',
  ref: 'myRef',
  // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
  // 那么 `$refs.myRef` 会变成一个数组。
  refInFor: true
}

Functional Components

Via. https://cn.vuejs.org/v2/guide/render-function.html

函数式组件是一个只接受 prop 的函数。它无状态的 (没有响应式数据),也没有实例 (没有 this 上下文,也没有生命周期方法)。

Vue.component('my-component', {
  functional: true,
  // Props 是可选的
  props: {
    // ...
  },
  // 为了弥补缺少的实例
  // 提供第二个参数作为上下文
  render: function (createElement, context) {
    // ...
  }
})

组件需要的一切都是通过 context 参数传递,它是一个包括如下字段的对象:

VNode

createElement 函数返回的值称之为虚拟节点,即VNode,而由VNode组成的树便是虚拟DOM

VNode 必须唯一