当前位置: 首页 > Web前端 > vue.js

Vue.js渲染函数那些事儿

时间:2023-04-01 00:49:07 vue.js

很多时候我会用template,vue单文件来渲染组件。虽然知道Vue中有render函数,但很少主动在项目中使用。用的最多的地方就是在使用一些UI框架的时候,比如iview表格中的按钮操作,都会用到render函数。另外,在阅读一些VueUI框架源码的时候,经常会遇到使用render函数的地方,这也激发了我学习学习的欲望。如果您也有兴趣,请继续阅读。在这篇文章中,将会有以下内容:什么是Vue渲染函数Vue编译器如何处理渲染函数什么是Vue渲染函数Vue.js模板非常强大,几乎可以完成我们应用程序中需要的一切。但是,也有一些场景,比如根据输入或插槽值创建动态组件,渲染函数可以更好地满足这些用例。那些来自React世界的开发人员可能对渲染功能非常熟悉。它们通常在JSX中用于构建React组件。虽然Vue渲染函数也可以用JSX编写,但我们将继续使用原始JS,这将有助于我们更轻松地了解Vue组件系统的基础知识。.每个Vue组件都实现了一个渲染函数。大多数时候,这个函数将由Vue编译器创建。当我们在组件上指定模板时,该模板的内容将被Vue编译器处理,编译器最终会返回render函数。render函数本质上返回一个虚拟DOM节点,该节点将由Vue在浏览器DOM中呈现。现在来虚拟DOM的概念,“虚拟DOM是什么鬼?”虚拟文档对象模型(或“DOM”)允许Vue在更新浏览器之前在其内存中渲染组件。这让一切变得更快,同时也避免了代价高昂的DOM重新渲染。因为每个DOM节点对象都包含很多属性和方法,使用虚拟DOM提前在内存中操作可以省去很多浏览器直接创建DOM节点对象的开销。当Vue更新浏览器DOM时,它会将更新后的虚拟DOM与之前的虚拟DOM进行比较,并仅使用修改的部分更新实际DOM。这意味着需要更改的元素更少,从而提高了性能。Render函数返回一个虚拟DOM节点,在Vue生态系统中通常称为VNode,它是允许Vue将这些对象写入浏览器DOM中的接口。它们包含使用Vue所需的所有信息。下一个版本的Vue将包含一个全新的虚拟DOM实现,它将比当前版本更快。React、Riot、Inferno和许多其他框架也使用了虚拟DOM的概念。我们可以在任何Vue组件中实现Vue渲染功能。另外,由于Vue的数据响应性,每当组件的数据更新时,都会再次调用渲染函数。这是一个如何使用组件中的渲染函数直接渲染h1标签的简单示例:}});有一些内置组件利用了渲染功能的强大功能,例如过渡和保持活动。这些组件直接在渲染函数中操作VNode。如果Vue不提供这个功能特性,这些功能将无法实现。Vue编译器如何处理渲染函数?大多数情况下,Vue渲染函数将在项目构建期间由Vue编译器编译(例如,使用Webpack)。因此,编译器不会最终出现在您的生产代码中,从而减小包的大小。这就是为什么当您使用“单一文件组件”时,除非我们真的需要/想要,否则您实际上不需要使用渲染功能。但是如果我们想在我们的代码中使用编译器,我们可以使用带有编译器的Vue版本。简而言之,我们正在使用Vue编译器来编译自定义模板。假设我们在做一个电商项目,我们可以将其注入到购物车中,这样我们就可以有更多的控制权。我们编写了一个实现自定义呈现功能的组件,该功能采用用户创建的模板并替换我们的默认模板。下面是一个如何使用编译器将模板字符串编译成渲染函数的简单示例:consttemplate=`

    {{item}}
`;constcompiledTemplate=Vue.compile(template);newVue({el:'#app',data(){return{items:['Item1','Item2']}},render(createElement){返回compiledTemplate.render.call(this,createElement);}});如您所见,编译器将返回一个对象(compiledTemplate),其中包含可供使用的渲染函数。创建一个呈现没有模板标签或属性的组件的组件。相反,他们定义了一个名为render的函数,该函数接收一个createElement(renderElement:String|Component,define:Object,children:String|Array)参数(由于某种原因通常别名为h,怪JSX)并返回使用该函数创建的元素.其他一切都保持不变。exportdefault{data(){return{isRed:true}},/**与下面的模板相同**/render(h){returnh('div',{'class':{'is-red':this.isRed}},[h('p','ExampleText')])}}在渲染函数中使用指令Vue模板有各种方便的函数,用于向模板添加基本逻辑和绑定功能。渲染函数无法访问它们。相反,它们必须用纯Javascript实现,这对于大多数指令来说相当简单。v-if这很简单。您可以在createElement中调用普通的if(expr)语句,而不是使用v-if。{{item.name}}未找到任何项目。

