组件:
之前vue实例,必须挂载到一个指定的DOM节点上,这样的话,太过死板,不灵活。所以,就有了组件,可以任意的挂载到一个vue实例下。组件本身,就可以看作是一个vue实例,和vue实例一样,具有生命周期。
注册与渲染:
只用组件之前,需要先注册注册的方式分为两种,一种是全局Vue注册, 这样的话,可以在任意的vue实例下调用该组件;另一种就是局部注册,
组件之间的信息获取:
一般而言,组件是无法获取上级或者下级组件的数据信息的,会警告。如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<component-demo1></component-demo1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
var demo1 = {
data: function() {
return {
name: 'components'
}
},
template:`<div>
<p>我想要父组件的信息: {{message}}</p>
</div>`
};
var app = new Vue({
el: '#app',
data(){
return{
message: 'app'
}
},
components: {
'component-demo1': demo1,
},
template: `<div>
<p>hello, {{message}}</p>
<component-demo1></component-demo1>
</div>`
});
</script>
</body>
</html>
组件可以通过props直接接收从标签传递过来的数据。传入的对象,内组件内部可以像data内的数据一样被使用。不过,貌似好像只能传入简单类型的数据。props一般用于实现父子组件之间的简单通信。
$slots插槽,可以在父组件渲染组件的时候,加入html代码,通过这种方式,将相关的数据在组件内显示。
props和$slots案例在这里:https://blog.csdn.net/weixin_47870554/article/details/106946942#t6
除了props和$slots之外,还有provide / inject、$refs、$parent、$children、$scopedSlots等方式获取父子组件之间的信息。
provide / inject:
可以接受父级或者更高级的组件传递的数据。代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<component-demo2></component-demo2>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
var demo1 = {
data: function() {
return {
name: 'components'
}
},
inject: ['superior'],
template:`<div>
<p>demo1 组件 inject接收数据: </p>
<p>{{superior}}</p>
</div>`
};
var demo2 = {
data: function() {
return {
message: '这里是中间组件 demo2'
}
},
components: {
'component-demo1': demo1
},
template: `<div>
<p>hello, {{message}}</p>
<hr>
<component-demo1></component-demo1>
</div>`
};
var app = new Vue({
el: '#app',
provide: {
superior: '从高级组件传来的数据'
},
components: {
'component-demo2': demo2,
}
});
</script>
</body>
</html>
$refs、ref:
配合ref特殊属性,可以让父组件获取子组件的元素信息。(需要在生命周期mounted挂载之后才行,且只能获取子组件,不能获取子组件内部的组件信息)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
var demo1 = {
data: function() {
return {
message: '这里是底层组件 demo1'
}
},
template:`<div>
<p ref="demo1">hi, {{message}}</p>
</div>`
};
var demo2 = {
data: function() {
return {
message: '这里是中间组件 demo2'
}
},
components: {
'component-demo1': demo1
},
template: `<div>
<p ref='demo2'>hello, {{message}}</p>
<hr>
<component-demo1></component-demo1>
</div>`
};
var app = new Vue({
el: '#app',
components: {
'component-demo2': demo2,
},
data: function() {
return {
message: '这里最初的组件app'
}
},
methods: {
getRefs(){
console.log("this.$refs:", this.$refs);
}
},
template: `
<div ref="template">
<p ref="message">{{message}}</p>
<button v-on:click='getRefs'>获取refs</button>
<hr>
<component-demo2 ref="component"></component-demo2>
</div>
`,
});
</script>
</body>
</html>
只获取了本身template内的元素或者组件信息,组件内部的信息不能直接通过$refs获取。
$parent、$children、$root:
$parent、$children获取父组件或者子组建的vue实例信息。$root获取最上层地组件,如果不存在,就是自己。
$parent、$children部分代码:
methods: {
getParent(){
console.log("this.$parent:", this.$parent);
},
getChildren(){
console.log("this.$children:", this.$children);
}
},
template: `<div>
<p ref='demo2'>hello, {{message}}</p>
<button v-on:click='getParent'>获取parent</button>
<button v-on:click='getChildren'>获取children</button>
<hr>
<component-demo1 ref='componentDemo2'></component-demo1>
</div>`
$root部分代码:
methods: {
getRoot(){
console.log("this.$root:", this.$root);
}
},
template:`<div>
<p ref="demo1">hi, {{message}}</p>
<button v-on:click='getRoot'>获取root</button>
</div>`
$scopedSlots、$slots:
$scopedSlots、$slots可用于获取从父组件处获取地插槽信息。($scopedSlots返回的是函数集)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
var demo = {
data: function() {
return {
message: '这里是中间组件 demo2'
}
},
methods: {
getSlots(){
console.log("this.$slots:", this.$slots);
},
getScopedSlots(){
console.log("this.$scopedSlots:", this.$scopedSlots);
},
getScopedSlotsVNode(){
console.log("default:", this.$scopedSlots.default());
console.log("slot2:", this.$scopedSlots.slot2());
console.log("slot3:", this.$scopedSlots.slot3());
},
},
template: `<div>
<slot></slot>
<slot name="slot2"></slot>
<slot name="slot3"></slot>
<button v-on:click='getSlots'>获取slots</button>
<button v-on:click='getScopedSlots'>获取scopedSlots</button>
<button v-on:click='getScopedSlotsVNode'>获取scopedSlotsVNode</button>
</div>`
};
var app = new Vue({
el: '#app',
components: {
'component-demo': demo,
},
data: function() {
return {
message: '这里最初的组件app',
slotMessage1: '我是作用域插槽 slot1',
slotMessage2: '我是作用域具名插槽 slot3'
}
},
methods: {
getRefs(){
console.log("this.$refs:", this.$refs);
}
},
template: `
<div ref="template">
<component-demo ref="component">
<p >hello, 我是插槽 slot</p><p >hello, {{slotMessage1}}</p>
<template v-slot:slot2>
<p >hello, 我是具名插槽 slot2</p>
</template>
<template v-slot:slot3>
<p>hello, {{slotMessage2}}</p>
</template>
</component-demo>
</div>
`,
});
</script>
</body>
</html>
is:
用于实现动态组件。代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
var demo = {
template: `<p>我是组件1号</p>`
};
var app = new Vue({
el: '#app',
components: {
'component1': {
template: `<p>我是组件1号</p>`,
activated (){
console.log("1号 激活 activated");
},
deactivated (){
console.log("1号 停用 deactivated");
},
beforeDestroy: function(){
console.log('1号 销毁之前');
},
destroyed: function(){
console.log('1号 销毁');
},
},
'component2': {
template: `<p>我是组件2号</p>`,
activated (){
console.log("2号 激活 activated");
},
deactivated (){
console.log("2号 停用 deactivated");
},
beforeDestroy: function(){
console.log('2号 销毁之前');
},
destroyed: function(){
console.log('2号 销毁');
},
},
'component3': {
template: `<p>我是组件3号</p>`,
activated (){
console.log("3号 激活 activated");
},
deactivated (){
console.log("3号 停用 deactivated");
},
beforeDestroy: function(){
console.log('3号 销毁之前');
},
destroyed: function(){
console.log('3号 销毁');
},
},
},
data: function() {
return {
currentComponent: 'component1',
currentComponentId: 0,
}
},
methods: {
changeComponent(){
this.currentComponentId = (this.currentComponentId + 1) % 3;
this.currentComponent = 'component' + (this.currentComponentId+1)
}
},
template:
`<div>
<component v-bind:is="currentComponent"></component>
<button v-on:click='changeComponent'>changeComponent</button>
</div>`,
});
</script>
</body>
</html>
更换的时候,触发了组件地销毁。
特殊组件:
transition:
<transition>
元素作为单个元素/组件的过渡效果。<transition>
只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中。可用于前端动画,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style type="text/css">
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to/* .fade-leave-active below version 2.1.8 */{
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
var
demo = {
data (){
return {
show: true,
}
},
template: `<div>
<button v-on:click="show = !show"> show: {{show}} </button>
<transition name="fade">
<h1 v-if="show">I am demo2</h1>
</transition>
</div>`,
},
app = new Vue({
el: '#app',
components: {
'demo': demo,
},
template: `<demo></demo>`,
});
</script>
</body>
</html>
keep-alive:
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition>
相似,<keep-alive>
是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
具有独有地生命周期,activated和deactivated,代表被 keep-alive 缓存的组件激活和停用。
部分代码改成码如下:
template:
`<div>
<keep-alive>
<component v-bind:is="currentComponent"></component>
</keep-alive>
<button v-on:click='changeComponent'>changeComponent</button>
</div>`,
都没有触发销毁。
可以为keep-alive设置参数,max、include、exclude,max表示最多可以缓存多少组件实例;include表示允许组件缓存地名单;exclude表示不允许组件缓存地名单。
-
max:
template:
`<div>
<keep-alive max="2">
<component v-bind:is="currentComponent"></component>
</keep-alive>
<button v-on:click='changeComponent'>changeComponent</button>
</div>`,
(虽然2可以,符合预期的情况,但是max改成1的话,有点问题的样子......)
-
include:
template:
`<div>
<keep-alive include="component1">
<component v-bind:is="currentComponent"></component>
</keep-alive>
<button v-on:click='changeComponent'>changeComponent</button>
</div>`,
只对component1进行了缓存。
-
exclude:
template:
`<div>
<keep-alive exclude="component1">
<component v-bind:is="currentComponent"></component>
</keep-alive>
<button v-on:click='changeComponent'>changeComponent</button>
</div>`,
只有component1没有缓存。
组件属性:
name:
允许组件模板递归地调用自身。指定 name
选项的另一个好处是便于调试。有名字的组件有更友好的警告信息。另外,当在有 vue-devtools,未命名组件将显示成 <AnonymousComponent>
,这很没有语义。通过提供 name
选项,可以获得更有语义信息的组件树。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
var
demo = {
template: `<p>我是组件1号</p>`,
created(){
console.log(this);
}
},
app = new Vue({
el: '#app',
components: {
'component1': demo,
},
template: `<component1></component1>`
});
</script>
</body>
</html>
没有name的话,会显示components中对应地属性名。
demo = {
name: 'demo',
template: `<p>我是组件1号</p>`,
created(){
console.log(this);
}
},
有name属性的话,会显示name的值。
inheritAttrs:
组件绑定地属性,如果不被认作 props的话,根据inheritAttrs做处理,默认true。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<custom-input :value="inheritAttrsText" :test="test"></custom-input>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
Vue.component('custom-input', {
props: ['value'],
template: "<input v-bind:value='value' >",
})
const app = new Vue({
el:'#app',
data:{
inheritAttrsText: "inheritAttrsText",
test:'1'
}
});
</script>
</body>
</html>
改成true。
Vue.component('custom-input', {
inheritAttrs: false,
props: ['value'],
template: "<input v-bind:value='value' >",
})
functional:
表示组建的渲染方式,使组件无状态 (没有 data
) 和无实例 (没有 this
上下文)。只能用一个简单的 render
函数返回虚拟节点使它们渲染的代价更小。默认false。
正常的template渲染。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vueTest</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<my-component></my-component>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
Vue.component('component-demo', {
template: '<h1>template</h1>',
});
var app = new Vue({
el: '#app',
template: `<component-demo></component-demo>`
})
</script>
</body>
</html>
functional改为true
Vue.component('component-demo', {
functional: true,
template: '<h1>template</h1>',
});
报错无法渲染。
使用render
Vue.component('component-demo', {
functional: true,
render: function (createElement, context) {
return createElement('div', 'render');
}
});
正常渲染。