组件注册
1 2 3 4
   | Vue.component(组件名称,{     data:组件数据,     template:组件模板内容 })
  | 
简单用法
1 2 3 4 5 6 7 8 9 10 11 12 13
   | Vue.component('button-counter', {     data: function () {         return {             count: 0         }     },     template: '<button @click="add">点击了{{count}}次</button>',     methods: {         add() {             this.count++         }     }, })
  | 
1 2 3
   | <div id="app">     <button-counter><button-counter> </div>
   | 

组件可以多次复用,并且每个组件之间互不影响。
 组件注意事项
定义组件时data属性为一个函数,函数内返回一个对象。这个对象中包含数据
1 2 3 4 5
   | data: function () {     return {         count: 0     } }
  | 
组件模板内容必须是单个根元素
1
   | template: '<button @click="add">点击了{{count}}次<p>p标签</p></button>',
  | 
组件模板内容可以是模板字符串
组件命名方式
 局部组件注册方式
1 2 3 4 5 6 7
   | var ComponentA = {} var vm = new Vue({     el: '#app',     components: {         'component-a': ComponentA     } });
  | 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
   | <body>     <div id="app">         <component-a></component-a>         <component-b></component-b>         <component-c></component-c>     </div>     <script type="text/javascript" src="../js/vue.js"></script>     <script type="text/javascript">         var ComponentA = {             data: function () {                 return {                     msg: 'HelloWorld'                 }             },             template: '<div>{{msg}}</div>'         }         var ComponentB = {             data: function () {                 return {                     msg: 'HelloTOM'                 }             },             template: '<div>{{msg}}</div>'         }         var ComponentC = {             data: function () {                 return {                     msg: 'HelloVue'                 }             },             template: '<div>{{msg}}</div>'         }         var vm = new Vue({             el: '#app',             components: {                 'component-a': ComponentA,                 'component-b': ComponentB,                 'component-c': ComponentC             }
          });     </script> </body>
   | 
 Vue调试工具
可以直接使用谷歌商城进行安装:https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd/related?hl=zh
也可以参考官方自行构建后安装:https://github.com/vuejs/vue-devtools
如果遇到Vue.js not detected问题可能导致的原因:
没有开启权限


 组件间的交互
 父组件向子组件传值
在组建中定义props属性,值为一个数组,用于接受来自父组件的值
1 2 3 4 5 6 7 8 9
   | Vue.component('menu-item', {     props: ['title'],     data: function () {         return {             msg: '子组件'         }     },     template: '<div>{{msg}}---{{title}}</div>' })
  | 
在使用时可以有两种写法,
静态传入
1
   | <menu-item title='来自父组件的值'></menu-item>
   | 
通过属性绑定
1
   | <menu-item :title='ptitle'></menu-item>
   | 
1 2 3 4 5 6 7
   | var vm = new Vue({     el: '#app',     data: {         pmsg: "父组件",         ptitle: '来自父组件的值'     } });
  | 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | <body>     <div id="app">         <menu-item title='来自父组件的值'></menu-item>         <menu-item :title='ptitle'></menu-item>     </div>     <script type="text/javascript" src="../js/vue.js"></script>     <script type="text/javascript">         Vue.component('menu-item', {             props: ['title'],             data: function () {                 return {                     msg: '子组件'                 }             },             template: '<div>{{msg}}---{{title}}</div>'         })         var vm = new Vue({             el: '#app',             data: {                 pmsg: "父组件",                 ptitle: '来自父组件的值'             }         });     </script> </body>
   | 
 props属性名
 规则
在props使用驼峰式传值,那么模板中需要使用短横线的形式
1 2 3 4
   | Vue.component('menu-item', {     props: ['TitleMsg'],     template: '<div>{{TitleMsg}}</div>' })
  | 
1
   | <menu-item title-msg='nihao'></menu-item>
   | 
在HTML中大小写是不敏感的。
字符串模板中没有这个限制
1 2 3 4
   | Vue.component('menu-item', {     props: ['TitleMsg'],     template: '<div><menu-item TitleMsg="nihao"></menu-item></div>' })
  | 
 属性值
以下类型均可以传递
字符串
数值类型
如果通过v-bind绑定,那么数值为数字类型
布尔类型
如果通过v-bind绑定,那么数值为布尔类型
数组类型
对象
 子组件向父组件传值
子组件通过自定义事件向父组件传递信息
1
   | <menu-item :parr='parr' @enlarge-text='handle'></menu-item>
   | 
