相信大家都或多或少的在 code 中见过 或使用过 Render,如果你对它还是一脸懵逼,那就快上车!今天就带你来盘它。附 Element 表格自定义全选示例。


文章目录

  • 一、Render 的资料简介
  • 二、与 Render 的初次相遇
    • 2.1、节点、树
    • 2.2、虚拟 DOM
  • 三、与 Render 的约会
    • 3.1 createElement 参数
    • 3.2 深入数据对象
    • 3.3 举个小栗子
    • 3.4 约束
  • 四、Render 的小个性
    • 4.1 v-if 和 v-for
    • 4.2 v-model
    • 4.3 事件 & 按键修饰符
    • 4.4 插槽
  • 五、实战
  • 六、扩展-JSX
  • 七、Element 表格自定义全选示例
  • 码字不易,觉得有帮助的小伙伴点个赞支持下~
  • 扫描上方二维码关注我的订阅号~

一、Render 的资料简介

Render 函数是 Vue2.x 新增的一个函数、主要用来提升节点的性能,它是基于 JavaScript 计算。使用 Render 函数将 Template 里面的节点解析成虚拟的 Dom 。

Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。

简单的说,在 Vue 中我们使用模板 HTML 语法组建页面的,使用 Render 函数我们可以用 Js 语言来构建 DOM。

因为 Vue 是虚拟 DOM,所以在拿到 Template 模板时也要转译成 VNode 的函数,而用 Render 函数构建 DOM,Vue 就免去了转译的过程。

二、与 Render 的初次相遇

你第一次邂逅它的时候,它可能是这样的:

  • IView
render:(h, params)=>{return h('div', {style:{width:'100px',height:'100px',background:'#ccc'}}, '地方')
}
  • Element
<el-table-column :render-header="setHeader">
</el-table-column>
setHeader (h) {return h('span', [h('span', { style: 'line-height: 40px;' }, '备注'),h('el-button', {props: { type: 'primary', size: 'medium', disabled: this.isDisable || !this.tableData.length },on: { click: this.save }}, '保存当前页')])])
},

或者这样的:

renderContent (createElement, { node, data, store }) {return createElement('span', [// 显示树的节点信息createElement('span', node.label)// ......])
}

那它的真身到底是什么样的呢?这还要从它的身世说起。

2.1、节点、树

在深入渲染函数之前,了解一些浏览器的工作原理是很重要的。以下面这段 HTML 为例:

<div><h1>My title</h1>Some text content<!-- TODO: Add tagline -->
</div>

当浏览器读到这些代码时,它会建立一个DOM 节点树来保持追踪所有内容,如同你会画一张家谱树来追踪家庭成员的发展一样。

上述 HTML 对应的 DOM 节点树如下图所示:

每个元素都是一个节点。每段文字也是一个节点。甚至注释也都是节点。一个节点就是页面的一个部分。就像家谱树一样,每个节点都可以有孩子节点 (也就是说每个部分可以包含其它的一些部分)。

高效地更新所有这些节点会是比较困难的,不过所幸你不必手动完成这个工作。你只需要告诉 Vue 你希望页面上的 HTML 是什么,这可以是在一个模板里:

<h1>{{ blogTitle }}</h1>

或者一个渲染函数里:

render: function (createElement) {return createElement('h1', this.blogTitle)
}

在这两种情况下,Vue 都会自动保持页面的更新,即便 blogTitle 发生了改变。

2.2、虚拟 DOM

Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。请仔细看这行代码:

return createElement('h1', this.blogTitle)

createElement到底会返回什么呢?其实不是一个_实际的_ DOM 元素。它更准确的名字可能是 createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为“VNode”。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。

注:当使用render函数描述虚拟 DOM 时,vue 提供一个函数,这个函数是就构建虚拟 DOM 所需要的工具。官网上给他起了个名字叫 createElement。还有约定的简写叫 h,将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。

有点意思~ 其实它就是 createElement,接下来让我们来走近一点点,来深入的了解它吧~

三、与 Render 的约会

3.1 createElement 参数

createElement(TagName,Option,Content)接受三个参数
createElement(" 定义的元素 ",{ 元素的性质 }," 元素的内容"/[元素的内容])

  • 官方文档
// @returns {VNode}
createElement(// {String | Object | Function}// 一个 HTML 标签名、组件选项对象,或者// resolve 了上述任何一种的一个 async 函数。必填项。'div',// {Object}// 一个与模板中属性对应的数据对象。可选。{// (详情见下一节-3.2 深入数据对象)},// {String | Array}// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,// 也可以使用字符串来生成“文本虚拟节点”。可选。['先写一些文字',createElement('h1', '一则头条'),createElement(MyComponent, {props: {someProp: 'foobar'}})]
)

3.2 深入数据对象

{// 与 `v-bind:class` 的 API 相同,// 接受一个字符串、对象或字符串和对象组成的数组'class': {foo: true,bar: false},// 与 `v-bind:style` 的 API 相同,// 接受一个字符串、对象,或对象组成的数组style: {color: 'red',fontSize: '14px'},// 普通的 HTML 特性attrs: {id: 'foo'},// 组件 propprops: {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
}

3.3 举个小栗子

render:(h) => {return h('div',{//给div绑定value属性props: {value:''},//给div绑定样式style:{width:'30px'}, //给div绑定点击事件  on: {click: () => {console.log('点击事件')}},})
}

3.4 约束

它也是有小脾气的~ 要记得这个约束哟~

  • VNode 必须唯一

组件树中的所有 VNode 必须是唯一的。这意味着,下面的渲染函数是不合法的:

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

如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现。例如,下面这渲染函数用完全合法的方式渲染了 20 个相同的段落:

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

上面的都是基础,大家要记牢哟,下面介绍一些它的特性


四、Render 的小个性

4.1 v-if 和 v-for

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

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

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

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 found.')}
}

4.2 v-model

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

props: ['value'],
render: function (createElement) {var self = thisreturn createElement('input', {domProps: {value: self.value},on: {input: function (event) {self.$emit('input', event.target.value)}}})
}

这就是深入底层的代价,但与 v-model 相比,这可以让你更好地控制交互细节。

4.3 事件 & 按键修饰符

对于 .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 分别修改为 altKeyshiftKey 或者 metaKey)

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

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

4.4 插槽

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

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

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

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)}}})])
}

