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>
image.png
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不用对象返回,而是通过函数返回的,因为通过对象定义,那么多个组件就会共用一份数据,就会导致数据混乱,将这个方法返回的数据和当前创建的组件绑定在一起,就避免了数据混乱
的问题
image.png
<!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>
image.png
image.png
image.png
我们发现切换之后之前选中的状态不见了,那么怎么样保持之前的状态呢?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>
image.png
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>
image.png
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>