VUE基础 笔记叁章 组件化开发

VUE基础之组件化开发

1. 组件化开发思想

1.1 现实中的组件化思想体现

标准、分治、重用、组合。

1.2 编程中的组件化思想体现

1.3 组件化规范:Web Components

  • 我们希望尽可能多的重用代码。
  • 自定义组件的方式不太容易(html、css和js)。
  • 多次使用组件可能导致冲突。
  • Web Components通过创建封装好功能的定制元素解决上述问题。VUE部分实现了Web Components规范。
  • https://developer.mozilla.org/zh-CN/docs/Web/Web_Components

2. 组件注册

2.1 全局组件注册语法

组件模板内容语法与我们之前所学的基础内容语法是一样的。

      Vue.component(组件名称, {
        data: 组件数据,
        template: 组件模板内容
      });

2.2 组件用法

    <div id="app">
      <组件名称></组件名称>
    </div>

Vue实例也是组件,而新建的组件为子组件,将子组件放入Vue实例组件模板(也称为根组件模板)中,形成了父子级关系。

组件的事件处理方式与之前所学的基础语法是相似的。组件是可以重用的。组件重用之后,每一个组件中的数据都是独立的。

  <body>
    <div id="app">
      <button-counter></button-counter>
      <button-counter></button-counter>
      <button-counter></button-counter>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
      /*
      组件注册
    */
      Vue.component("button-counter", {
        data: function () {
          return {
            count: 0,
          };
        },
        template: '<button @click="handle">点击了{{count}}次</button>',
        methods: {
          handle: function () {
            this.count += 2;
          },
        },
      });
      var vm = new Vue({
        el: "#app",
        data: {},
      });
    </script>
  </body>

2.3 组件注册注意事项

  1. data必须是一个函数
  • 分析函数与普通对象的对比。
  • Vue实例中所传递的参数与在定义组件的时候,绝大多数是相通的,但data这个值是最大的一个不同,一个是对象,一个是函数。
  1. 组件模板内容必须是单个根元素
  2. 组件模板内容可以是是模板字符串
    模板字符串需要浏览器提供支持(ES6语法)
    Vue.component('button-counter', {
      data: function(){
        return {
          count: 0
        }
      },
      template: `
        <div>
          <button @click="handle">点击了{{count}}次</button>
          <button>测试123</button>
        </div>
      `,
      methods: {
        handle: function(){
          this.count += 2;
        }
      }
    })
  1. 组件命名方式
    如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是在普通的标签模板中,必须使用短横线的方式使用组件。
  • 短横线命名方式
    Vue.component = ('my-component', { /* ... */ });
  • 驼峰命名方式
    Vue.component = ('MyComponent', { /* ... */ });
<body>
  <div id="app">
    <button-counter></button-counter>
    <hello-world></hello-world>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      组件注册注意事项
      如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是
      在普通的标签模板中,必须使用短横线的方式使用组件
    */
    Vue.component('HelloWorld', {
      data: function(){
        return {
          msg: 'HelloWorld'
        }
      },
      template: '<div>{{msg}}</div>'
    });
    Vue.component('button-counter', {
      data: function(){
        return {
          count: 0
        }
      },
      template: `
        <div>
          <button @click="handle">点击了{{count}}次</button>
          <button>测试123</button>
          <HelloWorld></HelloWorld>
        </div>
      `,
      methods: {
        handle: function(){
          this.count += 2;
        }
      }
    })
    var vm = new Vue({
      el: '#app',
      data: {
        
      }
    });
  </script>
</body>

2.4 局部组件注册

局部组件只能在注册它的父组件中使用。自定义指令和自定义过滤器有相同的特性。

    var ComponentA = { /* ... */ };
    var ComponentB = { /* ... */ };
    var ComponentC= { /* ... */ };
    var vm = new Vue({
      el: '#app',
      components: {
        'component-a': ComponentA,
        'component-b': ComponentB,
        'component-c': ComponentC,
      }
    });
