Vue组件化开发

组件注册

  • 组件
    • 组件 (Component) 是 Vue.js 最强大的功能之一
    • 组件可以扩展 HTML 元素,封装可重用的代码

全局组件

  • 全局组件注册语法
    • Vue.component('组件名称', { }) 第1个参数是标签名称,第2个参数是一个选项对象
    • 全局组件 注册后,任何vue实例都可以用
// 全局组件注册模板
Vue.component(组件名称, {
    data: 组件数据,
    template: 组件模板内容,
    methods: {
        方法名: function(){
            this.组件数据
        }
    }
})
// 定义一个名为button-counter的新组件
Vue.component('button-counter', {
    data: function(){
        return {
            count: 0
        }
    },
    template: '<button v-on:click="count++">点击了{{ count }}次.</button>'
})
  • 组件注意事项
    • 组件参数的data值必须是函数同时这个函数要求返回一个对象
      • 分析函数与普通对象的对比
    • 组件模板内容必须是单个根对象
      • 具体查看特殊写法
    • 组件模板内容可以是模板字符串
      • 模板字符串需要浏览器提供支持(ES6语法)
    • 组件命名方式
      • 短横线方式
        • Vue.component('my-component',{/*...*/})
      • 驼峰方式
        • Vue.component('MyComponent',{/*...*/})
        • 如果使用驼峰方式,在使用组件时,组件调用时仍然需要短横线调用

``html

```
<!-- 模板字符串 -->
<section id='app'>
    <button-counter></button-counter>
    <button-counter></button-counter>
</section>
<script>
    Vue.component('button-counter', {
        data: function(){
            return {
               count: 0
            }
        },
        template: `
            <div>
                <button v-on:click="handle">点击了{{ count }}次.</button>
                <button>测试</button>
            </div>
        `,
        methods: {
            handle: function(){
                this.count+= 2;
            }
        }
    });
    var vm= new Vue({
        el: '#app',
        data: {
            
        }
    });
</script>
<!-- 驼峰式命名的调用 -->
<section id='app'>
    <button-counter></button-counter>
    <button-counter></button-counter>
    <hello-world></hello-world>
</section>
<script>
    Vue.component('HelloWorld', {
      // 1、组件参数的data值必须是函数 
      // 同时这个函数要求返回一个对象
        data: function(){
            return {
                msg: 'hello'
            }
        },
        template: '<div>{{ msg }}</div>'
    });
    Vue.component('button-counter', {
        data: function(){
            return {
               count: 0
            }
        },
      //  2、组件模板必须是单个根元素
      //  3、组件模板的内容可以是模板字符串 
        template: `
            <div>
                <button v-on:click="handle">点击了{{ count }}次.</button>
                <button>测试</button>
            </div>
        `,
        methods: {
            handle: function(){
                this.count+= 2;
            }
        }
    });
    var vm= new Vue({
        el: '#app',
        data: {
            
        }
    });
</script>

局部组件

  • 局部注册
    • 只能在当前注册它的vue实例中使用
<section id="app">
    <hello-world></hello-world>
    <hello-tom></hello-tom>
    <hello-jerry></hello-jerry>
    <test-com></test-com>
</section>
<script>
    /*
      局部组件注册
      局部组件只能在注册他的父组件中使用
    */
    Vue.component('test-com',{
        // 全局注册
      template: '<div>Test<hello-world></hello-world></div>'
    });
    var HelloWorld = {
        // 局部组件的模板定义
      data: function(){
        return {
          msg: 'HelloWorld'
        }
      },
      template: '<div>{{msg}}</div>'
    };
    var HelloTom = {
      data: function(){
        return {
          msg: 'HelloTom'
        }
      },
      template: '<div>{{msg}}</div>'
    };
    var HelloJerry = {
      data: function(){
        return {
          msg: 'HelloJerry'
        }
      },
      template: '<div>{{msg}}</div>'
    };
    var vm = new Vue({
      el: '#app',
      data: {
        
      },
      components: {
          // 局部组件注册的定义
        'hello-world': HelloWorld,
        'hello-tom': HelloTom,
        'hello-jerry': HelloJerry
      }
    });
</script>

Vue组件之间传值

父组件向子组件传值

  • 父组件向子组件传值

    • 父组件发送的形式是以属性的形式绑定值到子组件身上
    • 然后子组件用属性props接收
    • props中使用驼峰形式,
    • 模板中需要使用短横线的形式
    • 字符串形式的模板中没有这个限制
  • 父组件向子组件传值-基本使用

    • 组件中props数组内为模板解析标签内的属性名
    • 若没有v-bind绑定,传递的数据为直接数据,数据类型为字符串
    • 若有v-bind绑定,传递的数据为顶层组件中的数据。数据类型为基本类型
<section id="app">
  <div>{{pmsg}}</div>
    <!--1、menu-item  在 APP中嵌套着 故 menu-item   为  子组件      -->
    <!-- 给子组件传入一个静态的值 -->
  <menu-item title='来自父组件的值'></menu-item>
  <!-- 2、 需要动态的数据的时候 需要属性绑定的形式设置 此时 ptitle  来自父组件data 中的数据 . 
    传的值可以是数字、对象、数组等等
-->
  <menu-item :title='ptitle' content='hello'></menu-item>
</section>
<script type="text/javascript">
  Vue.component('menu-item', {
    // 3、 子组件用属性props接收父组件传递过来的数据  
    props: ['title', 'content'],
    data: function() {
      return {
        msg: '子组件本身的数据'
      }
    },
    template:  `<div>
                {{ msg }}<br>
                {{ title }}<br>
                {{ content }}
            </div>`
  });
  var vm = new Vue({
    el: '#app',
    data: {
      pmsg: '父组件中内容',
      ptitle: '动态绑定属性'
    }
  });
</script>
  • 父组件向子组件传值-命名规则
    • HTML大小写敏感,所以HTML中的命名只能是横线式的
    • 在props中使用驼峰形式,模板中需要使用短横线的形式
    • 传值规则:顶层组件的数据向次级组件的props属性传值,然后在解析的HTML页面中进行数据绑定,以期完成数据传递
      • Javascript中驼峰式:props: ['menuTitle']
      • HTML中短横线:<menu-item v-bind:menu-title='ptitle'></menu-item>
<section id="app">
    <div>{{ pmsg }}</div>
    <menu-item v-bind:menu-title='ptitle'></menu-item>
</section>
<script type="text/javascript">
    /*  父组件向子组件传值-props属性名规则  */
    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>`
    });
    var vm= new Vue({
        el: '#app',
        data: {
            pmsg: '父组件中的内容',
            ptitle: '父组件动态绑定属性'
        },
    });
