一、组件基础

1、组件介绍

组件:是一个独立显示的视图单元,在构建大型应用时,我们通常会把可复用性强的部分。
. (例如:头部导航、侧边栏等需要在多个页面展示的部分)提取出来,最终组合成一个完整的页面。
. 在vue中,组件是可复用的vue实例。在这里插入图片描述

2、组件的定义

①组件的注册:
  • 全局组件:注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中,全局组件必须写在Vue实例创建之前。
    :::warning
    全局组件缺点:组件注册之后无法被回收,一直保存在内存中。
    :::

  • 局部组件:使用对象定义组件,在vue实例对象上使用components属性声明本模块的局部组件,在components对象中,属性名就是自定义组件名,属性值就是该组件的选项对象。

②组件的引用
  • 引用方式类似于标签,用尖括号包裹组件名
③组件的复用
  • 每个组件都可以任意次数的复用
  • 每个组件都是独立的,它们都有自己的实例,每个组件只能有一个根元素
④组件引用规则
  • 由于HTML对于大小写是不敏感的,我们在DOM模板中需要使用kebab-case写法
  • 在单文件组件、字符串模板和 JSX 中,组件引用时需要使用PascalCase写法
⑤组件的闭合
  • 在 DOM 模板中,建议使用标签闭合组件
  • 在单文件组件、字符串模板和 JSX 中,建议使用自闭和组件:
<body>
<div id="app">
    <my-com></my-com>
    <one></one>
    <two></two>
</div>
<script>
    //注册全局组件,new Vue之前,一旦注册,一直占用内存
    //优点:调用速度快;缺点:占用内存
    //命名不区分大小写,用-连接命名
    Vue.component("my-com",{
        template : "<div>我是全局组件</div>"
    })
    //注册局部组件
    let com1={
        template : "<div>我是局部组件1</div>"
    }
    let com2={
        template : "<div>我是局部组件2</div>"
    }
    new Vue({
        el : "#app",
        data : {
            text : ""
        },
        //挂载到vue实例的components上
        components:{
            'one':com1,
            'two':com2
        }

    });
</script>
</body>

3、组件与实例的共性分析

Vue的实例与组件的关系:通过new Vue创建的Vue的根实例,SPA应用中,只会创建一个Vue根实例,组件都是通过这个根实例启动的
在这里插入图片描述

4、组件中的data属性

在组件中data属性与实例的data属性声明有所不同,vue实例中的data可以是一个对象,但组件中的data必须是一个返回值为object对象的函数

<body>
<div id="app">
    <my-com></my-com>
    <one></one>
    <two></two>
</div>
<script>
    //注册全局组件,new Vue之前,一旦注册,一直占用内存
    //优点:调用速度快;缺点:占用内存
    //命名不区分大小写,用-连接命名
    Vue.component("my-com",{
        template : "<div>我是全局组件</div>"
    })
    //注册局部组件
    let com1={
        template : "<div>我是局部组件1{{msg}}</div>",
        //组件没有el属性
        //组件的data属性必须是一个函数,而且必有有返回值,返回值是对象
        data:function() {
            return {
                msg : "!"
            }
        }
    }
    let com2={
        template : "<div>我是局部组件2</div>"
    }
    new Vue({
        el : "#app",
        data : {
            text : ""
        },
        //挂载到vue实例的components上
        components:{
            'one':com1,
            'two':com2
        }

    });
</script>
</body>

5、组件的生命周期在这里插入图片描述

在这里插入图片描述

二、组件的特性

1、prop属性

①操作过程单向数据流

单向数据流的过程是:用户访问view,view发出用户交互的Action,在Action里对state进行更新。state更新后,会触发View更新页面。这样的数据总是单向流转,便于维护
在这里插入图片描述

传递数据是单向的,数据总是由父组件传递到子组件。子组件在内部维护着自己的状态,它无权修改父组件传递给它的数据。如果我们在子组件修改props,vue将会报出警告。这样更有利于组件间的解耦。
在这里插入图片描述

②单向数据流的意义

在这里插入图片描述

如果一个子组件修改了父组件的数据,那么该父组件下的其它子组件的数据也会发生改变。

③props 属性

组件中的特殊属性,用于接收来自父组件的数据。可以以数组或对象的方式声明。
对象类型声明方式:
当无需对属性进行更多限定时可以采用数组类型定义多个属性。
数组类型声明方式:
props中声明的属性是全局属性,可以直接在模板、method、计算属性中使用this访问,使用方法与data类似。

④父组件props 属性传递

