Vue.js 2.x render 渲染函数 & JSX
Vue.js 2.x render
渲染函数 & JSX
Vue绝大多数情况下使用template创建 HTML。但是比如一些重复性比较高的场景,需要运用 JavaScript 的完全编程能力,可以使用render函数。
1. 节点、树以及虚拟DOM
每个元素都是一个节点。每片文字也是一个节点。甚至注释也都是节点。
一个节点就是页面的一个部分。每个节点都可以有子节点。

比如上面的节点树就表示下面的代码:
<div>
<h1>My title</h1>
Some text content
<!-- TODO: 添加标签行 -->
</div>
1.1 虚拟 DOM
Vue 通过建立一个虚拟 DOM 对真实 DOM 发生的变化保持追踪。
虚拟 DOM 是对Vue组件树建立起来的整个 VNode(Virtual Node)树的称呼。
1.1.1 createElement 参数
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一个 HTML 标签字符串,组件选项对象,或者
// 解析上述任何一种的一个 async 异步函数
// 必须参数
'div',
// {Object}
// 一个包含模板相关属性的数据对象
// 你可以在 template 中使用这些特性
// 可选参数
{
// 属性参见 1.1.2 深入 data 对象
},
// {String | Array}
// 子虚拟节点(VNodes) 由 createElement() 构建而成
// 也可以使用字符串来生成“文本虚拟节点”
// 可选参数
[
'文字',
createElement('h1', '一个标题'),
createElement(MyComponent, {
props: {
someProp: 'foo'
}
})
]
)
1.1.2 深入 data 对象
在 VNode 数据对象中,下列属性名是级别最高的字段。该对象也允许绑定普通的 HTML 特性,就像 DOM 属性一样,比如innerHTML(这会取代v-html指令)。
{
// 和 v-bind:calss 一样的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
},
// 自定义指令
// 注意你无法对 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
}
1.1.3 完整示例
一个生成锚点标题的组件:
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
}
}
})
使用效果:
<div id="app">
<anchored-heading :level="1">first</anchored-heading>
<anchored-heading :level="2">second</anchored-heading>
<anchored-heading :level="3">
third
<small>thirdSub</small>
</anchored-heading>
</div>