五、实战

Element 中的 Tree

// 树节点的内容区的渲染回调
renderContent(h, { node, data, store }) {let aa = () => {console.log(data)}return  h('span', [h('span', {class: "custom-tree-node"}, [h('i', { class: "icon-folder" }), h('span', { props: { title: node.label }, class: "text ellipsis" }, node.label),h('el-popover', {props: {placement: "bottom",title: "",width: "61",popperClass: "option-group-popover",trigger: "hover"}}, [h('ul', { class: "option-group" }, [h('li', {class: "pointer-text",on: {click: aa}}, '编辑'),h('li', { class: "pointer-text" }, '删除'),h('li', { class: "pointer-text" }, '添加')]),h('i', { slot: "reference", class: "el-icon-more fr more-icon",on: {click: (e) => {e.stopPropagation();}}})])])])
},

六、扩展-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: function (h) {return (<AnchoredHeading level={1}><span>Hello</span> world!</AnchoredHeading>)}
})

七、Element 表格自定义全选示例

<el-table-column width="40" :render-header="selectAllHeaderRender"><template slot-scope="scope"><el-checkboxv-model="scope.row.isSelected":disabled="scope.row.isDisabled"@change="val => selectRow(val, scope.row)"></el-checkbox></template>
</el-table-column>selectAllHeaderRender(h: any) {return h('el-checkbox', {on: {change: (val: any) => {this.isSelectedAll = val;this.selectAll();},},});
}

码字不易,觉得有帮助的小伙伴点个赞支持下~


扫描上方二维码关注我的订阅号~