<body>
  <div id="app">
    <hello-world></hello-world>
    <hello-tom></hello-tom>
    <hello-jerry></hello-jerry>
    <test-com></test-com>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      局部组件注册
      局部组件只能在注册他的父组件中使用
    */
    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>
</body>

3. VUE调试工具

3.1 调试工具安装

3.2 调试工具使用

4. 组件间数据交互

4.1 父组件向子组件传值

通过属性绑定的方式传递值,通过子组件的方式获取对应的值。之后,父组件和子组件共享这个绑定的数据,子组件改变了数据,父组件相应地也会有所变动。

4.1.1 组件内部通过props接收传递过来的值
Vue.component(‘menu-item', {
   props: ['title'],
   template: '<div>{{ title }}</div>'
})
4.1.2 父组件通过属性将值传递给子组件
   <menu-item title="来自父组件的数据"></menu-item>
   <menu-item :title="title"></menu-item>
<body>
  <div id="app">
    <div>{{pmsg}}</div>
    <menu-item title='来自父组件的值'></menu-item>
    <menu-item :title='ptitle' content='hello'></menu-item>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      父组件向子组件传值-基本使用
    */
    Vue.component('menu-item', {
      props: ['title', 'content'],
      data: function() {
        return {
          msg: '子组件本身的数据'
        }
      },
      template: '<div>{{msg + "----" + title + "-----" + content}}</div>'
    });
    var vm = new Vue({
      el: '#app',
      data: {
        pmsg: '父组件中内容',
        ptitle: '动态绑定属性'
      }
    });
  </script>
</body>
4.1.3 props属性名规则
  • 在props中使用驼峰形式,模板中需要使用短横线的形式(html元素属性是不区分大小写的)。
  • 字符串形式的模板中没有这个限制。
    Vue.component(‘menu-item', {
    // 在 JavaScript 中是驼峰式的
    props: [‘menuTitle'],
    template: '<div>{{ menuTitle }}</div>'
})
    <!– 在html中是短横线方式的 -->
    <menu-item menu-title=“nihao"></menu-item>
<body>
  <div id="app">
    <div>{{pmsg}}</div>
    <menu-item :menu-title='ptitle'></menu-item>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      父组件向子组件传值-props属性名规则
    */
    Vue.component('third-com', {
      props: ['testTile'],
      template: '<div>{{testTile}}</div>'
    });
    Vue.component('menu-item', {
      props: ['menuTitle'],
      template: '<div>{{menuTitle}}<third-com testTile="hello"></third-com></div>'
    });
    var vm = new Vue({
      el: '#app',
      data: {
        pmsg: '父组件中内容',
        ptitle: '动态绑定属性'
      }
    });
  </script>
</body>
4.1.4 props属性值类型

props属性值类型有:字符串 String、数值 Number、布尔值 Boolean、数组 Array、对象 Object。

对于数值和布尔值类型的数据,通过v-bind方式进行绑定,在子组件中就可以得到对应类型的数据。反之不绑定,得到的都是字符串类型的数据。

<body>
  <div id="app">
    <div>{{pmsg}}</div>
    <menu-item :pstr='pstr' :pnum='12' pboo='true' :parr='parr' :pobj='pobj'></menu-item>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      父组件向子组件传值-props属性值类型
    */
    
    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>
</body>

4.2 子组件向父组件传值

  • props传递数据原则:单向数据流 。直接在子组件中改变父组件传过来的绑定值,会使得父组件的数据有所改变,这样不好控制。
  • 子组件首先触发一个自定义事件,父组件则监听这个事件继而触发相应事件。
4.2.1 子组件通过自定义事件向父组件传递信息

enlarge-text 为自定义事件。

    <button v-on:click='$emit("enlarge-text") '>扩大字体</button>
4.2.2 父组件监听子组件的事件
    <menu-item v-on:enlarge-text='fontSize += 0.1'></menu-item>