单向数据流:父组件prop的更新会向下流动到子组件,子组件中所有的prop都会更新,使得子组件同步更新。但子组件不要更新父组件数据。
prop的命名规则:在定义prop时可以使用camelCase和kebab-case两种方式。但在模板中引入组件时,由于html是忽略大小写的,因此只能使用kebab-case方式引入。
:::warning
父组件props 属性传递的注意事项:
许多场景下props数据属性并非只读属性,需要修改:
设定props的观察属性,接收父组件传递参数的克隆对象
父组件传递的参数变化后,子组件props属性自动更新,但子组件并不知道更新的事件点
设定props的观察属性,监控数据变化,从而实现局部刷新
:::

<body>
<div id="app">
<parent></parent>
</div>
<script>
    let child={
        //接收父组件的参数
        props:['msg'],
        template : `<div>我是子组件,{{msg}}</div>`,
    }
    let parent={
        //给子组件传递参数
        template : `<div>我是父组件<child v-bind:msg="msg"></child></div>`,
        data() {// =data:function(){
            return{
                msg : '来自父组件的问候'
            }
        },
        components : {
            'child':child
        }
    }

    new Vue({
        el : "#app",
        data : {
        },
        components : {
            'parent':parent
        }
    });
</script>
</body>
<body>
  <div id="app">
  <parent></parent>
  </div>
  <script>
  let child = {
  //接收父组件的参数
  props: ['stu'],
  template: `
          <div><h2>个人信息</h2>
          <div style="border: 1px solid black;width: 200px">
<!--            可以绑定对象的属性-->
            <p>姓名:<input v-model="user.name"></p>
            <p>性别:<input v-model="stu.sex"></p>
            <p>年龄:{{ stu.age }}</p>
            <p>地址:{{ stu.address }}</p>
          </div>
          </div>`,
  data (){
    return {
      //初始化user对象
      user : {}
    }
  },
  watch:{
    stu:function (val) {
      // 对象克隆,把val对象里的属性和方法都复制到user对象里,新对象内存地址和val不一样,达到解绑的目的
      this.user = Object.assign({},val);
    },
  }
}
let parent = {
  //给子组件传递参数
  template: `
          <div><h2>信息表</h2>
          <table>
            <tr>
              <th>姓名</th>
              <th>性别</th>
              <th>年龄</th>
              <th>地址</th>
              <th>操作</th>
            </tr>
            <tr v-for="stu in dataList">
              <td>{{ stu.name }}</td>
              <td>{{ stu.sex }}</td>
              <td>{{ stu.age }}</td>
              <td>{{ stu.address }}</td>
              <td>
<!--                将stu对象传入edit方法-->
                <button @click="edit(stu)">修改</button>
              </td>
            </tr>
          </table>
<!--          子组件默认不显示,点击编辑后更改show的值后显示,接收的stu对象来自data-->
          <child v-show="show" v-bind:stu="stu"></child>
          </div>`,
  data() {// =data:function(){
    return {
      dataList: [{name: '李雷', sex: '男', age: 20, address: '宁波'}, {name: '韩梅梅', sex: '女', age: 18, address: '长春'}],
      show: false,
      stu: ''
    }
  },
  methods: {
    edit(stu) {
      //子对象显示
      this.show = true;
      //将传入的stu对象赋值给data中的stu
      this.stu = stu;
    }
  },
  components: {
    'child': child
  }
}

new Vue({
  el: "#app",
  data: {},
  components: {
    'parent': parent
  }
});
</script>
  </body

2、动态组件

父组件对应的子组件不确定,需要通过捕获用户的操作决定动态加载组件
component组件:vue内置抽象组件,代表为具体命名的组件

<body>
<div id="app">
    <parent></parent>
</div>
<script>
    let child1={
        props:['msg'],
        template : `<div>我是子组件1</div>`,
    }
    let child2={
        props:['msg'],
        template : `<div>我是子组件2</div>`,
    }
    let parent={
        template : `<div>我是父组件
        <button @click="show(1)">组件1</button><button @click="show(2)">组件2</button>
<!--        :is后是一个变量,变量的值是组件的名字-->
        <component :is='currentView'></component>
        </div>`,
        data() {// =data:function(){
            return{
                msg : '来自父组件的问候',
                currentView: 'child2'
            }
        },
        methods:{
            show(i){
                this.currentView='child'+i;
            },
        },
        components : {
            'child1':child1,
            'child2':child2
        }
    }
    new Vue({
        el : "#app",
        data : {
        },
        components : {
            'parent':parent
        }
    });
</script>
</body>

3、递归组件

组件在自己的模板中调用自己渲染,它们必须有name属性,在组件中通过name属性调用本组件标签

<body>
<div id="app">
    <my-com></my-com>
