1.无论哪种方式定义组件,template属性的HTML结构中必须有且只有唯一一个根元素,否则会报错
2.在组件中定义data属性和Vue实例中不同,必须是定义一个方法,在方法中返回一个对象,如方式三中定义的.
3.局部注册的组件只能在注册它的父组件中使用.
<dvi id="app">
<my-component></my-component>
</dvi>
<script>
//使用Vue.extend来创建全局的vue组件对象
var mycomponent = Vue.extend({
//通过template属性指定组件要展示的HTML结构
template: '这是一个自定义的组件
'
});
//定义全局组件,Vue.component(组件名称,创建的Vue组件对象)
//组件名称使用驼峰命名时,在引用组件时必须改写为my-component格式
Vue.component('myComponent', mycomponent);
let vm = new Vue({
el: '#app',
data: {
}
});
</script>
<div id="app">
<mycomponent></mycomponent>
</div>
<script>
//这种方式实际上还是跟第一种方式一样,只是省略了先创建Vue组件对象,直接在组件定义时使用字面量的形式传递Vue组件对象参数
Vue.component('mycomponent', {
template: '这是方式二创建的Vue组件
'
});
let vm = new Vue({
el: '#app'
});
</script>
<template id="temp">
<div>
<a href="">登录</a> | <a href="">注册</a>
<div>{{msg}}</div>
</div>
</template>
<div id="app">
<mycomponet></mycomponet>
</div>
<script>
Vue.component('mycomponet', {
template: '#temp',
data: function () {
return {
msg: '组件定义中data属性返回的数据'
}
}
});
let vm = new Vue({
el: '#app'
});
</script>
<div id="app">
<mycomponent></mycomponent>
</div>
<template id="teml">
<div>
<h1>这是定义的私有组件</h1>
</div>
</template>
<script>
let vm = new Vue({
el: '#app',
components: {
mycomponent: {
template: '#teml'
}
}
});
</script>
<div id="app">
<input type="button" value="登录" @click="flag = true">
<input type="button" value="注册" @click="flag = false">
<login v-if="flag"></login>
<register v-else="flag"></register>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
flag: false
},
components: {
login: {
template: '登录页面
'
},
register: {
template: '注册页面
'
}
}
});
</script>
<div id="app">
<input type="button" value="登录" @click="comName = 'login'">
<input type="button" value="注册" @click="comName = 'register'">
//是vue提供来展示对应名称的组件
//:is属性用来指定要展示的组件的名称
<components :is="comName"></components>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
// flag: false
comName: ''
},
components: {
login: {
template: '登录页面
'
},
register: {
template: '注册页面
'
}
}
});
</script>
1,使用元素将要实现动画效果的组件包裹起来
2.通过元素的mode属性设置组件切换的模式
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(100px);
}
.v-enter-active,
.v-leave-active {
transition: all 1s ease;
}
<transition mode="out-in">
<components :is="comName"></components>
</transition>
默认情况下,子组件无法直接访问到父组件data中的数据和methods中的方法,需要在引用子组件时通过v-bind:将要传递的数据绑定到子组件,然后在子组件的props中定义才可以使用
<div id="app">
<!-- 在引用子组件时,通过v-bind将要传递给子组件的数据以属性绑定的形式传递到子组件中 -->
<mycomponent :parentmsg="msg"></mycomponent>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
msg: '这是父组件data中的msg数据'
},
components: {
mycomponent: {
template: '这是子组件!----{{parentmsg}}
',
//把父组件中传递过来的数据先在props中定义,才能使用这个数据
//子组件props中的所有数据都是父组件传递给子组件的
//props中的数据都是只读的,无法重新赋值
props: ['parentmsg']
}
}
});
</script>
原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在触发方法的时候当作参数传递进去
this.$emit(‘自定义的事件名称’,参数1,参数2,…);触发绑定的自定义事件
<template id="teml">
<div>
<h2>这是子组件</h2>
<input type="button" value="这是子组件的按钮" v-on:click="sendMsg">
</div>
</template>
<div id="app">
//2.在子组件中v-on绑定自定义事件并且指向父组件中的方法
//如果子组件触发事件时传递了参数,在父组件的方法中用必须用$event接收,可以省略不写,比如
// 但是如果方法后面跟了()则必须写
<mycom v-on:parfun="getMsg($event)"></mycom>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
parentMsg: ''
},
methods: {
//4.在父组件中定义的方法中接受传递过来的参数,这样就可以接收到子组件中的数据
getMsg: function (data) {
console.log(data);
this.parentMsg = data;
}
},
//1.定义一个子组件
components: {
mycom: {
template: '#teml',
data: function () {
return {
sonMsg: '这是子组件中的msg'
}
},
methods: {
sendMsg: function () {
//3.在子组件定义的方法中使用this.$emit触发自定义的事件实际就是触发事件指向的父组件中的方法,并且将data中的数据作为参数传递过去
//this.$emit('自定义的事件名称',参数1,参数2...)
this.$emit('parfun', this.sonMsg);
}
},
}
}
});
</script>
Vue父子组件通讯,遵循单向数据流,就是说只能从父组件到子组件,不能直接在子组件中修改父组件的数据,子组件要修改父组件中的数据,需要通过$emit(),触发自定义事件.
<div id="app">
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<template id="tomTmpl">
<div>
<span>tom{{count}}</span>
<input type="button" value="点击" @click="add()">
</div>
</template>
<template id="jerryTmpl">
<div>
<span>jerry{{count}}</span>
<input type="button" value="点击" @click="add()">
</div>
</template>
<script>
//1.定义事件中心,用来监听和触发自定义事件
var hub = new Vue();
let vm = new Vue({
el: '#app',
components: {
'test-tom': {
template: '#tomTmpl',
data() {
return {
count: 0,
num: 3
}
},
methods: {
//3.定义一个方法,在方法内通过事件中心hub触发其他组件的自定义事件,并且传值过去
add: function () {
hub.$emit('add-jerry', this.num);
}
},
mounted() {
//2.定义自定义事件,$on(事件名称,事件处理函数),自定义事件可以在其他组件中通过事件中心hub被触发并且接收参数
hub.$on('add-tom', (val) => {
this.count += val;
});
},
},
'test-jerry': {
template: '#jerryTmpl',
data() {
return {
count: 0,
num: 1
}
},
//2.定义自定义事件,$on(事件名称,事件处理函数),自定义事件可以在其他组件中通过事件中心hub被触发并且接收参数
mounted() {
hub.$on('add-jerry', (val) => {
this.count += val;
})
},
methods: {
//3.定义一个方法,在方法内通过事件中心hub触发其他组件的自定义事件,并且传值过去
add: function () {
hub.$emit('add-tom', this.num);
}
},
}
}
});
</script>
销毁事件
hub.$off('add-tom');
hub.$off('add-jerry');
provide / inject这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深(普通组件传值只能父子或兄弟关系),并在起上下游关系成立的时间里始终生效。
provide 选项应该是:一个对象或返回一个对象的函数
inject 选项应该是:一个字符串数组,或 一个对象,对象的 key 是本地的绑定名
父组件中提供
provide() { //重要一步,在父组件中注入一个变量或函数
return {
msg: "demo"
// 提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
}
},
子组件中引入
export default {
inject: ['msg'], // 子孙组件中使用inject接住变量即可,可直接在本组件调用或更改父组件传过来的变量
}
运行顺序:
1.data
2.provide
3.created // 在这个阶段$el还未生成,在这先处理privide的逻辑,子孙组件才可以取到inject的值
4.mounted
…
<div id="app">
<!-- 2.组件标签中嵌套的内容会替换掉模板中所有的<slot>元素,如果组件标签中没有嵌套的内容,则会使用模板中slot中默认的值,如果都没有值,页面中不会渲染 -->
<alert-box>有一个bug</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box>
</div>
<script>
Vue.component('alert-box', {
template: `
ERROR:
//1.在子组件模板中定义元素(插槽),插槽内可以包含任何模板代码,包括HTML,当组件渲染时,元素将会被组件标签中嵌套的内容替换
`,
});
let vm = new Vue({
el: '#app',
});
</script>
使用 中的 “name” 属性绑定元素
<base-layout>
<!-- 2.组件标签中嵌套的内容会替换掉模板中所有的<slot>元素,如果组件标签中没有嵌套的内容,则会使用模板中slot中默认的值,如果都没有值,页面中不会渲染 -->
<!-- 通过slot属性来匹配子组件模板中插槽的那么属性,如果没有匹配到则不会渲染 -->
<p slot="header">标题信息</p>
<!-- 没有定义slot属性的内容会替换子组件模板中没有定义name属性的slot元素 -->
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部信息信息</p>
</base-layout>
<base-layout>
<!-- template元素是临时的包裹标签最终不会渲染到页面上 -->
<template slot="header">
<div>标题信息1</div>
<div>标题信息2</div>
</template>
<div>主要内容1</div>
<div>主要内容2</div>
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
<template id="tmpl">
<div>
<header>
<!-- 1.在子组件模板中定义<slot>元素(插槽),插槽内可以包含任何模板代码,包括HTML,当组件渲染时,<slot>元素将会被组件标签中嵌套的内容替换 -->
<!-- 使用name属性指定当前插槽的名字 -->
<slot name="header1"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script>
Vue.component('base-layout', {
template: '#tmpl',
});
let vm = new Vue({
el: '#app',
});
</script>
<div id="app">
<fruit-list :list="list">
<!-- 2.slot-scope属性可以获取到子组件模板中<slot>元素自定义属性info绑定的数据 ,slotProps是变量,slotProps.info指向子组件模板中<slot>元素自定义属性info绑定的数据-->
<template slot-scope="slotProps">
<strong v-if="slotProps.info.id == 2" class="current">{{slotProps.info.name}}</strong>
</template>
</fruit-list>
</div>
<template id="tmpl">
<div>
<li v-for="item in list" :key="item.id">
<!-- 1.定义<slot>元素,并且绑定自定义属性info, -->
<slot :info="item">{{item.name}}</slot>
</li>
</div>
</template>
<script>
Vue.component('fruit-list', {
template: '#tmpl',
props: ['list']
});
let vm = new Vue({
el: '#app',
data: {
list: [{
id: 1,
name: 'apple'
}, {
id: 2,
name: 'orange'
}, {
id: 3,
name: 'banana'
}]
}
});
</script>