vue两大核心
1.数据驱动界面
2.组件化
什么是组件化?
1.组件化就是把一个大界面拆分成一个个小组件,相同结构的组件可以进行复用,每一个小界面就是一个组件
组件化有利于提高复用性,简化Vue实例化代码
全局组件
全局组件在任何一个Vue实例控制区域都能使用
Vue中如何创建组件?
- 创建组件构造器
- 注册已经创建好的组件
- 使用注册号的组件
[注意]:创建组件构造器的时候只能有一个根元素,因此我们通常用div
直接把我们想包含的元素包起来
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<!-- 3.使用注册号的组件-->
<shanjialan></shanjialan>
</div>
<script>
// 1. 创建组件构造器
let component=Vue.extend({
template:`
<div>
<p>shanjialan111</p>
<p>shanjialan222</p>
</div>
`});
// 2.注册已经创建好的组件
Vue.component("shanjialan",component);
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰"
},
methods:{
myFn(){
alert("sjl");
}
}
});
</script>
</body>
</html>
vue创建组件的简写方法
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 获取注册的组件 (始终返回构造器)
var MyComponent = Vue.component('my-component')
2. 注册组件,传过来一个组件构造器
Vue.component("shanjialan",Vue.extend({
template:`<a href="#">link</a>`
}))
3.省略组件构造器Vue-extend,也会直接调用
Vue.component("bcd",{
template:`<a href="#">zhiboyu</a>`
})
总结:上面三种方法定义组件构造器html代码都没有提示,效率比较低,因此我们要想其他办法来避免这个问题——使用script标签添加id属性,并且将type设为text/html即可,再通过id作为构造器的第二个参数传递给vue-component,代码如下:
<script id="info" type="text/html">
<a href="#">单佳兰</a>
</script>
但是这种方法其实是不大科学的,明明是HTML,为什么要用script进行封装?于是有了第五种方法,使用<template>
加上id属性来封装,这个template
就相当于<script id="#" type="text/html>
<template id="shanshun">
<a>shuanshun</a>
</template>
Vue.component("shanshun",{
template:"#shanshun"
})
局部组件
局部组件的使用——在vue实例中添加components以键值对的方式定义,里面的格式和全局组件一样
<template id="shanshun">
<a>shuanshun</a>
</template>
let vue=new Vue(
components:{
"shanshun":{
template:"#shanshun"
})
}
data和method
1.自定义组件中的data和methods
Vue实例相当于一个大的组件,其中有data和methods属性,相同地,自定义组件相当于一个小的组件,因此也有data和methods属性,但是在自定义组件中data不大一样,在自定义组件中使用data必须赋值一个函数,通过函数返回值来定义数据
Vue.component("shanshun", {
template: "#shanshun",
data: function() {
return{
name: "shanshun22"
}
},
methods:{
myApp(){alert('shanjialan');}
})
为什么自定义组件的data不用对象返回,而是通过函数返回的,因为通过对象定义,那么多个组件就会共用一份数据,就会导致数据混乱,将这个方法返回的数据和当前创建的组件绑定在一起,就避免了数据混乱
的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<ab></ab>
<ab></ab>
<ab></ab>
</div>
<template id="addnum">
<div>
<button @click="add">按钮</button>
<p>{{number}}</p>
</div>
</template>
<script>
//自定义组件
Vue.component("ab",{
template:"#addnum",
data:function () {
return {
number: 0
};
},
methods:{
add(){
this.number++;
}
}});
let vue=new Vue(
{
el:"#app"
}
)
</script>
</body>
</html>
组件切换
用v-if 的方式进行组件的动态切换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<button @click="toggle">按钮</button>
<photo v-if="isShow"></photo>
<home v-else></home>
</div>
<template id="photo">
<a href="vue-animation.html">lainjei</a>
</template>
<template id="home">
<p>2222</p>
</template>
<script>
Vue.component("photo",{
template:"#photo",
});
Vue.component("home",{
template:"#home",
})
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰",
isShow:true
},
methods:{
myFn(){
alert("sjl");
},
toggle(){
this.isShow=!this.isShow;
}
}
});
</script>
</body>
</html>
这种方式并不是太专业,vue给我们提供了更加专业的做法进行[动态组件 & 异步组件]
语法:<component v-bind:is="currentTabComponent"></component>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<button @click="toggle">按钮</button>
<component v-bind:is="name"></component>
</div>
<template id="photo">
<a href="vue-animation.html">lainjei</a>
</template>
<template id="home">
<div>
<input type="checkbox">
<span>2222</span>
</div>
</template>
<script>
Vue.component("photo",{
template:"#photo",
});
Vue.component("home",{
template:"#home",
})
let vue=new Vue({
el:"#app",
data:{
isShow:true,
name:'photo'
},
methods:{
toggle(){
this.name = this.name==='photo'? 'home':'photo';
}
}
});
</script>
</body>
</html>
我们发现切换之后之前选中的状态不见了,那么怎么样保持之前的状态呢?Vue提供了Keep-alive
<div id="app">
<button @click="toggle">按钮</button>
<keep-alive>
<component v-bind:is="name"></component>
</keep-alive>
</div>
这样切换也可以保存之前的状态了
组件动画
给组件添加动画和给元素添加动画是一样的,不过不同的是过渡动画的离开和进入动画是同时进行的,要想不同时进行,必须给transition
设置mode属性
<component v-bind:is="name"></component>
</transition>
父子组件
父子组件数据传递
- 在Vue子组件不能访问父组件的数据,如果子组件想要访问父组件的数据,必须通过父组件传递
- 如何传递?
- 在父组件中通过v-bind传递数据
接受格式 v-bind:自定义接受名 = “要传递的数据” - 在子组件中通过props接收数据
接收格式: props:[自定义接受名]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<p>{{name}}---{{age}}</p>
// - 在父组件中通过v-bind传递数据
// 接受格式 v-bind:自定义接受名 = “要传递的数据”
<son :parentname="name" :parentage="age"></son>
</div>
</template>
<template id="son">
<div>
<p>我是子组件</p>
<p>{{parentname}}---{{parentage}}</p>
</div>
</template>
<script>
Vue.component("father",{
template:"#father",
data:function (){
return {name:"shanjialan",age:19};
},
components:{
"son":{
template: "#son",
// - 在子组件中通过props接收数据
// 接收格式: props:[自定义接受名]
props:["parentname","parentage"]
}
}
});
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰"
},
methods:{
myFn(){
alert("sjl");
}
}
});
</script>
</body>
</html>
父子组件数据传递方法
- 在Vue子组件不能直接使用父组件的方法,如果子组件想要使用父组件的方法,必须通过父组件传递
- 如何传递?
- 在父组件中通过v-on传递方法
接受格式 v-on:自定义接受名 = “要传递的方法” - 在子组件methods中自定义一个方法,方法中通过this.$emit(自定义接收名)来接受方法
- 在子组件中通过v-on绑定子组件的方法
接收格式: @click=”子组件的方法"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<button @click="say">parent</button>
<p>我是父组件</p>
<p>{{name}}---{{age}}</p>
<!-- - 在父组件中通过v-on传递方法 -->
<!-- 接受格式 v-on:自定义接受名 = “要传递的方法” -->
<son :parentname="name" :parentage="age" @parentsay="say"></son>
</div>
</template>
<template id="son">
<div>
<!-- - 在子组件中通过v-on绑定子组件的方法 -->
<!-- 接收格式: @click=”子组件的方法" -->
<button @click="mySon">son</button>
<p>我是子组件</p>
<p>{{parentname}}---{{parentage}}</p>
</div>
</template>
<script>
Vue.component("father",{
template:"#father",
data:function (){
return {name:"shanjialan",age:19};
},
// - 在子组件methods中自定义一个方法,方法中通过this.$emit(自定义接收名)来接受方法
methods: {
say(){
console.log("hello,father");
}
},
components:{
"son":{
template: "#son",
props:["parentname","parentage"],
methods:{
mySon(){
this.$emit("parentsay");
}
}
}
}
});
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰"
},
methods:{
myFn(){
alert("sjl");
}
}
});
</script>
</body>
</html>
子组件给父组件方法传递参数
this.$emit(第一个参数,第二个参数)
- 第一个参数,父组件需要传递的方法
- 第二个参数,子组件给父组件传递的参数
this.$emit("parentsay","家和万事兴");
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<button @click="say">parent</button>
<p>我是父组件</p>
<p>{{name}}---{{age}}</p>
<son :parentname="name" :parentage="age" @parentsay="say"></son>
</div>
</template>
<template id="son">
<div>
<button @click="mySon">son</button>
<p>我是子组件</p>
<p>{{parentname}}---{{parentage}}</p>
</div>
</template>
<script>
Vue.component("father",{
template:"#father",
data:function (){
return {name:"shanjialan",age:19};
},
methods: {
say(data){
console.log("hello,father");
console.log(data);
}
},
components:{
"son":{
template: "#son",
props:["parentname","parentage"],
methods:{
mySon(){
//第一个参数,父组件需要传递的方法,第二个参数,子组件给父组件传递的参数
this.$emit("parentsay","家和万事兴");
}
}
}
}
});
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰"
},
methods:{
myFn(){
alert("sjl");
}
}
});
</script>
</body>
</html>
vue组件的命名注意点
- 注册组件“驼峰命名法”,在使用时用“短横线”代替
<div id="app">
<!-- 两者都可以-->
<my-father></my-father>
<my-Father></my-Father>
</div>
如果想使用驼峰命名的数据,在传递时必须使用使用短横线+小写的方式,接受时自动转化为驼峰命名
如果是传递方法,则不能用驼峰命名法,只能用短横线命名法
<son :parent-name="name" :parent-age="age" @parentsay="say"></son>
components:{
"son":{
template: "#son",
props:["parentName","parentAge"],
methods:{
mySon(){
//第一个参数,父组件需要传递的方法,第二个参数,子组件给父组件传递的参数
this.$emit("parentsay","家和万事兴");
}
}
}
}
数据和方法多级传递
补充:
多层传递只能一层一层往下传递,不管是方法还是数据都是如此!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<grandfather></grandfather>
</div>
<template id="grandfather">
<div>
<button @click="say">爷爷</button>
<p>{{name}}</p>
<father :gfname="name" @gfsay="say"></father>
</div>
</template>
<template id="father">
<div>
<button @click="fathersay">爸爸</button>
<p>{{gfname}}</p>
<son :fname="gfname" @ssay="fathersay"></son>
</div>
</template>
<template id="son">
<div>
<button @click="sonsay">儿子</button>
<p>{{fname}}</p>
</div>
</template>
<script>
Vue.component("grandfather",{
template: "#grandfather",
data:function (){
return{
name:"grandfather"
}
},
methods: {
say(){
console.log("hello");
}
},
components:{
"father":{
template:"#father",
methods: {
fathersay() {
this.$emit("gfsay");
}
},
props:["gfname"],
components: {
"son":{
template:"#son",
props: ["fname"],
methods:{
sonsay(){
this.$emit("ssay");
}
}
}
}
}
}
})
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰"
},
methods:{
myFn(){
alert("sjl");
}
}
});
</script>
</body>
</html>
vue匿名插槽
默认情况下是不能给子组件添加额外的内容的,如果想要添加,必须使用插槽,——插槽就像一个“坑”,由使用者根据自己的需求来填“坑”,如果不填坑
,则显示默认数据
,如果填坑
的话则将添加的内容替换掉slot
;
匿名插槽:有多少个匿名插槽,填充的数据则会拷贝几份;虽然我们可以制定多个匿名插槽,但是在企业开发中推荐只写一个匿名插槽,若一定要有几分,则使用具名插槽
具名插槽
由于匿名插槽的弊端,因此有了具名插槽来弥补不足
如何使用??
- 定义插槽的时候给
slot
指定name
- 填充的内容指定slot="name"来填充指定插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<father>
<div slot="one">111</div>
</father>
<son>
<div slot="two">222</div>
</son>
</div>
<template id="son">
<div>
<p>头部</p>
<slot name="one"></slot>
<p>底部</p>
</div>
</template>
<template id="father">
<div>
<p>头部</p>
<slot name="two"></slot>
<p>底部</p>
</div>
</template>
<script>
Vue.component(
"father",{
template:"#father"
}
);
Vue.component(
"son",{
template:"#son"
}
);
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰"
},
methods:{
myFn(){
alert("sjl");
}
}
});
</script>
</body>
</html>
v-slot指令
专门用于替换slot属性,vue推荐的方式
【注意点】v-slot只能添加在template
标签上,v-slot:slotname
,简写方式:#
<!--</html>-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<father>
<template v-slot:two>
<div>slot1</div>
<div>slot11</div>
</template>
</father>
<son>
<template v-slot:one>
<div>slot2</div>
<div>slot22</div>
</template>
</son>
</div>
<template id="son">
<div>
<p>头部</p>
<slot name="one"></slot>
<p>底部</p>
</div>
</template>
<template id="father">
<div>
<p>头部</p>
<slot name="two"></slot>
<p>底部</p>
</div>
</template>
<script>
Vue.component(
"father",{
template:"#father"
}
);
Vue.component(
"son",{
template:"#son"
}
);
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰"
},
methods:{
myFn(){
alert("sjl");
}
}
});
</script>
</body>
</html>
作用域插槽
- 什么是作用域插槽?
作用域插槽就是带数据的插槽,让父组件在填充子组件插槽的同时能够使用自组件的数据;
作用域插槽的应用场景: 子组件提供数据, 父组件决定如何渲染
2.怎样使用作用域插槽?
2.1 先给自组件的插槽slot添加v-bind:外界访问的名称="需要暴露的数据"
2.2 在父组件添加的那个插槽<template>中添加slot-scope="scopename"
属性接收数据
2.3 在template中通过`scopename.外界访问的名称"接收访问数据
v-bind:names="names"作用: 将子组件names数据暴露给父组件;
slot-scope="abc"作用: 接收子组件插槽暴露的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<father></father>
</div>
<template id="father" >
<son>
<template slot-scope="abc">
<li v-for="(name,index) in abc.names">{{name}}--{{index}}</li>
</template>
</son>
</template>
<template id="son">
<div>
<p>头部{{names}}</p>
<slot v-bind:names="names"></slot>
<p>底部</p>
</div>
</template>
<script>
Vue.component(
"father", {
template: "#father",
components: {
"son": {
template: "#son",
data: function () {
return {names: ["zs", "ls", "ww"]};
}
}
}
}
);
let vue=new Vue({
el:"#app",
data:{
name:"单佳兰"
},
methods:{
myFn(){
alert("sjl");
}
}
});
</script>
</body>
</html>
v-slot指令
- 在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。
它取代了 slot 和 slot-scope - 也就是说我们除了可以通过v-slot指令告诉Vue内容要填充到哪一个具名插槽中,还可以通过v-slot指令告诉Vue如何接收作用域插槽暴露的数据
- v-slot:插槽名称="作用域名称"
匿名插槽名称为default
<template v-slot:default="abc">
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>
简写:
<template #default="abc">
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>