1、创建
Vue.component('组件名', {
render: function (createElement) { createElement用来创建虚拟dom即VNode
return createElement(
'div', 标签名|组件名|字符串模板,或resolve('...')三种形式的一个async函数
字符串模板:const Home = { template: '<p>home page</p>' }
{ 可选,若无可直接在此写第三个参数
class: {...}, `v-bind:class`相同,接受一个字符串、对象或字符串和对象组成的数组
style: {}, `v-bind:style`相同,接受一个字符串、对象,或对象组成的数组
attrs: { 普通的HTML属性
id: 'foo'
},
props: { 组件传参,和组件模板中props相同
myProp: 'bar' 默认值可直接写
},
domProps: { 和直接调用DOM相关的属性
innerHTML: 'baz'
},
on: { 监听事件,不再支持如`v-on:keyup.enter`这样的修饰器。
click: this.clickHandler
},
nativeOn: { 仅用于组件,`this.$emit`触发的事件。
click: this.nativeClickHandler
},
directives: [ 自定义指令,无法对 `binding` 中的 `oldValue`,赋值,因为 Vue 已经自动为你进行了同步。
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
作用域插槽:即可以传递参数的插槽,具体可看第5步
scopedSlots: {格式为:{ name: props => VNode | Array<VNode> }
default: props => createElement('span', props.text) 对应:<span>...</span>
具名插槽名称: props => [createElement('span', props.text),...]
},
slot: 'name-of-slot', 如果组件是其它组件的子组件,需为插槽指定名称
key: 'myKey', 其它特殊顶层property
ref: 'myRef',
refInFor: true 其它特殊顶层property,相同ref名,$refs.myRef会变成一个数组。
},
[ 标签中的子内容
'可以使用字符串来生成文本虚拟节点',
createElement('h1', '一则头条'), 子级虚拟节点(VNodes),必须由`createElement()`构建而成,
]
)
},
renderError (h, err) { 当render函数遭遇错误时,提供另外一种渲染输出,其错误将会作为第二个参数传递到renderError,这个功能配合hot-reload非常实用,只在开发者环境下工作。
return h('pre', { style: { color: 'red' }}, err.stack)
}
其他组件配置
props:{...},
...
})
2、工厂函数生成多个相同VNode
组件树中的所有VNode必须是唯一的
错误示例:
var myParagraphVNode = createElement('p', 'hi')
return createElement('div', [
myParagraphVNode, myParagraphVNode
])
解决方法:使用工厂函数
render: function (createElement) {
return createElement('div',
Array.apply(null, { length: 20 }).map(function () {
return createElement('p', 'hi')
})
)
}
2.5合并多个参数配置(vue3.0)
mergeProps其返回的是一个新创建的对象,而作为参数传递的对象则不会被修改。
可以传递不限数量的对象,后面参数的property优先。
事件监听器、class和style,这些property的值是被合并的而不是覆盖的
import { h, mergeProps } from 'vue'
export default {
inheritAttrs: false,
render() {
const props = mergeProps({
class: 'active' 该 class 将与 $attrs 中的其他 class 合并。
}, this.$attrs)
return h('div', props)
}
}
3、需要使用逻辑实现v-if、v-for、v-model等指令效果
(1)v-if和v-for:
<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.')
}
}
(2)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)
}
}
})
}
4、事件和按键修饰符
on:{
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~!mouseover': this.doThisOnceInCapturingMode
修饰符转换:
.passive:&
.capture:!
.once:~
.capture.once 或.once.capture: ~!
其他修饰符需要使用原生实现:
.stop event.stopPropagation()
.prevent event.preventDefault()
.self if(event.target !== event.currentTarget) return;
按键:
.enter,.13 if(event.keyCode!==13) return;
修饰键:
.ctrl,.alt,.shift,.meta if(!event.ctrlKey)return; (可将ctrlKey分别修改为altKey、shiftKey或者metaKey)
}
5、使用插槽设置子节点内容
方式一:this.$slots
如:`<div><slot></slot></div>`
render: function (createElement) {
return createElement('div', this.$slots.default) default表示未命名的插槽
}
方式二:this.$scopedSlots
也可以通过this.$scopedSlots访问作用域插槽,每个作用域插槽都是一个返回若干VNode的函数:
如: `<div><slot :text="message"></slot></div>`
props: ['message'],
render: function (createElement) {
return createElement('div', [
this.$scopedSlots.default({
text: this.message
})
])
}
方式三:scopedSlots
如:`<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
render: function (createElement) {
return createElement('div', [
createElement('child', {
scopedSlots: {
default: function (props) {
return createElement('span', props.text)
}
}
})
])
}
6、替换render函数中createElement方法为jsx语法:
<anchored-heading :level="1">
<span>Hello</span> world!
</anchored-heading>
可实现成:
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render: function (h) { 将h作为createElement的别名是Vue生态系统中的一个通用惯例,实际上也是JSX所要求的,从Vue的Babel插件的3.4.0版本开始,我们会在以ES2015语法声明的含有JSX的任何方法和getter中自动注入 const h = this.$createElement,这样你就可以去掉(h)参数了。
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
7、函数式组件
无状态组件,即没有使用管理任何状态,也没有监听任何传递给它的状态,也没有使用生命周期方法,只是一个接受一些 prop 的函数
好处:因为函数式组件只是函数,所以渲染开销也低很多。适用于只做单纯展示的组件
Vue.component('my-component', {
functional: true, 这意味它无状态(没有响应式数据)
props: {
...
},
render: function (createElement, context) { 为了弥补缺少的实例,提供第二个参数作为上下文
context包含如下属性:
props:提供传递的参数
childre:VNode 子节点的数组
slots:一个函数,返回了包含所有插槽的对象,.slots()
scopedSlots:(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
parent:对父组件的引用
listeners:(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
injections:(2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的 property。
return createElement('button', context.data, context.children)
通过向createElement传入context.data作为第二个参数,就把该组件上面所有的属性和事件监听器都传递下去了
事实上这是非常透明的,以至于那些事件甚至并不要求.native 修饰符。
}
})
在 2.5.0 及以上版本中,如果你使用了单文件组件,那么基于模板的函数式组件可以这样声明:
<template functional>
</template>
代码示例:
实现效果:
<h1> h1可替换成h2、h3等
<a name="hello-world" href="#hello-world">
Hello world!
</a>
</h1>
使用渲染函数的方式:
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // 创建h标签名称
[
createElement('a', { //创建a标签
attrs: { //锚点设置
name: headingId,
href: '#' + headingId
}
}, this.$slots.default) //插槽内容
]
)
},
props: {
level: {
type: Number,
required: true
}
}
})