用Vue已经有2年多了,用本文详解讲解Vue组件开发过程当中的方方面面,供你们学习,若是以为还能够,点个赞收藏一下,持续更新中。html
给组件命名有两种方式,一种是使用链式命名my-component
,一种是使用大驼峰命名MyComponent
,vue
在字符串模板中<my-component></my-component>
和 <MyComponent></MyComponent>
均可以使用,ios
在非字符串模板中最好使用<MyComponent></MyComponent>
,由于要遵循 W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符),避免和当前以及将来的 HTML 元素相冲突。axios
Vue.component('my-component', {
props:{
title:{
type: String,
default:'全局组件',
}
},
data(){
return {}
},
created(){},
mounted(){},
methods:{
handleClick(){
alert(this.title)
}
},
template: '<h3 @click="handleClick">{{ title }}</h3>',
})
复制代码
在项目中static文件中创建my_component.html文件,文件内容以下数组
<h3 @click="handleClick">{{ title }}</h3>
复制代码
Vue.component('my-component', (resolve, reject) =>{
// 能够请求一个html文件,既然存放模板仍是html文件存放比较好
axios.get("../static/my_component.html").then(res =>{
resolve({
template: res,
props: {
title: {
type: String,
default: '全局组件',
}
},
methods: {
handleClick() {
alert(this.title)
}
},
})
});
});
复制代码
同步注册浏览器
import myComponent from './my_component';
export default {
components: {
myComponent,
},
}
复制代码
异步注册(懒加载)bash
export default{
components:{
myComponent: resolve => require(['./my_component'],resolve)
}
}
复制代码
在非字符串模板中,若是prop是用小驼峰方式命名,在模板中要使用其等价的链式方式命名。iview
在字符串模板中就不要这么处理了。dom
index.vue文件异步
<template>
<myComponent :my-title="my_title"></myComponent>
</template>
<script>
export default{
data(){
return{
my_title:"个人组件"
}
},
components:{
myComponent:resolve => require(['./my_component'],resolve)
}
}
</script>
复制代码
my_component.vue文件
<template>
<div>
<h1>{{myTitle}}</h1>
</div>
</template>
<script>
export default{
props:{
myTitle:{
type:String,
default:'',
}
},
data(){
return{}
},
}
</script>
复制代码
由于HTML(非字符串模板)中的特性名是大小写不敏感的,因此浏览器会把全部大写字符解释为小写字符。
<myComponent my-title="个人组件"></myComponent>
复制代码
<myComponent :num="100"></myComponent>
<myComponent :isload="true"></myComponent>
复制代码
不能,防止从子组件意外改变父级组件的状态。
在data中定义一个值,并将prop值赋值给它,若是prop值是对象和数组时,赋值时要深拷贝一下。
<script>
export default{
props:{
myTitle:{
type:String,
default:'',
},
myInfo:{
type:Object,
default(){
return:{}
}
}
},
//this.deepClone为自定义的深拷贝方法
data(){
return{
title:this.myTitle,
info:this.deepClone(this.myInfo),
}
},
}
</script>
复制代码
2.若是prop以一种原始的值传入且须要进行转换。要使用这个 prop 的值来定义一个计算属性。
<script>
export default{
props:{
myAchievement:{
type:Number,
default:0,
},
},
data(){
return{}
},
computed: {
result: function () {
return this.myAchievement*0.7+12;
}
}
}
</script>
复制代码
String、Number、Boolean、Array、Object、Date、Function、Symbol。 此外还能够是一个自定义的构造函数Personnel
,而且经过 instanceof 来验证propwokrer
的值是不是经过这个自定义的构造函数建立的。
function Personnel(name,age){
this.name = name;
this.age = age;
}
export default {
props:{
wokrer:Personnel
}
}
复制代码
export default {
props:{
propA:String,
propB:{
type:Number,
},
propC:Boolean,
}
}
复制代码
export default {
props:{
propA:[String,Number],
propB:{
type:[String,Number]
}
}
}
复制代码
export default{
props:{
propA:{
type: String,
required: true,
}
}
}
复制代码
export default{
props:{
propA:{
type:Object,
default(){
return {
a: 1,
}
}
},
propB:{
type:Array,
default(){
return [1,2,3]
}
}
}
}
复制代码
export default{
props:{
propA:{
validator(val){
return 0 < val < 18
}
}
}
}
复制代码
不会,由于prop会在一个组件实例建立以前进行验证,因此data和computed在default或validator函数中是不可用的。
使用不带参数的 v-bind,例:对于一个给定的对象 post
post: {
id: 1,
title: 'My Journey with Vue'
}
复制代码
<myComponent v-bind="post"></myComponent>
复制代码
至关
<myComponent :id="post.id" :title="post.title"></myComponent>
复制代码
先举个例子,用v-model
来控制一个组件的显示隐藏
my_component.vue
<template>
<div v-show="value">
<span>个人组件</span>
<button @click="$emit('input',false)">隐藏</button>
</div>
</template>
<script>
export default{
props:{
value:{
type:Boolean,
default:false,
}
},
data(){
return{}
},
}
</script>
复制代码
<template>
<div>
<myComponent v-model="value"></myComponent>
<button @click="value=true">显示</button>
</div>
</template>
<script>
export default{
data(){
return{
value:false,
}
},
components:{
myComponent:resolve =>require(['./my_component'],resolve),
}
}
</script>
复制代码
那为何这么写?不明白的能够看的不是很清楚,那么在看下面代码就清楚。
<template>
<div>
<myComponent :value="value" @input="value=$event"></myComponent>
<button @click="value=true">显示</button>
</div>
</template>
复制代码
在父组件中用$event
访问经过$emit
抛出的值。
这么写是否是很清楚了,组件上的v-model='value'
至关 :value="value" @input="value=$event"
。
再举个例子
<template>
<div>
{{value}}
<myInput v-model="value"></myInput>
<!-- 等价于 -->
<myInput :value="value" @input="value=$event"></myInput>
</div>
</template>
<script>
export default {
data() {
return {
value: '',
}
},
components: {
myInput: resolve => require(['./my_input'], resolve),
}
}
</script>
复制代码
my_input.vue
<template>
<input :value="value" @input="$emit('input',$event.target.value)"/>
</template>
<script>
export default{
props:['value'],
data(){
return{}
},
}
</script>
复制代码
从以上代码能够看出,组件上的v-model='value'
至关 :value="value" @input="value=$event"
,
其组件的自定义事件必须是input
。
既而后能够实现v-model,那么model选项是用来干吗的,
由于一个组件上的v-model 默认会利用名为value的prop和名为input的事件,
可是像单选框、复选框等类型的输入控件可能会将value 特性用于不一样的目的,
model 选项能够用来避免这样的冲突。
那么上面的例子也能够这么实现
my_input.vue
<template>
<input :value="val" @input="$emit('change',$event.target.value)"/>
</template>
<script>
export default{
model:{
prop:'val',
event:'change'
},
props:['val'],
data(){
return{}
},
}
</script>
复制代码
<myComponent>
经过插槽传进去的内容
</myComponent>
复制代码
<template>
<div>
<h1>个人组件</h1>
<p>
<slot></slot>
</p>
</div>
</template>
复制代码
页面渲染出
个人组件
经过插槽传进去的内容
复制代码
若是 <myComponent>
没有包含一个 <slot></slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
<slot>默认内容</slot>
复制代码
<template>
<div>
<h2>默认插槽</h2>
<slot>默认内容</slot>
<h2>头部插槽</h2>
<slot name="header"></slot>
<h2>侧边栏插槽</h2>
<slot name="sider"></slot>
</div>
</template>
复制代码
<template>
<div>
<myComponent>
我是默认插槽
<template v-slot:header>我是头部插槽</template>
<template #sider>
我是侧边栏插槽
</template>
</myComponent>
</div>
</template>
复制代码
v-slot
指令的缩写是 #
,v-slot
只能添加在<template></template>
上
<slot></slot>
上面没有name属性,会自动渲染成<slot name="default"></slot>
,
也能够这么调用<template v-slot:default></template>
在实际项目中,咱们要获取子组件的数据在父组件中渲染插槽的内容。怎么办?
首先要在子组件中给slot
绑定一个子组件的data中的一个对象childrenData,
例:<slot :toParent="childrenData"></slot>
topParent是给父组件中调用的,以上称为插槽prop。
而后在父组件中这样调用 <template v-slot:default="slotProps">{{slotProps.toParent}}</template>
,
其中slotProps是表明插槽prop的集合,其命名能够随便取的。此时slotProps.toParent的值
就是childrenData的值
下面是完整例子
<template>
<div>
<h2>默认插槽</h2>
<slot :toParent="childrenData" :toParent2="childrenData2">
{{childrenData.data1}}
{{childrenData2.data1}}
</slot>
<h2>头部插槽</h2>
<slot name="header" :headerData="headerData">
{{headerData.data1}}
</slot>
</div>
</template>
<script>
export default{
data(){
return{
childrenData:{
data1:'子组件数据一',
data2:'子组件数据二',
},
childrenData2:{
data1:'子组件数据三',
data2:'子组件数据四',
},
headerData:{
data1:'子组件头部数据一',
data2:'子组件头部数据二',
},
}
},
}
</script>
复制代码
<template>
<div>
<myComponent>
<template v-slot:default="slotProps">
{{slotProps.toParent.data2}}
{{slotProps.toParent2.data2}}
</template>
<template v-slot:header="headerProps">
{{headerProps.headerData.data2}}
</template>
</myComponent>
</div>
</template>
复制代码
当有子组件slot上有多个插槽prop时,父组件调用时候能够ES6对象解构的方法,例:
<template>
<div>
<myComponent>
<template v-slot:default="{toParent,toParent2}">
{{toParent.data2}}
{{toParent2.data2}}
</template>
<template v-slot:header="{headerData}">
{{headerData.data2}}
</template>
</myComponent>
</div>
</template>
复制代码
也能够给子组件slot上绑定的值从新命名,例:
<template>
<div>
<myComponent>
<template #default="{toParent:a,toParent2:b}">
{{a.data2}}
{{b.data2}}
</template>
<template #header="{headerData:c}">
{{c.data2}}
</template>
</myComponent>
</div>
</template>
复制代码
在组件标签上添加的属性,没有在组件中props中定义,这些属性会被添加到这个组件的根元素上,这就是组件的非Prop特性,例:
<template>
<div data="">
<h2>个人组件</h2>
</div>
</template>
复制代码
<template>
<div>
<myComponent data="noProp"></myComponent>
</div>
</template>
复制代码
按F12打开调试工具能够看到
若是组件的根节点已有data="children"
,那么在组件标签再定义data="noProp"
,这样根节点的data属性就会被替换。
按F12打开调试工具能够看到
若是组件的根节点上有Class、Style属性,那么在组件标签再定义Class、Style,这样根节点的Class、Style是把二者合并后在赋值。例:
<template>
<div class="children" style="font-size:16px;">
<h2>个人组件</h2>
</div>
</template>
复制代码
<template>
<div>
<myComponent class="parent" style="font-size:18px;color:red">
</myComponent>
</div>
</template>
复制代码
按F12打开调试工具能够看到
用实例属性$attrs,包含了组件标签上不做为 prop被识别(且获取)的特性绑定(class 和 style 除外)。
在写基础组件中常常用到,下面举个基础输入框组件为例子:
<template>
<label>
{{ label }}
<input
v-bind="$attrs"
:value="value"
@input="$emit('input', $event.target.value)"
>
</label>
</template>
<script>
export default {
inheritAttrs: false,
props: ['label', 'value'],
data(){
return{}
},
mounted(){
console.log(this.$attrs)
}
}
</script>
复制代码
<template>
<baseIinput class="input" style="color:red" v-model="val" placeholder="请输入" label="基础输入框"></baseInput>
</template>
<script>
export default {
data() {
return {
val:'',
};
},
components:{
baseInput:resolve => require(['./base_input.vue'],resolve)
}
};
</script>
复制代码
例子中。mounted钩子函数打印出来是{placeholder:'请输入'}
,用v-bind
指令将 $attrs
绑定到要继承的元素上便可继承。
组件中的inheritAttrs选项为false时禁止组件的根节点继承非Prop特性,但不影响Class、Style
用v-on
的 .native
修饰符,来实现。例:监听输入框获取焦点事件
<baseInput @focus.native="onFocus"></baseInput>
复制代码
可是你会发现,监听失败,也不产生任何报错。
这是由于这个组件的根元素是<label></label>
,而这标签是没有focus
这个事件。
你能够用实例属性$listeners
,其包含做用在这个组件上的全部监听器,配合v-on="$listeners"
将全部的事件监听器指向组件内部的任一个元素,若是改元素上还有其余监听器,能够用computed属性来合并监听器。
下面请看实例:
<template>
<label>
{{ label }}
<input v-bind="$attrs" v-bind:value="value" v-on="inputListeners">
</label>
</template>
<script>
export default {
props: ['label', 'value'],
data() {
return {}
},
computed: {
inputListeners: function(){
return Object.assign(
{},
// 组件标签上添加全部的监听器
this.$listeners,
//组件内部原来的监听器
{
input:(event) =>{
this.$emit('input',event.target.value)
}
}
)
}
}
}
</script>
复制代码
<template>
<div>
{{val}}
<baseInput v-model="val" @focus="onFocus"></baseInput>
</div>
</template>
<script>
export default {
data() {
return {
val:'',
}
},
components: {
baseInput: resolve => require(['./base_input'], resolve),
},
methods:{
onFocus(){
console.log('获取到焦点')
}
}
}
</script>
复制代码
component
标签和 is
特殊特性在组件上的应用<component :is="componentName"></component>
复制代码
componentName
能够是在本页面已经注册的局部组件名和全局组件名,也能够是一个组件的选项对象。
当控制componentName
改变时就能够动态切换选择组件。
有些HTML元素,诸如 <ul>、<ol>、<table> 和 <select>
,对于哪些元素能够出如今其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>
,只能出如今其它某些特定的元素内部。
<ul>
<card-list></card-list>
</ul>
复制代码
因此上面<card-list></card-list>
会被做为无效的内容提高到外部,并致使最终渲染结果出错。应该这么写
<ul>
<li is="cardList"></li>
</ul>
复制代码
递归引用能够理解为组件调用自身,在开发多级菜单组件时就会用到,调用前要先设置组件的name选项。
注意必定要配合v-if使用,避免造成死循环
用element-vue组件库中NavMenu导航菜单组件开发多级菜单为例:
<template>
<el-submenu :index="menu.id" popper-class="layout-sider-submenu" :key="menu.id">
<template slot="title">
<Icon :type="menu.icon" v-if="menu.icon"/>
<span>{{menu.title}}</span>
</template>
<template v-for="(child,i) in menu.menus">
<side-menu-item v-if="Array.isArray(child.menus) && child.menus.length" :menu="child"></side-menu-item>
<el-menu-item :index="child.id" :key="child.id" v-else>
<Icon :type="child.icon" v-if="child.icon"/>
<span>{{child.title}}</span>
</el-menu-item>
</template>
</el-submenu>
</template>
<script>
export default{
name: 'sideMenuItem',
props: {
menu: {
type: Object,
default(){
return {};
}
}
}
}
</script>
复制代码
经过props来通讯
经过在父组件上自定义一个监听事件<myComponent @diy="handleDiy"></myComponent>
在子组件用this.$emit('diy',data)
来触发这个diy
事件,其中data为子组件向父组件通讯的数据, 在父组件中监听diy
个事件时,能够经过$event
访问data这个值。
<baseInput ref="myInput"></<baseInput>
this.$refs.myInput.focus()
this.$refs.myInput.value
用this.$parent
来访问
上面写到,子组件能够用this.$parent
访问父组件的实例,孙子组件能够用this.$parent.$parent
来访问,那曾孙子组件呢,是否是要写不少个$parent
。
若是父组件下不少个子孙组件都要用祖先组件中的一个数据,这时候就要用到组件的依赖注入。
依赖注入,是经过provide
inject
这两选项实现,provide
是写在祖先组件中,inject
是写在须要注入的子孙组件中。
provide
选项应该是一个对象或返回一个对象的函数.
inject
选项应该是一个字符串数组或一个对象
对象的key是本地的绑定名,value是provide
的 key (字符串或 Symbol)。
value也能够是个一个对象,该对象的:
from 属性是provide
的 key (字符串或 Symbol)
default属性是在form定义的key在provide
中未定义,使用的默认值。
祖先组件
<template>
<div>
<son></son>
</div>
</template>
<script>
export default{
data(){
return {}
},
provide(){
return{
top:20,
}
},
components:{
son:resolve => require(['./son.vue'],resolve)
}
}
</script>
复制代码
son儿子组件
<template>
<div>
<grandson></grandson>
</div>
</template>
<script>
export default{
data(){
return {}
},
inject:['top'],
components:{
grandson:resolve => require(['./grandson.vue'],resolve)
},
mounted(){
console.log(this.top)//20
}
}
</script>
复制代码
grandson儿子组件
<template>
<div></div>
</template>
<script>
export default{
data(){
return {}
},
inject:{
top:{
from:'top',
default:'30'
},
bottom:{
from:'bottom',
default:'30'
}
},
components:{
grandson:resolve => require(['./grandson.vue'],resolve)
},
mounted(){
console.log(this.top)//20
console.log(this.bottom)//30
}
}
</script>
复制代码
一般在子组件中是不容许对props中的值进行修改,可是有些状况下,要对一个 prop 进行“双向绑定”。真正的双向绑定会带来维护上的问题,由于子组件能够修改父组件,且在父组件和子组件都没有明显的改动来源。
这时候咱们能够用.sync修饰符来进行“双向绑定”,其原理是经过update:myPropName 的模式触发事件。
<template>
<div>
<button @click="show=true">显示</button>
<myComponent :show="show" v-on:update:show="show = $event"></myComponent>
</div>
</template>
<script>
export default{
data(){
return{
show:false,
}
},
components:{
myComponent:resolve=>require(['./my_conponent'],resolve)
}
}
</script>
复制代码
<template>
<div v-show="show">
<button @click="hide">隐藏</button>
</div>
</template>
<script>
export default{
props:{
show:{
type:Boolean,
default:false,
}
},
data(){
return{}
},
methods:{
hide(){
this.$emit('update:show',false)
}
}
}
</script>
复制代码
为了方便起见,咱们为这种模式提供一个缩写,即 .sync
修饰符:
<my-component :show.sync="show"></my-component>
复制代码
注意带有 .sync 修饰符的 v-bind 不能和表达式一块儿使用,:show.sync="name=='小明'? true : false"
是无效的
将 v-bind.sync 用在一个对象上,例:v-bind.sync='{ title: doc.title }'
也是无效的。
<myComponent
:show.sync="info.show"
:name.sync="info.name"
:age.sync="info.age"
></myComponent>
复制代码
若是遇到上述状况能够怎么这么写
<myComponent v-bind.sync="info"></myComponent>
复制代码
这样会把 info 对象中的每个属性 (如 show) 都做为一个独立的 prop 传进去,而后各自添加用于更新的 v-on 监听器。
render选项的值是一个函数,一般叫渲染函数。
该函数接收一个createElement的方法做为第一参数来建立VNode(虚拟DOM),并返回。
先举个全局组件的例子
Vue.component('myComponent', {
render: function (createElement) {
return createElement(
'h1',
this.title
)
},
props: {
title: {
type: String,
default: '我是渲染出来的组件'
}
}
})
复制代码
以上组件调用后渲染出<h1>我是渲染出来的组件</h1>
在单页面组件中使用要去掉<template></template>
标签
<script>
export default {
props: {
title: {
type: String,
default: "我是渲染出来的组件"
}
},
data() {
return {};
},
render: function(createElement) {
return createElement("h1", this.title);
},
};
</script>
复制代码
以上组件被调用后也渲染出<h1>我是渲染出来的组件</h1>
接收一个String类型的元素标签如'div'
,或者接收一个Function类型的函数返回元素标签;
总之第一个参数是必填项,接收一个元素标签。
class项,绑定ClassName,对应:class
,值为一个字符串、对象或字符串和对象组成的数组,例:
值为对象:
data(){
classA:true
},
render: function (createElement) {
return createElement('div',
{
class:{'classA':this.classA},
},
'我是渲染出来的组件',
);
},
复制代码
值为字符串:
render: function (createElement) {
return createElement('div',
{
class:'classA',
},
'我是渲染出来的组件',
);
},
复制代码
值为字符串和对象组成的数组
data() {
return {
classB:true
};
},
render: function (createElement) {
return createElement('div',
{
class:['classA',{'classB':this.classB}],
},
'我是渲染出来的组件',
);
},
复制代码
style项,绑定style,对应:style
,接受一个字符串、对象,或对象组成的数组,例:
值为字符串
render: function (createElement) {
return createElement('div',
{
style:'color:red'
},
'我是渲染出来的组件',
);
},
复制代码
值为对象
render: function (createElement) {
return createElement('div',
{
style:{
color:'red',
fontSize: '14px'
}
},
'我是渲染出来的组件',
);
},
复制代码
值为对象组成的数组
render: function (createElement) {
return createElement('div',
{
style:[
{
color:'red',
},
{
fontSize: '14px'
}
]
},
'我是渲染出来的组件',
);
},
复制代码
attrs项,普通的HTML特性,如id、title特性,例:
render: function (createElement) {
return createElement('div',
{
attrs:{
id:'idA',
title:'我是渲染出来的组件'
}
},
'我是渲染出来的组件',
);
},
复制代码
渲染出<div id="idA" title="我是渲染出来的组件">我是渲染出来的组件</div>
domProps项,DOM属性,如innerHTML,列:
render: function (createElement) {
return createElement('div',
{
domProps: {
innerHTML: 'DOM属性内容'
}
},
'我是渲染出来的组件',
);
},
复制代码
渲染出<div>DOM属性内容</div>
我是渲染出来的组件
props项,用法和组件的props同样;
on项,事件监听器,其它用法和v-on
同样,例:
render: function (createElement) {
return createElement('div',
{
on: {
click: function(e){
console.log(e)
},
}
},
'我是渲染出来的组件',
);
},
复制代码
nativeOn项,只用于第一个参数为组件标签时,用来监听组件内根节点上的事件,列:
components:{
myComponent:resolve =>require(['./my_component'],resolve)
},
render: function (createElement) {
return createElement('myComponent',
{
nativeOn: {
click: function(e){
console.log(e)
}
}
},
);
},
复制代码
scopedSlots项,在父组件中用子组件的数据渲染插槽内容,例:
子组件
<template>
<div>
<slot :title="title"></slot>
<slot name="content" :content="content"></slot>
</div>
</template>
<script>
export default {
data() {
return {
title: '个人组件',
content: '内容'
}
},
}
</script>
复制代码
父组件
components: {
myComponent: resolve => require(['./my_component'], resolve)
},
render: function (createElement) {
return createElement('myComponent',
{
scopedSlots: {
default: ({ title }) => createElement('span', title),
content: ({ content }) => createElement('p', content)
}
},
);
},
复制代码
slot项,若是组件是其它组件的子组件,需为插槽指定名称(demo待续);
directives项,自定义指令(demo待续);
key项,定义组件的key(demo待续);
ref项,给组件增长ref属性,经过this.$refs
,在render函数组件中调用其子组件的方法,例:
子组件
<template>
<div>{{title}}</div>
</template>
<script>
export default {
data() {
return {
title: '个人组件',
}
},
methods:{
show(){
console.log(this.title)
}
}
}
</script>
复制代码
render函数组件
<script>
export default {
data() {
return {};
},
components: {
myComponent: resolve => require(['./my_component'], resolve)
},
render: function (createElement) {
var myChild = createElement('myComponent', { ref: 'myRef' });
const _this = this;
return createElement('div', {
on: {
click: function () {
_this.$refs.myRef.show();//个人组件
}
},
ref: 'myRef',
}, [myChild])
},
};
</script>
复制代码
类型能够是String,能够是文字类型的节点,例:
render: function (createElement) {
return createElement('div', '我是渲染出来的组件');
},
复制代码
或者
<script>
export default {
props: {
title: {
type: String,
default: "我是渲染出来的组件"
}
},
data() {
return {};
},
render: function (createElement) {
return createElement('div', this.title);
},
};
</script>
复制代码
类型能够是Array,其中值可为字符串、createElement函数,例:
<script>
export default {
props: {
title: {
type: Function,
default: "我是渲染出来的组件"
}
},
data() {
return {};
},
render: function (createElement) {
return createElement('div', [this.title,'我是字符串',createElement('h1','我是子节点')]);
},
};
</script>
复制代码
利用this.$slots
在render函数组件中实现插槽功能,例:
子组件
<script>
export default {
data() {
return {
};
},
render: function (createElement) {
return createElement(
'div',
[
createElement('h1',this.$slots.default),
createElement('h2',this.$slots.header)
]
)
},
};
</script>
复制代码
父组件
<template>
<div>
<myRender>
父默认内容
<template v-slot:header>父内容</template>
</myRender>
</div>
</template>
<script>
export default {
data() {
return {}
},
components: {
myRender: resolve => require(['./my_render.vue'], resolve),
},
}
</script>
复制代码
渲染出
<div>
<h1>父默认内容</h1>
<h2>父内容</h2>
</div>
复制代码
利用this.$scopedSlots
实现调用render函数组件时用render函数组件中的数据编辑插槽,例:
子组件
<script>
export default {
data() {
return {
info:'我是render函数组件'
};
},
render: function(createElement) {
return createElement("div", [
this.$scopedSlots.header({
title: this.info
})
]);
}
};
</script>
复制代码
父组件
<template>
<div>
<myRender>
<template v-slot:header="{title}">{{title}}</template>
</myRender>
</div>
</template>
<script>
export default {
data() {
return {};
},
components: {
myRender: resolve => require(["./my_render.vue"], resolve)
}
};
</script>
复制代码
组件树中的全部 VNode 必须是惟一的,因此当参数类型为Array时,每一个值不可重复,例:
render: function (createElement) {
var myChild = createElement('p', 'hi')
return createElement('div', [
// 错误 - 重复的 VNode
myChild,myChild,myChild
])
},
复制代码
应该这么作
render: function (createElement) {
return createElement('div',
Array.from({ length: 20 }).map(function () {
return createElement('p', 'hi')
})
)
}
复制代码
v-if
v-else
v-for
模板功能,例:<ul v-if="lists.length">
<li v-for="item in lists">{{ item.name }}</li>
</ul>
<p v-else>没有数据</p>
复制代码
props: ['lists'],
render: function (h) {
if (this.lists.length) {
return h('ul', this.lists.map(function (item) {
return h('li', item.name)
}))
} else {
return h('p', '没有数据')
}
}
复制代码
v-model
模板功能,例:props: ["value"],
render: function(h) {
var _this = this;
return h("input", {
domProps: {
value: _this.value
},
on: {
input: function(event) {
_this.$emit("input", event.target.value);
}
}
});
}
复制代码
在使用iview中Tree组件,你会发现组件提供了render属性让你能够自定义树节点。那是怎么实现的,下面举个简单列子:
子组件
<script>
export default {
props: {
render: {
type: Function,
},
data: {
type: Object,
default(){
return {}
},
}
},
render: function (h) {
const params = {
data: this.data,
};
return this.render(h,params);
},
};
</script>
复制代码
父组件
<template>
<div>
<myRender :render="this.render" :data="this.data">
</myRender>
</div>
</template>
<script>
export default {
data() {
return {
render: function(h,{data}) {
const info= `我是${data.name},今年${data.age}岁`;
return h('h1', info);
},
data:{
name:'小明',
age:18,
},
}
},
components: {
myRender: resolve => require(['./my_render.vue'], resolve),
},
}
</script>
复制代码
渲染出
我是小明,今年18岁
复制代码
注意 render值不能用箭头函数,不然this指向会出错。
什么是函数式组件,知足如下几个特色能够称为函数式组件
函数式组件优势,渲染开销低
怎么使用函数式组件
将functional
选项标记为true
,此时组件成为
那么上个例子的子组件能够这么写
<script>
export default {
functional:true,
render: function (h,context) {
const params = {
data: context.props.data,
};
return context.props.render(h,params);
},
};
</script>
复制代码
组件须要的一切都是经过 context 参数传递,它是一个包括以下字段的对象:
在模板中使用函数式组件这样声明:
<template functional>
<div>
<p v-for="item in props.lists" @click="props.listClick(item);">
{{ item }}
</p>
</div>
</template>
复制代码