继前几天学习了指令,属性学习后,这两天又深刻学习了组件。每次学习事后发一篇文章,本身对知识点的记忆也更加深入了,同时也但愿本身的分享可以帮助到其余人html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>组件中的细节点</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <table> <tbody> <row></row> <row></row> <row></row> </tbody> </table> </div> <script> Vue.component('row', { template:'<tr><td>this is a row</td></tr>' }) var vm = new Vue({ el:'#root' }) </script> </body> </html> 复制代码
上述代码将this is a row封装成一个名为row的Vue全局组件,并在tbody中引用了row组件,使用时彻底符合html5页面规范table里面有tbody,tbody里面有tr,页面显示出来的结果以下,看起来也没有任何问题vue
<tbody> <tr is="row"></tr> <tr is="row"></tr> <tr is="row"></tr> </tbody> 复制代码
这个时候在tr后面加一个is属性让它等于row就行了,意思就是虽然我是tr可是我实际上是一个row组件,这样既可以保证tr里面显示的是咱们的row组件又保证咱们符合h5的编码规范,程序不会有BUG。而且建议像ul、ol、select下面也不要直接使用row这个组件,由于有些浏览器会显示bug,因此就用Vue提供的is属性就行了html5
在组件中使用data以下vuex
Vue.component('row', { data:{ content:'this is arow' }, template: '<tr><td>{{content}}</td></tr>' }) 复制代码
页面渲染报错数组
data() { return { content: 'this is a row' } }, 复制代码
Vue不建议咱们在代码里面去操做dom,可是在处理一些及其复杂的动做效果时,你不操做dom光靠Vue的数据绑定,有的时候处理不了这样的状况,因此在必要状况下须要操做dom。那么咱们如何操做dom呢?咱们须要经过ref这种引用的形式获取到进行dom的操做。浏览器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>组件中的细节点</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <div ref='hello' @click="handleClick">hello world </div> </div> <script> var vm = new Vue({ el: '#root', methods: { handleClick: function() { console.log(this.$refs.hello) } }, }) </script> </body> </html> 复制代码
咱们在div上添加了一个ref属性并命名为hello,而且绑定了一个名为handleClick的点击方法,在Vue实例的methods方法中打印了一串代码。bash
this.$refs.hello 复制代码
这个代码的意思就是整个Vue实例里面全部的引用里面的名为hello的引用,这个引用只的就是被修饰的div的DOM结点,因此下面的this.$refs.hello指向的就是相对应的div的dom结点,打印结果以下markdown
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>组件中的细节点</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <!-- 当子组件触发change事件时,父组件能监听到这个事件并执行handleChange这个方法 --> <counter ref="one" @change="handleChange"></counter> <counter ref="two" @change="handleChange"></counter> <div>{{total}}</div> </div> <script> Vue.component('counter', { template: '<div @click="handleClick">{{number}}</div>', data() { return { number: 0 } }, methods: { handleClick: function() { this.number ++ // 子组件向父组件传值,这里的意思是当子组件触发事件时同时告诉外面触发一个事件 this.$emit('change') } }, }) var vm = new Vue({ el: '#root', data:{ total: 0 }, methods:{ handleChange: function() { this.total = this.$refs.one.number + this.$refs.two.number } } }) </script> </body> </html> 复制代码
上述代码中封装了一个counter组件实现了一个点击实现num++的功能,在html引用了两次counter组件。咱们须要实现的是在父组件中计算两个counter组件中值的和。这时就须要在子组件上添加ref属性。而后再父组件中用 this.$refs.(ref名称)获取组件并经过.number获取每一个引用组件各自的number值并相加实现求total的功能。app
父组件如何向子组件传值呢? Vue中父组件向子组件传都是经过属性的形式向子组件传值的框架
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>父子组件传值</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <!-- count前面加了冒号,传递给子组件的就是数组,不加冒号传递给子组件的就是字符串,由于加了冒号后,后面的双引号的内容其实是一个js表达式了就不是一个字符串了 --> <counter :count="0"></counter> <counter :count="1"></counter> </div> <script> var counter = { // 在子组件中写一个props(用来接收父组件传递过来的内容),接收完之后就能够在子组件中直接使用了 props:['count'], template: '<div>{{count}}</div>' } var vm = new Vue({ el: '#root', components:{ counter } }) </script> </body> </html> 复制代码
上面栗子,声明了一个局部counter组件并挂载到了vm根实例中并在页面引用了counter组件,其中父组件经过在counter组件中添加属性:count="0/1"(count前面加了冒号,传递给子组件的就是数组,不加冒号传递给子组件的就是字符串,由于加了冒号后,后面的双引号的内容其实是一个js表达式了就不是一个字符串了)。那么子组件如何接收父组件的传值呢?子组件须要在内部写一个props用来接收父组件传递过来的内容。这样就能在子组件内部使用啦,这时候咱们再修改一会儿组件中的代码
var counter = { // 在子组件中写一个props(用来接收父组件传递过来的内容),接收完之后就能够在子组件中直接使用了 props:['count'], template: '<div @click="handleClick">{{count}}</div>', methods: { handleClick: function() { this.count ++ } }, } 复制代码
在子组件中添加一个方法实现点击+1的效果。你会发现页面上实现正确,可是打开控制台就有报错信息了
var counter = { // 在子组件中写一个props(用来接收父组件传递过来的内容),接收完之后就能够在子组件中直接使用了 props:['count'], data() { return { // 复制了一份count放到子组件本身的data中 number:this.count } }, template: '<div @click="handleClick">{{number}}</div>', methods: { handleClick: function() { this.number ++ } }, } 复制代码
咱们须要在子组件的data中自定义一个number复制一份父组件传递过来的count,这样就能够在组件中使用子组件本身number了。这样控制台就不会报错啦。
子组件向父组件传值是经过**$emit()**方法来实现对外暴露的
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>父子组件传值</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <counter :count="3" @inc="handleIncrease"></counter> <counter :count="2" @inc="handleIncrease"></counter> <div>{{total}}</div> </div> <script> var counter = { props:['count'], data() { return { // 复制了一份count放到子组件本身的data中 number:this.count } }, template: '<div @click="handleClick">{{number}}</div>', methods: { handleClick: function() { this.number = this.number + 2 // 每次触发事件时都告诉外部触发了一个inc事件,而且携带了一个参数(能够携带多个参数) this.$emit('inc',2) } }, } var vm = new Vue({ el: '#root', data:{ total: 5 }, components:{ counter }, methods:{ // 子组件触发inc事件时传递了一个参数,这时就能够在父组件中的方法中接收这个参数了 handleIncrease:function(num) { this.total+=num } } }) </script> </body> </html> 复制代码
代码中在子组件counter中的handleClick方法中经过this.$emit(inc,2)向父组件暴露了一个inc事件,而且携带了一个参数2(不只仅能携带一个参数,它能够携带多个参数)。而在父组件中
<counter :count="3" @inc="handleIncrease"></counter> <counter :count="2" @inc="handleIncrease"></counter> 复制代码
经过handleIncrease来监听inc事件,在vm根实例中的methods方法中定义handleIncrease方法,因为子组件向父组件传递了参数,因此handleIncrease能够在方法中接收这个参数并使用。
什么是组件的参数校验? 父组件向子组件传递了内容,那么子组件有权对这些内容进行约束,这些约束就是参数的校验。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>组件参数校验与非Props特性</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <child content="hello world"></child> </div> <script> Vue.component('child', { props: { // content:String,//子组件接收到的content必须是String类型的,若是不是String类型那么当子组件用了content值得时候控制台将会发出警告。 // 若是须要给定多个类型则须要把类型放入数组中 // content:[ Number, String ], // 固然这些约束还能够更加复杂,实际上还能够跟一个对象 content:{ type:String, //type规定类型 required:true, //required则表示这个参数是必传的,若是父组件不传这个属性则会报错 default:'default value', //当required为false时(接收到的属性值不是必传的),设置default则会在父组件没传值的时候用默认值(default value)代替 validator:function(value) { return (value.length > 5 ) } } }, template:`<div> <p>{{content}}</p> </div>` }) var vm = new Vue({ el: '#root' }) </script> </body> </html> 复制代码
上面的代码中父组件向子组件传递了一个content属性。那么咱们如何在子组件中对它进行约束呢?首先咱们须要把prop定义成一个对象。最简单的就是只约束类型而且类型只有一个了。下面的这段代码的意思是子组件接收到的content必须是String类型的,若是不是String类型那么当子组件用了content值得时候控制台将会发出警告。
props: {
conten:String
}
复制代码
若是要给content规定多个类型呢?只须要在后面使用数组就好了
content:[ Number, String ]
复制代码
固然这些约束还能够更加复杂,实际上还能够跟一个对象。能够看下面这段代码以及其中的注释
content:{ type:String, //type规定类型 required:true, //required则表示这个参数是必传的,若是父组件不传这个属性则会报错 default:'default value', //当required为false时(接收到的属性值不是必传的),设置default则会在父组件没传值的时候用默认值(default value)代替 validator:function(value) { return (value.length > 5 ) } } }, 复制代码
什么叫作非Pops特性和Props特性呢? Props特性指的是当你的父组件使用子组件的时候,经过属性属性向子组件传值的时候刚好子组件里面声明了对父组件传递过来的属性的接收。非Pops特性值的时父组件向子组件传递了一个属性,可是子组件并无声明接收父组件传递过来的内容。
先看一段代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>给子组件绑定原生事件</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <child @click="handleClick"></child> </div> <script> Vue.component('child', { template:` <div> Child </div> ` }) var vm = new Vue({ el:'#root', methods: { handleClick: function() { alert('点击了') } }, }) </script> </body> </html> 复制代码
上面的代码乍一眼看当咱们点击child组件的dom时,会触发父组件上的点击事件方法。可是运行上面的代码时会发如今父组件中定义的点击事件是没有用的。这是怎么一回事呢?当给一个组件绑定一个事件的时候,实际上这个事件绑定的时一个自定义的事件,也就是说若是你的鼠标点击触发的事件,并非绑定的click事件,若是想要触发自定义的click事件,必须在子组件里面对须要触发事件的dom元素进行事件的绑定,这个事件才是真正的原生事件。添加下面部分代码
Vue.component('child', { template:` <div @click="handleChildClick"> Child </div> `, methods:{ handleChildClick: function() { alert("child click") } } }) 复制代码
点击dom元素时child click 被打印出来了子组件的原生事件被触发了,而父组件的事件没有被触发
this.$emit('click') 复制代码
只须要在子组件methods中调用$click(由于自定义事件的名称为click,这里的名字必须与自定义事件名称保持一致),这样父组件中的事件就能被调用了,这样当点击child组件绑定了handleChildClick事件的dom元素就会再打印下面这条消息了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>给子组件绑定原生事件</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <!-- 在组件上加一个native修饰符,表示监听的是原生的点击事件 --> <child @click.native="handleClick"></child> </div> <script> Vue.component('child', { template:` <div @click="handleChildClick"> Child </div> ` }) var vm = new Vue({ el:'#root', methods: { handleClick: function() { alert('点击了') } }, }) </script> </body> </html> 复制代码
在代码中的自定义事件上加上**.native**修饰就表示触发的是原生的点击事件了,这就是给组件绑定原生事件的方法了
<child @click.native="handleClick"></child> 复制代码
一个网页能够拆分为不少部分,每个部分就是代码中的组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>非父子组件间的传值(Bus/总线/发布订阅模式/观察者模型)</title> <script src="../vue.js"></script> </head> <body> <div id="app"> <child content='Yu'></child> <child content='Han'></child> </div> <script> // 在Vue的prototype上挂载了一个名字叫作bus的属性,而且这个属性指向Vue()的实例,只要咱们以后去调用new.vue或者建立组件的时候,每一个组件都会有bus这个属性 // 由于每一个组件或者每一个Vue实例都是经过类来建立的,而我在vue的prototype上挂载了一个bus属性 Vue.prototype.bus = new Vue() Vue.component('child',{ props:{ content:String }, data() { return { selfcontent:this.content } }, template:'<div @click="handleClick">{{selfcontent}}</div>', methods: { handleClick:function() { this.bus.$emit('change',this.selfcontent) //由于bus是vue的实例,因此可使用$emit } }, // 声明一个mounted生命钩子函数 mounted:function() { // 在钩子函数中this指向会发生改变因此须要作如下工做 var this_ = this this.bus.$on('change',function(msg) { this_.selfcontent = msg // })//由于bus是个实例因此有$on的方法,它就能监听到bus上面触发出来的事件(change),而且能接收带过来的参数 }, }) var vm = new Vue({ el: '#app' }) </script> </body> </html> 复制代码
this.bus.$emit('change',this.selfcontent) 复制代码
this.bus.$on('change',function(msg) { this_.selfcontent = msg // })//由于bus是个实例因此有$on的方法,它就能监听到bus上面触发出来的事件(change),而且能接收带过来的参数 复制代码
当使用组件时须要往里面添加一些内容,不只能够用父组件传值的方法,还能够利用插槽来实现
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Vue中的插槽(slot)</title> <script src="../vue.js"></script> </head> <body> <div id="app"> <child> <p>Yu</p> </child> </div> <script> Vue.component('child',{ template:`<div> <P>HELLO</P> <slot>默认内容</slot> </div>` }) var vm = new Vue({ el: '#app' }) </script> </body> </html> 复制代码
上面的代码在child组件中插入了p标签和Yu内容,如何在组件中接收这个内容呢只须要在模板中使用slot就好了。而且能够在slot标签中写上默认内容,当不传递插槽内容的时候则会显示默认内容。再来看一个
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Vue中的插槽(slot)</title> <script src="../vue.js"></script> </head> <body> <div id="app"> <contentx> <div class="header">header</div> <div class="footer">footer</div> </contentx> </div> <script> Vue.component('contentx',{ template:`<div> <slot></slot> <div class='content'>content</div> <slot></slot> </div>` }) var vm = new Vue({ el: '#app' }) </script> </body> </html> 复制代码
上面这段代码的目的是在页面上显示出一个header、一个content、一个footer。结果倒是
<div id="app"> <contentx> <div class="header" slot="header">header</div> <div class="footer" slot="footer">footer</div> </contentx> </div> <script> Vue.component('contentx',{ template:`<div> <slot name='header></slot> <div class='content'>content</div> <slot name='footer'></slot> </div>` }) var vm = new Vue({ el: '#app' }) 复制代码
当一个组件中某些标签元素是经过循环自身data中的某个数组来进行渲染的时候,而这个组件又须要给其余组件使用,这时这个组件中的模板样式就不能被写死,由于每一个组件须要循环的次数是不同的。或者组件中的某一部分须要经过外部传递进来的时候,这时候就须要用到做用域插槽了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Vue中的做用域插槽</title> <script src="../vue.js"></script> </head> <body> <div id="app"> <child> <!-- 必定要在最外层套用一个template,这时固定写法,同时要写一个slot-scope="自定义名字" 它的意思是当子组件用slot的时候会往slot里面传递一个item数据,这样就可使用item了,而item是放在slot-scope="自定义名字"当中--> <!-- 这个时候每一项应该显示成什么,就不禁子组件决定了,而是父组件调子组件的时候给子组件去传递对应的模板 --> <template slot-scope="props"> <li>{{props.item}} - hello</li> </template> </child> </div> <script> Vue.component('child',{ data() { return { list:[1,2,3,4] } }, template:`<div> <ul> <slot v-for="item of list" :item=item></slot> </ul> </div>` }) var vm = new Vue({ el: '#app' }) </script> </body> </html> 复制代码
但愿这篇文章能对您有所帮助,太困了晚安