Vue使用JavaScript代替模板功能(渲染函数篇)

举报
前端老实人 发表于 2022/01/07 13:27:38 2022/01/07
【摘要】 createElement参数createElement接收的参数:createElement(标签名(必需), 与模板中属性对应的数据对象(可选), 子级虚拟节点(可选)); 深入数据对象{ // 与 `v-bind:class` 的 API 相同,接受一个字符串、对象或字符串和对象组成的数组 class: { foo: true, bar: false }, // ...

createElement参数

createElement接收的参数:

createElement(标签名(必需), 与模板中属性对应的数据对象(可选), 子级虚拟节点(可选));

深入数据对象

{
  // 与 `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属性
  domProps: {
    innerHTML: 'baz',
  },
  // 事件监听器,不支持如“v-on:keyup.enter”这样的修饰器
  on: {
    click: this.onClick
  },
  // 仅用于组件,用于监听原生事件,而不是组件内部使用 vm.$emit 触发的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定义指令。注意,无法对 `binding` 中的 `oldValue`赋值,因为 Vue 已经自动为你进行了同步。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // 其它特殊顶层属性
  key: 'myKey',
  ref: 'myRef',
  // 如果在渲染函数中给多个元素都应用了相同的 ref 名,那么 `$refs.myRef` 会变成一个数组。
  refInFor: true
  // 作用域插槽,格式为:{ name: props => VNode | Array<VNode> }
  // 如果组件是其它组件的子组件,需为插槽指定名称
  slot: 'name-of-slot',
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
}

使用JavaScript代替模板功能

v-if 和 v-for

只要在原生的 JavaScript 中可以轻松完成的操作,Vue 的渲染函数就不会提供专有的替代方法。比如,在模板中使用的 v-if 和 v-for:

<ul v-if="items.length">
  <li v-for="item in items">{{ item }}</li>
</ul>
<p v-else>No items found.</p>

这些都可以在渲染函数中用 JavaScript 的 if/else 和 map 来重写:

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

v-model

渲染函数中没有与v-model的直接对应—必须自己实现相应的逻辑:

<input v-model="value" />
data () {
  return {
    value: 'ceshi',
  }
},
render (createElement) {
  const self = this;
  return createElement('input', {
    attrs: {
      value: self.value
    },
    on: {
      input (e) {
        self.value = e.target.value;
      }
    },
  });
},

事件&按键修饰符

对于 .passive、.capture 和 .once 这些事件修饰符, Vue 提供了相应的前缀可以用于 on:

修饰符 前缀
.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  对于别的按键修饰符来说,可将 13 改为另一个按键码
修饰键:.ctrl, .alt, .shift, .meta if (!event.ctrlKey) return (将 ctrlKey 分别修改为 altKey、shiftKey 或者 metaKey)

插槽

可以通过 this.$slots 访问静态插槽的内容,每个插槽都是一个 VNode 数组:

<div>
  <slot></slot>
</div>
render: function (createElement) {
  return createElement('div', this.$slots.default)
}

也可以通过 this.$scopedSlots 访问作用域插槽,每个作用域插槽都是一个返回若干 VNode 的函数:

<div>
  <slot :text="message"></slot>
</div>
data() {
  return {
    msg: 'hello world',
  }
},
render: function (createElement) {
  return createElement('div', [
    this.$scopedSlots.default({
      text: this.msg
    })
  ])
}

如果要用渲染函数向子组件中传递作用域插槽,可以利用 VNode 数据对象中的 scopedSlots 字段:

<div>
  <base-slot v-slot="slotProps">
    {{ slotProps.text }}
  </base-slot>
</div>
render: function (createElement) {
  return createElement('div', [
    createElement('base-slot', {
       // 在数据对象中传递 `scopedSlots` 
       // 格式为 { name: props => VNode | Array<VNode> } 
      scopedSlots: {
        default: function (props) {
          return createElement('span', props.text)
        }
      }
    })
  ])
}

最后

如果对您有帮助,希望能给个👍评论/收藏/三连!

博主为人老实,无偿解答问题哦❤

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。