1.1.4 约束
组件树中的所有 VNodes 必须是唯一的。
比如这样写是错误的:
render: function (createElement) {
var myParagraphVNode = createElement('p', 'hi')
return createElement('div', [
// 错误-重复的 VNodes
myParagraphVNode, myParagraphVNode
])
}
如果需要重复多次元素/组件,可以用工厂函数来实现:
render: function(createElement) {
return createElement('div',
Array.apply(null, { length: 20 }.map(function() {
return createElement('p', 'hello')
}))
);
}
1.2 使用 JavaScript 代替模板功能
1.2.1 v-if 和 v-for
只要在原生的 JavaScript 中可以轻松完成的操作,Vue的render函数就不会提供专有的替代方法。
比如这里的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 方法重写:
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 here.');
}
}
1.2.2 v-model
render 函数中没有与 v-model直接对应的方法,需要手动实现:
props: ['value'],
render: function (createElement) {
var self = this;
return createElement('input', {
// DOM属性
domProps: {
value: self.value
},
on: {
input: function(e) {
self.$emit('input', e.target.value);
}
}
})
}
1.3 事件&按键修饰符
对于.passive、.capture、.once事件修饰符,Vue提供了相应的前缀可以用于on:
| Modifier(s) | Prefix |
|---|---|
.passive |
& |
.capture |
! |
.once |
~ |
.capture.once或.once.capture |
~! |
比如:
on: {
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~!mouseover': this.doThisOnceInCapturingMode
}
对于其他的修饰符,可以在事件处理函数中使用事件方法:
| Modifier(s) | Equivalent in Handler |
|---|---|
.stop |
e.stopPropagation() |
.prevent |
e.preventDefault() |
.self |
if (e.target !== e.currentTarget) return; |
.enter, .13 |
e.keyCode === 13 |
比如:
on: {
keyup: function (event) {
// 如果触发事件的元素不是事件绑定的元素
// 则返回
if (event.target !== event.currentTarget) return
// 如果按下去的不是 enter 键或者
// 没有同时按下 shift 键
// 则返回
if (!event.shiftKey || event.keyCode !== 13) return
// 阻止 事件冒泡
event.stopPropagation()
// 阻止该元素默认的 keyup 事件
event.preventDefault()
// ...
}
}
1.4 插槽
可以通过this.$slots访问静态插槽内容,得到的是一个 VNodes 数组:
render: function(createElement) {
// `<div><slot></slot></div>`
return createElement('div', this.$slots.default)
}
也可以通过this.$scopeSlots访问作用域插槽,得到的是一个 VNodes 的函数:
props: ['message'],
render: function(createElement) {
// `<div><slot :text="message"></slot></div>`
return createElement('div', [
this.$scopedSlots.default({
text: this.message
})
])
}
如果要用渲染函数句子向子组件中传递作用域插槽,可以利用 VNode 数据对象中的scopedSlots域:
render: function(createElement) {
return createElement('div', [
createElement('child', {
// 在数据对象中传递 scopedSlots
// 格式:{ name: props => VNode | Array<VNode> }
scopedSlots: {
default: function(props) {
return createElement('span', props.text)
}
}
})
])
}
2. JSX
在Vue中使用JSX语法,可以避免书写繁杂冗余的render代码,Babel插件用于在Vue中使用JSX语法。
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#app',
render: function(h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world
</AnchoredHeading>
)
}
})
3. 函数式组件
一个函数是组件,意味着它是无状态(没有响应式数据),无实例(没有this上下文)的:
Vue.component('my-component', {
functional: true,
// props可选 v2.3.0^
props: {},
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render(createElement, context) {
}
})
在v2.5.0^版本中,单文件组件基于模板的函数式组件可以这样声明:
<template functional></template>
组件需要的一切都是通过上下文来传递:
props提供所有prop对象childrenVNode子节点的数组slots返回所有插槽的对象的函数scopedSlots一个暴露传入的作用域插槽以及函数形式的普通插槽的对象data传递给组件的数据对象,作为CreateElement的第二个参数传入组件parent对父组件的引用listeners一个包含了所有在父组件上注册的事件侦听器的对象,data.on的别名injections如果使用了inject选项,该对象包含了应当被注入的属性
之前的锚点标题组件可以改为如下的函数式组件:
...
Vue.component('anchored-heading', {
functional: true,
render: function (createElement, context) {
// 创建 kebab-case 风格的ID
var headingId = getChildrenTextContent(context.children)
.toLowerCase()
.replace(/\W+/g, '-')
.replace(/(^-|-$)/g, '')
return createElement(
'h' + context.props.level,
[
createElement('a', {
attrs: {
name: headingId,
href: '#' + headingId
}
}, context.children)
]
)
},
props: {
level: {
type: Number,
required: true
}
}
})
3.1 想子组件或子元素传递特性和事件
在普通组件中,没有被定义为 prop 的特性会自动添加到组件的根元素上,将现有的同名特性替换或智能合并。
而函数式组件要求必须显示定义该行为:
Vue.component('my-functional-button', {
functional: true,
render: function(createElement, context) {
return createElement('button', context.data, context.children)
}
})
如果使用模板的函数式组件,还需要手动添加特性和监听器。
<template functional>
<button
class="btn btn-primary"
v-bind="data.attrs"
v-on="listeners"
>
<slot/>
</button>
</template>
3.2 slots()和children对比
总的来说个人理解,slots()可以拿到具名插槽,children拿到所有。
4. 模板编译
The end
2019-8-13 15:45:59
Vue.js 2.x render 渲染函数 & JSX的更多相关文章
- 理解Vue中的Render渲染函数
理解Vue中的Render渲染函数 VUE一般使用template来创建HTML,然后在有的时候,我们需要使用javascript来创建html,这时候我们需要使用render函数.比如如下我想要实现 ...
- vue render 渲染函数
vue render 渲染函数 经常看到使用render渲染函数的示例,而且在一些特殊情况下,确实更好使用,可以更加有效地细分组件,因而借助vue-element-admin来学习一波 render函 ...
- 浅析Vue.js 中的条件渲染指令
1 应用于单个元素 Vue.js 中的条件渲染指令可以根据表达式的值,来决定在 DOM 中是渲染还是销毁元素或组件. html: <div id="app"> < ...
- 基于VueJS的render渲染函数结合自定义组件打造一款非常强大的IView 的Table
基于VueJS的render渲染函数结合自定义组件打造一款非常强大的IView 的Table https://segmentfault.com/a/1190000015970367
- Vue之render渲染函数和JSX的应用
一.模板缺陷 模板的最大特点是扩展难度大,不易扩展.可能会造成逻辑冗余 <Level :type="1">哈哈</Level> <Level :typ ...
- Render渲染函数和JSX
1.Render函数:render是用来替换temlate的,需要更灵活的模板的写法的时候,用render. 官网API地址:https://cn.vuejs.org/v2/guide/render- ...
- vue从入门到进阶:渲染函数 & JSX(八)
Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML.然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template 更接 ...
- vue 渲染函数&jsx
前端更新状态,更新视图,所以性能问题主要由Dom操作引起的,而js解析编译dom渲染就要快得多, 所把要js和html混写. vue 的动态js操作 html 方法:reader函数: vue ...
- vue渲染函数&JSX
Vue推荐在绝大多数情况下使用template来创建你的HTML.然而在一些场景中,你真的需要JavaScript的完全编程能力,这时你可以使用render函数,它比template跟接近编译器. 虚 ...
随机推荐
- 权限管理(chown、chgrp、umask)
对于文件或目录的权限的修改,只能管理员和文件的所有者拥有此权限,但是对于文件或目录的的所有者的更改,只有管理员拥有此权限(虽然普通用户创建的文件或目录,用户也不能修改文件或目录的所有者). 1.cho ...
- C博客作业01--分支,顺序结构1
0.展示PTA总分 1.本章学习总结 1.1 学习内容总结 1.学习了if--else,else--if的分支嵌套结构 if (判断条件) { 执行语句1: } else { 执行语句2: }//用来 ...
- element ui 合计/table show-summary
在el-table 上面加上show-summary就可以对table的数据进行合计 但是上次出现了合计栏有的为空,有的合计不对的情况,如果出现的是空,那么说明你渲染的数据有undefine(即后台返 ...
- Java 12 骚操作, String居然还能这样玩!
Java 13 都快要来了,12必须跟栈长学起! Java 13 即将发布,新特性必须抢先看! 栈长之前在Java技术栈微信公众号分享过<Java 11 已发布,String 还能这样玩!> ...
- mybatis or的用法
@Test public void test3(){ CaseSmallListExample caseSmallListExample = new CaseSmallListExample(); c ...
- NIO Channel 管道
Java NIO的通道类似流,但又有些不同: 既可以从通道中读取数据,又可以写数据到通道.但流的读写通常是单向的. 通道可以异步地读写. 通道中的数据总是要先读到一个Buffer,或者总是要从一个Bu ...
- idea乱码问题
## 1. 文件中内容中文乱码 这个原因是文件的编码和项目的编码不一致导致,将项目工程和文件的编码设置成一致的(如,文件编码是GBK,那就都设置为GBK,若为UTF-8就都设置为UTF-8) ## 2 ...
- deepnude | 福利
程序好下载github有,但是没有lib,就是没有训练好的model. 以下是搜到的win平台程序的下载链接: magnet:?xt=urn:btih:7BE4EB8D640742D2FFEBD649 ...
- jdbc批量写入
jdbc加了rewriteBatchedStatements=true就可以提升很多倍,
- ElasticSearch5.2.2 安装(老版本)
https://www.elastic.co/downloads/elasticsearchElasticSearch是一个高可扩展的开源的全文搜索分析引擎.它允许你快速的存储.搜索和分析大量数据.E ...