レンダリング関数と JSX
目次
- テンプレート関数の代わりに JavaScript を使用する
属性の受け渡しおよびイベントから子要素または子コンポーネントへ
- ##テンプレートのコンパイル
##基本- Vue では、ほとんどの場合、テンプレートを使用して HTML を作成することをお勧めします。ただし、JavaScript の完全なプログラミング機能が本当に必要なシナリオもいくつかあります。現時点では、テンプレートよりもコンパイラに近い レンダリング関数
render 関数が非常に役立つ簡単な例を見てみましょう。いくつかのアンカー付きタイトルを生成したいとします。 <h1>
<a name="hello-world" href="#hello-world">
Hello world!
</a>
</h1>
上記の HTML では、次のようにコンポーネント インターフェイスを定義することにします: <anchored-heading :level="1">Hello world!</anchored-heading>
## のみを渡すことができるヘッダーの作成を開始するとき#levelprop が見出し (見出し) コンポーネントを動的に生成する場合、次のように実装することをすぐに思いつくかもしれません:
<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 } } })ここでテンプレートを使用することは最良の選択ではありません。コードが長くなるだけでなく、 <slot></slot>
と記述し、アンカー要素を挿入するときにもう一度繰り返します。
テンプレートはほとんどのコンポーネントで非常に便利ですが、ここでは明らかに適していません。そこで、render
関数を使用して上記の例を書き直してみましょう。
Vue.component('anchored-heading', { render: function (createElement) { return createElement( 'h' + this.level, // 标签名称 this.$slots.default // 子节点数组 ) }, props: { level: { type: Number, required: true } } })
見た目はずっとシンプルですね!この方法では、コードははるかに単純になりますが、Vue のインスタンス プロパティについてよく理解しておく必要があります。この例では、v-slot
ディレクティブなしで子ノードをコンポーネントに渡すとき (anchored-Heading
の Hello world!## など) を知っておく必要があります。 、これらの子ノードはコンポーネント インスタンスの
$slots.default に保存されます。まだ理解していない場合は、レンダリング関数に入る前に
インスタンス プロパティ API を読むことをお勧めします。
レンダリング関数に入る前に、いくつかのことを学びましょう ブラウザがどのように動作するかは重要です。次の HTML 部分を例に挙げます。
<div> <h1>My title</h1> Some text content <!-- TODO: Add tagline --> </div>
ブラウザがこのコードを読み取ると、すべてのコンテンツを追跡するために
"DOM ノード" ツリー
上記の HTML に対応する DOM ノード ツリーは次のとおりです。
各要素はノードです。テキストの各部分もノードです。コメントもノードです。ノードはページの一部です。家系図と同様に、各ノードには子を持つことができます (つまり、各部分に他の部分を含めることができます)。
これらすべてのノードを効率的に更新するのは難しい場合がありますが、幸いなことに、手動で行う必要はありません。ページに表示したい HTML を Vue に伝えるだけです。これはテンプレート内で指定できます:<h1>{{ blogTitle }}</h1>またはレンダリング関数内で指定できます:
render: function (createElement) { return createElement('h1', this.blogTitle) }どちらの場合も、Vue のページは次のようになります。
blogTitle
が変更された場合でも、自動的に更新され続けます。Vue は、仮想 DOM
を作成することで自身を追跡します。実際の DOM を変更します。このコード行を注意深く見てください:return createElement('h1', this.blogTitle)createElement 正確には何が返されるのでしょうか?実際には実際の DOM 要素ではありません。より正確な名前は
createNodeDescription です。これに含まれる情報は、子ノードの説明情報など、ページ上にどの種類のノードをレンダリングする必要があるかを Vue に伝えるためです。このようなノードを「仮想ノード」と呼び、しばしば「
VNode」と省略されます。 「仮想 DOM」とは、Vue コンポーネント ツリーから構築された VNode ツリー全体を指します。
createElement
パラメータ
次に行う必要があることcreateElement
関数のテンプレートでこれらの関数を使用する方法をよく理解してください。 createElement
が受け入れるパラメータは次のとおりです:
// @returns {VNode} createElement( // {String | Object | Function} // 一个 HTML 标签名、组件选项对象,或者 // resolve 了上述任何一种的一个 async 函数。必填项。 'div', // {Object} // 一个与模板中属性对应的数据对象。可选。 { // (详情见下一节) }, // {String | Array} // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成, // 也可以使用字符串来生成“文本虚拟节点”。可选。 [ '先写一些文字', createElement('h1', '一则头条'), createElement(MyComponent, { props: { someProp: 'foobar' } }) ] )
データ オブジェクトの詳細
注意すべき点: v-bind:class
と v-bind:style
がテンプレート構文で特別に扱われるのと同様に、これらには対応するものもあります。 VNode データ オブジェクトのトップレベル フィールド。このオブジェクトを使用すると、通常の HTML 属性だけでなく、innerHTML
などの DOM 属性もバインドできます (これは v-html
ディレクティブをオーバーライドします)。
{ // 与 `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 }
完全な例
この知識があれば、当初達成したかったことを達成できるようになります。コンポーネント:
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 } } })
##制約
VNode は一意である必要があります
コンポーネント ツリー内のすべての VNode は一意である必要があります。これは、次のレンダリング関数が不正であることを意味します:render: function (createElement) { var myParagraphVNode = createElement('p', 'hi') return createElement('div', [ // 错误 - 重复的 VNode myParagraphVNode, myParagraphVNode ]) }本当に要素/コンポーネントを何度も繰り返す必要がある場合は、ファクトリ関数を使用してそれを実現できます。たとえば、次のレンダリング関数は、完全に合法的な方法で 20 の同一の段落をレンダリングします。
render: function (createElement) { return createElement('div', Array.apply(null, { length: 20 }).map(function () { return createElement('p', 'hi') }) ) }
テンプレート関数の代わりに JavaScript を使用する
##v-if
および v -for#Vue のレンダリング関数は、ネイティブ JavaScript で簡単に実行できる操作に代わる独自の代替手段を提供しません。たとえば、テンプレートで使用されている
と v-for
:
これらは、JavaScript の <ul v-if="items.length">
<li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>
と map
を書き換えます: 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.')
}
}
##v-model#レンダリング関数には v-model
との直接の対応関係はありません。対応するロジックを自分で実装する必要があります。
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) } } }) }
これは、最下層ですが、これにより、v-model
よりもインタラクションの詳細をより詳細に制御できます。
#イベントとキー修飾子
.passive の場合、 .capture
と.once はイベント修飾子です。Vue は、
on:
に使用できる対応するプレフィックスを提供します。
プレフィックス | |
---|---|
## ####&#################。捕獲############!########## ### ##.once | ~ |
##.capture.once または | .once.capture
|
| ##例: on: { '!click': this.doThisInCapturingMode, '~keyup': this.doThisOnce, '~!mouseover': this.doThisOnceInCapturingMode } 他のすべての修飾子の場合、イベント ハンドラーでイベント メソッドを使用できるため、プライベート プレフィックスは必要ありません。 |
Modifier 関数での同等の操作の処理
| #.stop |
##.prevent
#.self | |
---|---|
| キーストローク: .enter | ,
if (event.keyCode !== 13) return | (他のキー修飾子の場合は、13 を に変更できます)別のキーコード | )
修飾キー: | .ctrl、 .alt 、 | .shift
if (!event.ctrlKey) return (置換 ctrlKey | は altKey に変更されました, shiftKey または metaKey) |