Fonctions de rendu et JSX
Répertoire
- , qui est plus proche du compilateur que des modèles.
Plongeons dans un exemple simple où la fonction
render
est très utile. Supposons que nous souhaitions générer des titres avec des ancres :<h1> <a name="hello-world" href="#hello-world"> Hello world! </a> </h1>
Pour le code HTML ci-dessus, vous décidez de définir l'interface du composant comme ceci :<anchored-heading :level="1">Hello world!</anchored-heading>
Lorsque vous commencez à écrire un titre qui ne peut être généré dynamiquement que via le niveau
< /code> prop ), vous pourriez rapidement penser à l'implémenter comme ceci :
<script type="text/x-template" id="anchored-heading-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>
Vue.component('anchored-heading', { template: '#anchored-heading-template', props: { level: { type: Number, required: true } } })
Utiliser des modèles ici n'est pas le meilleur choix : non seulement le code est long, mais en plus Bien que les modèles fonctionnent très bien dans la plupart des composants, ils ne conviennent évidemment pas ici. Essayons donc de réécrire l'exemple ci-dessus en utilisant la fonction
Nœuds, arbres et DOM virtuel🎜render
:Vue.component('anchored-heading', { render: function (createElement) { return createElement( 'h' + this.level, // 标签名称 this.$slots.default // 子节点数组 ) }, props: { level: { type: Number, required: true } } })
Ça a l'air beaucoup plus simple ! De cette façon, le code est beaucoup plus simple, mais vous devez être très familier avec les propriétés d'instance de Vue. Dans cet exemple, vous devez savoir que lorsque vous transmettez des nœuds enfants sans la directive
v-slot
dans le composant, commeHello world! in
anchored-heading
< /code>, ces nœuds enfants sont stockés dans$slots.default
dans l'instance du composant. Si vous ne le comprenez pas encore,v-slot
指令的子节点时,比如anchored-heading
中的Hello world!
,这些子节点被存储在组件实例中的$slots.default
中。如果你还不了解,在深入渲染函数之前推荐阅读实例属性 API。节点、树以及虚拟 DOM
在深入渲染函数之前,了解一些浏览器的工作原理是很重要的。以下面这段 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
发生了改变。虚拟 DOM
Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。请仔细看这行代码:
return createElement('h1', this.blogTitle)
createElement
到底会返回什么呢?其实不是一个实际的 DOM 元素。它更准确的名字可能是createNodeDescription
Lecture recommandée avant de plonger dans la fonction de renduAPI d'attribut d'instance.
🎜 Avant de plonger dans les fonctions de rendu, il est important de comprendre un peu le fonctionnement des navigateurs. Prenons comme exemple le code HTML suivant : 🎜🎜// @returns {VNode} createElement( // {String | Object | Function} // 一个 HTML 标签名、组件选项对象,或者 // resolve 了上述任何一种的一个 async 函数。必填项。 'div', // {Object} // 一个与模板中属性对应的数据对象。可选。 { // (详情见下一节) }, // {String | Array} // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成, // 也可以使用字符串来生成“文本虚拟节点”。可选。 [ '先写一些文字', createElement('h1', '一则头条'), createElement(MyComponent, { props: { someProp: 'foobar' } }) ] )
🎜Lorsque le navigateur lit ces codes, il créera un "DOM Node" arbre généalogique pour suivre tout, tout comme vous dessineriez un arbre généalogique pour suivre l'évolution des membres de la famille. 🎜🎜L'arborescence des nœuds DOM correspondant au code HTML ci-dessus est la suivante : 🎜🎜🎜Chaque élément est un nœud. Chaque morceau de texte est également un nœud. Même les commentaires sont des nœuds. Un nœud fait partie d'une page. Tout comme un arbre généalogique, chaque nœud peut avoir des enfants (c'est-à-dire que chaque partie peut contenir d'autres parties). 🎜🎜Mettre à jour efficacement tous ces nœuds peut être difficile, mais heureusement, vous n'avez pas à le faire manuellement. Il vous suffit d'indiquer à Vue quel HTML vous voulez sur la page, cela peut être dans un modèle : 🎜
{ // 与 `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 }
🎜 ou dans une fonction de rendu : 🎜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 } } })
🎜 Dans les deux cas, Vue gardera automatiquement la page à jour, même siblogTitle
a changé. 🎜🎜🎜🎜🎜Virtual DOM🎜🎜🎜Vue suit la façon dont vous souhaitez créer un 🎜Virtual DOM🎜 Modification le vrai DOM. Veuillez regarder attentivement cette ligne de code : 🎜
render: function (createElement) { var myParagraphVNode = createElement('p', 'hi') return createElement('div', [ // 错误 - 重复的 VNode myParagraphVNode, myParagraphVNode ]) }
🎜createElement
Qu'est-ce qui sera renvoyé exactement ? Pas réellement un véritable élément DOM. Son nom plus précis peut êtrecreateNodeDescription
, car les informations qu'il contient indiqueront à Vue quel type de nœud doit être affiché sur la page, y compris les informations de description de ses nœuds enfants. Nous décrivons un tel nœud comme un « nœud virtuel » et l'abrégons souvent en « 🎜VNode🎜 ». "Virtual DOM" est ce que nous appelons l'ensemble de l'arborescence VNode construite à partir de l'arborescence des composants Vue. 🎜🎜🎜🎜createElement
参数接下来你需要熟悉的是如何在
createElement
函数中使用模板中的那些功能。这里是createElement
接受的参数:render: function (createElement) { return createElement('div', Array.apply(null, { length: 20 }).map(function () { return createElement('p', 'hi') }) ) }
深入数据对象
有一点要注意:正如
v-bind:class
和v-bind:style
在模板语法中会被特别对待一样,它们在 VNode 数据对象中也有对应的顶层字段。该对象也允许你绑定普通的 HTML 特性,也允许绑定如innerHTML
这样的 DOM 属性 (这会覆盖v-html
指令)。<ul v-if="items.length"> <li v-for="item in items">{{ item.name }}</li> </ul> <p v-else>No items found.</p>
完整示例
有了这些知识,我们现在可以完成我们最开始想实现的组件:
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.') } }
约束
VNode 必须唯一
组件树中的所有 VNode 必须是唯一的。这意味着,下面的渲染函数是不合法的:
props: ['value'], render: function (createElement) { var self = this return createElement('input', { domProps: { value: self.value }, on: { input: function (event) { self.$emit('input', event.target.value) } } }) }
如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现。例如,下面这渲染函数用完全合法的方式渲染了 20 个相同的段落:
on: { '!click': this.doThisInCapturingMode, '~keyup': this.doThisOnce, '~!mouseover': this.doThisOnceInCapturingMode }
使用 JavaScript 代替模板功能
v-if
和v-for
只要在原生的 JavaScript 中可以轻松完成的操作,Vue 的渲染函数就不会提供专有的替代方法。比如,在模板中使用的
v-if
和v-for
:on: { keyup: function (event) { // 如果触发事件的元素不是事件绑定的元素 // 则返回 if (event.target !== event.currentTarget) return // 如果按下去的不是 enter 键或者 // 没有同时按下 shift 键 // 则返回 if (!event.shiftKey || event.keyCode !== 13) return // 阻止 事件冒泡 event.stopPropagation() // 阻止该元素默认的 keyup 事件 event.preventDefault() // ... } }
这些都可以在渲染函数中用 JavaScript 的
if/else
和map
来重写:render: function (createElement) { // `<div><slot></slot></div>` return createElement('div', this.$slots.default) }
v-model
渲染函数中没有与
v-model
的直接对应——你必须自己实现相应的逻辑:props: ['message'], render: function (createElement) { // `<div><slot :text="message"></slot></div>` return createElement('div', [ this.$scopedSlots.default({ text: this.message }) ]) }
这就是深入底层的代价,但与
v-model
相比,这可以让你更好地控制交互细节。事件 & 按键修饰符
对于
.passive
、.capture
和.once
这些事件修饰符, Vue 提供了相应的前缀可以用于on
:Modifier Préfixe .passive
.passive
&
.capture
!
.once
~
.capture.once
或.once.capture
~!
例如:
render: function (createElement) { return createElement('div', [ createElement('child', { // 在数据对象中传递 `scopedSlots` // 格式为 { name: props => VNode | Array<VNode> } scopedSlots: { default: function (props) { return createElement('span', props.text) } } }) ]) }
对于所有其它的修饰符,私有前缀都不是必须的,因为你可以在事件处理函数中使用事件方法:
🎜修饰符 处理函数中的等价操作 .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
分别修改为altKey
、shiftKey
或者metaKey
&
.capture
🎜🎜< code >!🎜🎜🎜🎜.once
🎜🎜~
🎜🎜🎜🎜.capture.once
ou
< code>.once.capture🎜🎜~!
🎜🎜🎜🎜Par exemple :
createElement( 'anchored-heading', { props: { level: 1 } }, [ createElement('span', 'Hello'), ' world!' ] )
Pour tous les autres modificateurs, le préfixe privé est non obligatoire, car vous pouvez utiliser des méthodes d'événement dans les gestionnaires d'événements :
🎜🎜🎜🎜Modificateurs🎜🎜Opérations équivalentes dans les fonctions de gestionnaire🎜🎜🎜🎜🎜🎜.stop
🎜 🎜event .stopPropagation()
🎜🎜🎜🎜.prevent
🎜🎜event.preventDefault()
🎜🎜🎜🎜.self< /code>🎜🎜
. Pour les versions antérieures du plugin, l'application générera une erreur siif (event.target !== event.currentTarget) return
🎜🎜🎜🎜Clés :.enter
,.13
🎜 🎜if (event.keyCode !== 13) return
(Pour les autres modificateurs de touches, vous pouvez remplacer13
par Un autre code clé)🎜🎜🎜🎜Clés de modification :.ctrl
,. alt
,.shift
,.meta
🎜🎜if (!event.ctrlKey) return
(remplacerctrlKey
code> ont été remplacés paraltKey
,shiftKey
oumetaKey
)🎜🎜🎜🎜Voici un exemple utilisant tous les modificateurs :
<anchored-heading :level="1"> <span>Hello</span> world! </anchored-heading>
slots
Vous pouvez accéder au contenu des slots statiques via
this.$slots
, chaque slot est un tableau VNode :this.$slots
访问静态插槽的内容,每个插槽都是一个 VNode 数组:import AnchoredHeading from './AnchoredHeading.vue' new Vue({ el: '#demo', render: function (h) { return ( <AnchoredHeading level={1}> <span>Hello</span> world! </AnchoredHeading> ) } })
也可以通过
this.$scopedSlots
访问作用域插槽,每个作用域插槽都是一个返回若干 VNode 的函数:Vue.component('my-component', { functional: true, // Props 是可选的 props: { // ... }, // 为了弥补缺少的实例 // 提供第二个参数作为上下文 render: function (createElement, context) { // ... } })
如果要用渲染函数向子组件中传递作用域插槽,可以利用 VNode 数据对象中的
scopedSlots
字段:<template functional> </template>
JSX
如果你写了很多
render
函数,可能会觉得下面这样的代码写起来很痛苦:var EmptyList = { /* ... */ } var TableList = { /* ... */ } var OrderedList = { /* ... */ } var UnorderedList = { /* ... */ } Vue.component('smart-list', { functional: true, props: { items: { type: Array, required: true }, isOrdered: Boolean }, render: function (createElement, context) { function appropriateListComponent () { var items = context.props.items if (items.length === 0) return EmptyList if (typeof items[0] === 'object') return TableList if (context.props.isOrdered) return OrderedList return UnorderedList } return createElement( appropriateListComponent(), context.data, context.children ) } })
特别是对应的模板如此简单的情况下:
Vue.component('my-functional-button', { functional: true, render: function (createElement, context) { // 完全透传任何特性、事件监听器、子节点等。 return createElement('button', context.data, context.children) } })
这就是为什么会有一个 Babel 插件,用于在 Vue 中使用 JSX 语法,它可以让我们回到更接近于模板的语法上。
<template functional> <button class="btn btn-primary" v-bind="data.attrs" v-on="listeners" > <slot/> </button> </template>
将
h
作为createElement
的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。从 Vue 的 Babel 插件的 3.4.0 版本开始,我们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入const h = this.$createElement
,这样你就可以去掉(h)
参数了。对于更早版本的插件,如果h
在当前作用域中不可用,应用会抛错。要了解更多关于 JSX 如何映射到 JavaScript,请阅读使用文档。
函数式组件
之前创建的锚点标题组件是比较简单,没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数。
在这样的场景下,我们可以将组件标记为
functional
,这意味它无状态 (没有响应式数据),也没有实例 (没有this
<my-functional-component> <p v-slot:foo> first </p> <p>second</p> </my-functional-component>
peut également être transmisthis.$scopedSlots< /code>
accède aux emplacements de portée. Chaque emplacement de portée est une fonction qui renvoie plusieurs VNodes :rrreeeSi vous souhaitez utiliser la fonction de rendu pour transmettre l'emplacement de portée au composant enfant, vous pouvez utiliser le
🎜🎜. champ scopedSlots
dans l'objet de données VNode : rrreee🎜JSX🎜🎜 h2>
🎜Si vous avez écrit beaucoup de fonctionsrender
, vous trouverez peut-être le code suivant très pénible à écrire :🎜🎜rrreee🎜Surtout lorsque le modèle correspondant est si simple :🎜 rrreee🎜C'est pourquoi il y a un plugin Babel pour utiliser la syntaxe JSX dans Vue, qui permet Revenons à une syntaxe plus proche des templates. 🎜rrreee🎜Utiliser
h
comme alias pourcreateElement
est une convention courante dans l'écosystème Vue et est en fait requise par JSX. Depuis la version 3.4.0 du plugin Babel de VueEn commençant, nous injecterons automatiquementconst h = this.$createElement
dans n'importe quelle méthode et getter contenant JSX déclaré avec la syntaxe ES2015 (pas dans les fonctions ou les fonctions fléchées), afin que vous puissiez supprimer < code>(h )h
n'est pas disponible dans la portée actuelle. 🎜🎜Pour en savoir plus sur la manière dont JSX est mappé à JavaScript, veuillez lire la documentation d'utilisation . 🎜🎜🎜🎜🎜Composants fonctionnels🎜🎜
🎜Le composant de titre d'ancrage créé précédemment est relativement simple , il ne gère aucun état, ne surveille aucun état qui lui est transmis et ne dispose d'aucune méthode de cycle de vie. En fait, c'est juste une fonction qui accepte certains accessoires. 🎜🎜🎜Dans un tel scénario, nous pouvons marquer le composant commefonctionnel
, ce qui signifie qu'il est sans état (non données réactives), et aucune instance (pas dethis< /code> contexte). 🎜🎜Un 🎜composant fonctionnel🎜 ressemble à ceci : 🎜rrreee
Remarque : Dans les versions antérieures à 2.3.0, si un composant fonctionnel voulait recevoir des accessoires, l'option
props
était requise. Dans la version 2.3.0 ou supérieure, vous pouvez omettre l'optionprops
et toutes les propriétés du composant seront automatiquement et implicitement résolues en accessoires.props
选项是必须的。在 2.3.0 或以上的版本中,你可以省略props
选项,所有组件上的特性都会被自动隐式解析为 prop。
当使用函数式组件时,该引用将会是 HTMLElement,因为他们是无状态的也是无实例的。在 2.5.0 及以上版本中,如果你使用了单文件组件,那么基于模板的函数式组件可以这样声明:
rrreee组件需要的一切都是通过
context
参数传递,它是一个包括如下字段的对象:props
:提供所有 prop 的对象children
: VNode 子节点的数组slots
:一个函数,返回了包含所有插槽的对象scopedSlots
:(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。data
:传递给组件的整个数据对象,作为createElement
的第二个参数传入组件parent
:对父组件的引用listeners
:(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。injections
:(2.3.0+) 如果使用了inject
选项,则该对象包含了应当被注入的属性。
在添加
functional: true
之后,需要更新我们的锚点标题组件的渲染函数,为其增加context
参数,并将this.$slots.default
更新为context.children
,然后将this.level
更新为context.props.level
。因为函数式组件只是函数,所以渲染开销也低很多。
在作为包装组件时它们也同样非常有用。比如,当你需要做这些时:
程序化地在多个组件中选择一个来代为渲染;
在将
children
、props
、data
传递给子组件之前操作它们。
下面是一个
smart-list
Lors de l'utilisation de composants fonctionnels, la référence sera HTMLElement car ils sont sans état et sans instance.Dans la version 2.5.0 et supérieure, si vous utilisez un composant de fichier unique< /a >, alors le composant fonctionnel basé sur un modèle peut être déclaré comme ceci : rrreeeTout ce dont le composant a besoin est passé via le paramètre
context
, qui est un objet comprenant les champs suivants :props
: fournit tous les objets prop🎜- 🎜
children
: tableau de nœuds enfants VNode of 🎜 - 🎜
slots
: Une fonction qui renvoie un objet contenant tous les slots 🎜 - 🎜
scopedSlots
: ( 2.6.0 +) Un objet qui expose l'emplacement de portée transmis. Expose également les emplacements normaux en tant que fonctions. 🎜 - 🎜
data
: L'intégralité des Objet de données, passé dans le composant comme deuxième paramètre decreateElement
🎜 - 🎜
parent
: une référence au composant parent 🎜< /li> - 🎜
listeners
: (2.3.0+) Un objet contenant tous les écouteurs d'événements enregistrés par les composants parents pour le composant actuel. Il s'agit d'un alias pour data.on. 🎜 - 🎜
injections
: (2.3.0+) si vous utilisez🎜inject🎜
, l'objet contient des propriétés qui doivent être injectées. 🎜
functionnel: true
, nous devons mettre à jour la fonction de rendu de notre composant de titre d'ancrage, y ajouter le paramètrecontext
, et mettez à jourthis.$slots.default
encontext.children
, puis mettez à jourthis.level
encontext.props.level< / code>. 🎜🎜Étant donné que les composants fonctionnels ne sont que des fonctions, la surcharge de rendu est bien moindre. 🎜🎜Ils sont également très utiles comme composants d’emballage. Par exemple, lorsque vous devez faire ceci : 🎜
Tous les attributs et écouteurs d'événements ci-dessus sont transmis. En fait, c'est si transparent que ces événements ne nécessitent même pas le modificateur- 🎜Sélectionnez par programme l'un des multiples composants à restituer en votre nom ; 🎜
- 🎜Manipulez les
children
, lesprops
, lesdata
avant de les transmettre aux composants enfants. 🎜
smart-list
, qui peut restituer des composants plus spécifiques en fonction de la valeur de l'accessoire entrant : 🎜rrreee🎜🎜🎜🎜 🎜 🎜Transmettre les propriétés et les événements aux éléments enfants ou aux composants enfants🎜🎜🎜Dans les composants ordinaires, les attributs qui ne sont pas définis comme accessoires seront automatiquement ajoutés à l'élément racine du composant, remplaçant ou fusionnant intelligemment les attributs existants portant le même nom.
Cependant, les composants fonctionnels nécessitent que vous définissiez explicitement ce comportement :
rrreeeEn passant
context.data
comme deuxième paramètre àcreateElement
, nous avons simplement >mon-fonctionnel- bouton.native
.createElement
传入context.data
作为第二个参数,我们就把my-functional-button
上面所有的特性和事件监听器都传递下去了。事实上这是非常透明的,以至于那些事件甚至并不要求.native
修饰符。如果你使用基于模板的函数式组件,那么你还需要手动添加特性和监听器。因为我们可以访问到其独立的上下文内容,所以我们可以使用
rrreeedata.attrs
传递任何 HTML 特性,也可以使用listeners
(即data.on
的别名) 传递任何事件监听器。slots()
和children
对比你可能想知道为什么同时需要
rrreeeslots()
和children
。slots().default
不是和children
类似的吗?在一些场景中,是这样——但如果是如下的带有子节点的函数式组件呢?对于这个组件,
children
会给你两个段落标签,而slots().default
只会传递第二个匿名段落标签,slots().foo
会传递第一个具名段落标签。同时拥有children
和slots()
,因此你可以选择让组件感知某个插槽机制,还是简单地通过传递children
,移交给其它组件去处理。模板编译
你可能会有兴趣知道,Vue 的模板实际上被编译成了渲染函数。这是一个实现细节,通常不需要关心。但如果你想看看模板的功能具体是怎样被编译的,可能会发现会非常有意思。下面是一个使用
rrreeeVue.compile
Si vous utilisez des composants fonctionnels basés sur des modèles, vous devez également ajouter des attributs et des écouteurs manuellement. Parce que nous avons accès à son contexte indépendant, nous pouvons transmettre n'importe quel attribut HTML en utilisantdata.attrs
, ou nous pouvons utiliser desécouteurs
(c'est-à-diredata.on< /code >) pour transmettre tous les écouteurs d'événement.
slots()
etchildren
comparaisonVous vous demandez peut-être pourquoi
Pour ce composant,slots()
est nécessaire à en même temps et lesenfants
.slots().default
n'est-il pas similaire àchildren
? Dans certains scénarios, cela est vrai, mais que se passe-t-il s'il s'agit d'un composant fonctionnel avec des nœuds enfants comme celui-ci ?
rrreeechildren
vous donnera deux balises de paragraphe, tandis queslots().default
ne transmettra que la deuxième balise de paragraphe anonyme,slots () .foo
transmettra la première balise de paragraphe nommée. Vous avez à la foischildren
etslots()
, vous pouvez donc choisir de rendre le composant conscient d'un mécanisme de slot, ou simplement le transmettre en passantchildren code> Laissez les autres composants le gérer. 🎜🎜🎜🎜
Compilation de modèles
🎜Vous Il peut être intéressant de savoir que les modèles de Vue sont en réalité compilés en fonctions de rendu. Il s’agit d’un détail d’implémentation et il n’y a généralement rien à craindre. Mais si vous voulez voir comment les fonctionnalités du modèle sont compilées, cela pourrait vous être très intéressant. Voici un exemple simple d'utilisation deVue.compile
pour compiler des chaînes de modèles à la volée : 🎜🎜🎜🎜🎜🎜🎜🎜🎜
<slot>< dans le titre de chaque niveau. /slot>
, et répétez l'opération lorsque vous souhaitez insérer un élément d'ancrage.