</div>
<script>
    let myCom={
        //递归组件的模板中必须包含自己
        //递归组件必须有结束条件
        template : `<div><button @click="getChild">递归组件</button><com v-if="show"></com></div>`,
        //必须给组件起名字,必须有name属性
        name : 'com',
        data(){
            return{
                show : false,
            }
        },
        methods:{
            getChild(){
                this.show=!this.show;
            }
        }
    }
    new Vue({
        el : "#app",
        data : {},
        components : {
            'my-com':myCom
        }
    });
</script>
</body>

4、异步组件

vue允许以工厂函数的方式定义组件,这个工厂函数会异步解析组件定义,只有在组件需要渲染的时候才会触发该工厂函数,且把结果缓存起来供未来重新渲染。

<body>
<div id="app">
    <my-com></my-com>
</div>
<script>
    //异步组件第一个的参数是一个函数,函数的返回值是一个组件对象
    //第二个参数是一个对象,对象中的属性是组件的一些配置
    Vue.component('my-com',function (resolve,reject) {
        //异步组件的特点:只有在使用的时候才会加载
        setTimeout(function () {
            resolve({
                template : `<div>我是异步组件</div>`
            })
        },3000)
    })
    new Vue({
        el : "#app",
        data : {},

    });
</script>
</body>

5、组件api接口

vue组件的api接口:通过this指针调用(Vue实例对象也具备)
数据属性类:
在这里插入图片描述

DOM属性类:
在这里插入图片描述

数据操作类:
在这里插入图片描述

<body>
<div id="app">
    <div ref="nb">宁波</div>
    <button @click="getRef">获取</button>
    <br><input v-model="msg">
    <button @click="getMsg">获取</button>{{abc}}
</div>
<script>
    new Vue({
        el : "#app",
        data : {
            msg:'',
        },
        methods:{
            getMsg(){
                //获取input的值
                //console.info(this.$data.msg);
                //js写法不会双向绑定
                // this.abc='ok';
                // console.info(this.abc);
                this.$set(this,'abc','ok');
            },
            getRef(){
                //获取dom元素,必须有ref属性,否则获取不到
                console.info(this.$refs.nb)
            }
        }
    })
</script>
</body>

三、组件通信

1、发布订阅者模式

①父子通信信息流:

在这里插入图片描述

②发布订阅者模式:

用于定义发布者对象与订阅者对象的一对一或一对多的关系,当发布者对象状态发生改变时,所有依赖于它的订阅者对象都会得到通知。
. 1.支持广播通信
. 2.发布者和订阅者低耦合

③发布订阅者模式实现机制

在这里插入图片描述

2、父子通信

父组件向子组件传递数据:在引用子组件时,通过v-bind绑定自定义属性,通过props传递给子组件,子组件中通过props接收。

  • 传递数据通过props属性
  • 数据变更通过子组件的观察属性监控

3、子父通信

子组件向父组件传递数据:

  • 子组件通过$emit触发自定义事件,第一个参数为事件名,第二个参数为要传递的参数。
  • 父组件监听子组件的事件,通过参数取到。
<body>
<div id="app">
<parent></parent>
</div>
<script>
    let child1={
        template:`<div>
            <h2>子组件1</h2>
            <button @click="send">发送</button>
        </div>`,
        methods:{
            send(){
                // 子组件通过$emit触发自定义事件,第一个参数为事件名,第二个参数为要传递的参数。
                this.$emit('sendMsg','我是子组件1');
            }
        }
    }
    let child2={
        template:`<div>
            <h2>子组件2</h2>
            <button @click="send">发送</button>
        </div>`,
        methods:{
            send(){
                //发布消息
                this.$emit('sendMsg','我是子组件2');
            }
        }
    }
    let parent={
        template:`<div>
            <h2>父组件</h2>
            <child1 @sendMsg="getMsg"></child1>
            <child2 @sendMsg="getMsg"></child2>
        </div>`,
        methods:{
            //父组件监听子组件的事件,通过参数取到。
            getMsg(msg){
                console.info(msg);
            }
        },
        components:{
            'child1':child1,
            'child2':child2
        }
    }
    new Vue({
        el : "#app",
        data : {
        },
        components : {
            'parent':parent
        }
    })
</script>
</body>

4、非父子通信

方案一:

创建一个Vue实例作为中央事件总线,通过它来监听( o n ) 和触发 ( on)和触发( on)和触发(emit)事件。适用于组件间全部通信方式。

