核心目标
为了可重用性高,减少重复性开发,我们可以按照template、style、script的拆分方式,放置到对应的.vue文件中。
组件概括
vue组件可以理解为预先定义好的ViewModel类。一个组件可以预定义很多选项,最核心的有:
- 模板template:模板反映了数据和最终展现给用户的DOM之间的映射关系,
- 初始数据data:一个组件的初始数据状态,对于可重复的组件来说,通常是私有的状态。
- 接收的外部的参数(props):组件之间通过参数来进行数据的传递和共享,参数默认是单项绑定,但也可以显示声明为双向绑定。
- 方法(methods):对数据的改动操作一般都在组件内进行。可以通过v-on指令将用户输入事件和组件方法进行绑定。
- 生命周期函数(钩子函数):一个组件会触发多个生命周期函数,在这些钩子函数中可以封装一些自定义逻辑。可以理解为controller的逻辑被分散到了这些钩子函数中。
注册组件
注册组件就是利用Vue.component()方法,先传入一个自定义组件的名字,然后传入这个组件的配置。
(组件名不要带有大写字母,多个单词使用中划线my-dom)
-
全局组件
(直接使用Vue.component()创建的组件,所有的Vue实例都可以使用。)
(一般写插件的时候全局组件使用多一些)
Vue.component('mycomponent',{ //自定义组件名mycomponent
template: `<div>这是一个自定义组件</div>`,
data () {
return {
message: 'hello world'
}
}
})
-
局部注册组件
使用三部曲1.创建这个组件;2.注册这个组件;3.使用这个组件;
<div id="app">
<mycomponent></mycomponent> //3.组件的使用
<my-component></my-component> //3.组件的使用
</div>
<script>
//1.创建这个组件
let mycomponent={
template: `<div>这是一个局部的自定义组件,只能在当前Vue实例中使用{{aa}}</div>`,
data(){
return {'aa':'hello'}
}
};
let app = new Vue({
el: '#app',
data: {
},
components: {
mycomponent,//2.注册这个组件
}
}
})
</script>
- 模板的要求:组件的模板只能有一个根元素。
template: `<div>
元素1.....
元素2.....
元素3.....
</div>`,
- 组件中的data必须是函数。
data:function(){
return {'aaa':'bbb'}//template中可以使用aaa
},
- 组件的属性props。
Vue组件通过props属性来声明一个自己的属性,然后父组件就可以往里面传递数据。
<div id="app1">
动态操作属性:name是变量(读取实例中data的值)
<hello :n="name"></hello>
</div>
<div id="app2">
静态的属性:name是常量(就是name)
<hello n="name"></hello>
</div>
<script>
Vue.component('hello',{
props:['n'],//通过父组件传过来的数据 name变量
data:function(){
return {'aaa':'bbb'}
},
template:'<p>{{aaa}} {{n}}</p>'
})
var app1=new Vue({
el:'#app1',
data:{
name:'haha'
}
})
var app2=new Vue({
el:'#app2',
data:{
name:'haha'
}
})
</script>
渲染结果
- props的验证。
组件之间的通信
子组件给父组件传递数据,利用事件的订阅发布模式
- 1.给子组件的template上的元素绑定事件(如click),执行子组件的方法(如changeData),子组件的方法中发射一个事件(如s),传一个数据(如lalala);
- 2.父组件中,定义一个方法(如getData)用来拿到子组件的数据。
- 3.在自定义的组件上绑定子组件传过去的事件(s),执行事件(s)执行的是getData函数,getData函数中拿到数据(data,就是子组件传过去的lalala)。
<div id="app">
<parent></parent>
</div>
<template id="parent">
<div>
<p>父组件{{msg}}</p>//点击子组件触发getData获取到子组件转来的msg数据,绑定到父组件上
<children @s="getData"></children>
</div>
</template>
<script>
var app=new Vue({
el:'#app',
components:{
parent:{
template:'#parent',
data(){
return {msg:'aaa'};
},
methods:{
getData(data){
alert(data);//子组件传过来的数据
this.msg=data;
}
},
components:{
children:{
template:'<p @click="changeData">子组件</p>',
methods:{
changeData(){
this.$emit('s','lalala');//传递给父组件
}
}
}
}
}
}
})
</script>
父组件给子组件传递数据(利用props属性)
<div id="app">
<parent></parent>
</div>
<script>
var app=new Vue({
el:'#app',
components:{//一个父组件里边可以包含多个子组件
parent:{
template:'<h1 >{{msg}},父组件<children :n="msg"></children></h1>',
data:function(){//父组件的数据 传给子组件
return {msg:'hello'}
},
components:{
children:{
props:['n'],//根据props拿到父组件中写的动态属性拿到数据
template:'<h2>{{n}},子组件</h2>',
}
}
}
}
})
兄弟组件之间传递数据 (事件车---原理也是事件的订阅发布模式)
注意:
- 每一个vue的实例都是独立的;相互之间不能直接进行改变数据;
- 给两个不同的组件找一个载体;把共同的方法放在这个载体上;
- 这个载体就是 let eventBus = new Vue; // 创建一个新的vue实例;
- 在这个新的实例上,有 $on: 订阅 $emit: 发布;
- $on的绑定要基于钩子函数,一般放在created或者mounted上
<div id="app">
<tmp1></tmp1>
<tmp2></tmp2>
</div>
<script>
var eventBus=new Vue();
var app = new Vue({
el: '#app',
data: {},
components: {
tmp1: {
template: "<h1>组件1{{msg}}</h1>",
data(){
return {msg:""}
},
mounted(){//钩子函数,页面一加载进来的时候就已经绑定好了
eventBus.$on('aaa',(data)=>{
this.msg=data;
})
}
},
tmp2: {
template: "<h1 @click='sendData'>组件2{{msg}}</h1>",
data(){
return {msg: 'hello'}
},
methods:{
sendData(){
//发布数据
eventBus.$emit('aaa',this.msg)
}
}
}
}
})
</script>
数据同步(子组件的数据和父组件保持一致)
数据同步的核心:父组件给子组件传递“引用数据类型的数据”;
<div id="app">
<parent></parent>
</div>
<template id="parent">
<div>
<h1>父组件 <mark>{{msg.name}}</mark></h1>
<children :n="msg"></children>
</div>
</template>
<template id="children">
<h2 @click="changeData">子组件 {{n.name}}</h2>
</template>
<script>
//数据同步的核心:父组件给子组件传递“引用数据类型的数据”;
var app=new Vue({
el:'#app',
components:{
parent:{
template:'#parent',
data(){
return {msg:{name:'hahha'}}
},
components:{
children:{
props:['n'],
template:'#children',
methods:{
changeData(){
this.n.name='lallala'
},
}
}
}
}
}
})
</script>
数据不同步(不直接使用父组件传的值,用data属性再自己的组件内做一个中间变量,防止报错)
<parent></parent>
</div>
<template id="parent">
<div>
<h1>父组件 <mark>{{msg}}</mark></h1>
<children :n="msg"></children>
</div>
</template>
<script>
//数据不同步的核心:中间变量接收避免报错;
var app=new Vue({
el:'#app',
components:{
parent:{
template:'#parent',
data(){
return {msg:'hahha'}
},
components:{
children:{
props:['n'],
template:'<h2 @click="changeData">子组件 {{b}}</h2>',
data(){
return {b:this.n}
},
methods:{
changeData(){
this.b='lallala'
},
}
}
}
}
}
})
</script>
组件切换
js动态控制template(也可以用is属性控制)
<div id="app">
<div class="container">
<div @click="comp='zujian1'">显示组件1</div>
<div @click="comp='zujian2'">显示组件2</div>
</div>
<!--aaa标签是核心,必须得写,通过is决定显示那个组件-->
<aaa :is="comp"></aaa>
</div>
<script>
var app=new Vue({
el:'#app',
data:{
comp:'zujian2',
},
components:{
zujian1:{
template:'<h1>组件1</h1>'
},
zujian2:{
template:'<h1>组件2</h1>'
}
}
})
</script>
插槽(slot官网有详细介绍)
slot相当于子组件设置了一个地方,如果在调用它的时候,往它的开闭标签之间放了东西,那么它就把这些东西放到slot中。
- 当子组件中没有slot时,父组件放在子组件标签内的东西将被丢弃;
- 子组件的slot标签内可以放置内容,当父组件没有放置内容在子组件标签内时,slot中的内容会渲染出来;
- 当父组件在子组件标签内放置了内容时,slot中的内容被丢弃
实例:
<div id="app">
<hello>//使用组件
lalalala
<!--给插槽起好名字-->
<div slot="div1">1111111111111</div>
<div slot="div2">2222222222222</div>
</hello>
</div>
<template id="temp1">//模板
<h1>
hello
<!--无名插槽-->
<slot></slot>
<!--有名插槽 可以根据插槽切换顺序-->
<slot name="div2"></slot>
<slot name="div1"></slot>
</h1>
</template>
<script>
var app=new Vue({
el:'#app',
data:{
},
components:{
hello:{
template:"#temp1"
}
}
})
组件文件示例
父组件.vue
<template>
<div class="">
<div class="home-container">
<Banner></Banner>
<quick-link :allAmount="allAmount"> </quick-link>
<Recommended></Recommended>
</div>
<m-footer></m-footer>
</div>
</template>
<script>
import axios from 'axios'
import MFooter from '../footer.vue'
import Banner from './banner.vue'
import QuickLink from './quickLink.vue'
import Recommended from './recommended.vue'
import {setSession} from '../../common/userCenter'
import {loadAppHomeAct,loadStatisticalData} from '../../api/api'
export default {
data(){
return{
allAmount:[]
}
},
components:{
MFooter,
Banner,
QuickLink,
Recommended
},
created(){
this.fetchCookies();
},
methods:{
getQueryString: function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var url=decodeURIComponent(window.location.search);
var r = url.substr(1).match(reg);
if (r != null) return (r[2]);
return "";
}
}
}
</script>
<style>
@import "home.css";
</style>