</script>
  • 父组件向子组件传值-传值类型
    • 字符串 String
    • 数值 Number
    • 布尔值 Boolean
    • 数组 Array
    • 对象 Object
<section id="app">
    <div>{{ pmsg }}</div>
    <menu-item :pstr='pstr' :pnum='12' pboo='true' :parr='parr' :pobj='pobj'></menu-item>
</section>
<script type="text/javascript">
    /*  父组件向子组件传值-传值类型  */
    Vue.component('menu-item', {
      props: ['pstr','pnum','pboo','parr','pobj'],
      template: `
        <div>
          <div>{{pstr}}</div>
          <div>{{12 + pnum}}</div>
          <div>{{typeof pboo}}</div>
          <ul>
            <li :key='index' v-for='(item,index) in parr'>{{item}}</li>
          </ul>
            <span>{{pobj.name}}</span>
            <span>{{pobj.age}}</span>
          </div>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        pmsg: '父组件中内容',
        pstr: 'hello',
        parr: ['apple','orange','banana'],
        pobj: {
          name: 'lisi',
          age: 12
        }
      }
    });
</script>

子组件向父组件传值

  • 子组件向父组件传值
    • 子组件可以使用$emit触发父组件的自定义事件
    • vm.$emit( event, arg ) 第一个参数为 自定义的事件名称,第二个参数为需要传递的数据,触发当前实例上的事件
    • vm.$on( event, fn ) 父组件用v-on监听子组件的事件
    • props传递数据原则:单向数据流
<section id="app">
    <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
    <menu-item :parr='parr' @enlarge-text='handle'></menu-item>
</section>
<script type="text/javascript">
    /*  子组件向父组件传值-基本用法  */
    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>
          <button @click='$emit("enlarge-text, 10")'>扩大父组件中字体大小</button>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        pmsg: '父组件中内容',
        parr: ['apple','orange','banana'],
        fontSize: 10
      },
      methods: {
        handle: function(){
          // 扩大字体大小
          this.fontSize += 5;
        }
      }
    });
</script>

兄弟组件之间数据传递

  • 兄弟组件之间数据传递
    • 兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据
      • 提供事件中心var hub = new Vue()
    • 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
    • 接收数据方,通过mounted(){}钩子中,触发hub.$on()方法名
    • 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
<section id="app">
    <div>父组件</div>
    <div>
      <button @click='handle'>销毁事件</button>
    </div>
    <test-tom></test-tom>
    <test-jerry></test-jerry>
</section>
<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', 2);
            }
        },
        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', 1);
            }
        },
        mounted: function() {
            // 监听事件
            hub.$on('jerry-event', (val) => {
                this.num += val;
            });
        }
        });
        var vm = new Vue({
        el: '#app',
        data: {
            
        },
        methods: {
            handle: function(){
                hub.$off('tom-event');
                hub.$off('jerry-event');
            }
        }
    });
</script>

组件插槽

  • 组件插槽

    • 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
  • 匿名插槽

    • 当组件渲染的时候,这个 <slot> 元素将会被替换为“组件标签中嵌套的内容”。
    • 插槽内可以包含任何模板代码,包括 HTML
<section id="app">
  <!-- 这里的所有组件标签中嵌套的内容会替换掉slot  如果不传值 则使用 slot 中的默认值  --> 
    <alert-box>有bug发生</alert-box>
    <alert-box>有一个警告</alert-box>
    <alert-box></alert-box>
</section>
<script type="text/javascript">
    /*  组件插槽:父组件向子组件传递内容  */
    Vue.component('alert-box', {
      template: `
        <div>
          <strong>ERROR:</strong>
          
          <slot>默认内容</slot>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        
      }
    });
