您的位置:首页 > 产品设计 > UI/UE

VUE快速入门心得——深入了解render函数

2017-07-30 22:56 399 查看
前边我们有说了一些render函数的基础,以及render函数的用法。今天呢,我们进来深入了解一下render函数。我们就从
creatElement
参数开始,前边我们也说粗略的说了下
creatElement
,接下来我们继续谈谈
creatElement


creatElement
参数

首先我们需要熟悉的是如何在 createElement 函数中生成模板。这里是
createElement
接受的参数:

// @returns {VNode}
createElement(
// {String | Object | Function}
// 一个 HTML 标签字符串,组件选项对象,或者一个返回值类型为String/Object的函数,必要参数
'div',
// {Object}
// 一个包含模板相关属性的数据对象
// 这样,您可以在 template 中使用这些属性.可选参数.
{
// (详情见下一节)
},
// {String | Array}
// 子节点 (VNodes),由 `createElement()` 构建而成,
// 或简单的使用字符串来生成“文本结点”。可选参数。
[
'先写一些文字',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)


深入了解
data object
参数

有一件事要注意:正如在模板语法中,
v-bind:class
v-bind:style
,会被特别对待一样,在
VNode
数据对象中,下列属性名是级别最高的字段。该对象也允许你绑定普通的 HTML 特性,就像 DOM 属性一样,比如
innerHTML
(这会取代
v-html
指令)。

{
// 和`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 指定名称
slot: 'name-of-slot',
// 其他特殊顶层属性
key: 'myKey',
ref: 'myRef'
}


完整示例

有了上边这些知识,现在可以完成我们最开始想要实现的组件:

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) {
// create kebabCase 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
}
}
})


规定

VNodes 必须唯一

组件树中的所有 VNodes 必须是唯一的。这意味着,下面的 render function 是无效的:

render: function (createElement) {
var myParagraphVNode = createElement('p', 'hi')
return createElement('div', [
// 错误-重复的VNodes
myParagraphVNode, myParagraphVNode
])
}


如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现。例如,下面这个例子 render 函数完美有效地渲染了 20 个重复的段落:

render: function (createElement) {
return createElement('div',
Array.apply(null, { length: 20 }).map(function () {
return createElement('p', 'hi')
})
)
}


使用JavaScript代替模板功能

v-if
v-for


由于使用原生的 JavaScript 来实现某些东西很简单,Vue 的 render 函数没有提供专用的 API。比如, template 中的
v-if
v-for
:

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


这些都会在 render 函数中被 JavaScript 的
if
/
else
map
重写:

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


render函数中没有与
v-model
相应的api - 你必须自己来实现相应的逻辑:

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


这就是深入底层要付出的,尽管麻烦了一些,但相对于
v-model
来说,你可以更灵活地控制。

事件&按键修饰符

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



例如:

on: {
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
`~!mouseover`: this.doThisOnceInCapturingMode
}


对于其他的修饰符, 前缀不是很重要, 因为你可以直接在事件处理函数中使用事件方法:



这里是一个使用所有修饰符的例子:

on: {
keyup: function (event) {
// 如果触发事件的元素不是事件绑定的元素
// 则返回
if (event.target !== event.currentTarget) return
// 如果按下去的不是enter键或者
// 没有同时按下shift键
// 则返回
if (!event.shiftKey || event.keyCode !== 13) return
// 阻止 事件冒泡
event.stopPropagation()
// 阻止该元素默认的keyup事件
event.preventDefault()
// ...
}
}


slots

你可以从
this.$slots
获取VNodes列表中的静态内容:

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


还可以从
this.$scopedSlots
中获得能用作函数的作用域插槽,这个函数返回 VNodes:

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


如果要用 render 函数向子组件中传递作用域插槽,可以利用VNode数据中的
scopedSlots
域:

render (createElement) {
return createElement('div', [
createElement('child', {
// pass `s
b158
copedSlots` in the data object
// in the form of { name: props => VNode | Array<VNode> }
scopedSlots: {
default: function (props) {
return createElement('span', props.text)
}
}
})
])
}


jsx

如果你写了很多
render
函数,可能会觉得痛苦:

createElement(
'anchored-heading', {
props: {
level: 1
}
}, [
createElement('span', 'Hello'),
' world!'
]
)


特别是模板如此简单的时候:

<anchored-heading :level="1">
<span>Hello</span> world!
</anchored-heading>


这就是为什么会有一个 Babel 插件 ,用于在 Vue 中使用 JSX 语法的原因,它可以让我们回到更接近于模板的语法上。

import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})


h
作为
createElement
的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的,如果在作用域中
h
失去作用, 在应用中会触发报错。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: