vue(2)

Vue组件化开发

  • 组件化开发的思想
  • 组件的注册
  • 组件间的数据交互
  • 组件插槽的用法
  • Vue调试工具的用法

组件开发思想

  • 标准
  • 分治
  • 重用
  • 组合

组件化规范:Web Components(并非所有的浏览器都支持)

  • 我们希望尽可能多的重用代码
  • 自定义组件的方式不太容易(html、css和js)
  • 多次使用组件可能导致冲突

Web Components通过创建封装好功能的定制元素解决上述问题(Vue部分实现了上述规范)

组件注册

Vue.component('组件名称',{
    // 组件内容
    data: '组件数据',
    template: '组件模板内容'
})
<!-- 组件使用 -->
<div id="app">
    <h1>{{name}}</h1>
    <button-count></button-counter>
    <!-- 组件是可以重用的 -->
    <button-count></button-counter>
    <button-count></button-counter>
</div>
<script>
    // 定义一个名为 button-counter 的新组件
    Vue.component('button-counter', {
        data: function() {
            return {
                count: 0
            }
        },
        template: '<button v-on:click="count++">点击{{count}}次</button>'
    })
</script>

组件注册注意事项:

  1. data必须是一个函数 => 分析函数和普通对象的对比
  2. 组件模板内容必须是单个根元素 => 分析演示实例的效果
  3. 组件模板内容可以是模板字符串 => 模板字符串需要浏览器提供支持(ES6语法)
  4. 组建的命名方式
  • 短横线方式Vue.component('my-component',{ /*...*/ }
  • 驼峰式Vue.component('myComponent',{ /*...*/ }
  • 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中使用驼峰的方式命名组件,但是在普通的标签模板中,必须使用短横线的方式命名组件

局部组件注册

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
    el: '#app',
    components: {
        'component-a' :componentA,
        'component-b' :componentB,
        'component-c' :componentC
    }
})
// 局部组件只能在其父组件中使用

Vue调试工具

  1. 克隆仓库
  2. 安装依赖包
  3. 构建
  4. 打开Chrome扩展页面
  5. 选中开发者模式
  6. 加载已解压的扩展,选择shells/chrome

组件间的数据交互

  1. 父组件向子组件传值

    • 组件内部通过props接收传递过来的值

      Vue.component('menu-item', {
          props: ['title'],
          template: '<div>{{title}}</div>'
      })
      
    • 父组件通过属性将值传递给子组件

      <menu-item title="来自父组件传递的数据"></menu-item>
      <menu-item :title="title"></menu-item>
      
    • props属性名规则

      • props中使用驼峰形式,模板中则需要使用短横线的形式
      • 字符串形式的模板中则没有这个限制
      <menu-item menu-title="hello"></menu-item>
      <script>
          Vue.component('menu-item', {
              // 在JavaScript中是驼峰式的
              props: ['menuTitle'],
              template: `<div>{{menuTitle}}`
          })
      </script>
      
      <div id="app">
          <h1>{{name}}</h1>
          <div>{{pmsg}}</div>
          <menu-item :menu-title="ptitle" content='hello'></menu-item>
      </div>
      <script>
          Vue.component('third-com', {
              props:['testTitle'],
              template:'<div>{{testTitle}}</div>'
          });
          Vue.component('menu-item', {
              props:['menuTitle'],
              template:'<div>{{menuTitle}}<third-com testTitle="hello"></third-com></div>'
          });
          let vm = new Vue({
              el:'#app',
              data:{
                  name:"myVue",
                  pmsg:'父组件的内容',
                  ptitle:'动态绑定的属性'
              },
              methods: {
      
          }
          });
      </script>
      
    • props属性值类型

      • 字符串 String
      • 数值 Number
      • 布尔值 Boolean
      • 数组 Array
      • 对象 Object
      <div id="app">
          <h1>{{name}}</h1>
          <div>{{pmsg}}</div>
          <!-- 不带引号为字符串类型 -->
          <menu-item :pstr="pstr" :pnum="13" :pboo="true" :parr="parr" :pobj="pobj"></menu-item>
      </div>
      <script>
          Vue.component('menu-item', {
              props: ['pstr','pnum','pboo','parr','pobj'],
              template:`
                  <div>
                      <div>{{pstr}}</div>
                      <div>{{12 + pnum}}</div>
                      <div>{{pboo}}</div>
                      <ul>
                          <li :key="index" v-for="(item,index) in parr">{{item}}</li>
                      </ul>
                      <div>
                          <span>{{pobj.name}}</span>
                          <span>{{pobj.age}}</span>
                          <span>{{pobj.sex}}</span>
                      </div>
                  </div>`
          });
          let vm = new Vue({
              el: '#app',
              data: {
                  name: "myVue",
                  pmsg: '父组件的内容',
                  pstr: 'hello',
                  parr:['苹果','香蕉','哈密瓜'],
                  pobj:{
                      name:'张三',
                      age:19,
                      sex:'男'
                  }
              },
              methods: {
      
              }
          });
      </script>
      
  2. 子组件向父组件传值

    • 子组件通过自定义事件向父组件传递消息
      <button v-on:click='$emit("enlarge-text")'>扩大字体</button>

    • 父组件监听子组件事件
      <menu-item v-on:enlarge-text='fontSize += 0.1'></menu-item>

      <div id="app">
          <h1>{{name}}</h1>
          <div :style="{fontSize: fontSize + 'px'}">{{pmsg}}</div>
          <menu-item @enlarge-text="handle"></menu-item>
      </div>
      <script>
      /* 
      子组件向父组件传递数据-基本用法
          props传递数据原则:单项数据绑定
      */
      Vue.component('menu-item', {
          template:`
              <div>
                  <button @click="$emit('enlarge-text')">扩大字体</button>
              </div>`
      });
      let vm = new Vue({
          el: '#app',
          data: {
              name: "myVue",
              pmsg: '父组件的内容',
              fontSize: 10
          },
          methods: {
              handle:function() {
                  // 扩大字体大小
                  this.fontSize+=10;
              }
          }
      });
      </script>
      
    • 子组件通过自定义事件向父组件传递信息
      <button v-on:click='$emit("enlarge-text", 0.1)'>扩大字体</button>

    • 父组件监听子组件事件
      <menu-item v-on:enlarge-text='fontSize += $event'></menu-item>

      <div id="app">
          <h1>{{name}}</h1>
          <div :style="{fontSize: fontSize + 'px'}">{{pmsg}}</div>
          <!-- 不带引号为字符串类型 -->
          <menu-item @enlarge-text="handle($event)"></menu-item>
      </div>
      <script>
          /* 
          子组件向父组件传递数据-基本用法
              props传递数据原则:单项数据绑定
          */
          Vue.component('menu-item', {
              template:`
                  <div>
                      <button @click="$emit('enlarge-text',5)">扩大字体</button>
                      <button @click="$emit('enlarge-text',-5)">减小字体</button>
                  </div>`
          });
          let vm = new Vue({
              el: '#app',
              data: {
                  name: "myVue",
                  pmsg: '父组件的内容',
                  fontSize: 10
              },
              methods: {
                  handle:function(val) {
                      // 扩大字体大小
                      this.fontSize+=val;
                  }
              }
          });
      </script>
      
  3. 非父子组件间传值(兄弟间组件传值)

    • 单独的事件中心管理组件间的通信 var eventHub = new Vue()

    • 监听事件与销毁事件

      eventHub.$on('add-todo', addTab)
      eventHub.off('add-todo')
      
    • 触发事件eventHub.$emit('add-todo', id)

    <div id="app">
        <h1>{{name}}</h1>
        <div>
            <button @click="handle">销毁事件</button>
        </div>
        <text-tom></text-tom>
        <text-jerry></text-jerry>
    </div>
    <script type="text/javascript">
        // 兄弟组件间数据传递
        // 提供事件中心
        var hub = new Vue();
        Vue.component('text-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', 2)
                }
            },
            mounted: function () {
                // 监听事件
                hub.$on('tom-event', (val) => {
                    this.num += val;
                })
            }
        });
        Vue.component('text-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', 1)
                }
            },
            mounted: function () {
                // 监听事件
                hub.$on('jerry-event', (val) => {
                    this.num += val;
                })
            }
        });
        let vm = new Vue({
            el: '#app',
            data: {
                name: "myVue"
            },
            methods: {
                handle: function() {
                    hub.$off('tom-event');
                    hub.$off('jerry-event');
                }
            }
        });
    </script>
    

