diff --git a/src/guide/render-function.md b/src/guide/render-function.md index 4b676daf..43c438ac 100644 --- a/src/guide/render-function.md +++ b/src/guide/render-function.md @@ -21,7 +21,9 @@ Vue では、大多数のケースにおいてテンプレートを使ってア コンポーネントは、`level` の値に応じた見出しを生成する必要があります。手っ取り早くこれで実現しましょう: ```js -const app = Vue.createApp({}) +const { createApp } = Vue + +const app = createApp({}) app.component('anchored-heading', { template: ` @@ -58,12 +60,12 @@ app.component('anchored-heading', { ほとんどのコンポーネントでテンプレートがうまく働くとはいえ、明らかにこれはそうではないものの一つです。そこで、 `render()` 関数を使ってこれを書き直してみましょう。 ```js -const app = Vue.createApp({}) +const { createApp, h } = Vue + +const app = createApp({}) app.component('anchored-heading', { render() { - const { h } = Vue - return h( 'h' + this.level, // タグ名 {}, // props/属性 @@ -95,20 +97,13 @@ render 関数に取り掛かる前に、ブラウザがどのように動くの ブラウザはこのコードを読み込むと、血縁関係を追跡するために家系図を構築するのと同じように、全てを追跡する [「DOM ノード」のツリー](https://javascript.info/dom-nodes)を構築します。 - - 上の HTML の DOM ノードツリーはこんな感じになります。 ![DOM ツリーの可視化](/images/dom-tree.png) すべての要素はノードです。テキストのすべてのピースはノードです。コメントですらノードです!それぞれのノードは子供を持つことができます。 (つまり、それぞれのノードは他のノードを含むことができます) -これらすべてのノードを効率的に更新することは難しくなり得ますが、ありがたいことに、それを手動で行う必要はありません。代わりに、テンプレートや render 関数で、ページ上にどのような HTML が欲しいかを Vue に伝えるのです。 - -テンプレート: +これらすべてのノードを効率的に更新することは難しくなり得ますが、ありがたいことに、それを手動で行う必要はありません。代わりに、Vue にどのような HTML を表示させたいのかをテンプレートで伝えます: ```html

{{ blogTitle }}

@@ -118,7 +113,7 @@ When a browser reads this code, it builds a tree of “DOM nodes” to help it k ```js render() { - return Vue.h('h1', {}, this.blogTitle) + return h('h1', {}, this.blogTitle) } ``` @@ -129,7 +124,7 @@ render() { Vue は、実際の DOM に反映する必要のある変更を追跡するために **仮想 DOM** を構築して、ページを最新の状態に保ちます。この行をよく見てみましょう: ```js -return Vue.h('h1', {}, this.blogTitle) +return h('h1', {}, this.blogTitle) ``` `h()` 関数が返すものはなんでしょうか?これは、 _正確には_ 実際の DOM 要素ではありません。それが返すのは、ページ上にどんな種類のノードをレンダリングするのかを Vue に伝えるための情報をもったプレーンなオブジェクトです。この情報には子供のノードの記述も含まれます。私たちは、このノードの記述を *仮想ノード* と呼び、通常 **VNode** と省略します。「仮想 DOM」というのは、Vue コンポーネントのツリーから構成される VNode のツリー全体のことなのです。 @@ -141,21 +136,23 @@ return Vue.h('h1', {}, this.blogTitle) ```js // @returns {VNode} h( - // {String | Object | Function } tag - // HTMLタグ名、コンポーネントまたは非同期コンポーネント - // nullを返す関数を使用した場合、コメントがレンダリングされます。 + // {String | Object | Function} tag + // HTMLタグ名、コンポーネント、非同期コンポーネント、 + // または関数型コンポーネント。 // // 必須 'div', // {Object} props - // テンプレート内で使うであろう属性、プロパティ、イベントに対応するオブジェクト + // テンプレート内で使うであろう + // 属性、プロパティ、イベントに対応するオブジェクト // // 省略可能 {}, // {String | Array | Object} children - // `h()` で作られた子供のVNode、または文字列(テキストVNodeになる)、 + // `h()` で作られた子供のVNode、 + // または文字列(テキストVNodeになる)、 // またはスロットをもつオブジェクト // // 省略可能 @@ -169,12 +166,16 @@ h( ) ``` +props がない場合は、通常 children を第2引数として渡すことができます。それがあいまいな場合は、 `null` を第2引数として渡して、 children を第3引数にしておけます。 + ## 完全な例 -この知識によって、書き始めたコンポーネントを今では完成させることができます: +この知識によって、今度は書き始めたコンポーネントを完成させることができます: ```js -const app = Vue.createApp({}) +const { createApp, h } = Vue + +const app = createApp({}) /** 子供のノードから再帰的にテキストを取得する */ function getChildrenTextContent(children) { @@ -197,8 +198,8 @@ app.component('anchored-heading', { .replace(/\W+/g, '-') // 英数字とアンダースコア以外の文字を-に置換する .replace(/(^-|-$)/g, '') // 頭と末尾の-を取り除く - return Vue.h('h' + this.level, [ - Vue.h( + return h('h' + this.level, [ + h( 'a', { name: headingId, @@ -225,8 +226,8 @@ app.component('anchored-heading', { ```js render() { - const myParagraphVNode = Vue.h('p', 'hi') - return Vue.h('div', [ + const myParagraphVNode = h('p', 'hi') + return h('div', [ // おっと - VNode が重複しています! myParagraphVNode, myParagraphVNode ]) @@ -237,14 +238,59 @@ render() { ```js render() { - return Vue.h('div', - Array.apply(null, { length: 20 }).map(() => { - return Vue.h('p', 'hi') + return h('div', + Array.from({ length: 20 }).map(() => { + return h('p', 'hi') }) ) } ``` +## コンポーネントの VNodes を作る + +コンポーネントの VNode を作るためには、 `h` の第1引数にコンポーネントそのものを渡します: + +```js +render() { + return h(ButtonCounter) +} +``` + +コンポーネントを名前解決する必要がある場合は、 `resolveComponent` で呼び出せます: + +```js +const { h, resolveComponent } = Vue + +// ... + +render() { + const ButtonCounter = resolveComponent('ButtonCounter') + return h(ButtonCounter) +} +``` + +`resolveComponent` は、テンプレートがコンポーネントを名前解決するために内部的に使っているものと同じ関数です。 + +`render` 関数は通常、 [グローバルに登録された](/guide/component-registration.html#グローバル登録) コンポーネントにだけ `resolveComponent` を使う必要があります。 [ローカルのコンポーネント登録](/guide/component-registration.html#ローカル登録) は通常、完全に省略できます。次のような例を考えてみましょう: + +```js +// これを単純化してみると +components: { + ButtonCounter +}, +render() { + return resolveComponent('ButtonCounter') +} +``` + +コンポーネントの名前を登録して、それを調べるというよりも、直接使うことができます: + +```js +render() { + return h(ButtonCounter) +} +``` + ## テンプレートの機能をプレーンな JavaScript で置き換える ### `v-if` と `v-for` @@ -264,23 +310,26 @@ render() { props: ['items'], render() { if (this.items.length) { - return Vue.h('ul', this.items.map((item) => { - return Vue.h('li', item.name) + return h('ul', this.items.map((item) => { + return h('li', item.name) })) } else { - return Vue.h('p', 'No items found.') + return h('p', 'No items found.') } } ``` +テンプレートでは、 `