自定义事件名为enlarge-text,触发的函数名为handle
1 2 3 4 5 6 7 8 9 10 11
   | Vue.component('menu-item', {     props: ['parr'],     template: ` <div> <ul> <li :key='index' v-for='(item,index) in parr'>{{item}}</li> </ul> <button @click='$emit("enlarge-text")'>扩大父组件中字体大小</button> </div> ` });
  | 
对按钮进行单击事件绑定,绑定的事件为enlarge-text。$emit为固定用法。
父组件通过监听子组件的事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | var vm = new Vue({     el: '#app',     data: {         pmsg: '父组件中内容',         parr: ['apple', 'orange', 'banana'],         fontSize: 10     },     methods: {         handle: function () {                          this.fontSize += 5;         }     } });
  | 
 子组件向父组件传值-携带参数
定义组件时可以通过$emit的第二个参数进行传值。组件调用时通过$event来接受。
使用组件
1
   | <menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
   | 
定义组件
1 2 3 4 5 6 7 8 9 10 11
   | Vue.component('menu-item', {     props: ['parr'],     template: ` <div> <ul> <li :key='index' v-for='(item,index) in parr'>{{item}}</li> </ul> <button @click='$emit("enlarge-text",5)'>扩大父组件中字体大小</button> </div> ` });
  | 
父组件接受值
1 2 3 4 5 6
   | methods: {     handle: function (val) {                  this.fontSize += val;     } }
  | 
 非父子组件间传值
单独的事件中心管理组件间的通信
监听事件与销毁事件
1 2 3
   | hub.$on('tom-event', (val) => {     this.num += val })
  | 
触发事件
1
   | hub.$emit('jerry-event', 1)
  | 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
   | <body>     <div id="app">         <test-tom></test-tom>         <test-jerry></test-jerry>     </div>     <script type="text/javascript" src="../js/vue.js"></script>     <script type="text/javascript">                  var hub = new Vue()         Vue.component('test-tom', {             data: function () {                 return {                     num: 0                 }             },             template: ` <div> <div>TOM:{{num}}</div> <div><button @click='handle'>点击</button></div>         </div> `,             methods: {                 handle: function () {                                          hub.$emit('jerry-event', 1)                 }             },             mounted: function () {                 hub.$on('tom-event', (val) => {                     this.num += val                 })             },         });         Vue.component('test-jerry', {             data: function () {                 return {                     num: 0                 }             },             template: ` <div> <div>jerry:{{num}}</div> <div><button @click='handle'>点击</button></div>         </div> `,             methods: {                 handle: function () {                     hub.$emit('tom-event', 2)                 }             },             mounted: function () {                 hub.$on('jerry-event', (val) => {                     this.num += val                 })             },         });         var vm = new Vue({             el: '#app',         });     </script> </body>
   | 
 组件插槽
父组件向子组件传递内容(模板内容)

定义插槽
1 2 3 4 5 6 7 8
   | Vue.component('alert-box', {     template: ` <div> <strong>Error:<strong> <slot>默认内容</slot>     </div> ` })
  | 
定义插槽使用slot标签,如果没有传值,那么显示定义时的默认内容。
使用插槽
1 2
   | <alert-box>有bug发生</alert-box> <alert-box></alert-box>
   | 

当传递值时显示值的内容,否则显示默认内容(如果有)
 具名插槽
即对slot标签添加name属性,表示当前插槽的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | Vue.component('base-layout', {     template: ` <div> <header> <slot name='header'></slot>     </header>   <main> <slot></slot>     </main>   <footer> <slot name='footer'></slot>     </footer>   </div> ` })
  | 
调用时,通过slot属性指定填充插槽名。没有指定的则添加到默认插槽
1 2 3 4 5
   | <base-layout>     <p slot="header">标题</p>     <p>主要内容</p>     <p slot="footer">尾部</p> </base-layout>
   | 

关于调用也可以使用如下方法
1 2 3 4 5 6 7 8 9 10 11
   | <base-layout>     <template slot="header">         <p slot="header">标题</p>     </template>     <template>         <p>主要内容</p>     </template>     <template slot="footer">         <p slot="footer">尾部</p>     </template> </base-layout>
   | 
 作用域插槽
