zoukankan      html  css  js  c++  java
  • 2020-03-05:JSX、透传、函数式组件、createElement渲染函数

    补充

    render

    • 在渲染函数中引入静态资源地址
    import logo from '../assets/logo.png'
    export default {
      render() {
        return <div>
          <img alt="Vue logo" src={logo} />
        </div>
      }
    }
    
    • render中使用的组件,只需要import引入便可,不需要components注册

    JSX

    • 好像jsx和tsx不支持绑定key中包含:,例如:vOn:click会引发报错

    ————————————————————————————————————————————————————————————————————

    总结

    JSX

    • 根据babel-core的版本不同,使用了不同插件,导致存在两个版本的JSX语法
      • @vue/babel-preset-jsx 这个插件只适用于 Babel 7.x
      • vuejs/babel-plugin-transform-vue-jsx 这个插件适用于 6.x
      • babel-plugin-transform-vue-jsx插件从3.4.0开始,自动向使用了JSX语法并具有get方法的函数注入变量h指向渲染函数。例如:render、computed下的方法等,而自定义属性不会。@vue/babel-preset-jsx没有版本划分

    @vue/babel-preset-jsx

    // Attributes/Props
    return <input
      type="email"
      placeholder={this.placeholderText}
    />
    
    const inputAttrs = {
      type: 'email',
      placeholder: 'Enter your email'
    }
    return <input {...{ attrs: inputAttrs }} />
    
    // Slots
    <MyComponent>
      <header slot="header">header</header>
      <footer slot="footer">footer</footer>
    </MyComponent>
    
    // scoped slots
    const scopedSlots = {
      header: (props) => <header>{props.text}</header>,
      footer: (propst) => <footer>{props.text}</footer>
    }
    return <MyComponent scopedSlots={scopedSlots} />
    
    // v-model
    <input vModel_trim={this.newTodoText} />
    
    // v-on
    <input vOn:click_stop_prevent={this.newTodoText} /> // jsx 不支持 vOn:
    <input onClick_stop={this.doSome} /> // 并不是所有的修饰符都支持,目前已知按键修饰不支持,例如:_enter
    <Component nativeOn-click={ ... } />
    <Component nativeOnClick={ ... } />
    
    // v-html
    <p domPropsInnerHTML={html} />
    <Component domProp-innerHTML={ ... } />
    
    // v-自定义指令, 例如:vClamp 
    // 注释:个人经验,并非官方文档说明的
    <div vClamp={{clamp}}>需要出现超出省略的文本</div>
    
    // 函数式组件
    // 注释:小写的导出是不会被转换为选项对象的,这里的const b
    export const A = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
    export const b = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
    export default ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
    export const A = { // 转换后的结果
      functional: true,
      render: (h, {
        props,
        listeners
      }) => <div onClick={listeners.click}>{props.msg}</div>
    }
    

    babel-plugin-transform-vue-jsx

    • 如果组件名以小写开头,那么需要进行components注册后才能使用。如果是大写开头可以直接在JSX中使用该组件无需注册。
    • 在JSX中vue原生指令除了v-show={value}其他指令都不支持
    • 自定义指令可以通过v-name={value}使用,但是不支持参数和修饰符
      • Pass everything as an object via value, e.g. v-name={{ value, modifier: true }}
        • 注释:可以通过传入一个对象来解决,应该是插件提供的,并不是模板语言原有。
        • 疑问:只支持修饰符?
      • 使用渲染函数第2个参数中的directives绑定指令
    render (h) {
      return h('div', {
        props: {
          msg: 'hi',
          onCustomEvent: this.customEventHandler // propsOnCustomEvent={this.customEventHandler}
        },
        attrs: {
          id: 'foo' // id="foo"
        },
        domProps: {
          innerHTML: 'bar' // domPropsInnerHTML="bar"
        },
        on: {
          click: this.clickHandler // onClick={this.clickHandler}
        },
        nativeOn: {
          click: this.nativeClickHandler // nativeOnClick={this.nativeClickHandler}
        },
        class: { // class={{ foo: true, bar: false }} 注释:通过{...}绑定一个表达式
          foo: true,
          bar: false
        },
        style: { // style={{ color: 'red', fontSize: '14px' }}
          color: 'red',
          fontSize: '14px'
        },
        key: 'key', // key="key"
        ref: 'ref', // ref="ref"
        // assign the `ref` is used on elements/components with v-for
        refInFor: true, // refInFor
        slot: 'slot' // slot="slot"
      })
    }
    

    函数式组件

    • 使用场景
      • 程序化地在多个组件中选择一个来代为渲染;
      • 在将 children、props、data 传递给子组件之前操作它们。
    • 特点
      • 没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期,可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
      • 当使用函数式组件时,该引用将会是 HTMLElement,因为他们是无状态的也是无实例的

    透传

    • 在普通组件内实现透传
      • vm.$attrs 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
      • vm.$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
      • vm.$scopedSlots 用来访问作用域插槽。对于每一个插槽(包括 slot),该对象都包含一个返回相应 VNode 的函数。注释:不能重名,包括default
      • vm.$slots 用来访问被插槽分发的内容。每个具名插槽 有其相应的属性 (例如:v-slot:foo 中的内容将会在 vm.$slots.foo 中被找到)。default 属性包括了所有没有被包含在具名插槽中的节点,或 v-slot:default 的内容。注释:同一个具名插槽不知道绑定顺序
    • 函数式组件 context 参数
      • props:提供所有 prop 的对象
      • children: VNode 子节点的数组。注释:只包含普通插槽的,等价于slots().values(),反应了父组件中子集的插入顺序,方便透传
      • slots: 一个函数,返回了包含所有插槽的对象。
      • scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。注释:如果同名作用域插槽会覆盖普通插槽
      • data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件。注释:即当前组件被实例化时传入的createElement函数的第二个参数,方便透传
      • parent:对父组件的引用
      • listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
      • injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。
    Vue.component('my-functional-button', {
      functional: true,
      render: function (createElement, context) {
        // 完全透传任何 attribute、事件监听器、子节点等。
        return createElement('button', context.data, context.children)
      }
    })
    

    createElement 参数

    // @returns {VNode}
    createElement(
      // {String | Object | Function}
      // 一个 HTML 标签名、组件选项对象,或者
      // resolve 了上述任何一种的一个 async 函数。必填项。
      // 注释:当前组件的根元素,可以是dom名或者其他组件的选项对象
      'div',
    
      // {Object}
      // 一个与模板中属性对应的数据对象。可选。(可省略)
      // 注释:绑定到根元素上的属性,当根元素是一个组件时可以视为传入组件的组件选项值
      {
        // 与 `v-bind:class` 的 API 相同,
        // 接受一个字符串、对象或字符串和对象组成的数组
        'class': {
          foo: true,
          bar: false
        },
        // 与 `v-bind:style` 的 API 相同,
        // 接受一个字符串、对象,或对象组成的数组
        style: {
          color: 'red',
          fontSize: '14px'
        },
        // 普通的 HTML attribute
        // 注释:这是真实写入html的属性,在控制台html中可以看见。(性能不好)
        // 注释:在普通组件中没有被定义为 prop 的 attribute 会自动添加到组件的根元素上,将已有的同名 attribute 进行替换或与其进行智能合并。
        // 注释:在函数式组件如果有需要的话要求你显式定义该行为。在 2.3.0 之前的版本中,只能通过 context.data.attrs 获得,在 2.3.0 或以上的版本中,所有组件上的 attribute 都会被自动隐式解析为 prop。直接通过 context.props 获得
        attrs: {
          id: 'foo'
        },
        // 组件 prop
        // 注释:在函数组件中可以不设置props选项,这么所有绑定的属性都可以在props中访问
        props: {
          myProp: 'bar'
        },
        // DOM 属性
        // 注释:和根元素的子级不同,innerHTML 是v-html在渲染函数中的实现
        // 注释:这是给某个dom对象的属性进行赋值,不在html中表现(性能应该更高)
        // 注释:使用模板语法或JSX时直接绑定到元素上的属性会优先判断是否props > domProps > attrs
        domProps: {
          innerHTML: '<div>some</div>',
          value: self.value // 等价于模板中<div :value="value"></div>
        },
        // 事件监听器在 `on` 属性内,
        // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
        // 需要在处理函数中手动检查 keyCode。
        // 对于 .passive(立即触发)、.capture(捕获阶段触发) 和 .once 这些事件修饰符, Vue 提供了相应的前缀。对于.stop、.prevent(禁止默认)、.self、按键、修饰键 可以在事件处理函数中使用事件方法
        on: {
          click: this.clickHandler
        },
        // 仅用于组件,用于监听原生事件,而不是组件内部使用 `vm.$emit` 触发的事件。
        nativeOn: {
          click: this.nativeClickHandler
        },
        // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
        // 赋值,因为 Vue 已经自动为你进行了同步。
        directives: [ // v-my-custom-directive:foo.bar="1 + 1"
          {
            name: 'my-custom-directive',
            value: '2', // 表达式的计算结果
            expression: '1 + 1', // 绑定的表达式
            arg: 'foo', // 参数,参数可以由外部决定是什么,可以配合value动态传对象
            modifiers: { // 修饰符,由指令内部决定好了有哪些,进行真假判断后走不同程序分支
              bar: true
            }
          }
        ],
        // 作用域插槽的格式为
        // { name: props => VNode | Array<VNode> }
        // 注释:当前元素是组件并具有作用域插槽时使用,该值为传入作用域插槽的渲染函数,props获取组件中作用域插槽传入的值。注意,作用传入域插槽的元素并不是写在子级中而是在这里作为参数
        // 注释:当前根组件中具有作用域插槽,使用当前根组件时向作用域插槽内插入元素。下面例子对应的当前根组件为
        // render(h) {
        //   return h("div", {}, [this.$scopedSlots.default && this.$scopedSlots.default({ text: "some" })]);
        // }
        scopedSlots: {
          default: props => createElement('span', props.text)
        },
        // 如果组件是其它组件的子组件,需为插槽指定名称
        // 注释:在子级元素中使用,当前元素在子级中出现,作为某个组件的插槽元素。
        // 注释:指明当前元素插入的插槽名,它对应的父组件为
        // render(h) {
        //  return h("div", {}, [this.$slots.default && this.$slots.default]);
        // }
        slot: 'default',
        // 其它特殊顶层属性
        key: 'myKey',
        ref: 'myRef',
        // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
        // 当设置 refInFor 为 true 时 `$refs.myRef` 会变成一个元素数组,否则为最后一个应用该名称的元素
        refInFor: true
      },
    
      // {String | Array}
      // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
      // 也可以使用字符串来生成“文本虚拟节点”。可选。
      // 注释:根元素下包含的元素,当根元素是组件时可以视为插入插槽的行为
      // 注释:可以为单个 vnode/String ,也可以示 vnode/String 的二维数组,例如:[[vnode,vnode],vnode](不建议,按官网文档来)
      [
        '先写一些文字',
        createElement('h1', '一则头条'),
        createElement(MyComponent, {
          props: {
            someProp: 'foobar'
          }
        })
      ]
    )
    

    ———————————————————————————————————————————————————————————————————————————————————————————

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

    • 2.x

    基础

    实例属性 API

    vm.$data

    • 类型:Object
    • 详细:Vue 实例观察的数据对象。Vue 实例代理了对其 data 对象属性的访问。

    vm.$props

    • 2.2.0 新增
    • 类型:Object
    • 详细:当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象属性的访问。
    • 注释:没有传入对用props时返回空对象,即使该props有默认值

    vm.$el

    • 类型:Element
    • 只读
    • 详细:Vue 实例使用的根 DOM 元素。
    • 注释:当前实例 render 中的根dom元素,注意不是他替换的元素,而是组件内元素

    vm.$options

    • 类型:Object
    • 只读
    • 详细:用于当前 Vue 实例的初始化选项。需要在选项中包含自定义属性时会有用处:
    • 注释:this.$options返回的对象中不包含自定义属性,但是通过this.$options.customOption可以访问到,自定义属性实际绑定到当前实例原型上的
    • 注释:这个对象是响应的,对其值得修改会导致当前组件重新渲染
    new Vue({
      customOption: 'foo',
      created: function () {
        console.log(this.$options.customOption) // => 'foo'
      }
    })
    

    vm.$parent

    • 类型:Vue instance(Vue实例)
    • 只读
    • 详细:父实例,如果当前实例有的话。

    vm.$root

    • 类型:Vue instance
    • 只读
    • 详细:当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。

    vm.$children

    • 类型:Array
    • 只读
    • 详细:当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。
    • 注释:由于生命周期的缘故,在created中返回空数组。mounted中能够正常获取,当然渲染函数中不能正常获取
    • 注释:一般在methods中获取全部子组件
    • 疑问:具体作用?

    vm.$slots

    • 类型:{ [name: string]: ?Array }
      • 注释:?代表键可以为任意个
    • 只读
    • 详细:
      • 用来访问被插槽分发的内容。每个具名插槽 有其相应的属性 (例如:v-slot:foo 中的内容将会在 vm.$slots.foo 中被找到)。default 属性包括了所有没有被包含在具名插槽中的节点,或 v-slot:default 的内容。
      • 注意: v-slot:foo 在 2.6 以上的版本才支持。对于之前的版本,你可以使用废弃了的语法.
    • 注释:即使当前组件没有插槽,但在父组件插入内容时依然可以通过default获取
    Vue.component('blog-post', {
      render: function (createElement) {
        var header = this.$slots.header
        var body   = this.$slots.default
        var footer = this.$slots.footer
        return createElement('div', [
          createElement('header', header),
          createElement('main', body),
          createElement('footer', footer)
        ])
      }
    })
    

    vm.$scopedSlots

    • 2.1.0 新增
    • 类型:{ [name: string]: props => Array | undefined }
    • 只读
    • 详细:用来访问作用域插槽。对于每一个插槽(包括 slot),该对象都包含一个返回相应 VNode 的函数。
    • 注意:从 2.6.0 开始,这个属性有两个变化:
      • 作用域插槽函数现在保证返回一个 VNode 数组,除非在返回值无效的情况下返回 undefined。
      • 所有的 $slots 现在都会作为函数暴露在 $scopedSlots 中。Vue3趋势
    • 注释:和$slot相同,未具名的插入组件存放在default属性下
    • 注释:v-slot只能使用在组件上,可以是用template组件包裹原生dom
    • 注释:$scopedSlots和$slots共用相同的key名,所以同时为具名插槽时,作用域插槽会覆盖掉具名插槽

    vm.$refs

    • 类型:Object
    • 只读
    • 详细:一个对象,持有注册过 ref 特性 的所有 DOM 元素和组件实例。
    • 注释:ref可以绑定到组件或dom上,相应的$refs返回组件实例或dom,或者他们组成的数组

    vm.$isServer

    • 类型:boolean
    • 只读
    • 详细:当前 Vue 实例是否运行于服务器。

    vm.$attrs

    • 2.4.0 新增
    • 类型:{ [key: string]: string }
    • 只读
    • 详细:包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

    vm.$listeners

    • 2.4.0 新增
    • 类型:{ [key: string]: Function | Array }
    • 只读
    • 详细:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

    节点、树以及虚拟 DOM

    虚拟 DOM

    createElement 参数

    // @returns {VNode}
    createElement(
      // {String | Object | Function}
      // 一个 HTML 标签名、组件选项对象,或者
      // resolve 了上述任何一种的一个 async 函数。必填项。
      // 注释:当前组件的根元素,可以是dom名或者其他组件的选项对象
      'div',
    
      // {Object}
      // 一个与模板中属性对应的数据对象。可选。(可省略)
      // 注释:绑定到根元素上的属性,当根元素是一个组件时可以视为传入组件的组件选项值
      {
        // (详情见下一节)
      },
    
      // {String | Array}
      // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
      // 也可以使用字符串来生成“文本虚拟节点”。可选。
      // 注释:根元素下包含的元素,当根元素是组件时可以视为插入插槽的行为
      // 注释:可以为单个 vnode/String ,也可以示 vnode/String 的二维数组,例如:[[vnode,vnode],vnode](不建议,按官网文档来)
      [
        '先写一些文字',
        createElement('h1', '一则头条'),
        createElement(MyComponent, {
          props: {
            someProp: 'foobar'
          }
        })
      ]
    )
    

    深入数据对象

    {
      // 与 `v-bind:class` 的 API 相同,
      // 接受一个字符串、对象或字符串和对象组成的数组
      'class': {
        foo: true,
        bar: false
      },
      // 与 `v-bind:style` 的 API 相同,
      // 接受一个字符串、对象,或对象组成的数组
      style: {
        color: 'red',
        fontSize: '14px'
      },
      // 普通的 HTML attribute
      // 注释:这是真实写入html的属性,在控制台html中可以看见。(性能不好)
      // 后文:在 2.3.0 之前的版本中,如果一个函数式组件想要接收 prop,则 props 选项是必须的。在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。
      // 后文:函数式组件要求你显式定义该行为。注释:函数组件不会自动将 attribute 添加到根元素上
      // 后文:在普通组件中,没有被定义为 prop 的 attribute 会自动添加到组件的根元素上,将已有的同名 attribute 进行替换或与其进行智能合并。注释:普通组件是指非函数组件
      attrs: {
        id: 'foo'
      },
      // 组件 prop
      props: {
        myProp: 'bar'
      },
      // DOM 属性
      // 注释:和根元素的子级不同,这是v-html在渲染函数中的实现
      // 注释:这是给某个dom对象的属性进行赋值,不在html中表现(性能应该更高)
      // 注释:使用模板语法或JSX时直接绑定到元素上的属性会优先判断是否props > domProps > attrs
      domProps: {
        innerHTML: '<div>some</div>',
        value: self.value // 等价于模板中<div :value="value"></div>
      },
      // 事件监听器在 `on` 属性内,
      // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
      // 需要在处理函数中手动检查 keyCode。
      on: {
        click: this.clickHandler
      },
      // 仅用于组件,用于监听原生事件,而不是组件内部使用
      // `vm.$emit` 触发的事件。注释:这句话需要和上一句一起读
      nativeOn: {
        click: this.nativeClickHandler
      },
      // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
      // 赋值,因为 Vue 已经自动为你进行了同步。
      directives: [ // v-my-custom-directive:foo.bar="1 + 1"
        {
          name: 'my-custom-directive',
          value: '2', // 表达式的计算结果
          expression: '1 + 1', // 绑定的表达式
          arg: 'foo', // 参数,参数可以由外部决定是什么,可以配合value动态传对象
          modifiers: { // 修饰符,由指令内部决定好了有哪些,进行真假判断后走不同程序分支
            bar: true
          }
        }
      ],
      // 作用域插槽的格式为
      // { name: props => VNode | Array<VNode> }
      // 注释:当前元素是组件并具有作用域插槽时使用,该值为传入作用域插槽的渲染函数,props获取组件中作用域插槽传入的值。注意,作用传入域插槽的元素并不是写在子级中而是在这里作为参数
      // 注释:当前根组件中具有作用域插槽,使用当前根组件时向作用域插槽内插入元素。下面例子对应的当前根组件为
      // render(h) {
      //   return h("div", {}, [this.$scopedSlots.default && this.$scopedSlots.default({ text: "some" })]);
      // }
      scopedSlots: {
        default: props => createElement('span', props.text)
      },
      // 如果组件是其它组件的子组件,需为插槽指定名称
      // 注释:在子级元素中使用,当前元素在子级中出现,作为某个组件的插槽元素。
      // 注释:指明当前元素插入的插槽名,它对应的父组件为
      // render(h) {
      //  return h("div", {}, [this.$slots.default && this.$slots.default]);
      // }
      slot: 'default',
      // 其它特殊顶层属性
      key: 'myKey',
      ref: 'myRef',
      // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
      // 当设置 refInFor 为 true 时 `$refs.myRef` 会变成一个元素数组,否则为最后一个应用该名称的元素
      refInFor: true
    }
    

    完整示例

    • 注释:vnode.text包含文本节点的字符串内容,dom节点为undefined

    约束

    • 组件树中的所有 VNode 必须是唯一的

    使用 JavaScript 代替模板功能

    v-if 和 v-for

    v-model

    事件 & 按键修饰符

    • 对于 .passive、.capture 和 .once 这些事件修饰符, Vue 提供了相应的前缀
      • .passive 前缀 & (默认事件立即被触发)
      • .capture 前缀 ! (在捕获阶段触发)
      • .once 前缀 ~
      • .capture.once 或 .once.capture 前缀 ~!
    on: {
      '!click': this.doThisInCapturingMode,
      '~keyup': this.doThisOnce,
      '~!mouseover': this.doThisOnceInCapturingMode
    }
    
    • 对于所有其它的修饰符,私有前缀都不是必须的,因为你可以在事件处理函数中使用事件方法:
      • .stop:停止事件冒泡 event.stopPropagation()
      • .prevent:禁止默认事件触发 event.preventDefault()
      • .self:只允许当前元素触发 if (event.target !== event.currentTarget) return
      • 按键:.enter, .13:按动某个按键 if (event.keyCode !== 13) return
      • 修饰键:.ctrl, .alt, .shift, .meta:按动某些组合键 if (!event.ctrlKey) return (将 ctrlKey 分别修改为 altKey、shiftKey 或者 metaKey)

    插槽

    JSX

    • 有一个 Babel 插件,用于在 Vue 中使用 JSX 语法,它可以让我们回到更接近于模板的语法
    • 将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。
    • 疑问:从 Vue 的 Babel 插件的 3.4.0 版本开始,我们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入 const h = this.$createElement,这样你就可以去掉 (h) 参数了。

    函数式组件

    • 没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期,可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
    • 注释:无状态是指没有data,但是当传入的props改变时,依然能够响应重新渲染
    • 注释:没有监听任何传递给它的状态是指没有对任何对象设置getset方法,props导致的渲染只是传参改变导致渲染函数运算结果的改变而已。
    • 注释:整个vnode树上的每一个vnode节点实际上都是一个computed
    Vue.component('my-component', {
      functional: true,
      // Props 是可选的
      props: {
        // ...
      },
      // 为了弥补缺少的实例
      // 提供第二个参数作为上下文
      render: function (createElement, context) {
        // ...
      }
    })
    
    • 在 2.3.0 之前的版本中,如果一个函数式组件想要接收 prop,则 props 选项是必须的。在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。
    • 注释:在函数式组件中必须没有props选项这种隐式解析才会实现。一旦有,即使是空props,attribute 依然不会被解析为 props
    • 当使用函数式组件时,该引用将会是 HTMLElement,因为他们是无状态的也是无实例的
    • 在 2.5.0 及以上版本中,如果你使用了单文件组件,那么基于模板的函数式组件可以这样声明:
    <template functional>
    </template>
    
    • 组件需要的一切都是通过 context 参数传递,它是一个包括如下字段的对象
      • props:提供所有 prop 的对象
      • children: VNode 子节点的数组。注释:只包含普通插槽的,等价于slots().values(),反应了父组件中子集的插入顺序,方便透传
      • slots: 一个函数,返回了包含所有插槽的对象。
      • scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。注释:如果同名作用域插槽会覆盖普通插槽
      • data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件。注释:即当前组件被实例化时传入的createElement函数的第二个参数,方便透传
      • parent:对父组件的引用
      • listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
      • injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。
    • 因为函数式组件只是函数,所以渲染开销也低很多。
    • 作为包装组件时它们也同样非常有用。比如,当你需要做这些时:
      • 程序化地在多个组件中选择一个来代为渲染;
      • 在将 children、props、data 传递给子组件之前操作它们。

    向子元素或子组件传递 attribute 和事件

    • 在普通组件中,没有被定义为 prop 的 attribute 会自动添加到组件的根元素上,将已有的同名 attribute 进行替换或与其进行智能合并。函数式组件要求你显式定义该行为。
    • 注释:智能合并应该只支持class
    • 注释:普通组件是指非函数组件。函数组件不会自动将 attribute 添加到根元素上
    • 完全透传任何 attribute、事件监听器、子节点等。这是非常透明的,以至于那些事件甚至并不要求 .native 修饰符。
    • 注释:只是实例化时选项的传递,返回的vnode根据这个选项生成。该例子中button是原生元素,my-functional-button是组件,但是他们都共用on这个选项来绑定事件,一个绑定原生事件一个绑定组件事件
    • 注释:同理,如果组件上传入nativeOn事件,由于原生button禁止使用该选项,所以会报错
    Vue.component('my-functional-button', {
      functional: true,
      render: function (createElement, context) {
        // 完全透传任何 attribute、事件监听器、子节点等。
        return createElement('button', context.data, context.children)
      }
    })
    
    • 如果你使用基于模板的函数式组件,那么你还需要手动添加 attribute 和监听器
    • 疑问:函数的模板是以 context 为上下文的?
    <template functional>
      <button
        class="btn btn-primary"
        v-bind="data.attrs"
        v-on="listeners"
      >
        <slot/>
      </button>
    </template>
    

    slots() 和 children 对比

    模板编译

    • 使用 Vue.compile 来实时编译模板字符串(文档突然没了)

    ——————————————————————————————————————————————————————————————————————————

    https://github.com/vuejs/jsx

    • 注释:使用 Vue JSX 插件后的支持的JSX语法

    Babel Preset JSX

    Installation

    npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
    
    • Then add the preset to .babelrc
    {
      "presets": ["@vue/babel-preset-jsx"]
    }
    

    Syntax

    Content

    • with dynamic content:
    render() {
      return <p>hello { this.message }</p>
    }
    

    Attributes/Props

    • 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

    • named slots:
    render() {
      return (
        <MyComponent>
          <header slot="header">header</header>
          <footer slot="footer">footer</footer>
        </MyComponent>
      )
    }
    
    • scoped slots:
    render() {
      const scopedSlots = {
        header: (props) => <header>{props.text}</header>,
        footer: (propst) => <footer>{props.text}</footer>
      }
    
      return <MyComponent scopedSlots={scopedSlots} />
    }
    

    Directives

    <input vModel={this.newTodoText} />
    
    • with a modifier:
    • 注释:v-model的3个修饰符。.trim:自动过滤用户输入的首尾空白字符
    <input vModel_trim={this.newTodoText} />
    
    • with an argument:
    • 注释:传入参数click字符串
    <input vOn:click={this.newTodoText} />
    
    • with an argument and modifiers:
    <input vOn:click_stop_prevent={this.newTodoText} />
    
    • v-html:
    • 注释:v-html实际上是对dom的innerHTML进行赋值
    • 疑问:可以用Attributes/Props中的domProps传入吗?
    <p domPropsInnerHTML={html} />
    

    Functional Components

    • Transpiles arrow functions that return JSX into functional components, when they are either default exports:
    • 疑问:一定要箭头函数吗?
    export default ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
    // 等价于
    export default {
      functional: true,
      render: (h, {
        props,
        listeners
      }) => <div onClick={listeners.click}>{props.msg}</div>
    }
    

    Compatibility

    • Babel 7+. For Babel 6 support, use vuejs/babel-plugin-transform-vue-jsx
    • 注释:这个插件只适用于 Babel 7+ , 6.x 版本请使用 vuejs/babel-plugin-transform-vue-jsx
    • Vue 2+. JSX is not supported for older versions.

    ——————————————————————————————————————————————————————————————————————————

    https://github.com/vuejs/jsx/tree/dev/packages/babel-preset-jsx

    • 注释:Babel 7+使用的转换插件集合,

    @vue/babel-preset-jsx

    Babel Compatibility Notes

    • This repo is only compatible with Babel 7.x, for 6.x please use vuejs/babel-plugin-transform-vue-jsx
    • 注释:这个插件只适用于 Babel 7.x , 6.x 版本请使用 vuejs/babel-plugin-transform-vue-jsx

    Usage

    • You can toggle specific features, by default all features are enabled, e.g.:
    • 注释:可以自定义哪些转换生效(似乎很鸡肋),默认全部生效
    {
      "presets": [
        [
          "@vue/babel-preset-jsx",
          {
            "vModel": false
          }
        ]
      ]
    }
    
    • Options are:
      • functional @vue/babel-sugar-functional-vue - Functional components syntactic sugar
      • injectH @vue/babel-sugar-inject-h - Automatic h injection syntactic sugar。注释:render中自动注入h为渲染函数
      • vModel @vue/babel-sugar-v-model - vModel syntactic sugar
      • vOn @vue/babel-sugar-v-on - vOn syntactic sugar

    ——————————————————————————————————————————————————————————————————

    https://github.com/vuejs/jsx/blob/dev/packages/babel-sugar-functional-vue/README.md

    • 注释:@vue/babel-preset-jsx 下的一部分,用来处理函数式组件的转换

    @vue/babel-sugar-functional-vue

    Babel Compatibility Notes

    Usage

    • Install the dependencies:
    # for yarn:
    yarn add @vue/babel-sugar-functional-vue
    # for npm:
    npm install @vue/babel-sugar-functional-vue --save
    
    • In your .babelrc:
    {
      "plugins": ["@vue/babel-sugar-functional-vue"]
    }
    

    Details

    • This plugin transpiles arrow functions that return JSX into functional components but only if it's an uppercase variable declaration or default export:
    • 注释:小写的导出是不会被转换为选项对象的
    // Original:
    export const A = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
    export const b = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
    export default ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
    
    // Result:
    export const A = {
      functional: true,
      render: (h, {
        props,
        listeners
      }) => <div onClick={listeners.click}>{props.msg}</div>
    }
    export const b = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
    export default {
      functional: true,
      render: (h, {
        props,
        listeners
      }) => <div onClick={listeners.click}>{props.msg}</div>
    }
    

    ——————————————————————————————————————————————————————————————————

    https://github.com/vuejs/jsx/blob/dev/packages/babel-sugar-inject-h/README.md

    @vue/babel-sugar-inject-h

    • 注释:@vue/babel-preset-jsx 下的一部分,用来向渲染函数注入变量h为渲染函数

    Babel Compatibility Notes

    Usage

    • Install the dependencies:
    # for yarn:
    yarn add @vue/babel-sugar-inject-h
    # for npm:
    npm install @vue/babel-sugar-inject-h --save
    
    • In your .babelrc:
    {
      "plugins": ["@vue/babel-sugar-inject-h"]
    }
    

    Details

    • This plugin automatically injects h in every method that has JSX. By using this plugin you don't have to always specifically declare h as first parameter in your render() function.
    • 注释:只有在使用了JSX的渲染函数中注入

    ——————————————————————————————————————————————————————————————————

    https://github.com/vuejs/jsx/blob/dev/packages/babel-sugar-v-model/README.md

    @vue/babel-sugar-v-model

    • 注释:@vue/babel-preset-jsx 下的一部分,用来处理JSX中的vModel

    Babel Compatibility Notes

    Usage

    • Install the dependencies:
    # for yarn:
    yarn add @vue/babel-sugar-v-model
    # for npm:
    npm install @vue/babel-sugar-v-model --save
    
    • In your .babelrc:
    {
      "plugins": ["@vue/babel-sugar-v-model"]
    }
    

    Details

    • It is recommended to use camelCase version of it (vModel) in JSX, but you can use kebab-case too (v-model).
    • 注释:也可以使用v-model的写法(但不建议)

    ——————————————————————————————————————————————————————————————————

    https://github.com/vuejs/jsx/blob/dev/packages/babel-sugar-v-on/README.md

    @vue/babel-sugar-v-on

    • 注释:@vue/babel-preset-jsx 下的一部分,用来处理JSX中的v-on

    Babel Compatibility Notes

    Usage

    • Install the dependencies:
    # for yarn:
    yarn add @vue/babel-sugar-v-on
    # for npm:
    npm install @vue/babel-sugar-v-on --save
    
    • In your .babelrc:
    {
      "plugins": ["@vue/babel-sugar-v-on"]
    }
    

    Details

    • It is recommended to use camelCase version of it (vOn) in JSX, but you can use kebab-case too (v-on).
    • 注释:可以使用v-on的形式,但不建议

    ————————————————————————————————————————————————————————————————————

    https://github.com/vuejs/babel-plugin-transform-vue-jsx

    • 注释:Babel 6.x使用的转换插件

    babel-plugin-transform-vue-jsx

    Babel Compatibility Notes

    • If using Babel 7, use 4.x
    • If using Babel 6, use 3.x
    • 疑问:Babel 7+ 不是使用 @vue/babel-preset-jsx 吗?

    Requirements

    • 疑问:Assumes you are using Babel with a module bundler e.g. Webpack, because the spread merge helper is imported as a module to avoid duplication.

    Usage

    npm install
      babel-plugin-syntax-jsx
      babel-plugin-transform-vue-jsx
      babel-helper-vue-jsx-merge-props
      babel-preset-env
      --save-dev
    
    • In your .babelrc:
    {
      "presets": ["env"],
      "plugins": ["transform-vue-jsx"]
    }
    

    h auto-injection

    • Starting with version 3.4.0 we automatically inject const h = this.$createElement in any method and getter (not functions or arrow functions) declared in ES2015 syntax that has JSX so you can drop the (h) parameter.
    • 注释:从3.4.0开始,自动注入变量h指向渲染函数。
    • 疑问:只有具有getter的函数才会自动注入
    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>
      }
    }
    

    Difference from React JSX

    • The equivalent of the above in Vue 2.0 JSX is:
    • 注释:这里的语法和@vue/babel-preset-jsx插件支持的语法不同
    render (h) {
      return h('div', {
        props: {
          msg: 'hi',
          onCustomEvent: this.customEventHandler // propsOnCustomEvent={this.customEventHandler}
        },
        attrs: {
          id: 'foo' // id="foo"
        },
        domProps: {
          innerHTML: 'bar' // domPropsInnerHTML="bar"
        },
        on: {
          click: this.clickHandler // onClick={this.clickHandler}
        },
        nativeOn: {
          click: this.nativeClickHandler // nativeOnClick={this.nativeClickHandler}
        },
        class: { // class={{ foo: true, bar: false }} 注释:通过{...}绑定一个表达式
          foo: true,
          bar: false
        },
        style: { // style={{ color: 'red', fontSize: '14px' }}
          color: 'red',
          fontSize: '14px'
        },
        key: 'key', // key="key"
        ref: 'ref', // ref="ref"
        // assign the `ref` is used on elements/components with v-for
        refInFor: true, // refInFor
        slot: 'slot' // slot="slot"
      })
    }
    

    Component Tip

    • If a custom element starts with lowercase, it will be treated as a string id and used to lookup a registered component. If it starts with uppercase, it will be treated as an identifier, which allows you to do:
    • 注释:如果组件名以小写开头,那么需要进行注册后才能使用。如果是大写开头可以直接在JSX中使用该组件无需注册。新版本插件没有这个限制
    import Todo from './Todo.js'
    
    export default {
      render (h) {
        return <Todo/> // no need to register Todo via components option
      }
    }
    

    JSX Spread

    • JSX spread is supported, and this plugin will intelligently merge nested data properties. For example:
    const data = {
      class: ['b', 'c']
    }
    const vnode = <div class="a" {...data}/>
    
    { class: ['a', 'b', 'c'] }
    

    Vue directives

    • 在JSX中vue原生指令除了v-show={value}其他指令都不支持
    • 自定义指令可以通过v-name={value}使用,但是不支持参数和修饰符
      • Pass everything as an object via value, e.g. v-name={{ value, modifier: true }}
        • 注释:可以通过传入一个对象来解决,应该是插件提供的,并不是模板语言原有。
        • 疑问:只支持修饰符?
      • 使用渲染函数第2个参数中的directives绑定指令
  • 相关阅读:
    zookeeper集群的部署
    【转】始于Jupyter Notebooks:一份全面的初学者实用指南
    【转】Jupyter Notebook主题字体设置及自动代码补全
    【转】pip install 快速下载
    【转】Sublime Text 3 常用快捷键
    【转】Python——DataFrame基础操作
    【转】sqlite3 小结
    【转】SQLite3的安装与使用
    Python Pandas pandas.DataFrame.to_sql函数方法的使用
    处理异常 ‘try’——‘except’ 方法
  • 原文地址:https://www.cnblogs.com/qq3279338858/p/12370796.html
Copyright © 2011-2022 走看看