<body>
  <div id="app">
    <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
    <menu-item :parr='parr' @enlarge-text='handle'></menu-item>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      子组件向父组件传值-基本用法
      props传递数据原则:单向数据流
    */
    
    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>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        pmsg: '父组件中内容',
        parr: ['apple','orange','banana'],
        fontSize: 10
      },
      methods: {
        handle: function(){
          // 扩大字体大小
          this.fontSize += 5;
        }
      }
    });
  </script>
</body>
4.2.3 子组件通过自定义事件向父组件传递信息 - 携带参数

通过 $event 传递到父组件。

    <button v-on:click='$emit("enlarge-text", 0.1) '>扩大字体</button>
4.2.4 父组件监听子组件的事件 - 携带参数
    <menu-item v-on:enlarge-text='fontSize += $event'></menu-item>
<body>
  <div id="app">
    <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
    <menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <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", 5)'>扩大父组件中字体大小</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(val){
          // 扩大字体大小
          this.fontSize += val;
        }
      }
    });
  </script>
</body>

4.3 非父子组件间传值

在这里插入图片描述

4.3.1 单独的事件中心管理组件间的通信
    var eventHub = new Vue();
4.3.2 监听事件与销毁事件
    eventHub.$on('add-todo', addTodo)
    eventHub.$off('add-todo')
4.3.3 触发事件
    eventHub.$emit('add-todo', id)
<body>
  <div id="app">
    <div>父组件</div>
    <div>
      <button @click='handle'>销毁事件</button>
    </div>
    <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', 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>
</body>

5. 组件插槽

5.1 组件插槽的作用

  • 父组件向子组件传递内容。父组件向子组件传递模板内容,此时组件模板中的 slot 标签此时如一个占位符。
    在这里插入图片描述

5.2 组件插槽基本用法

5.2.1 插槽位置
    Vue.component('alert-box', {
     template: `
       <div class="demo-alert-box">
       <strong>Error!</strong>
       <slot></slot>
       </div>
    `
})
5.2.2 插槽内容
    <alert-box>Something bad happened.</alert-box>
<body>
  <div id="app">
    <alert-box>有bug发生</alert-box>
    <alert-box>有一个警告</alert-box>
    <alert-box></alert-box>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      组件插槽:父组件向子组件传递内容
    */
    Vue.component('alert-box', {
      template: `
        <div>
          <strong>ERROR:</strong>
          <slot>默认内容</slot>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        
      }
    });
  </script>
</body>

5.3 具名插槽用法

5.3.1 插槽定义
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
5.3.2 插槽内容
  <base-layout>
    <h1 slot="header">标题内容</h1>
    <p>主要内容1</p>
    <p>主要内容2</p>
    <p slot="footer">底部内容</p>
  </base-layout>

<template>用于临时包裹一下要传递给模板占位符的内容,最终并不会渲染到页面中。把插槽的名字标注到同类标签上的话只能标注一个,如果想让多个同类标签一起替换slot占位符,这时候就需要template了。

<body>
  <div 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>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <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>
</body>

5.4 作用域插槽

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

父组件中的<template>``</template>中的内容决定了组件间中 slot 标签应该如何显示内容。父组件可以通过slot-scope最终获取子组件的数据,也就是获取到子组件中 slot 标签相应属性绑定值。

5.4.1 插槽定义
  <ul>
    <li v-for= "item in list" v-bind:key= "item.id" >
      <slot v-bind:item="item">
        {{item.name}}
      </slot>
   </li>
  </ul>
5.4.2 插槽内容
   <fruit-list v-bind:list= "list">
     <template slot-scope="slotProps">
       <strong v-if="slotProps.item.current">
         {{ slotProps.item.text }}
       </strong>
     </template>
  </fruit-list>
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<style type="text/css">
  .current {
    color: orange;
  }
</style>
<body>
  <div 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>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <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>
</body>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值