Skip to content

Vue 中除了使用 template 模板外,还可以使用 render 函数(优先级高于 template)。

render 函数接收一个 createElement 函数作为参数,返回 VNode(虚拟节点)。

10-1.createElement

createElement 函数是 Vue 提供的,用于创建 VNode。也可以使用别名 h

基本格式如下:

js
// https://v2.cn.vuejs.org/v2/guide/render-function.html
const render = function (createElement) {
	// @returns {VNode}
	return createElement(
		// {String | Object | Function}
		// 一个 HTML 标签名、组件选项对象,或者
		// resolve 了上述任何一种的一个 async 函数。必填项。
		'div',

		// {Object} vue2-render-attrs
		// 一个与模板中 attribute 对应的数据对象。可选。
		{
		},

		// {String | Array}
		// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
		// 也可以使用字符串来生成“文本虚拟节点”。可选。
		[
			'Hello World',
			createElement('h1', '你好'),
			createElement(MyComponent, {
				props: {
					someProp: 'foobar'
				}
			})
		]
	)
}

vue2-render-attrs 详细补充如下:

js
render (h) {
  return h('div', {
    // Component props
    props: {
      msg: 'hi',
      onCustomEvent: this.customEventHandler
    },
    // normal HTML attributes
    attrs: {
      id: 'foo'
    },
    // DOM props
    domProps: {
      innerHTML: 'bar'
    },
    // Event handlers are nested under "on", though
    // modifiers such as in v-on:keyup.enter are not
    // supported. You'll have to manually check the
    // keyCode in the handler instead.
    on: {
      click: this.clickHandler
    },
    // For components only. Allows you to listen to
    // native events, rather than events emitted from
    // the component using vm.$emit.
    nativeOn: {
      click: this.nativeClickHandler
    },
    // class is a special module, same API as `v-bind:class`
    class: {
      foo: true,
      bar: false
    },
    // style is also same as `v-bind:style`
    style: {
      color: 'red',
      fontSize: '14px'
    },
    // other special top-level properties
    key: 'key',
    ref: 'ref',
    // assign the `ref` is used on elements/components with v-for
    refInFor: true,
    slot: 'slot'
  }, [
    'Hello World',
    h('h1', '你好'),
    h(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ])
}

10-2.jsx

在业务中直接使用 createElement 函数,比较繁琐,因此 Vue 提供了 jsx 语法简化操作。

js
const render = function (h) {
  return (
    <div
      // normal attributes or prefix with on props.
      id="foo"
      propsOnCustomEvent={this.customEventHandler}
      // DOM properties are prefixed with `domProps`
      domPropsInnerHTML="bar"
      // event listeners are prefixed with `on` or `nativeOn`
      onClick={this.clickHandler}
      nativeOnClick={this.nativeClickHandler}
      // other special top-level properties
      class={{ foo: true, bar: false }}
      style={{ color: 'red', fontSize: '14px' }}
      key="key"
      ref="ref"
      // assign the `ref` is used on elements/components with v-for
      refInFor
      slot="slot">
      Hello World
      <h1>你好</h1>
      <MyComponent someProp="foobar" />
    </div>
  )
}

如果要在 Vue 项目中使用 jsx,则需要安装 babel 插件。

  1. 如果是 babel@7,那么:
bash
pnpm add @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props -D

babel.config.js 中配置:

js
module.exports = {
  presets: ['@vue/babel-preset-jsx']
}
  1. 如果是 babel@6,那么:
bash
pnpm add babel-plugin-transform-vue-jsx -D

babel.config.js 中配置:

js
module.exports = {
  plugins: ['transform-vue-jsx']
}

额外链接补充:

10-3.functional

假设一个组件没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。而只是一个接受一些 prop

那么这个组件,我们可以改造成函数式组件

事实上,这个函数式组件就是一个接受 props 并返回 VNode 的函数。因此函数式组件的特性有:

  • 没有响应式数据
  • 没有 this 上下文,全局状态通过 context 获取
  • 没有生命周期方法

10-3-1.context

函数式组件没有 this 上下文,但是需要访问组件 propsslotsscopedSlots 等属性,因此 Vue 提供了 context 上下文。

context 对象包含以下属性:

  • props:提供所有 prop 的对象;
  • childrenVNode 子节点的数组;
  • slots:一个函数,返回了包含所有插槽的对象;
  • scopedSlots:(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽;
  • data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件;
  • parent:对父组件的引用;
  • listeners:(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名;
  • injections:(2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的 property

10-3-2.JS语法

我们可以使用 template 或者 render 函数来声明函数式组件。

render 函数为例:

js
const MyComponent = {
  functional: true,
  props: {
    msg: {
      type: String,
      default: 'Hello World'
    }
  },
  render (h, context) {
    return (
      <div>{context.props.msg}</div>
    )
  }
}

10-3-3.vue单文件语法

vue 单文件组件中,使用 functional 属性在 <template> 标签上声明,这样 vue 会自动将 context 作为上下文传入。

这样我们就可以在模板中直接使用 props 访问 prop 数据。

vue
<template functional>
  <div>{{ props.msg }}</div>
</template>