gitHub地址:https://github.com/huangpna/vue_learn/example里面的lesson06
一 vue组件基本实例
举个例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>app1</title> </head> <body> <div id="app1"> <button-counter></button-counter> <!--注意:点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。--> <button-counter></button-counter> <button-counter></button-counter> </div> </body> <script src="../js/vue.min.js"></script> <script> Vue.component('button-counter',{ //全局注册一个组件 data:function () { return { count:0 } }, template:'<div><button @click="count++">点击一下</button> <span>{{count}}</span></div>' }); new Vue({el:'#app1'}); //创建一个vue实例 </script> </html>
(1)因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
(2)一个组建的data选项必须是一个函数,而不是像之前一样直接接受一个对象
(3)有两种注册注册组件的方式:全局注册和局部注册;至此,我们的注册都只是通过Vue.component全局注册的。
(4)全局注册的组件可以用在其被注册之后的任何 (通过 new Vue
) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
二 通过prop给子组件传递数据
举个例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>app1</title> </head> <body> <div id="app1"> <button-counter title="My journey with Vue"></button-counter> //通过自定义特性传递值 <button-counter title="Blogging with Vue"></button-counter> <button-counter title="Why Vue is so fun"></button-counter> </div> </body> <script src="../js/vue.min.js"></script> <script> Vue.component('button-counter',{ //全局注册一个组件 props:['title'], //可以传递任何类型的值 data:function () { return { count:0 } }, template:'<div><button @click="count++">点击一下</button> <span v-if="count == 1">{{title}}</span></div>' //值显示,查看是否传递成功 }); new Vue({el:'#app1'}); //创建一个vue实例 </script> </html>
一个组件默认可以用过任意数量的prop,任何值都可以传递给任何 prop。在上述例子中,你会发现我们能够在组件实例中访问这个值,就像访问 data
中的值一样。
一个 prop 被注册之后,你就可以像上面例子一样把数据作为一个自定义特性传递进来。
例子2:
注意:这个时候我们想要给每篇博文渲染一个组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>app2</title> <style> #app2>div{ width:800px; height:160px; background-color: #ff4225; font-size:35px; text-align:center; line-height:160px; margin:0 auto 20px; } </style> </head> <body> <div id="app2"> <my-content v-for="item in dataList" v-bind:num="item.id" v-bind:title="item.title"></my-content> <!--动态传递值--> </div> </body> <script src="../js/vue.min.js"></script> <script> Vue.component('my-content',{ props:['title','num'], data:function () { }, template:'<div>{{title}}------{{num}}</div>' }); new Vue({ el:'#app2', data:{ dataList: [ { id: 1, title: '博文组件' }, { id: 2, title: '博文组件' }, { id: 3, title: '博文组件' } ] } }); </script> </html>
但是很多时候我们的博文不止需要标题和内容,可能要传递的数据更多,这个时候组件就会变得越来越复杂,这个时候你可以变成只接受一个单独的dataList props。
注意:必须将组件模板包含在一个父元素内,否则会报错。
三 通过事件向父级组件发送消息
举个例子(假如说我们现在需要引入一个可访问性的功能来放大博文的字号):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>app2</title> <style> #app2>div>div{ width:800px; height:160px; background-color: #ff4225; text-align:center; line-height:160px; margin:0 auto 20px; } </style> </head> <body> <div id="app2"> <div :style="{fontSize:postFontSize + 'px'}"> <!--它可以在模板中用来控制所有博文的字号--> <my-content v-for="item in dataList" v-on:enlarge-text="changeFontSize" v-bind:num="item.id" v-bind:title="item.title"></my-content> </div> <!--注意:然后我们用v-on 在博文组件上监听这个事件--> </div> </body> <script src="../js/vue.min.js"></script> <script> Vue.component('my-content',{ props:['title','num'], data:function () { }, //但是有的时候我们需要抛出一个特定的值,可以通过$emit的第二个参数 template:`<div>{{title}}------{{num}}------<button v-on:click="$emit('enlarge-text',0.5)">点击放大字体</button></div>` //现在我们需要给每篇博文添加一个按钮来放大字号 }); //注意:这个时候如果说我们需要点击这个按钮,需要调用内容的$emit方法并列入事件的名称来向父级组件触发一个事件 new Vue({ el:'#app2', data:{ dataList: [ { id: 1, title: '博文组件' }, { id: 2, title: '博文组件' }, { id: 3, title: '博文组件' } ], postFontSize: 20 //添加一个postFontSize数据属性来支持这个功能 }, methods:{ changeFontSize:function ($event) { //那么这个值将作为一个参数传入这个方法 let _this = this; _this.postFontSize += $event; } } }); </script> </html>
注释:首先我们可以通过在父组件中添加一个postFontSize数据属性来支持这个功能,它可以在模板中用来控制所有博文的字号;然后我们需要给每篇博文添加一个按钮来放大字号,当点击这个按钮时,需要调用内容的$emit方法并列入事件的名称来向父级组件触发一个事件,然后我们需要用v-on 在博文组件上监听这个事件,但是有的时候我们需要抛出一个特定的值,可以通过$emit的第二个参数;如果这个事件处理函数是一个方法,那么这个值将作为一个参数传入这个方法。
四 在组件上使用v-model
举个例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>app3</title> </head> <body> <div id="app3"> <div> <span>输入框1: </span> <input v-model="searchText"> </div> <!--等价于--> <custom-input v-bind:value="searchText" v-on:input="getInputV"></custom-input> </div> </body> <script src="../js/vue.min.js"></script> <script> Vue.component('custom-input',{ props:['value'], template:`<div><span>输入框2: </span><input v-bind:value="value" v-on:input="$emit('input',$event.target.value)"></div>` }); new Vue({ el:'#app3', data:{ searchText:'' }, methods:{ getInputV:function (v) { let _this = this; _this.searchText = v; } } }) </script> </html>
五 通过插槽分发内容
举个例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index4</title> <style> ._style{ width:500px; height:220px; background-color: #ff4225; } </style> </head> <body> <div id="app4"> <alert-box> Something bad happened. </alert-box> </div> </body> <script src="../js/vue.min.js"></script> <script> Vue.component('alert-box',{ props:[], template:`<div class="_style"><span>Error!</span><slot></slot></div>` }); new Vue({ el:'#app4' }) </script> </html>
如你所见,我们只要在需要的地方加入插槽就行了——就这么简单!
六 动态组件
有时候在不同组件之间进行动态切换是非常有用的,举例说明:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index5</title> <style> .tab-button:hover{ background: red; } #app5>.active{ background: red; } </style> </head> <body> <div id="app5"> <button v-for="item in navs" v-bind:key="item" v-bind:class="['tab-button',{active:item === currentTab}]" v-on:click="tab(item)"> {{item}} </button> <!-- 组件会在 `currentTabComponent` 改变时改变 --> <component v-bind:is="currentTabComponent"></component> </div> </body> <script src="../js/vue.min.js"></script> <script> Vue.component('my-home',{ template:'<div>Archive component111111</div>' }); Vue.component('my-posts',{ template:'<div>Archive component222222</div>' }); Vue.component('my-archive',{ template:'<div>Archive component333333</div>' }); new Vue({ el:'#app5', data:{ currentTab:'Home', navs:['Home','Posts','Archive'] }, methods:{ tab:function (v) { let _this = this; _this.currentTab = v; } }, computed:{ currentTabComponent:function () { let _this = this; return 'my-'+ _this.currentTab.toLowerCase(); }, } }) </script> </html>
在上述示例中,currentTabComponent
可以包括
- 已注册组件的名字,或
- 一个组件的选项对象
七 解析DOM模板时的注意事项
有些 HTML 元素,诸如 <ul>
、<ol>
、<table>
和 <select>
,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>
、<tr>
和 <option>
,只能出现在其它某些特定的元素内部。
这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:
<table>
|
这个自定义组件 <blog-post-row>
会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is
特性给了我们一个变通的办法:
<table>
|
需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:
- 字符串 (例如:
template: '...'
) - 单文件组件 (
.vue
) <script type="text/x-template">
到这里,你需要了解的组件的基础用法就了解完了,接下来需要深入学习组件了。