内容简介:面试官:谈谈你理解的Vue无渲染组件?自己先想一分钟。译者注:英语和文笔有限,不对之处欢迎留言斧正!原文地址:
面试官:谈谈你理解的Vue无渲染组件?
自己先想一分钟。
译者注:英语和文笔有限,不对之处欢迎留言斧正!原文地址: css-tricks.com/building-re…
网上有句话这样来形容Vue,说 “Vue 是 React 和 Angular 的产物”。老实说,我也一直有这种感觉。凭借着较低的学习曲线,广受开发人员的青睐和喜爱。正是由于Vue提供给开发者自由开放式的组件开发的能力,才有了我今天这篇文章。
术语 无渲染组件 意指不渲染任何内容的组件。本文,我们将介绍Vue是如何处理组件渲染工作的。
我们还将会看到如何使用 render()
函数来构建无渲染组件的。
在阅读本文之前假设你对Vue有一定的了解。如果你是一个新手请先阅读Sarah Drasner's post。官方文档也是不错的资源。
解开Vue如何渲染组件的神秘面纱
Vue提供了很多方法来渲染组件:
- 单文件组件。让我们像写普通HTML文件一样去定义组件
- Vue 提供的
template
属性。允许我们使用 JavaScript 的模板字符串来定义组件 - Vue 提供的
el
属性。告诉Vue查询DOM以获取用作组件模板
你可能听说过(可能很讨厌): 归根结底,Vue和它所有的组件都只是JavaScript 。我能理解你为什么觉得我们编写的HTML和CSS的数量是不对的了。用一个案例来阐明这一点:单文件组件。
使用单文件组件,我们可以像这样来定义Vue组件:
<template> <div class="mood"> {{ todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me' }} </div> </template> <script> export default { data: () => ({ todayIsSunny: true }) } </script> <style> .mood:after { content: ''; } </style> 复制代码
通过看上面官方提供的单文件标准格式,我们怎么能说Vue“只有JavaScript” 呢?但是,它确实就是。从表象来看,Vue只是想让我们便于管理我们的页面,样式和其他资源,而构建,编译等工作交给了第三方工具,比如 webpack。
webpack 在检索到 .vue
文件时,将进入编译阶段。期间,CSS会被提取到单独的文件,剩下的内容将编译成 JavaScript。类似下面的代码:
export default { template: ` <div class="mood"> {{ todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me' }} </div>`, data: () => ({ todayIsSunny: true }) } 复制代码
额... 其实不完全像上面的代码。要了解接下来会发生什么,我们需要先聊聊模板编译器。
模板编译器和渲染函数
对于编译和运行Vue当前实现的每个优化技术来说,Vue组件的构建过程这一步是必需的。
当模板编译器遇到下面的代码:
{ template: `<div class="mood">...</div>`, data: () => ({ todayIsSunny: true }) } 复制代码
首先它会提取模板属性并将其内容编译成 JavaScript,然后将渲染函数添加到组件对象上。反过来说就是,渲染函数会返回从模板属性内容转换成 JavaScript 后的内容。
这就是上面的模板在渲染函数中的样子:
... render(h) { return h( 'div', { class: 'mood' }, this.todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me' ) } ... 复制代码
有关渲染函数的更多内容,请参阅官方文档。
现在,当组件对象传递给Vue时,组件对象中的渲染函数会经过一些优化处理后变成一个 VNode(虚拟节点)。VNode会被 snabbdom (Vue内部用来管理虚拟DOM的库)接手做进一步的处理。Sarah Drasner解释了上面渲染函数中的 h
函数。
VNode 是Vue渲染组件的一种方式。顺便说一下,Vue还允许我们在渲染函数中使用 JSX 语法。
我们不必等Vue帮我们添加渲染函数 — 我们可以自己定义渲染函数,而且它的优先级是高于 el
和 template
属性的。移步这里去了解渲染函数及其选项。
使用 Vue CLI 或其他自定义脚手架 工具 构建Vue组件,你不用去考虑导入可能会影响构建文件大小的模板编译器。你的组件都是经过了预处理,压缩,优化等。在性能,文件体积方面都有出色的表现。
接下来... 无渲染组件
就像我说的,术语 无渲染组件 意指不渲染任何东西的组件。为什么我们想要一个无渲染的组件呢?
我们可以通过无渲染组件创建一个抽象组件。就像 Java 中的抽象类,它本身不会做一些事情,只是提供一些接口方法等让外部使用。后续我们可以通过不断拓展该组件来实现更好更强大的组件。这也是正是S.O.L.I.D 。
根据 S.O.L.I.D 的单一责任原则:
一个类应该只做一件事儿
我们可以把这种概念移植到Vue开发中,使每个组件只做一件事儿。
你可能会像Nicky一样,“是的,我知道。“好的,当然!” 你的组件可能会有一个名叫“password-input”,它肯定会呈现一个密码输入框。问题在于,当你想要在其他项目重用此组件时,你可能不得不查看组件源码修改样式或者HTML,以便能和新项目的样式或设计图保持统一。
如果你这样做就破坏了 S.O.L.I.D 原则。也就是开闭原则:
类或者组件,应该对拓展开放,对修改关闭。
意思是,你应该拓展它,而不是修改组件的源代码。
由于Vue了解S.O.L.I.D原则,所以它允许组件具有 props、events、slots、以及 scoped slots ,从而使组件的通信和拓展变得轻而易举。然后,我们可以构建具有所有功能的组件,而无需任何样式或HTML。对于编写可重用性和高性能的代码来说真的很不错。
构建一个 “Toggle” 无渲染组件
这很简单,不需要Vue CLI。
开关组件可以让你在开和关之间切换。并且它还提供了一些帮助方法供你使用。它对于构建组件(例如,开/关组件、自定义复选框和任何需要开/关状态的组件)非常有用。
先找到我们的组件:前往CodePen的 JavaScript 部分,然后继续。
// toggle.js const toggle = { props: { on: { type: Boolean, default: false } }, render() { return [] }, data() { return { currentState: this.on } }, methods: { setOn() { this.currentState = true }, setOff() { this.currentState = false }, toggle() { this.currentState = !this.currentState } } } 复制代码
目前组件代码还是比较少的,功能尚未完成。它需要一个模板,因为我们不希望这个组件展示任何东西,所以我们必须确保它能适用于任何组件。
暗示插槽!
在无渲染组件中使用插槽
插槽允许我们在标签体中放置内容。像这样:
<toggle> This entire area is a slot. </toggle> 复制代码
在Vue单文件组件中,我们可以这样来定义一个插槽:
<template> <div> <slot/> </div> </template> 复制代码
好了,在开关组件的 render()
函数中我们可以这样来做:
// toggle.js render() { return this.$slots.default } 复制代码
在开关组件里我们可以自由的放置东西了。
使用 Scoped Slots 向外部发送数据
在 toggle.js
中, methods
方法对象上有一些控制开关状态的方法和一些辅助方法。如果我们能让开发者调用他们那就太好了。而我们目前正在使用的插槽无法做到这一点,因为它不允许我们公开组件中的任何内容。
我们想要的其实是 scoped slots 。作用域插槽的工作方式跟插槽一样。但对比插槽的优点在于,具有作用域插槽的组件在不触发事件的情况下也可以暴露数据。看下面的代码:
<toggle> <div slot-scope="{ on }"> {{ on ? 'On' : 'Off' }} </div> </toggle> 复制代码
div
上的 slot-scope
属性通过对象解构并获取从开关组件透传过来的数据。
回到 render()
函数,我们这样做:
render() { return this.$scopedSlots.default({}) } 复制代码
这次,我们将 $scopedSlots
对象上的 default 属性作为方法调用。因为作用域插槽是带有一个参数的方法。这种情况下,方法名是默认的,因为我们没有提供具名插槽,所以它将作为唯一存在的作用域插槽。然后我们就可以把组件中的方法以作用域插槽参数的形式暴露出去了。在这个栗子中,让我们暴露 on
的当前状态和操作该状态的一些方法吧:
render() { return this.$scopedSlots.default({ on: this.currentState, setOn: this.setOn, setOff: this.setOff, toggle: this.toggle, }) } 复制代码
使用Toggle组件
我们做的这些操作都在Codepen 上,下面是截图:
下面是相关的HTML代码:
<div id="app"> <toggle> <div slot-scope="{ on, setOn, setOff }" class="container"> <button @click="click(setOn)" class="button">Blue pill</button> <button @click="click(setOff)" class="button isRed">Red pill</button> <div v-if="buttonPressed" class="message"> <span v-if="on">It's all a dream, go back to sleep.</span> <span v-else>I don't know how far the rabbit hole goes, I'm not a rabbit, neither do I measure holes.</span> </div> </div> </toggle> </div> 复制代码
click()
new Vue({ el: '#app', components: { toggle }, data: { buttonPressed: false, }, methods: { click(fn) { this.buttonPressed = true fn() }, }, }) 复制代码
我们仍然可以从 Toggle 组件传递 props 和触发事件。作用域插槽不会受到任何影响。
这是一个基础的示例,但我们可以看到,当我们开始构建日期选择器或自动完成提示等组件时,这种做法是非常适用且强大的。我们可以在多个项目重复使用这些组件,而不必担心那些讨厌的样式妨碍我们。
我们可以做的另一件事是从作用域插槽中公开可访问的属性而不必担心拓展组件后的访问性问题。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 实现一个react系列二:渲染组件
- 细说 Vue 组件的服务器端渲染
- React 组件模式-有状态组件 x 无状态组件、容器组件 x 展示组件、高阶组件 x 渲染回调(函数作为子组件)
- React源码分析与实现(一):组件的初始化与渲染
- 支持大数据渲染下拉列表组件开发 SuperSelect(基于antd Select)
- reactjs – 使用react-router-dom进行组件渲染时更改Url?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。