父组件对子组件的内容进行加工处理。
插槽定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | Vue.component('fruit-list', {     props: ['list'],     template: ` <div> <li :key='item.id' v-for='item in list'> <slot :info='item'>{{item.name}}</slot> </li> </div> ` }); var vm = new Vue({     el: '#app',     data: {         list: [{             id: 1,             name: 'apple'         }, {             id: 2,             name: 'orange'         }, {             id: 3,             name: 'banana'         }]     } });
  | 
插槽内容
1 2 3 4 5 6
   | <fruit-list :list='list'>     <template v-slot='slotProps'>         <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>         <span v-else>{{slotProps.info.name}}</span>     </template> </fruit-list>
   | 
值的传递:
插槽中定义了属性info为item(每次遍历的结果)
使用v-slot指令接受,其值可以为任意喜欢的名字。
1
   | <template v-slot='slotProps'></template>
   | 
其slotProps实际上就是info的值。
1 2 3 4 5 6 7 8
   | <fruit-list :list='list'>     <template v-slot='slotProps'>
          <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>         <span v-else>{{slotProps.info.name}}</span>         <span>{{slotProps}}</span>     </template> </fruit-list>
   | 

 购物车案例
 组件化重构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
   | <body>     <div id="app">         <div class="container">             <my-cart></my-cart>         </div>     </div>     <script type="text/javascript" src="../js/vue.js"></script>     <script type="text/javascript">
          var CartTitle = {             template: ` <div class="title">我的商品</div> `         }         var CartList = {             template: ` <div> <div class="item"> <img src="img/a.jpg"/> <div class="name"></div> <div class="change"> <a href="">-</a> <input type="text" class="num" /> <a href="">+</a>         </div> <div class="del">×</div>         </div> <div class="item"> <img src="img/b.jpg"/> <div class="name"></div> <div class="change"> <a href="">-</a> <input type="text" class="num" /> <a href="">+</a>         </div> <div class="del">×</div>         </div> <div class="item"> <img src="img/c.jpg"/> <div class="name"></div> <div class="change"> <a href="">-</a> <input type="text" class="num" /> <a href="">+</a>         </div> <div class="del">×</div>         </div> <div class="item"> <img src="img/d.jpg"/> <div class="name"></div> <div class="change"> <a href="">-</a> <input type="text" class="num" /> <a href="">+</a>         </div> <div class="del">×</div>         </div> <div class="item"> <img src="img/e.jpg"/> <div class="name"></div> <div class="change"> <a href="">-</a> <input type="text" class="num" /> <a href="">+</a>         </div> <div class="del">×</div>         </div>         </div> `         }         var CartTotal = {             template: ` <div class="total"> <span>总价:123</span> <button>结算</button>         </div> `         }         Vue.component('my-cart', {             template: ` <div class='cart'> <cart-title></cart-title> <cart-list></cart-list> <cart-total></cart-total>         </div> `,             components: {                 'cart-title': CartTitle,                 'cart-list': CartList,                 'cart-total': CartTotal             }         });         var vm = new Vue({             el: '#app',             data: {
              }         });
      </script> </body>
   | 
 标题和总价