<body>
<div id="app">
<parent></parent>
</div>
<script>
    let bus=new Vue();//bus当成事件总线对象
    let child1={
        template:`<div>
            <h2>子组件1</h2>
            <button @click="send">发送</button>
        </div>`,
        methods:{
            send(){
                // 子组件通过$emit触发自定义事件,第一个参数为事件名,第二个参数为要传递的参数。
                bus.$emit('sendMsg','我是子组件1');
            }
        }
    }
    let child2={
        template:`<div>
            <h2>子组件2</h2>
        </div>`,
        methods:{
        },
        mounted(){
            //监听子组件1的事件,通过参数取到。
            bus.$on('sendMsg',function(msg){
                console.info(msg);
            })
        }
    }
    let parent={
        template:`<div>
            <h2>父组件</h2>
            <child1 ></child1>
            <child2 ></child2>
        </div>`,
        components:{
            'child1':child1,
            'child2':child2
        }
    }
    new Vue({
        el : "#app",
        data : {
        },
        components : {
            'parent':parent
        }
    })
</script>
</body>
方案二:

• 1.子组件1先把数据传递给父组件,
• 2.父组件接受到数据后动态绑定给子组件2
• 3.子组件2通过props接受

方案三:

使用状态管理工具vuex

四、内置组件

1、component组件

component:渲染一个“元组件”为动态组件,根据is属性的值,来决定哪个组件被渲染
使用场景:tab切换

<body>
<div id="app">
    <parent></parent>
</div>
<script>
    let child1={
        props:['msg'],
        template : `<div>我是子组件1</div>`,
    }
    let child2={
        props:['msg'],
        template : `<div>我是子组件2</div>`,
    }
    let parent={
        template : `<div>我是父组件
        <ul>
          <li><button @click="show(1)">组件1</button></li>
          <li><button @click="show(2)">组件2</button></li>
        </ul>
<!--        :is后是一个变量,变量的值是组件的名字-->
        <component :is='currentView'></component>
        </div>`,
        data() {// =data:function(){
            return{
                msg : '来自父组件的问候',
                currentView: 'child2'
            }
        },
        methods:{
            show(i){
                this.currentView='child'+i;
            },
        },
        components : {
            'child1':child1,
            'child2':child2
        }
    }
    new Vue({
        el : "#app",
        data : {
        },
        components : {
            'parent':parent
        }
    });
</script>
</body>

:::warning
适用场景:需要多次访问、多次离开的组件
:::

2、keep-alive组件

keep-alive组件:会缓存不活动的组件实例,而不是销毁它们,这样当我们再次访问不活动的组件时,不需要请求数据渲染页面,大大地提高了性能。

<keep-alive include="['Home','view']">
  <component :is="view"></component>
</keep-alive>
<keep-alive>
	<Big-Num v-if="this.count > -1">非负数</Big-Num>
	<Small-Num v-else>负数</Small-Num>
</keep-alive>

keep-alive组件的模糊匹配:可以通过表达式设定那些组件被缓存。通过如下属性设定
. include 只有名称匹配的组件会被缓存,传入字符串或正则表达式或数组
. exclude 不会被缓存的组件,传入字符串或正则表达式或数组
. max 最多可以缓存的组件实例个数,传入数字类型

<keep-alive include="a,b"></keep-alive>
<keep-alive :exclude="/a|b/"></keep-alive>
<keep-alive :max=5></keep-alive>

keep-alive缓存的组件的特定生命周期
• activated:切换至该组件时触发
• deactivated:从该组件切换出去时触发
:::warning
适用场景:需要多次访问、多次离开的组件
:::

3、solt组件

slot插槽适用于结构相同、内容存在差异的场景

<body>
<div id="app">
    <parent></parent>
</div>
<script>
    let child={
        template : `<div>子组件<br>
                    <slot></slot><br>
                    <slot name='one'></slot><br>
                    <slot name='two'></slot><br>
                    <slot name='three' :msg="msg"></slot><br>
                    </div>`,
        data(){
            return {
                msg:'我是子组件的数据'
            }
        }
        }
    let parent={
        template:`<div>
            <h2>父组件</h2>
            <child>
<!--          以下写法一样,默认插槽,不会覆盖-->
              <span style="color: red">我是默认插槽1</span>
              <template><span style="color: red">我是默认插槽2</span></template>
<!--          以下写法一样,默认插槽,会覆盖-->
              <template v-slot:default><span style="color: red">我是默认插槽3</span></template>
              <template v-slot><span style="color: red">我是默认插槽4</span></template>
              <template #default><span style="color: red">我是默认插槽5</span></template>
<!--              以下是具名插槽-->
              <template #one><span style="color: red">我是具名插槽one</span></template>
              <template v-slot:two><span style="color: red">我是具名插槽two</span></template>
<!--              作用域插槽,通过prop获取子组件传出的属性-->
              <template v-slot:three="prop"><span style="color: red">我是具名插槽three,{{prop.msg}}</span></template>
            </child>
        </div>`,
        components:{
            'child':child
        }
    }
    new Vue({
        el : "#app",
        data : {
        },
        components : {
            'parent':parent
        }
    })
</script>
</body>
Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