1.组件的基本用法——代码复用
相当于我自定义了一个标签,以后就可以复用了,但是标签名不能使用驼峰命名法,而且使用ES6的新语法 `字符串`(TAB上面的按键)会更好,组件的构造与声明要在new Vue之前,并且把自定义的标签放在一个根标签中,这个根标签一定要是被Vue管理的一个标签。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <my-cpn></my-cpn> </div> <script src="../vue.js"></script> <script> //1.创建组件构造器对象 const cpnC=Vue.extend({ /*ES6的新语法*/ template:` <div> <h2>组件的基本用法</h2> <p>代码复用</p> </div>` }) //2.注册组件 Vue.component('my-cpn',cpnC); const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { }, filters: { } }) </script> </body> </html>2.全局组件和局部组件
全局组件:全局组件就是可以在任意Vue实例中使用的组件类型,一般使用Vue.component()来注册
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <my-cpn></my-cpn> </div> <div id="tes"> <my-cpn></my-cpn>> </div> <script src="../vue.js"></script> <script> //1.创建组件构造器对象 const cpnC=Vue.extend({ /*ES6的新语法*/ template:` <div> <h2>组件的基本用法</h2> <p>代码复用</p> </div>` }) //2.注册组件 Vue.component('my-cpn',cpnC); const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { }, filters: { } }) const app1 = new Vue({ el: '#tes', data: { }, computed: { }, methods: { }, filters: { } }) </script> </body> </html>局部组件:就是只能在固定的Vue实例内使用,注册的语法:(在Vue实例的components元素中 )标签名:组件构造器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <cpn></cpn> </div> <div id="tes"> <cpn></cpn> </div> <script src="../vue.js"></script> <script> //1.创建组件构造器对象 const cpnC=Vue.extend({ /*ES6的新语法*/ template:` <div> <h2>组件的基本用法</h2> <p>代码复用</p> </div>` }) //2.注册组件 //Vue.component('my-cpn',cpnC); const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { }, filters: { }, components:{ //cpn:使用时的标签名 //cpnC:组件的构造器 cpn:cpnC } }) const app1 = new Vue({ el: '#tes', data: { }, computed: { }, methods: { }, filters: { } }) </script> </body> </html>3.父组件与子组件
在父组件中的components属性中声明一个子组件,就可以在父组件的template中使用这个子组件了,在父组件中声明的子组件作用域只能在父组件中,在Vue实例化中或者其他组件中是无法使用的。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <cpn></cpn> </div> <div id="tes"> <cpn></cpn> </div> <script src="../vue.js"></script> <script> //1.创建组件构造器对象 const cpnC=Vue.extend({ /*ES6的新语法*/ template:` <div> <h2>组件的基本用法</h2> <p>代码复用</p> <p>我是子组件</p> </div>` }) const oCpnC=Vue.extend({ template:`<div> <p>我是父组件</p> <p>下面是子组件</p> <cpnc></cpnc> </div>`, components:{cpnc:cpnC} }) //2.注册组件 //Vue.component('my-cpn',cpnC); const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { }, filters: { }, components:{ //cpn:使用时的标签名 //cpnC:组件的构造器 cpn:oCpnC } }) const app1 = new Vue({ el: '#tes', data: { }, computed: { }, methods: { }, filters: { } }) </script> </body> </html>4.组件的语法糖
其实就是把创建组件的构造器对象写出来,直接传给extends函数或者付给组件成员对象。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <cpn></cpn><br> <my-cpn></my-cpn> </div> <div id="tes"> <cpn></cpn> </div> <script src="../vue.js"></script> <script> //1.创建组件构造器对象 //const cpnC=Vue.extend() const oCpnC=Vue.extend({ template:`<div> <p>我是父组件</p> <p>下面是子组件</p> <cpnc></cpnc> </div>`, components:{ cpnc:{ /*ES6的新语法*/ template:` <div> <h2>组件的基本用法</h2> <p>代码复用</p> <p>我是子组件</p> </div>` } } }) //2.注册组件 Vue.component('my-cpn',{ /*ES6的新语法*/ template:` <div> <h2>组件的基本用法</h2> <p>代码复用</p> <p>我是子组件</p> </div>` }); const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { }, filters: { }, components:{ //cpn:使用时的标签名 //cpnC:组件的构造器 cpn:oCpnC } }) const app1 = new Vue({ el: '#tes', data: { }, computed: { }, methods: { }, filters: { } }) </script> </body> </html>5.代码分离
(1)使用script标签分离代码
<!--1.使用script标签分离--> <script type="text/x-template" id="cpn"> <p>我使用script标签分离代码</p> </script>(2)我使用template标签分离代码
<!--2.使用template标签分离--> <template id="tmp"> <p>我使用template标签分离代码</p> </template>最终注册时应用该代码模板的语法
template:'#tmp' <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newcpn></newcpn> <newtmp></newtmp> </div> <!--1.使用script标签分离--> <script type="text/x-template" id="cpn"> <p>我使用script标签分离代码</p> </script> <!--2.使用template标签分离--> <template id="tmp"> <p>我使用template标签分离代码</p> </template> <script src="../vue.js"></script> <script> Vue.component('newcpn', { template: '#cpn' }) const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { }, filters: { }, components: { 'newtmp':{ template:'#tmp' } } }) </script> </body> </html>6.组件内部的数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newcpn></newcpn> <newtmp></newtmp> </div> <!--1.使用script标签分离--> <script type="text/x-template" id="cpn"> <p>我使用script标签分离代码</p> </script> <!--2.使用template标签分离--> <template id="tmp"> <div> <p>我使用template标签分离代码</p> <p>{{msg1}}</p> <p>{{msg2}}</p> </div> </template> <script src="../vue.js"></script> <script> Vue.component('newcpn', { template: '#cpn', }) Vue.component('newtmp', { template: '#tmp', data(){ return{msg2:'我是组件中的数据'} } }) const app = new Vue({ el: '#jdg', data: { msg1:'我是Vue实例中的数据' }, computed: { }, methods: { }, filters: { }, components: { } }) </script> </body> </html>组件不能直接使用Vue实例对象中的数据,但是我们可以在组件中储存自己的数据,不可以像Vue实例中那样直接使用data对象存储数据,我们必须使用一个data函数,返回一个对象,该对象中存储的是组件中的数据。我们还有要注意的一点是在标签中使用数据时,必须要在一个div根标签之下。
Vue.component('newtmp', { template: '#tmp', data(){ return{msg2:'我是组件中的数据'} } })关于为什么要使用一个函数存储组件数据:
js的引用数据类型变量会把对象的地址存储,当我们使用一个data对象存储数据时,会导致多个组件实例共用一个数据区域,而返回一个数据对象的话就可以使用多个组件时不分享一个数据空间。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newtmp></newtmp> <newtmp></newtmp> </div> <!--使用template标签分离--> <template id="tmp"> <div> <h2>当前计数:{{number}}</h2> <button v-on:click="add">+</button> <button v-on:click="sub">-</button> </div> </template> <script src="../vue.js"></script> <script> Vue.component('newtmp', { template: '#tmp', data(){ return{number:0} }, methods: { add(){ this.number++; }, sub(){ this.number--; } } }) const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { }, filters: { }, components: { } }) </script> </body> </html>当我们返回一个固定的数据对象时,那么所有组件公用的就是一个引用对象。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newtmp></newtmp> <newtmp></newtmp> </div> <!--使用template标签分离--> <template id="tmp"> <div> <h2>当前计数:{{number}}</h2> <button v-on:click="add">+</button> <button v-on:click="sub">-</button> </div> </template> <script src="../vue.js"></script> <script> let obj={number:0}; Vue.component('newtmp', { template: '#tmp', data(){ return obj; }, methods: { add(){ this.number++; }, sub(){ this.number--; } } }) const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { }, filters: { }, components: { } }) </script> </body> </html>7.组件之间的通信
父传子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newtmp :person='person'></newtmp> </div> <!--使用template标签分离--> <template id="tmp"> <div> <h2>{{person}}</h2> </div> </template> <script src="../vue.js"></script> <script> Vue.component('newtmp', { template: '#tmp', data(){ return{}; }, props:['person'] }) const app = new Vue({ el: '#jdg', data: { person:['艾弗森','保罗','欧文','库里'] }, computed: { }, methods: { }, filters: { }, components: { } }) </script> </body> </html>我们可以在组件的props数组属性中设置组件从父组件中接收的数据变量名,然后在调用组件的时候,动态绑定组件的接收变量等于父组件的数据,记住一定要动态绑定,否则像下面这样
<newtmp person='person'></newtmp>会把person字符串传给组件的接收变量。
我们不只可以使用props数组来存储父组件传过来的数据,也可以使用props对象存储传过来的数据,props对象中的元素可以指定值,确保传过来的数据是某个特定类型,如果指定为多个可能的类型,那么可以使用数组,此时传过来的数据就要一定是数组中指定的类型,也可以指定为一个对象,该对象中可以存放type属性用于指定特定的传输类型,可以存放default属性,用于指定在父组件没有传输数据时的默认数据,require属性设置为真则是必传值。
Vue.component('newtmp', { template: '#tmp', data(){ return{}; }, //props:['person'] props:{ //1.类型限制 msg:String, //2.提供一些默认值 omsg:{ type:Array, default:[1,2,11,5] } } }) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newtmp msg='person'> </newtmp> </div> <!--使用template标签分离--> <template id="tmp"> <div> <h2>{{omsg}}</h2> </div> </template> <script src="../vue.js"></script> <script> Vue.component('newtmp', { template: '#tmp', data(){ return{}; }, //props:['person'] props:{ //1.类型限制 msg:String, //2.提供一些默认值 omsg:{ type:Array, default:[1,2,11,5] } } }) const app = new Vue({ el: '#jdg', data: { person:['艾弗森','保罗','欧文','库里'] }, computed: { }, methods: { }, filters: { }, components: { } }) </script> </body> </html>我们在设置数组和字符串类型的默认值的时候必须使用default函数返回默认的数组和字符串,如果直接使用default对象会导致浏览器报错。
Vue.component('newtmp', { template: '#tmp', data(){ return{}; }, //props:['person'] props:{ //1.类型限制 msg:String, //2.提供一些默认值 omsg:{ type:Array, default(){ return [1111,2222,555]; } } } }) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newtmp> </newtmp> </div> <!--使用template标签分离--> <template id="tmp"> <div> <h2>{{omsg}}</h2> </div> </template> <script src="../vue.js"></script> <script> Vue.component('newtmp', { template: '#tmp', data(){ return{}; }, //props:['person'] props:{ //1.类型限制 msg:String, //2.提供一些默认值 omsg:{ type:Array, default(){ return [1111,2222,555]; } } } }) const app = new Vue({ el: '#jdg', data: { person:['艾弗森','保罗','欧文','库里'] }, computed: { }, methods: { }, filters: { }, components: { } }) </script> </body> </html>关于组件props的驼峰命名法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newtmp :oMsg="person"> </newtmp> </div> <!--使用template标签分离--> <template id="tmp"> <div> <h2>{{oMsg}}</h2> </div> </template> <script src="../vue.js"></script> <script> Vue.component('newtmp', { template: '#tmp', data(){ return{}; }, props:{ oMsg:{ type:Array, default(){ return [1111,2222,555]; } } } }) const app = new Vue({ el: '#jdg', data: { person:['艾弗森','保罗','欧文','库里'] }, computed: { }, methods: { }, filters: { }, components: { } }) </script> </body> </html>如果我在传数据时直接使用原来的驼峰变量就会出现赋值不成功的现象,必须把大写字母换成‘-x’(x是字母)
<div id="jdg"> <newtmp :oMsg="person"> </newtmp> </div>修改
<div id="jdg"> <newtmp :o-msg="person"> </newtmp> </div> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <div id="jdg"> <newtmp :o-msg="person"> </newtmp> </div> <!--使用template标签分离--> <template id="tmp"> <div> <h2>{{oMsg}}</h2> </div> </template> <script src="../vue.js"></script> <script> Vue.component('newtmp', { template: '#tmp', data(){ return{}; }, props:{ oMsg:{ type:Array, default(){ return [1111,2222,555]; } } } }) const app = new Vue({ el: '#jdg', data: { person:['艾弗森','保罗','欧文','库里'] }, computed: { }, methods: { }, filters: { }, components: { } }) </script> </body> </html>子传父
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> </style> </head> <body> <!--父组件模板--> <div id="jdg"> <newtmp v-on:itemclick="cpnclick"> </newtmp> </div> <!--子组件模板--> <template id="tmp"> <div> <button v-for="items in categories" @click="newclick(items)">{{items.name}}</button> </div> </template> <script src="../vue.js"></script> <script> Vue.component('newtmp', { template: '#tmp', data(){ return{ categories:[ {id:'aaa',name:'热门推荐'}, {id:'bbb',name:'我最喜爱'}, {id:'ccc',name:'搜索引擎'}, {id:'ddd',name:'朋友热门'} ] }; }, props:{ }, methods:{ newclick(items){ //子组件发射 this.$emit('itemclick',items) } } }) const app = new Vue({ el: '#jdg', data: { }, computed: { }, methods: { cpnclick(sonitem){ console.log('年轻人没有武德',sonitem.name) } }, filters: { }, components: { } }) </script> </body> </html>以上例子的思路是:
1.为每一个按钮绑定一个点击事件函数,该函数发送自定义的事件,声明在组件的methods中,发送的是this.$emit()函数,第一个参数是发送的自定义事件的名称,第二个参数是发送的数据
methods:{ newclick(items){ //子组件发射 this.$emit('自定义事件的名称',向父组件传递的数据) } }
methods:{ newclick(items){ //子组件发射 this.$emit('itemclick',items) } }2. 在父组件中调用子组件,同时使用v-on来接收自定义事件,并绑定事件所引发的函数操作
<div id="jdg">
<newtmp v-on:在子组件中自定义的事件="事件所引发的函数操作"> </newtmp>
</div>
<div id="jdg"> <newtmp v-on:itemclick="cpnclick"> </newtmp> </div>
子传父(自定义事件)的流程:
1.在子组件中通过$emit()来触发事件
2.在父组件中通过v-on来监听子组件事件
