首页 > 编程知识 正文

vue3 render函数,vue render v-model

时间:2023-05-04 00:02:09 阅读:201043 作者:2237

1.render简介

前面我们的组件的模板都是在模板里写的(template),模板最后都会被vue编译成virtual dom(虚拟dom),在某些情况下模板可能不好用,例如需要实现一个动态的文章标题,根据父组件的level属性,动态的渲染成h1~hx标签,用模板写部分代码如下。

<article-header :level="1">Hello world</article-header><script type="text/x-template" id="article-header-template"> <h1 v-if="level === 1"> <slot></slot> </h1> <h2 v-else-if="level === 2"> <slot></slot> </h2> <h3 v-else-if="level === 3"> <slot></slot> </h3> <h4 v-else-if="level === 4"> <slot></slot> </h4> <h5 v-else-if="level === 5"> <slot></slot> </h5> <h6 v-else-if="level === 6"> <slot></slot> </h6></script><script>Vue.component('article-header', { template: '#article-header-template', props: { level: { type: Number, required: true } }})</script>

代码写的很死板,不灵活。这时候使用render函数会变得非常的方便。改写如下

<!DOCTYPE html><html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></head><body><div id="app"><article-title :level="level">hello world</title></div><script>Vue.component("article-title",{render:function(createElement){return createElement('h'+this.level,{style:{color:'red'}},this.$slots.default)},props:{level:{type:Number,required:true}},data:function(){return {}}});var app11 = new Vue({el:'#app',data:{level:1}})</script></body></html> 2. createElement

其实render函数返回的createElement就是一个虚拟dom,来看下广告对createElement参数描述

// @returns {VNode}createElement( // {String | Object | Function} // 一个 HTML 标签名、组件选项对象,或者 // resolve 了上述任何一种的一个 async 函数。必填项。 'div', // {Object} // 一个与模板中属性对应的数据对象。可选。 { // 与 `v-bind:class` 的 API 相同, // 接受一个字符串、对象或字符串和对象组成的数组 'class': { foo: true, bar: false }, // 与 `v-bind:style` 的 API 相同, // 接受一个字符串、对象,或对象组成的数组 style: { color: 'red', fontSize: '14px' }, // 普通的 HTML 特性 attrs: { id: 'foo' }, // 组件 prop 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 }, // {String | Array} // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成, // 也可以使用字符串来生成“文本虚拟节点”。可选。 [ '先写一些文字', createElement('h1', '一则头条'), createElement(MyComponent, { props: { someProp: 'foobar' } }) ])

render函数内返回的createElement函数的第一个参数必填,一般是标签名,第二个参数,数据对象可选参数,第三个为子级虚拟节点。

3.约束

所有组件树中,如果VNode是组件或含有组件的slot,那么Vnode必须唯一,例如。

<!DOCTYPE html><html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></head><body><div id='app'> <ele></ele><div><script>var Child = { render: function (createElement) { return createElement("p", "text"); }};Vue.component("ele", { render: function (createElement) { var ChildNode = createElement(Child); return createElement("div", [ ChildNode , ChildNode ]); }})var app11 = new Vue({el:'#app',data:{}})</script></body></html>

简单的渲染看上去没问题,但是如果使用中涉及到其他复杂的特性,就可能会出问题,
正确的渲染多个重复的组件

Vue.component("ele", {render: function(createElement) {return createElement("div",Array.apply(null, {length: 20}).map(function() {return createElement(child);}))}});

使用javascript代替某些模板指令,向v-if,v-for,v-model等指令在render函数中都无法使用,都需要自己用js实现相同的逻辑,这就是深入底层的代价。
例如

<!DOCTYPE html><html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></head><body><div id='app'><ele :type="type" :len="len"></ele></div><script>var child = {render: function(createElement) {return createElement("p", "text");}};Vue.component("ele", {render: function(createElement) {if(this.type == 1){return createElement("div",Array.apply(null, {length: this.len}).map(function() {return createElement("p","one type");}));}else{return createElement("div",Array.apply(null, {length: this.len}).map(function() {return createElement("p","two type");}))}},props:{type:{type:Number,required:true,default:1},len:{type:Number,required:true,default:5}}});var app11 = new Vue({el: '#app',data: {type:2,len:6}})</script></body></html> 4. 在render函数中使用插槽

在render函数内,在父组件定义的插槽也是一个vnode,在render中可以通过this.$slots.插槽名去访问。

<!DOCTYPE html><html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></head><body><div id="app"><article-title><template v-slot:default>hello world</template><template v-slot:nameslot="nameslot">hello {{nameslot.name}}</template></title></div><script>var child = {render: function(createElement) {return createElement('p', 'hello');}}Vue.component("article-title", {render: function(createElement) {var node = createElement(child);return createElement('div',[node,this.$slots.default,//向作用域插槽传值createElement('div', [this.$scopedSlots.nameslot({name: 'ly'})])])},props: {},data: function() {return {}}});var app11 = new Vue({el: '#app',data: {}})</script></body></html>

如果要用渲染函数向子组件中传递作用域插槽,可以利用 VNode 数据对象中的 scopedSlots 字段。

5.函数式组件

函数式组件是无状态的,无实例(没有this),入参是渲染上下文context,一切的参数都通过,context传递

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

组件定义时,有个functional属性设置为true就表示当前组件是一个函数式组件,render函数新增context上下文,属性如上表,返回的对象。获取默认插槽由this.$slots.default改为了context.children,context.children会返回所有子元素。
也可以用slots().default获取默认插槽,

<!DOCTYPE html><html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></head><body><div id="app"><article-title :level="level">hello world</title></div><script>Vue.component("article-title",{functional:true,render:function(createElement,context){var props = context.props;return createElement('h'+props.level,context.data,context.children)},props:{level:{type:Number,required:true}},data:function(){return {}}});var app11 = new Vue({el:'#app',data:{level:1}})</script></body></html>

参考
https://cn.vuejs.org/v2/guide/render-function.html#slots-和-children-对比

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。