23. 渲染函数和jsx

2019-06-04  本文已影响0人  论宅

通常情况下,在我们使用模板的时候,不太方便在模板内部根据需要切换不同的标签,如:提供一个参数,根据这个参数模板返回h1-h7的标签,并填上内容,一般情况下会是这样——

var component1 = {
        data:function(){
          return {
            hdata:"你好啊",
            level:2
          }
        },
        template: `
      <div>
          <h1 v-if='level==1'>{{hdata}}</h1>
          <h2 v-if='level==2'>{{hdata}}</h2>
          <h3 v-if='level==3'>{{hdata}}</h3>
          <h4 v-if='level==4'>{{hdata}}</h4>
      
      </div>
      `
      };

但这样会使得内容过于繁重,所以出现了渲染函数。
渲染函数可以将如上的模板代码,用js代码写出来,使得内容变得简练:

var component2 = {
      data: function () {
        return {
          hdata: "你好啊2",
          level: 2
        }
      },
      render: function (createEle) {
        var arr = [createEle("div", "ccc"), createEle("div", "ccc")]
        return createEle(
          "h" + this.level, { class: "aaa" },
          arr
        )


      }
    }

如上,使用render函数代替了template,该参数接受一个createElement方法参数,调用这个方法会生成html代码。
这个方法接受三个参数,第一个是标签名,第二个是这个标签的属性,第三个是标签内容。

createEle可以理解为创建节点的方法,可以创造一个标签,赋予标签的属性,并且填入内容。
其内容是可以其他createEle创造出来的元素。
如上例。

节点,树和虚拟dom

标签,标签的内容,标签的属性都可以算作节点,而这些节点组合起来的就是树,而vue使用的节点并非实际的dom,而是虚拟的vue节点。

虚拟dom创造方法:createElement

这个方法会告诉vue页面需要渲染什么样的节点。

该方法的三个参数分别需要:

// {String | Object | Function}
  // 一个 HTML 标签名、组件选项对象,或者
  // resolve 了上述任何一种的一个 async 函数。必填项。
  'div',
 // {Object}
  // 一个与模板中属性对应的数据对象。可选。
  {
    // (详情见下一节)
  },
/ {String | Array}
  // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
  // 也可以使用字符串来生成“文本虚拟节点”。可选。

上下两个参数理解起来较为简单,中间的参数比较复杂:

{
  // 与 `v-bind:class` 的 API 相同,
  // 接受一个字符串、对象或字符串和对象组成的数组
  'class': {
    foo: true,
    bar: false
  },
  // 与 `v-bind:style` 的 API 相同,
  // 接受一个字符串、对象,或对象组成的数组
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 普通的 HTML 特性
  attrs: {
    id: 'foo'
  },
  // 组件 prop
  props: {
    myProp: 'bar'
  },
  // DOM 属性
  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',
  // 其它特殊顶层属性
  key: 'myKey',
  ref: 'myRef',
  // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
  // 那么 `$refs.myRef` 会变成一个数组。
  refInFor: true
}
``

理解了以上参数之后,渲染便可以这样书写:

var getChildrenTextContent = function (children) {
return children.map(function (node) {
return node.children
? getChildrenTextContent(node.children)
: node.text
}).join('')
}

Vue.component('anchored-heading', {
render: function (createElement) {
// 创建 kebab-case 风格的 ID
var headingId = getChildrenTextContent(this.slots.default) .toLowerCase() .replace(/\W+/g, '-') .replace(/(^-|-)/g, '')

return createElement(
  'h' + this.level,
  [
    createElement('a', {
      attrs: {
        name: headingId,
        href: '#' + headingId
      }
    }, this.$slots.default)
  ]
)

},
props: {
level: {
type: Number,
required: true
}
}
})


### 渲染函数的利弊
渲染函数不能使用相当的组件,如v-model,对应的逻辑必须自己手动去实现:
v-if,v-for

props: ['items'],
render: function (createElement) {
if (this.items.length) {
return createElement('ul', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'No items found.')
}
}


v-model

props: ['value'],
render: function (createElement) {
var self = this
return createElement('input', {
domProps: {
value: self.value
},
on: {
input: function (event) {
self.$emit('input', event.target.value)
}
}
})
}


### 插槽
你可以通过`this.$slots`来访问静态插槽的内容,每个插槽都是一个vnode数组。
可以通过`this.$scopedSlots`访问作用域插槽。

### JSX
上一篇 下一篇

猜你喜欢

热点阅读