这些可以用JavaScriptif/else重写,并映射到render函数中:props:['items'],render:function(h){if(this.items.length){returnh('ul',this.items.map(function(item){returnh('li',item.name)}))}else{returnh('p','Noitemsfound.')}}v-forv-forcan可以使用各种迭代方法中的任何一种来实现,例如for-of、Array.map、Array.filter等。我们可以以非常有趣的方式组合它们来过滤或分割数据。例如,我们可以用render(h){returnh('ul',this.list.map(item=>h('li',item.name)));}关于v-model需要记住的一点是v-model是将属性绑定到value上(也可以是其他属性),只需触发输入事件并设置数据属性的简写。不幸的是,渲染函数没有这样的简写。我们必须自己实现它,如下所示。render(h){returnh('input',{domProps:{value:this.myBoundProperty},on:{input:e=>{this.myBoundProperty=e.target.value}}})}等同于:v-bind属性和属性绑定以attrs、props和domProps的形式(类似于value和innerHTML等)放在元素定义中。render(h){returnh('div',{attrs:{//id:this.myCustomId},props:{//someProp:this.someValue},domProps:{//value:this.someValue}});}作为旁注,类和样式绑定直接在定义的根部处理,而不是作为attrs,props或domProps处理。render(h){returnh('div',{//"class"是JS中的保留字,所以需要用引号引起来。'class':{myClass:true,theirClass:false},style:{backgroundColor:'green'}});}v-on事件处理程序也可以直接添加到on中的元素定义中(或nativeOn,它与组件的v-on.native具有相同的效果)。render(h){returnh('div',{on:{click(e){console.log('Iclicked!')}}});}修饰符可以在处理器内部实现:.stop->e。stopPropagation().prevent->e.preventDefault().self->if(e.target!==e.currentTarget)returnkeymodifier.[TARGET_KEY_CODE]->if(event.keyCode!==TARGET_KEY_CODE)返回。[MODIFIER]->if(!event.MODIFIERKey)returnSlots可以通过this.$slots作为createElement()节点数组访问。这是slot1
这是slot2
Vue.component('my-slot',{render:function(h){constchild=h('div',{domProps:{innerHTML:'thisischild'}});returnh('div',[child,this.$slots.slot1,this.$slots.slot2])}})作用域槽存储在this.$scopedSlots[scope](props:object),作为返回createElement()节点数组的函数。
{{props.text}}
Vue.component('my-slot',{render:function(h){constchild=h('div',{domProps:{innerHTML:'thisischild'}});returnh('div',[child,this.$scopedSlots.default({text:'helloscope'})])}})Vue渲染函数中的事件绑定createElement函数可以接收一个函数称为数据对象的参数。这个对象可以有多个属性,这些属性等同于我们在标准模板中使用的v-bind:on等指令。下面是一个简单的计数器组件示例,它带有一个按钮,可以增加点击次数。newVue({el:'#app',data(){return{clickCount:0,}},方法:{onClick(){this.clickCount+=1;}},render(h){constbutton=h('button',{on:{click:this.onClick}},'点我');constcounter=h('span',['点击次数:',this.clickCount]);returnh('div',[按钮,计数器])}});但数据对象不限于事件绑定!我们还可以将CSS类应用于元素,就像使用v-bind:class指令一样。newVue({el:'#app',data(){return{clickCount:0,}},computed:{backgroundColor(){return{'pink':this.clickCount%2===0,'green':this.clickCount%2!==0,};}},方法:{onClick(){this.clickCount+=1;}},render(h){constbutton=h('button',{on:{click:this.onClick}},'点我');constcounter=h('span',{'class':this.backgroundColor,},['点击次数:',this.clickCount]);returnh('div',[按钮,计数器])}});模板覆盖的实际用例我认为了解Vue在幕后的工作方式会非常有趣。了解您是否可以最有效地使用工具的唯一方法是准确了解它的工作原理。这并不是说我们应该开始将所有模板都变成渲染函数,但有时它们可??以派上用场,所以我们至少应该知道如何使用它们。在上面的例子中,我展示了如何在组件中使用自定义渲染函数,它允许我们的一些组件被覆盖。首先,让我们创建初始模板。Headingtitleis:{{title}}
在将安装Vue应用程序的div中,我们编写自定义模板。接下来,我们希望模板覆盖我们将创建的标头组件的默认版本。我们做的第一件事是迭代我们的自定义模板并使用Vue编译器预编译它们:consttemplates=[];consttemplatesContainer=document.getElementById('app');//我们遍历每个自定义模板并预编译它们for(vari=0;i

{{title}}

`});现在,它只是一个简单的组件,带有一个名为title的属性。默认模板将呈现带有标题的h1。我们将使用我们随后创建的可重写组件包装该组件。这是我们将使用自定义渲染函数的地方。Vue.component('overridable',{props:['name'],render(createElement){//我们使用初始化应用程序时创建的模板数组consttemplate=templates.find(x=>x.name===this.name);//当没有自定义模板时,我们将使用默认槽返回默认内容。if(!template){returnthis.$slots.default[0];}//使用预编译的渲染函数returntemplate.renderFunction.render.call(this.$parent,createElement);}});然后,让我们挂载Vue应用程序:newVue({el:'#app',template:``});在这个例子中,我们可以看到默认模板,所以它是一个标准的h1标签。如果将自定义模板添加到div#app,您会看到标题组件将呈现为我们指定的自定义模板。最后,如果使用render函数来创建组件,会让你觉得很繁琐。那你也可以试试babel-plugin-transform-vue-jsx插件,它可以像React一样便携(属性细节略有不同,详见插件文档)。总的来说,使用render函数还是很有趣的,在v3.0中就派上用场了。Vue渲染函数是Vue本身的一个基础部分,所以我真的认为花一些时间彻底理解这个概念是值得的,特别是如果你已经使用这个框架很长时间了。随着Vue.js的发展和效率的提升,我们平时积累的底层基础知识也会对我们的发展有所帮助。也就是说,了解Vue渲染功能只是你技术进步的一小步,但却很重要。:)