@@ -19,8 +19,11 @@ Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在
19
19
```
20
20
21
21
当开始写一个只能通过 ` level ` prop 动态生成标题 (heading) 的组件时,我们很快就可以得出这样的结论:
22
+
22
23
``` js
23
- const app = Vue .createApp ({})
24
+ const { createApp } = Vue
25
+
26
+ const app = createApp ({})
24
27
25
28
app .component (' anchored-heading' , {
26
29
template: `
@@ -51,17 +54,18 @@ app.component('anchored-heading', {
51
54
}
52
55
})
53
56
```
57
+
54
58
这个模板感觉不太好。它不仅冗长,而且我们为每个级别标题重复书写了 ` <slot></slot> ` 。当我们添加锚元素时,我们必须在每个 ` v-if/v-else-if ` 分支中再次重复它。
55
59
56
60
虽然模板在大多数组件中都非常好用,但是显然在这里它就不合适了。那么,我们来尝试使用 ` render ` 函数重写上面的例子:
57
61
58
62
``` js
59
- const app = Vue .createApp ({})
63
+ const { createApp , h } = Vue
64
+
65
+ const app = createApp ({})
60
66
61
67
app .component (' anchored-heading' , {
62
68
render () {
63
- const { h } = Vue
64
-
65
69
return h (
66
70
' h' + this .level , // tag name
67
71
{}, // props/attributes
@@ -109,7 +113,7 @@ app.component('anchored-heading', {
109
113
110
114
``` js
111
115
render () {
112
- return Vue . h (' h1' , {}, this .blogTitle )
116
+ return h (' h1' , {}, this .blogTitle )
113
117
}
114
118
```
115
119
@@ -120,7 +124,7 @@ render() {
120
124
Vue 通过建立一个** 虚拟 DOM** 来追踪自己要如何改变真实 DOM。请仔细看这行代码:
121
125
122
126
``` js
123
- return Vue . h (' h1' , {}, this .blogTitle )
127
+ return h (' h1' , {}, this .blogTitle )
124
128
```
125
129
126
130
` h() ` 到底会返回什么呢?其实不是一个* 实际* 的 DOM 元素。它更准确的名字可能是 createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为 ** VNode** 。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。
167
171
有了这些知识,我们现在可以完成我们最开始想实现的组件:
168
172
169
173
``` js
170
- const app = Vue .createApp ({})
174
+ const { createApp , h } = Vue
175
+
176
+ const app = createApp ({})
171
177
172
178
/** Recursively get text from children nodes */
173
179
function getChildrenTextContent (children ) {
@@ -190,8 +196,8 @@ app.component('anchored-heading', {
190
196
.replace (/ \W + / g , ' -' ) // replace non-word characters with dash
191
197
.replace (/ (^ -| -$ )/ g , ' ' ) // remove leading and trailing dashes
192
198
193
- return Vue . h (' h' + this .level , [
194
- Vue . h (
199
+ return h (' h' + this .level , [
200
+ h (
195
201
' a' ,
196
202
{
197
203
name: headingId,
@@ -218,8 +224,8 @@ app.component('anchored-heading', {
218
224
219
225
``` js
220
226
render () {
221
- const myParagraphVNode = Vue . h (' p' , ' hi' )
222
- return Vue . h (' div' , [
227
+ const myParagraphVNode = h (' p' , ' hi' )
228
+ return h (' div' , [
223
229
// 错误 - 重复的Vnode!
224
230
myParagraphVNode, myParagraphVNode
225
231
])
@@ -230,9 +236,9 @@ render() {
230
236
231
237
``` js
232
238
render () {
233
- return Vue . h (' div' ,
239
+ return h (' div' ,
234
240
Array .from ({ length: 20 }).map (() => {
235
- return Vue . h (' p' , ' hi' )
241
+ return h (' p' , ' hi' )
236
242
})
237
243
)
238
244
}
@@ -257,24 +263,24 @@ render() {
257
263
props: [' items' ],
258
264
render () {
259
265
if (this .items .length ) {
260
- return Vue . h (' ul' , this .items .map ((item ) => {
261
- return Vue . h (' li' , item .name )
266
+ return h (' ul' , this .items .map ((item ) => {
267
+ return h (' li' , item .name )
262
268
}))
263
269
} else {
264
- return Vue . h (' p' , ' No items found.' )
270
+ return h (' p' , ' No items found.' )
265
271
}
266
272
}
267
273
```
268
274
269
275
### ` v-model `
270
276
271
- ` v-model ` 指令扩展为 ` modelValue ` 和 ` onUpdate:modelValue ` 在模板编译过程中,我们必须自己提供这些prop :
277
+ ` v-model ` 指令扩展为 ` modelValue ` 和 ` onUpdate:modelValue ` 在模板编译过程中,我们必须自己提供这些 prop :
272
278
273
279
``` js
274
280
props: [' modelValue' ],
275
281
emits: [' update:modelValue' ],
276
282
render () {
277
- return Vue . h (SomeComponent, {
283
+ return h (SomeComponent, {
278
284
modelValue: this .modelValue ,
279
285
' onUpdate:modelValue ' : value => this .$emit (' update:modelValue' , value)
280
286
})
@@ -283,25 +289,25 @@ render() {
283
289
284
290
### ` v-on `
285
291
286
- 我们必须为事件处理程序提供一个正确的prop名称 ,例如,要处理 ` click ` 事件,prop名称应该是 ` onClick ` 。
292
+ 我们必须为事件处理程序提供一个正确的 prop 名称 ,例如,要处理 ` click ` 事件,prop 名称应该是 ` onClick ` 。
287
293
288
294
``` js
289
295
render () {
290
- return Vue . h (' div' , {
296
+ return h (' div' , {
291
297
onClick : $event => console .log (' clicked' , $event .target )
292
298
})
293
299
}
294
300
```
295
301
296
302
#### 事件修饰符
297
303
298
- 对于 ` .passive ` 、 ` .capture ` 和 ` .once ` 事件修饰符,Vue提供了处理程序的对象语法 :
304
+ 对于 ` .passive ` 、 ` .capture ` 和 ` .once ` 事件修饰符,Vue 提供了处理程序的对象语法 :
299
305
300
306
实例:
301
307
302
308
``` javascript
303
309
render () {
304
- return Vue . h (' input' , {
310
+ return h (' input' , {
305
311
onClick: {
306
312
handler: this .doThisInCapturingMode ,
307
313
capture: true
@@ -321,19 +327,19 @@ render() {
321
327
322
328
对于所有其它的修饰符,私有前缀都不是必须的,因为你可以在事件处理函数中使用事件方法:
323
329
324
- | 修饰符 | 处理函数中的等价操作 |
325
- | ----------------------------------------------------- | -------- ------------------------------------------------------------------------------------------------------------ |
326
- | ` .stop ` | ` event.stopPropagation() ` |
327
- | ` .prevent ` | ` event.preventDefault() ` |
328
- | ` .self ` | ` if (event.target !== event.currentTarget) return ` |
329
- | 按键:<br >` .enter ` , ` .13 ` | ` if (event.keyCode !== 13) return ` (对于别的按键修饰符来说,可将 13 改为[ 另一个按键码] ( http://keycode.info/ ) |
330
- | 修饰键:<br >` .ctrl ` , ` .alt ` , ` .shift ` , ` .meta ` | ` if (!event.ctrlKey) return ` (将 ` ctrlKey ` 分别修改为 ` altKey ` , ` shiftKey ` , 或 ` metaKey ` ) |
330
+ | 修饰符 | 处理函数中的等价操作 |
331
+ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
332
+ | ` .stop ` | ` event.stopPropagation() ` |
333
+ | ` .prevent ` | ` event.preventDefault() ` |
334
+ | ` .self ` | ` if (event.target !== event.currentTarget) return ` |
335
+ | 按键:<br >` .enter ` , ` .13 ` | ` if (event.keyCode !== 13) return ` (对于别的按键修饰符来说,可将 13 改为[ 另一个按键码] ( http://keycode.info/ ) |
336
+ | 修饰键:<br >` .ctrl ` , ` .alt ` , ` .shift ` , ` .meta ` | ` if (!event.ctrlKey) return ` (将 ` ctrlKey ` 分别修改为 ` altKey ` , ` shiftKey ` , 或 ` metaKey ` ) |
331
337
332
338
这里是一个使用所有修饰符的例子:
333
339
334
340
``` js
335
341
render () {
336
- return Vue . h (' input' , {
342
+ return h (' input' , {
337
343
onKeyUp : event => {
338
344
// 如果触发事件的元素不是事件绑定的元素
339
345
// 则返回
@@ -358,15 +364,15 @@ render() {
358
364
``` js
359
365
render () {
360
366
// `<div><slot></slot></div>`
361
- return Vue . h (' div' , {}, this .$slots .default ())
367
+ return h (' div' , {}, this .$slots .default ())
362
368
}
363
369
```
364
370
365
371
``` js
366
372
props: [' message' ],
367
373
render () {
368
374
// `<div><slot :text="message"></slot></div>`
369
- return Vue . h (' div' , {}, this .$slots .default ({
375
+ return h (' div' , {}, this .$slots .default ({
370
376
text: this .message
371
377
}))
372
378
}
@@ -377,11 +383,13 @@ render() {
377
383
<!-- TODO: translation -->
378
384
379
385
``` js
386
+ const { h , resolveComponent } = Vue
387
+
380
388
render () {
381
389
// `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
382
- return Vue . h (' div' , [
383
- Vue . h (
384
- Vue . resolveComponent (' child' ),
390
+ return h (' div' , [
391
+ h (
392
+ resolveComponent (' child' ),
385
393
{},
386
394
// pass `slots` as the children object
387
395
// in the form of { name: props => VNode | Array<VNode> }
@@ -398,13 +406,13 @@ render() {
398
406
如果你写了很多渲染函数,可能会觉得下面这样的代码写起来很痛苦:
399
407
400
408
``` js
401
- Vue . h (
409
+ h (
402
410
' anchored-heading' ,
403
411
{
404
412
level: 1
405
413
},
406
414
{
407
- default : () => [Vue . h (' span' , ' Hello' ), ' world!' ]
415
+ default : () => [h (' span' , ' Hello' ), ' world!' ]
408
416
}
409
417
)
410
418
```
0 commit comments