</script>
  • 具名插槽
    • 具有名字的插槽
    • 使用 <slot> 中的 "name" 属性绑定元素
    • 具名插槽的渲染顺序,完全取决于模板,而不是取决于父组件中元素的顺序
<section id="app">
  <base-layout>
  <p slot='header'>标题信息</p>
  <p>主要内容1</p>
  <p>主要内容2</p>
  <p slot='footer'>底部信息信息</p>
</base-layout>
<base-layout>
  <template slot='header'>
    <p>标题信息1</p>
    <p>标题信息2</p>
  </template>
  <p>主要内容1</p>
  <p>主要内容2</p>
  <template slot='footer'>
    <p>底部信息信息1</p>
    <p>底部信息信息2</p>
  </template>
</base-layout>
</section>
<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>
    `
  });
  var vm = new Vue({
    el: '#app',
    data: {
      
    }
  });
</script>
  • 作用域插槽

    • 父组件对子组件加工处理
    • 既可以复用子组件的slot,又可以使slot内容不一致
  • 作用域插槽的应用

    • 当我们希望li 的样式由外部使用组件的地方定义,因为可能有多种地方要使用该组件, 但样式希望不一样 这个时候我们需要使用作用域插槽
    • 父组件中使用了<template>元素,而且包含scope="slotProps",slotProps在这里只是临时变量
    • 在子组件模板中,<slot>元素上有一个类似props传递数据给组件的写法msg="xxx"
    • 插槽可以提供一个默认内容,如果如果父组件没有为这个插槽提供了内容,会显示默认的内容。
    • 如果父组件为这个插槽提供了内容,则默认的内容会被替换掉
<section id="app">
  <fruit-list :list='list'>
    <template slot-scope='slotProps'>
      <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
      <span v-else>{{slotProps.info.name}}</span>
    </template>
  </fruit-list>
</section>
<script type="text/javascript">
  /*  作用域插槽  */
  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'
        }]
      }
    });
</script>
原文地址:https://www.cnblogs.com/SharkJiao/p/13624705.html