vue render函数

vue

VUE一般使用template来创建HTML,然后在有的时候,我们需要使用javascript来创建html,这时候我们需要使用render函数。
比如如下我想要实现如下html:

<div >

<h1>

<a href="#">

Hello world!

</a>

</h1>

</div>

  我们会如下使用:

<!DOCTYPE html>

<html>

<head>

<title>演示Vue</title>

<style>

</style>

</head>

<body>

<div >

<tb-heading :level="1">

<a href="#">Hello world!</a>

</tb-heading>

</div>

</body>

<script src="./vue.js"></script>

<script type="text/x-template" >

<h1 v-if="level === 1">

<slot></slot>

</h1>

<h2 v-else-if="level === 2">

<slot></slot>

</h2>

</script>

<script>

Vue.component('tb-heading', {

template: '#templateId',

props: {

level: {

type: Number,

required: true

}

}

});

new Vue({

el: '#container'

});

</script>

</html>

如上代码是根据参数 :level来显示不同级别的标题中插入锚点元素,我们需要重复的使用 <slot></slot>.

下面我们来尝试使用 render函数重写上面的demo;如下代码:

<!DOCTYPE html>

<html>

<head>

<title>演示Vue</title>

<style>

</style>

</head>

<body>

<div >

<tb-heading :level="2">

<a href="#">Hello world!</a>

</tb-heading>

</div>

</body>

<script src="./vue.js"></script>

<script>

Vue.component('tb-heading', {

render: function(createElement) {

return createElement(

'h' + this.level, // tag name 标签名称

this.$slots.default // 组件的子元素

)

},

props: {

level: {

type: Number,

required: true

}

}

});

new Vue({

el: '#container'

});

</script>

</html>

  如上 render函数代码看起来非常简单就实现了,组件中的子元素存储在组件实列中 $slots.default 中。

理解createElement
Vue通过建立一个虚拟DOM对真实的DOM发生变化保存追踪,如下代码:
return createElement('h1', this.title);
createElement返回的是包含的信息会告诉VUE页面上需要渲染什么样的节点及其子节点。我们称这样的节点为虚拟DOM,可以简写为VNode,

createElement 参数

// @return {VNode}

createElement(

// {String | Object | Function}

// 一个HTML标签字符串,组件选项对象,或者一个返回值类型为String/Object的函数。该参数是必须的

'div',

// {Object}

// 一个包含模板相关属性的数据对象,这样我们可以在template中使用这些属性,该参数是可选的。

{

},

// {String | Array}

// 子节点(VNodes)由 createElement() 构建而成。可选参数

// 或简单的使用字符串来生成的 "文本节点"。

[

'xxxx',

createElement('h1', '一则头条'),

createElement(MyComponent, {

props: {

someProp: 'xxx'

}

})

]

)

  理解深入data对象。

在模板语法中,我们可以使用 v-bind:class 和 v-bind:style 来绑定属性,在VNode数据对象中,下面的属性名的字段级别是最高的。
该对象允许我们绑定普通的html特性,就像DOM属性一样。如下:

{

// 和`v-bind:class`一样的 API

'class': {

foo: true,

bar: false

},

// 和`v-bind:style`一样的 API

style: {

color: 'red',

fontSize: '14px'

},

// 正常的 HTML 特性

attrs: {

id: 'foo'

},

// 组件 props

props: {

myProp: 'bar'

},

// DOM 属性

domProps: {

innerHTML: 'baz'

},

// 事件监听器基于 `on`

// 所以不再支持如 `v-on:keyup.enter` 修饰器

// 需要手动匹配 keyCode。

on: {

click: this.clickHandler

},

// 仅对于组件,用于监听原生事件,而不是组件内部使用 `vm.$emit` 触发的事件。

nativeOn: {

click: this.nativeClickHandler

},

// 自定义指令。注意事项:不能对绑定的旧值设值

// Vue 会为您持续追踪

directives: [

{

name: 'my-custom-directive',

value: '2',

expression: '1 + 1',

arg: 'foo',

modifiers: {

bar: true

}

}

],

// Scoped slots in the form of

// { name: props => VNode | Array<VNode> }

scopedSlots: {

default: props => createElement('span', props.text)

},

// 如果组件是其他组件的子组件,需为插槽指定名称

slot: 'name-of-slot',

// 其他特殊顶层属性

key: 'myKey',

ref: 'myRef'

}

  上面的data数据可能不太好理解,我们来看一个demo,就知道它是如何使用的了,如下代码:

<!DOCTYPE html>

<html>

<head>

<title>演示Vue</title>

<style>

</style>

</head>

<body>

<div >

<tb-heading :level="2">

Hello world!

</tb-heading>

</div>

</body>

<script src="./vue.js"></script>

<script>

var getChildrenTextContent = function(children) {

return children.map(function(node) {

return node.children ? getChildrenTextContent(node.children) : node.text

}).join('')

};

Vue.component('tb-heading', {

render: function(createElement) {

var headingId = getChildrenTextContent(this.$slots.default)

.toLowerCase()

.replace(/\W+/g, '-')

.replace(/(^\-|\-$)/g, '')

return createElement(

'h' + this.level,

[

createElement('a', {

attrs: {

name: headingId,

href: '#' + headingId

},

style: {

color: 'red',

fontSize: '20px'

},

'class': {

foo: true,

bar: false

},

// DOM属性

domProps: {

innerHTML: 'baz'

},

// 组件props

props: {

myProp: 'bar'

},

// 事件监听基于 'on'

// 所以不再支持如 'v-on:keyup.enter' 修饰语

// 需要手动匹配 KeyCode

on: {

click: function(event) {

event.preventDefault();

console.log(111);

}

}

}, this.$slots.default)

]

)

},

props: {

level: {

type: Number,

required: true

}

}

});