组件插槽

  • 父组件向子组件传递内容

插槽基本用法

  1. 插槽位置

    <div id="app">
        <h1>{{name}}</h1>
        <alert-box>有bug!</alert-box>
        <alert-box>有一个警告!</alert-box>
        <alert-box></alert-box>
    </div>
    <script>
        Vue.component('alert-box', {
            template: `
                <div class="demo-alert-box">
                    <strong>Error!</strong>
                    <slot></slot>
                </div>`
        })
        let vm = new Vue({
            el: '#app',
            data: {
                name: "myVue"
            },
            methods: {
    
            }
        });
    </script>
    
  2. 具名插槽用法

    <!-- 插槽定义 -->
    <div class="container">
        <header>
            <slot name="header"></slot>
        </header>
        <main>
            <slot></slot>
        </main>
        <footer>
            <slot name="footer"></slot>
        </footer>
    </div>
    <!-- 插槽内容 -->
    <base-layout>
        <h1 slot="header">标题内容</h1>
    
        <p>主要内容1</p>
        <p>主要内容2</p>
    
        <p slot="footer">底部内容</p>
    </base-layout>
    
    <div id="app">
        <h1>{{name}}</h1>
        <base-layout>
            <p slot="header">标题信息</p>
            <p>主要内容1</p>
            <p>主要内容2</p>
            <p slot="footer">底部信息</p>
        </base-layout>
        <base-layout>
            <template slot="header">
                <p slot="header">标题信息</p>
                <p slot="header">标题信息</p>
            </template>
            <p>主要内容1</p>
            <p>主要内容2</p>
            <template slot="footer">
                <p slot="header">尾部信息1</p>
                <p slot="header">尾部信息2</p>
            </template>
        </base-layout>
    </div>
    <script type="text/javascript">
        Vue.component('base-layout', {
            template: `
                <div>
                    <header>
                        <slot name="header"></slot>
                    </header>
                    <main>
                        <slot></slot>
                    </main>
                    <footer>
                        <slot name="footer"></slot>
                    </footer>
                </div>`
        });
        let vm = new Vue({
            el: '#app',
            data: {
                name: "myVue"
            },
            methods: {
    
            }
        });
    </script>
    
  3. 作用域插槽

  • 应用场景:父组件对子组件的内容进行加工处理

    <!-- 插槽定义 -->
    <ul>
        <li v-for="item in list" v-bind:key="item.id">
            <slot v-bind:item="item">
                {{item.name}}
            </slot>
        </li>
    </ul>
    <!-- 插槽内容 -->
    <fruit-list v-bind:list="list">
        <template slot-scope="slotProps">
            <strong v-if="slotProps.item.current">
                {{slotProps.item.text}}
            </strong>
        </template>
    </fruit-list>
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335