Vue 中的 Render 全面详解 (渲染函数 JSX)相关推荐

  1. keep alive PHP,vue中keep-alive使用方法详解

    这次给大家带来vue中keep-alive使用方法详解,vue中keep-alive使用的注意事项有哪些,下面就是实战案例,一起来看一下. 1.keep-alive的作用以及好处 在做电商有关的项目中 ...

  2. Vue 中的 ref 属性详解

    Vue 中的 ref 属性详解 我们先来读一下vue的官方文档 我们来分析官方文档 首先ref的引用是相当于一个DOM节点(如果是子组件则指向的是其实例),而且是一个string类型的值. 通俗的将就 ...

  3. Vue中process.env.NODE_ENV详解

    一文弄懂如何在 Vue 中配置 process.env.NODE_ENV 最近在研习 vue.config.js 文件,发现一行,甚是费解. process.env.NODE_ENV 查阅资料后,发现 ...

  4. 【CSDN软件工程师能力认证学习精选】Vue 中的事件处理机制详解

    CSDN软件工程师能力认证(以下简称C系列认证)是由中国软件开发者网CSDN制定并推出的一个能力认证标准.C系列认证历经近一年的实际线下调研.考察.迭代.测试,并梳理出软件工程师开发过程中所需的各项技 ...

  5. Vue中created和mounted详解

    目录 一.生命周期概念 二.浏览器渲染过程 三.生命周期中的浏览器渲染 beforeCreate阶段 created阶段 beforeMount阶段 mounted阶段 四.使用场景 五.常见相关问题 ...

  6. Vue中keep-alive的使用详解

    1.概念: keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁. 2.作用: 用来缓存组件,避免多次加载相同的组件,减少性能消耗,提高用户体验. 3.使用方 ...

  7. VUE中 ref $refs 使用详解,扩展到$parent 、$children 的使用

    $refs 的使用方法就是在元素或组件标签上添加ref属性指定一个引用信息,引用信息将会注册在父组件的$refs对象上,在js中使用$refs来指向DOM元素或组件实例: 应用一:在DOM元素上使用$ ...

  8. vue中如何引入jquery详解

    用vue-cli脚手架工具构建项目成功后 当需要引入JQ,可用以下方法: 1.首先在package.json里的dependencies加入"jquery" : "^3. ...

  9. vue中Mixin和extends详解

    一.认识Mixin 目前我们使用组件化开发方式来开发应用程序,但是在不同组件之间存在一些相同的代码逻辑,此时我们希望对相同的代码逻辑 进行抽取. 在vue2和vue3中都支持使用Mixin来解决,Mi ...

最新文章

  1. html css背景图片精灵技术,HTML入门学习笔记--CSS背景和精灵图(5)
  2. Android开发学习之路-让注解帮你简化代码,彻底抛弃findViewById
  3. 一友人昨夜接到电话,发生何事
  4. 四、记一次失败的 CAS 搭建 之 结果总是那么伤(客户端)
  5. 自行车实现无人驾驶,背后究竟有何“天机”?
  6. 后勤管理系统_充满“智慧”的后勤管理系统是什么样的?
  7. Java——集合带All的功能演示
  8. 程序员的职业生涯之我见
  9. 记录执行hivesql时报org.apache.hadoop.hdfs.BlockMissingException:Could not obtain block错误
  10. 【小梅哥SOPC学习笔记】系统时钟的使用
  11. QT控件出现乱码问题
  12. 如何实现代码自动生成?
  13. php 如何限制手机型号,修改Android设备信息,如修改手机型号为iPhone7黄金土豪版!_PHP教程...
  14. 信息系统项目管理师(2022年) —— 第 11 章 项目风险管理
  15. html5与access连接数据库操作,access是一个什么数据库?
  16. Kubernetes 节点弹性伸缩开源组件 Amazon Karpenter 实践:部署GPU推理应用
  17. android6.0相机假对焦,android相机对焦
  18. 扫雷JAVA课程设计报告感想,Java课程设计报告---扫雷游戏
  19. 华为鸿蒙系统平板电脑,华为5G鸿蒙系统平板电脑正式入网,搭载八核处理器麒麟9000芯片...
  20. vue2的 watch的理解(7)

热门文章

  1. 计算机重启报错69,计算机已经从检测错误后重新启动。检测错误: 0x0000000a (0x0000000000000014, - Microsoft Community...
  2. Laravel 5.1 使用短信验证码插件laravel-sms
  3. MyBatis-plus框架
  4. 悲催的程序员,以及程序员的悲催
  5. c语言定时1小时,1小时c语言入门.pdf
  6. SCADA数据采集与监控系统在制药生产过程中的应用
  7. 操作系统笔记----wsdchong
  8. 设置Oracle19CPDB随着CDB自启动
  9. 基于Json.net的省市县三级联动WinForm制作
  10. 前端面试知识点整理(持续更新)