new Vue({

el: '#container'

});

</script>

</html>

  对应的属性使用方法和上面一样既可以了,我们可以打开页面查看下效果也是可以的。如下

VNodes 不一定必须唯一 (文档中说要唯一)
文档中说 VNode必须唯一;说 下面的 render function 是无效的:
但是我通过测试时可以的,如下代码:

<!DOCTYPE html>

<html>

<head>

<title>演示Vue</title>

<style>

</style>

</head>

<body>

<div >

<tb-heading :level="2">

Hello world!

</tb-heading>

</div>

</body>

<script src="./vue.js"></script>

<script>

Vue.component('tb-heading', {

render: function(createElement) {

var pElem = createElement('p', 'hello world');

return createElement('div', [

pElem, pElem

])

},

props: {

level: {

type: Number,

required: true

}

}

});

new Vue({

el: '#container'

});

</script>

</html>

使用Javascript代替模板功能
 v-if 和 v-for
template 中有 v-if 和 v-for, 但是vue中的render函数没有提供专用的API。
比如如下:

<ul v-if="items.length">

<li v-for="item in items">{{ item.name }}</li>

</ul>

<p v-else>No item found.</p>

  在render函数中会被javascript的 if/else 和map重新实现。如下代码:

<!DOCTYPE html>

<html>

<head>

<title>演示Vue</title>

<style>

</style>

</head>

<body>

<div >

<tb-heading>

Hello world!

</tb-heading>

</div>

</body>

<script src="./vue.js"></script>

<script>

Vue.component('tb-heading', {

render: function(createElement) {

console.log(this)

if (this.items.length) {

return createElement('ul', this.items.map(function(item){

return createElement('li', item.name);

}))

} else {

return createElement('p', 'No items found.');

}

},

props: {

items: {

type: Array,

default: function() {

return [

{

name: 'kongzhi1'

},

{

name: 'kongzhi2'

}

]

}

}

}

});

new Vue({

el: '#container'

});

</script>

</html>

v-model

render函数中没有 与 v-model相应的api,我们必须自己来实现相应的逻辑。如下代码可以实现:

<!DOCTYPE html>

<html>

<head>

<title>演示Vue</title>

<style>

</style>

</head>

<body>

<div >

<tb-heading @input="inputFunc">

Hello world!

</tb-heading>

</div>

</body>

<script src="./vue.js"></script>

<script>

Vue.component('tb-heading', {

render: function(createElement) {

var self = this;

return createElement('input', {

domProps: {

value: '11'

},

on: {

input: function(event) {

self.value = event.target.value;

self.$emit('input', self.value);

}

}

})

},

props: {

}

});

new Vue({

el: '#container',

methods: {

inputFunc: function(value) {

console.log(value)

}

}

});

</script>

</html>

理解插槽

可以从 this.$slots 获取VNodes列表中的静态内容:如下代码:

<!DOCTYPE html>

<html>

<head>

<title>演示Vue</title>

<style>

</style>

</head>

<body>

<div >

<tb-heading :level="2">

<a href="#">Hello world!</a>

</tb-heading>

</div>

</body>

<script src="./vue.js"></script>

<script>

Vue.component('tb-heading', {

render: function(createElement) {

return createElement(

'h' + this.level, // tag name 标签名称

this.$slots.default // 子组件

)

},

props: {

level: {

type: Number,

required: true

}

}

});

new Vue({

el: '#container'

});

</script>

</html>

理解函数式组件

函数式组件我们标记组件为 functional, 意味着它无状态(没有data), 无实列(没有this上下文)。
一个函数式组件像下面这样的:

Vue.component('my-component', {

functional: true,

// 为了弥补缺少的实列

// 提供第二个参数作为上下文

render: function(createElement, context) {

},

// Props 可选

props: {

}

})

组件需要的一切通过上下文传递,包括如下:
props: 提供props对象
children: VNode子节点的数组
slots: slots对象
data: 传递给组件的data对象
parent: 对父组件的引用
listeners: (2.3.0+) 一个包含了组件上所注册的 v-on 侦听器的对象。这只是一个指向 data.on 的别名。
injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。

在添加 functional: true 之后,组件的 render 函数之间简单更新增加 context 参数,this.$slots.default 更新为 context.children,之后this.level 更新为 context.props.level。
如下代码演示:

<!DOCTYPE html>

<html>

<head>

<title>演示Vue</title>

<style>

</style>

</head>

<body>

<div >

{{msg}}

<choice>

<item value="1">test</item>

</choice>

</div>

</body>

<script src="./vue.js"></script>

<script>

Vue.component('choice', {

template: '<div><ul><slot></slot></ul></div>'

});

Vue.component('item', {

functional: true,

render: function(h, context) {

return h('li', {

on: {

click: function() {

console.log(context);

console.log(context.parent);

console.log(context.props)

}

}

}, context.children)

},

props: ['value']

})

new Vue({

el: '#container',

data: {

msg: 'hello'

}

});

</script>

</html>

 https://www.zhihu.com/question/54217073

以上是 vue render函数 的全部内容, 来源链接: utcz.com/z/378876.html

回到顶部