标题的实现很简单,为了演示功能,定义一个属性记录用户名,并传递给header
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | Vue.component('my-cart', {     data: function () {         return {             uname: '张三',         }     },     template: `         <div class='cart'>           <cart-title :uname='uname'></cart-title>           <cart-list></cart-list>           <cart-total></cart-total>         </div>       `, }
  | 
将值显示出来
1 2 3 4 5 6
   | var CartTitle = {     props: ['uname'],     template: ` <div class="title">{{uname}}的商品</div> ` }
  | 
对于总价,提供一个数据用于记录购物车的列表。并将值传递给total
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
   | Vue.component('my-cart', {     data: function () {         return {             uname: '张三',             list: [{                 id: 1,                 name: 'TCL彩电',                 price: 1000,                 num: 1,                 img: 'img/a.jpg'             }, {                 id: 2,                 name: '机顶盒',                 price: 1000,                 num: 1,                 img: 'img/b.jpg'             }, {                 id: 3,                 name: '海尔冰箱',                 price: 1000,                 num: 1,                 img: 'img/c.jpg'             }, {                 id: 4,                 name: '小米手机',                 price: 1000,                 num: 1,                 img: 'img/d.jpg'             }, {                 id: 5,                 name: 'PPTV电视',                 price: 1000,                 num: 2,                 img: 'img/e.jpg'             }]         }     },     template: ` <div class='cart'> <cart-title :uname='uname'></cart-title> <cart-list></cart-list> <cart-total :list='list></cart-total> </div> `, }
  | 
在CartTotal组件中根据列表中的内容计算总价。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | var CartTotal = {     props: ['list'],     template: ` <div class="total"> <span>总价:{{total}}</span> <button>结算</button> </div> `,     computed: {         total: function () {                          var sum = 0             console.log(this.list);             this.list.forEach(item => {                 sum += item.price * item.num             })             return sum         }     } }
  | 
 列表展示和删除
列表展示只需要在父组件中将数组列表传递给子组件,子组件在进行循环遍历即可。
1 2 3 4 5 6 7 8 9 10
   | Vue.component('my-cart', {     template: ` <div class='cart'> <cart-title :uname='uname'></cart-title> <cart-list :list='list'></cart-list> <cart-total :list='list'></cart-total> </div> `, }              )
  | 
子组件中循环遍历数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | var CartList = {     props: ['list'],     template: ` <div> <div :key='item.id' v-for='item in list' class="item"> <img :src="item.img"/> <div class="name">{{item.name}}</div> <div class="change"> <a href="">-</a> <input type="text" class="num" /> <a href="">+</a> </div> <div class="del"'>×</div> </div> </div> ` }
  | 
对于删除操作来说,由于数据来源于父组件,所以不建议直接删除,而是通过自定义事件通知父级。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | var CartList = {       props: ['list'],       template: `         <div>           <div :key='item.id' v-for='item in list' class="item">             <img :src="item.img"/>             <div class="name">{{item.name}}</div>             <div class="change">               <a href="">-</a>               <input type="text" class="num" />               <a href="">+</a>             </div>             <div class="del" @click='del(item.id)'>×</div>           </div>         </div>       `,       methods: {         del: function (id) {                      this.$emit('cart-del', id)         }       },     }
  | 
父级中定义删除的实际操作,并监听事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | Vue.component('my-cart', {     methods: {         delCart: function (id) {                                       var index = this.list.findIndex(item => {                 return item.id == id             })                          this.list.splice(index, 1)         }     },     template: ` <div class='cart'> <cart-title :uname='uname'></cart-title> <cart-list :list='list' @cart-del='delCart($event)'></cart-list> <cart-total :list='list'></cart-total> </div> `, });
  | 
 商品数量的变更
商品数量变更同样涉及到修改列表内容,同样的交给父组件去修改。
在子组件中定义自定义事件并对input标签绑定失去焦点时触发这个自定义事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | var CartList = {     props: ['list'],     template: ` <div> <div :key='item.id' v-for='item in list' class="item"> <img :src="item.img"/> <div class="name">{{item.name}}</div> <div class="change"> <a href="">-</a> <input type="text" class="num" :value='item.num' @blur='changeNum(item.id,$event)'/> <a href="">+</a> </div> <div class="del" @click='del(item.id)'>×</div> </div> </div> `,     methods: {         del: function (id) {                          this.$emit('cart-del', id)         },         changeNum: function (id, event) {                                       let value = event.target.value             this.$emit('change-num', { id: id, num: value })         }     }, }
  | 
自定义事件中,将商品id和修改的值传递给父组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | Vue.component('my-cart', {       template: `         <div class='cart'>           <cart-title :uname='uname'></cart-title>           <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>           <cart-total :list='list'></cart-total>         </div>       `,       methods: {         changeNum: function (val) {           console.log(val);                      this.list.some(item => {             if (item.id == val.id) {               item.num = val.num                              return true             }           })         }       }, });
  | 
父组件监听子组件定义的自定义方法,并触发父组件定义的自定义方法。
 按钮操作
按钮操作同样是需要利用父组件来修改数据。为了复用changeNum事件,将其在添加一个值type用于记录当前的操作。子组件修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | changeNum: function (id, event) {               let value = event.target.value     this.$emit('change-num', {         id: id,         num: value,         type: 'change'     }) },     sub: function (id) {         this.$emit('change-num', {             id: id,             type: 'sub'         })     },         add: function (id) {             this.$emit('change-num', {                 id: id,                 type: 'add'             })         },
  | 
父组件的修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | changeNum: function (val) {     if (val.type == 'change') {                  this.list.some(item => {             if (item.id == val.id) {                 item.num = val.num                                  return true             }         })     } else if (val.type == 'sub') {                  this.list.some(item => {             if (item.id == val.id) {                 item.num -= 1                                  return true             }         })     } else {                  this.list.some(item => {             if (item.id == val.id) {                 item.num += 1                                  return true             }         })     }
  |