|
1 | 1 | # render 函数组件 |
2 | 2 |
|
| 3 | +```html |
| 4 | +<!-- HTML --> |
| 5 | +<script type="text/x-template" id="anchored-heading-template"> |
| 6 | + <h1 v-if="level === 1"> <slot></slot> </h1> |
| 7 | + <h2 v-else-if="level === 2"> <slot></slot> </h2> |
| 8 | + <h3 v-else-if="level === 3"> <slot></slot> </h3> |
| 9 | + <h4 v-else-if="level === 4"> <slot></slot> </h4> |
| 10 | + <h5 v-else-if="level === 5"> <slot></slot> </h5> |
| 11 | + <h6 v-else-if="level === 6"> <slot></slot> </h6> |
| 12 | +</script> |
| 13 | + <!-- Javascript --> |
| 14 | +<script> |
| 15 | + Vue.component('anchored-heading', { template: '#anchored-heading-template', props: { level: { type: Number,required: true } } }) |
| 16 | +</script> |
| 17 | +``` |
| 18 | + |
| 19 | +这是我从网上找的一个非常常见的案例,大部分关于渲染函数的文章可能都会使用这个案例来说明在这种场景下使用template并不是最好的选择,首先是代码冗长,其次重复使用了slot,虽然一眼看上去简单明了,但这是不是最简单的写法,下面是更加简洁的render函数的写法: |
| 20 | + |
| 21 | +```html |
| 22 | +<script> |
| 23 | + Vue.components('anchored-heading', { |
| 24 | + render: function (h) => { |
| 25 | + return h(`h${this.level}`, this.slot.default) |
| 26 | + }, |
| 27 | + props: { level: { type: Number,required: true } } |
| 28 | + }) |
| 29 | +</script> |
| 30 | +``` |
| 31 | + |
3 | 32 | ## vue组件的渲染过程 |
4 | 33 |  |
5 | 34 |  |
@@ -37,7 +66,6 @@ module.exports = { |
37 | 66 |
|
38 | 67 | vue实例中有个$mount函数,通常我们在手动挂载组件的时候会用到这个方法,$mount是整个渲染过程的起始点。所谓手动挂载就是去手动让组件渲染,关于$mount的实践我在全局组件章节有提到,之后我们在vue的测试章节也会使用到。 |
39 | 68 |
|
40 | | - |
41 | 69 |  |
42 | 70 |  |
43 | 71 |
|
@@ -129,6 +157,11 @@ new Vue({ |
129 | 157 |
|
130 | 158 | render函数默认接受一个h的参数,有的时候别人也会将h换成`createElement`,这样的话函数更加明显。 |
131 | 159 |
|
| 160 | +我们接下来从上边这段代码出发结合部分[源代码](https://github.com/vuejs/vue/blob/2.6/dist/vue.common.dev.js)大概了解一下一个vue的实例化过程: |
| 161 | + |
| 162 | + |
| 163 | + |
| 164 | + |
132 | 165 | 我们回到上边的`$mount`源码的部分,在编译成render函数之后,之后调用了运行时版本的`$mount`这个函数,我们把这个代码截取出来: |
133 | 166 |
|
134 | 167 | ```js |
@@ -284,10 +317,113 @@ Vue.prototype._render = function () { |
284 | 317 |
|
285 | 318 | `第一个参数:String | Object | function ` |
286 | 319 |
|
| 320 | +```js |
| 321 | +Vue.component('custom-element', { |
| 322 | + render: function (createElement) { |
| 323 | + return createElement('div') |
| 324 | + } |
| 325 | +}) |
| 326 | + |
| 327 | +Vue.component('custom-element', { |
| 328 | + render: function (createElement) { |
| 329 | + return createElement({ template: `<div>Hello Vue!</div>` }) |
| 330 | + } |
| 331 | +}) |
| 332 | + |
| 333 | +Vue.component('custom-element', { |
| 334 | + render: function (createElement) { |
| 335 | + var eleFun = function () { |
| 336 | + return { template: `<div>Hello Vue!</div>` } |
| 337 | + } |
| 338 | + return createElement(eleFun()) |
| 339 | + } |
| 340 | +}) |
| 341 | +``` |
287 | 342 | `第二个参数是一个可选的数据对象`,这个说明还是看文档吧。 |
288 | 343 |
|
| 344 | +```js |
| 345 | +{ |
| 346 | + // 与 `v-bind:class` 的 API 相同, |
| 347 | + // 接受一个字符串、对象或字符串和对象组成的数组 |
| 348 | + 'class': { |
| 349 | + foo: true, |
| 350 | + bar: false |
| 351 | + }, |
| 352 | + // 与 `v-bind:style` 的 API 相同, |
| 353 | + // 接受一个字符串、对象,或对象组成的数组 |
| 354 | + style: { |
| 355 | + color: 'red', |
| 356 | + fontSize: '14px' |
| 357 | + }, |
| 358 | + // 普通的 HTML 特性 |
| 359 | + attrs: { |
| 360 | + id: 'foo' |
| 361 | + }, |
| 362 | + // 组件 prop |
| 363 | + props: { |
| 364 | + myProp: 'bar' |
| 365 | + }, |
| 366 | + // DOM 属性 |
| 367 | + domProps: { |
| 368 | + innerHTML: 'baz' |
| 369 | + }, |
| 370 | + // 事件监听器在 `on` 属性内, |
| 371 | + // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。 |
| 372 | + // 需要在处理函数中手动检查 keyCode。 |
| 373 | + on: { |
| 374 | + click: this.clickHandler |
| 375 | + }, |
| 376 | + // 仅用于组件,用于监听原生事件,而不是组件内部使用 |
| 377 | + // `vm.$emit` 触发的事件。 |
| 378 | + nativeOn: { |
| 379 | + click: this.nativeClickHandler |
| 380 | + }, |
| 381 | + // 自定义指令。注意,你无法对 `binding` 中的 `oldValue` |
| 382 | + // 赋值,因为 Vue 已经自动为你进行了同步。 |
| 383 | + directives: [ |
| 384 | + { |
| 385 | + name: 'my-custom-directive', |
| 386 | + value: '2', |
| 387 | + expression: '1 + 1', |
| 388 | + arg: 'foo', |
| 389 | + modifiers: { |
| 390 | + bar: true |
| 391 | + } |
| 392 | + } |
| 393 | + ], |
| 394 | + // 作用域插槽的格式为 |
| 395 | + // { name: props => VNode | Array<VNode> } |
| 396 | + scopedSlots: { |
| 397 | + default: props => createElement('span', props.text) |
| 398 | + }, |
| 399 | + // 如果组件是其它组件的子组件,需为插槽指定名称 |
| 400 | + slot: 'name-of-slot', |
| 401 | + // 其它特殊顶层属性 |
| 402 | + key: 'myKey', |
| 403 | + ref: 'myRef', |
| 404 | + // 如果你在渲染函数中给多个元素都应用了相同的 ref 名, |
| 405 | + // 那么 `$refs.myRef` 会变成一个数组。 |
| 406 | + refInFor: true |
| 407 | +} |
| 408 | +``` |
| 409 | + |
289 | 410 | `第三个参数是children: String | Array` |
290 | 411 |
|
| 412 | +```js |
| 413 | +Vue.component('custom-element', { |
| 414 | + render: function (createElement) { |
| 415 | + var self = this return createElement( |
| 416 | + // 第一个参数是一个简单的HTML标签字符 “必选” |
| 417 | + 'div', |
| 418 | + // 第二个参数是一个包含模板相关属性的数据对象 “可选” |
| 419 | + { class: { title: true }, style: { border: '1px solid', padding: '10px' } }, |
| 420 | + // 第三个参数是传了多个子元素的一个数组 “可选” |
| 421 | + [ createElement('h1', 'Hello Vue!'), createElement('p', '开始学习Vue!') ] |
| 422 | + ) |
| 423 | + } |
| 424 | +}) |
| 425 | +``` |
| 426 | + |
291 | 427 | 关于createElement这个函数是如何解析我们传入的内容,这部分可以在Vue源代码中搜索`createElement`这个函数自行了解,也可以去网上查阅类似专门分析的文章,我之前没有关心这部分内容,所以这里不做讲解。 |
292 | 428 |
|
293 | 429 |
|
0 commit comments