2.vue组件化开发

组件的定义:实现应用中局部功能代码资源的集合。

(一) 组件化基础

[1]. 全局组件和局部组件

注意点:

  1. 关于组件名:
    一个单词组成:
        第一种写法(首字母小写):school
        第二种写法(首字母大写):School
    多个单词组成:
        第一种写法(kebab-case命名):my-school
        第二种写法(CameCase命名):MySchool(需要脚手架支持)
  2. 关于组件标签:
    第一种写法:<school><school/>
    第二种写法:<school /> (需要使用脚手架,否则导致后续组件不能渲染)
  3. 一个简写
    const school = Vue.extend(options) 可简写为:const school = options;
  4. 组件中的this
    每次调用Vue.extend,返回的都是一个全新的VueComponent。
    组件配置中,data函数、methods中的函数、watch中的函数、computed中的函数,他们中使用的this均是【VueComponent实例对象】
  1. 全局组件
    全局组件可以在任意Vue实例下使用

    Vue.component('my-component-name', {
      // ... 选项 ...
    })
    
  2. 局部组件
    如果我们注册的组件是挂载在某个实例中,那么就是一个局部组件

    new Vue({
      el: '#app',
      components: {
        'component-a': ComponentA,
        'component-b': ComponentB
      }
    })
    
  3. 用例

    <div id="app">
      <!-- 3.使用全局组件 -->
      <my-com></my-com>
      <!-- 3.使用局部组件 -->
      <my-com2></my-com2>
    </div>
    
    <div id="app2">
      <!-- 3.使用全局组件 -->
      <my-com></my-com>
    </div>
    <script src="./vue.js"></script>
    <script>
      //1.创建组件构造器对象
      const cpnc = Vue.extend({
        template: '<div><h2>我是全局组件</h2></div>'
      });
      //2.注册全局组件,并且定义组件标签的名称
      Vue.component('my-com', cpnc);
    
      //3.注册局部组件,并将vue-devTools中显示该组件的名称改为 part
      const MyCom2 = { //省略Vue.extend()
        name: 'part',
        template: `
          <div>
            <p>{{info}}</p>
          </div>`,
        data() {
          return {
            info: '我是局部组件'
          }
        }
      }
    
      const vm = new Vue({
        el: "#app",
        components: {
          //组件名 : 组件模板
          'my-com2': MyCom2
        }
      });
    
    
      const app2 = new Vue({
        el: "#app2",
      });
    </script>
    

[2]. 父组件和子组件

  • VueComponent.prototype.__proto__ = Vue.prototype

    <div id="app">
        <parent></parent>
      </div>
    
      <script src="./vue.js"></script>
      <script>
        //1.创建子组件
        const son = Vue.extend({
          template: '<div><h4>{{info}}</h4></div>',
          data() {
            return {
              info: '我是子组件'
            }
          }
        });
    
        //2.创建父组件
        const parent = Vue.extend({
          template: `
       		<div>
              <h2>{{info}}</h2>
              <!-- 4.在父组件中使用子组件  -->
              <son></son>
            </div>`,
          //3.在父组件中注册子组件
          components: {
            son: son
          },
          data() {
            return {
              info: '我是父组件'
            }
          }
        });
    
    
        //root 组件
        const vm = new Vue({
          el: "#app",
          components: {
            //在根组件中注册父组件
            parent //parent: parent的简写
          }
        });
      </script>
    

[3]. 注册组件的语法糖

主要是省去了调用Vue.extend()的步骤,可以直接使用一个对象来代替。

<div id="app">
 <my_pnc1></my_pnc1>
  <my_pnc2></my_pnc2>
</div>

<script src="./vue.js"></script>
<script>
  //1.创建组件构造器对象(语法糖)
  var my_pnc1 = { //省略了Vue.extend()
    template: `
          <div>
            <h2>{{info}}</h2>
          </div>`,
    data() {
      return {
        info: '我是全局组件'
      }
    }
  }
  Vue.component('my_pnc1', my_pnc1); //创建全局组件

  //2.创建组件构造器对象(语法糖)
  let my_pnc2 = { //省略了Vue.extend()
    template: `
          <div>
            <h2>{{info}}</h2>
          </div>`,
    data() {
      return {
        info: '我是局部组件'
      }
    }
  }
  const app = new Vue({
    el: "#app",
    components: {
      my_pnc2: my_pnc2  //这里会自动判断是否省略了Vue.extend(),如果省略,则会自动调用
    }

  });
</script>

[4]. 抽离组件的模板

  1. 方式一:使用<script>标签,注意类型为text/x-template

  2. 方式二:使用<template>标签

    <div id="app">
      <my_pnc1></my_pnc1>
      <my_pnc2></my_pnc2>
    </div>
    
    <!-- 1.使用script标签 -->
    <script type="text/x-template" id="my_pnc1">
        <div> 
            <h2>我是标题2</h2>
            <p>我是副标题2</p>
        </div>    
    </script>
    
    <!-- 2.使用template标签 -->
    <template id="my_pnc2">
        <div>
            <h2>我是标题2</h2>
            <p>我是副标题2</p>
        </div>
    </template>
    
    <script src="./js/vue.js"></script>
    <script>
        Vue.component('my_pnc1', {
            template:'#my_pnc1',
        });
        Vue.component('my_pnc2',{
            template: '#my_pnc2'
        });
    
        const app = new Vue({
            el:"#app",
        });
    </script>
    

[5]. 组件数据存放

  1. 组件是一个单独功能模块的封装:
    这个模块有属于自己的HTML模板,也有属于自己的data数据。

  2. 组件有自己的data和methods等属性,data属性是一个函数,返回一个对象,对象内部保存着数据。

  3. 组件中的data设计成函数,避免多次使用组件时相互影响,当每次调用组件时,会返回一个全新的对象。如果data是一个对象,当多次调用组件时会造成数据黏连(第一个组件改变了data中的数据,第二个组件的数据也会跟着改变),在7中的例子可以体现出来

    <div id="app">
      <h2>{{info}}</h2>
      <cpn></cpn>
      <cpn></cpn>
    </div>
    
    <template id="my_pnc1">
      <div>
        <p>当前的数字为:{{num}}</p>
        <button @click="add">+</button>
        <button @click="sub">-</button>
      </div>
    </template>
    <script src="./vue.js"></script>
    <script>
      const cpn = {
        template: '#my_pnc1',
        data() { //组件中的data是一个方法
          return {
            num: 0
          }
        },
        methods: {
          add() {
            this.num++;
          },
          sub() {
            this.num--;
          }
        },
      }
    
      const vm = new Vue({
        el: "#app",
        data: { //vue实例的data是一个对象
          info: '各个组件间的data相互独立'
        },
        components: {
          cpn //注册组件
        }
      });
    </script>
    

[6]. 使用ref获取元素或子组件的引用信息

  • ref应用在html标签上获取的是真实DOM元素(id的替代者),应用在组件标签上获取的是组件实例对象(VueComponent)
  • 使用方式:(School是组件)
    打标识: <h1 ref=“xxx”> …</h1> 或 <School ref=“xxx”></School>
    获取:this.$refs.xxx
    <div id="app">
      <p ref="demo">案例</p>
      <School ref="comSchool"></School>
      <button @click="getRef()">点击获取ref</button>
    </div>
    
    <template id="school">
      <div>
        <p>{{name}}</p>
        <p>{{address}}</p>
      </div>
    </template>
    
    <script src="./vue.js"></script>
    <script>
      const School = {
        name: 'School',
        template: "#school",
        data() {
          return {
            name: '学校名称',
            address: '学校地址'
          }
        }
      };
    
      new Vue({
        el: '#app',
        components: {
          School
        },
        methods: {
          getRef() {
            console.log(this.$refs.demo); //<p>案例</p>
            console.log(this.$refs.comSchool); //组件实例Vuecomponent
          }
        },
      })
    </script>
    

[7]. 父组件访问子组件

①. $refs

  • ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。
  • 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件。
  • 我们通过ref给一个子组件绑定一个特定的ID。通过this.$refs.ID就可以访问到该组件了。
    <div id="app">
        <cpn></cpn>
        <cpn ref="id02"></cpn>
        <button @click="getCpn">获取子组件</button>
    </div>
    <template id="cpn">
        <h2>我是子组件</h2>
    </template>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const vm = new Vue({
            el:"#app",
            //创建子组件
            components:{
                cpn:{
                    template: '#cpn',
                    data() {
                        return {
                            name: '我是子组件的name'
                        }
                    },
                    methods:{
                        getSonCpn(){
                            console.log('我是子组件的方法');
                        }
                    }
                }
            },
            methods:{
                getCpn(){
                    //以对象的形式存储子组件
                    console.log(this.$refs);
                    console.log(this.$refs.id02.name);//获取到子组件的data中的name信息
                    this.$refs.id02.getSonCpn();//获取到子组件的methods中的getSonCpn方法
                }
            }
        });
    </script>
    

②. $children

  • $children 以数组的形式存储所有子组件

  • $children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。当子组件过多时,如果需要拿到其中某一个时,如果此时代码中写的固定的索引值,在以后添加另一个子组件时是会出错的。

    <div id="app">
       <cpn></cpn>
        <cpn></cpn>
        <button @click="getCpn">获取子组件</button>
    </div>
    <template id="cpn">
        <h2>我是子组件</h2>
    </template>
    <script src="./js/vue.js"></script>
    <script>
        const app = new Vue({
            el:"#app",
            //创建子组件
            components:{
                cpn:{
                    template: '#cpn',
                    data() {
                        return {
                            name: '我是子组件的name'
                        }
                    },
                    methods:{
                        getSonCpn(){
                            console.log('我是子组件的方法');
                            
                        }
                    }
                }
            },
            methods:{
                getCpn(){
                    //以数组的形式存储所有子组件
                    console.log(this.$children);
                    console.log(this.$children[0].name);
                    this.$children[0].getSonCpn();
                }
            }
        });
    </script>
    

[8]. 父子组件的通信

在这里插入图片描述

①. 父组件通过props(properties)向子组件传递数据

  • 1). 在子组件中,需要使用props选项来声明从父级接收到的数据。

  • 2). props接收值的两种方式:

    • 方式一:字符串数组 (数组中的字符串就是传递时的名称)。
    • 方式二:对象 (对象可以设置传递时的类型,也可以设置默认值等)。
      • [1] 当需要对props进行类型等验证时,就需要对象写法。
      • [2] 验证支持的类型有:String,Number,Boolean,Array,Object,Date,Function,Symbol,自定义验证的类型。
      • [3] 子组件中的props属性中不支持驼峰法命名,因为在“将父组件中的数据传递给子组件”时,v-bind不支持驼峰命名(可以将v-bind:cInfo变为v-bind:c-info)
      • [4] 子组件模板中包含多个标签时,必须都被同一个根标签包着。
      • [5] props是只读的,如果需要修改,则需要赋值props的内容到data中,然后修改data中相应的数据。
      • [6] https://cn.vuejs.org/v2/guide/components-props.html
    <div id="app">
        <!-- 4.将父组件中的数据传递给子组件 -->
        <!-- 如果直接传值(如stime),则v-bind非必须 -->
        <!-- 如果直接传值,传递的是数字(如sfun)则需要添加v-bind,否则传递过去的是一个字符串'1' -->
        <cpn v-bind:smovies="movies" stime="2020-03-02" :ssum="sum" :svalid="1" :sfn="myFn"></cpn>
    </div>
    
    <template id="soncpn">
        <div>
            <p>sNull:{{sNull}}</p>
            <p>smovies:{{smovies}}</p>
            <p>myTime:{{myTime}}</p>
            <p>ssum:{{ssum}}</p>
            <p>smessage:{{smessage}}</p>
            <p>svalid:{{svalid}}</p>
            <button @click="sfn">sfn</button>
        </div>
    </template>
    
    <script src="./vue.js"></script>
    <script>
        //1.创建子组件
        const cpn = {
            template: '#soncpn',
            data() {
                return {
                    myTime: this.stime //data中获取props中的数据
                }
            },
            //2.创建变量,将父组件中的movies数据保存到子组件的smovies中
            //2.1 通过字符串数组接收数据
            //props: ['smovies', 'time']
            //2.2 通过对象接收数组
            props: {
                //可以匹配任何类型,这里不传参
                sNull: null,
                //父组件传递过来的数据必须是字符串类型
                stime: String,
                //可以是多个类型
                ssum: [String, Number],
                //必须传参,而且是数组类型
                smovies: {
                    type: Array,
                    required: true //表示必须传参
                },
                //带有默认值的对象
                smessage: {
                    type: Object,
                    //对象或者数组默认值必须从一个工厂函数获取
                    default: function () {
                        return {
                            message: 'hello'
                        }
                    }
                },
                //自定义验证函数
                svalid: {
                    validator: function (value) {
                        //value值必须为1,当传递的参数不为1时报错
                        return value === 1;
                    }
                },
                sfn: {
                    type: Function
                }
            }
        };
    
    
        new Vue({
            el: "#app", //用于挂载要管理的元素
            data: { //定义数据
                movies: ['电影1', '电影2', '电影3'],
                sum: 100,
            },
            components: {
                //3.将子组件在父组件中注册
                //下边的相当于 cpn:cpn (即cpn:{...}) 是对象字面量增强写法中的属性增强写法
                cpn
            },
            methods:{
                myFn(){ //传递一个方法
                    console.log('一个方法');
                }
            }
        });
    </script>
    

② 子组件通过props和闭包向父组件传递数据

原理:父组件通过向子组件发送一个闭包来实现通信。

<div id="app">
  {{message}}
  <cpm :parent-send-info="getSonData"></cpm>
</div>


<template id="cpm">
  <div>
    <p>{{info}}</p>
    <button @click="sendData">子组件发送数据</button>
  </div>
</template>

<script src="./vue.js"></script>
<script>
  let cpm = {
    template: '#cpm',
    data() {
      return {
        info: '我是子组件',
        msg: '子组件向父组件传递的信息'
      }
    },
    props: {
      'parentSendInfo': {
        type: Function,
        required: true
      }
    },
    methods: {
      sendData() {
        this.parentSendInfo(this.msg)
      }
    }
  }


  const vm = new Vue({
    el: "#app",
    data: {
      message: ''
    },
    components: {
      cpm
    },
    methods: {
      getSonData(msg) {
        this.message = msg;
        console.log(this);//vue实例(vm)
      }
    },

  });
</script>

③. 子组件通过自定义事件向父组件传递数据($emit)

  • 在子组件中通过$emit(‘事件名称’ [, 参数] ) 触发事件。($emit中的事件名称不能使用驼峰法)

  • 父组件监听子组件中通过$emit发射过来的btn-click事件,事件的回调函数在父组件中。

  • 自定义事件可以使用事件的修饰符 (如:.once)

  • 组件上也可以绑定原生DOM事件(如:click),但是需要事件修饰符.native

    <!-- 1.父组件 -->
    <div id="app">
        <!-- 4.父组件监听子组件中通过$emit发射过来的btn-click事件 -->
        <!-- btn-click被绑定到 子组件(cpn)的VueComponent的实例对象上 -->
        <cpn v-on:btn-click="parentReceive"></cpn>
    </div>
    <!-- 2.子组件 -->
    <template id="cpn">
        <div>
            <!-- 3.点击子组件按钮将当前子组件信息提交给父组件 -->
            <button v-for="item in btn" @click="btnClick(item)">{{item.name}}</button>
        </div>
    </template>
    
    <script src="./js/vue.js"></script>
    <script>
        const cpn = {
            template: "#cpn",
            data(){
                return {
                    btn :[
                            {id:1,name:'按钮1'},
                            {id:2,name:'按钮2'},
                            {id:3,name:'按钮3'},
                        ]
                }
            },
            methods: {
                btnClick(item){
                    //通过$emit('事件名称', '参数')
                    this.$emit('btn-click', item);
                }
            }
        };
    
        const app = new Vue({
            el :"#app",
            data:{
                message:"hello world!"
            },
            components: {
                cpn
            },
            methods: {
            	//父组件监听子组件的方法
                parentReceive(son){
                    console.log('父组件接收', son);
                }
            }
        });
    </script>
    

④ 子组件通过自定义事件向父组件传递数据($on)

<!-- 1.父组件 -->
<div id="app">
  {{info}}
  <!-- 4.通过ref获取子组件实例VueComponent -->
  <cpn ref="demo"></cpn>
</div>

<!-- 2.子组件 -->
<template id="cpn">
  <div>
    <!-- 3.点击子组件按钮将当前子组件信息提交给父组件 -->
    <button @click="sendInfo">点击发送信息</button>
  </div>
</template>

<script src="./vue.js"></script>
<script>
  const cpn = {
    template: "#cpn",
    data() {
      return {
        info: '我是子组件中的信息'
      }
    },
    methods: {
      sendInfo() {
        //子组件触发父组件绑定给子组件的listen事件
        this.$emit('listen', this.info);
      }
    },
  };

  const vm = new Vue({
    el: "#app",
    components: {
      cpn
    },
    data: {
      info: ''
    },
    mounted() {
      //页面挂载完成后,给子组件的实例对象VueComponent绑上listen事件
      //$on的回调函数如果是非箭头函数,则this是子组件实例VueComponent
      //这种方式可以控制在什么时间给子组件绑定事件
      this.$refs.demo.$on('listen', (msg) => {
        console.log(this);
        this.info = msg;
      })

    },

  });
</script>

⑤ 祖组件向孙组件传递数据: provide / inject

  • provide 和 inject 绑定并不是可响应的。如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
  • 可以使用 对象、函数等方法使孙组件获得响应式的数据

用例1:

<!-- 1.根组件 -->
 <div id="app">
   {{info}}
   {{obj.message}}
   <button @click="editInfo">修改根组件的info</button>
   <button @click="editObj">修改obj的message</button>
   <cpn></cpn>
 </div>

 <!-- 2.子组件 -->
 <template id="cpn">
   <div>
     {{info}}
     <grandson></grandson>
   </div>
 </template>

 <!-- 3.孙组件 -->
 <template id="grandson">
   <div>
     {{info}}
     <!-- 方式一 、 二 的显示 -->
     <!-- <span style="color: red;">{{message}}</span> -->
     <!-- 方式三的显示 -->
     <!-- <span style="color: red;">{{obj.message}}</span> -->
     <!-- 方式四的显示, 可以将msgFn() 放入到计算属性中,更方便的使用 -->
     <span style="color: red;">{{msgFn()}}</span>
   </div>
 </template>

 <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
 <script>
   const grandson = {
     template: "#grandson",
     // inject: ['message'], // 方式一、二的接收
     // inject: ['obj'], // 方式三接收
     inject: ['msgFn'], // 方式四接收
     data() {
       return {
         info: '我是孙组件中的信息'
       }
     }
   };

   const cpn = {
     template: "#cpn",
     data() {
       return {
         info: '我是子组件中的信息'
       }
     },
     components: {
       grandson
     }
   };
   const vm = new Vue({
     el: "#app",
     components: {
       cpn
     },
     //方式一: 给孙组件传递一个简单信息
     // provide: {
     //   message: '我是根组件传的信息'
     // },
     //方式二:给孙组件传递一个data 中的信息 (孙组件获取的是非响应式的数据)
     // provide() {
     //   return {
     //     message: this.info
     //   }
     // },
     // 方式三:如果希望孙组件获得的是一个响应式的数据,将数据放在一个 对象中
     // provide() {
     //   return {
     //     obj: this.obj 
     //   }
     // },
     provide() {
       return {
         msgFn: () => this.info // 方式四:如果希望孙组件获得的是一个响应式的数据,将数据放在一个 函数的返回值中
       }
     },
     data: {
       info: '我是根(祖先)组件',
       obj: {
         message: '我是祖先组件的obj.message'
       }
     },
     methods: {
       editInfo() {
         this.info = '新的info信息'
       },
       editObj() {
         this.obj.message = '新的一条响应式的消息'
       }
     },
   });
 </script>

用例2:

<!-- 祖先组件 -->
<div id="app">
  <div style="border: 1px solid green">
    我是祖先组件
    <p>sum: {{ sum }}</p>
    <p>movies:{{movies}}</p>
    <p>moviesLength:{{moviesLength}}</p>
    <p>obj:{{obj}}</p>
    <button @click="updateAge">修改obj.age</button>
    <button @click="sum++">修改sum</button>
    <button @click="addMovies">添加movies</button>
    <Child />
  </div>
</div>

<!-- 中间组件(可以多层) -->
<template id="child">
  <div style="border:1px solid red">
    我是中间组件
    <Descendant />
  </div>
</template>

<!-- 后代组件 -->
<template id="descendant">
  <div style="border: 1px solid blue;">
    我是后代组件
    <p>otherMovies:{{otherMovies}}</p>
    <p>getAncestorSum:{{getAncestorSum}}</p>
    <p>obj:{{obj}}</p>
    <p>msg:{{msg}}</p>
    <p>moviesLength: {{moviesLength}}</p>
    <p>sum:{{sum}}</p>
    <p>otherSum:{{otherSum()}}</p>
    <button @click="getAncestorSum">调用getAncestorSum</button>
    <button @click="updateName">修改obj.name</button>
    <button @click="updateMsg">修改msg</button>
  </div>
</template>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script>

  // 后代组件,获取祖先组件传递的数据、方法等
  const Descendant = {
    template: '#descendant',
    inject: ['otherMovies', 'getAncestorSum', 'obj', 'msg', 'moviesLength', 'sum', 'otherSum'],
    methods: {
      updateMsg() {
        this.msg = '新消息'   //报错,提示不要修改msg
      },
      updateName() {
        this.obj.name = '王五'//不报错,响应式
      }
    }
  };

  // 中间组件充当过渡
  const Child = {
    template: '#child',
    components: {
      Descendant
    }
  };

  new Vue({
    el: "#app",
    components: {
      Child
    },
    provide() {
      return {  //要传递给后代组件的数据、方法
        otherMovies: this.movies,  // movies (key) 没有必须与值一样
        getAncestorSum: this.getAncestorSum, //sum将被响应式的修改
        obj: this.obj,
        msg: '不要捣蛋',
        moviesLength: this.moviesLength, // 计算属性非响应式
        sum: this.sum, // sum 为非响应式
        otherSum: () => this.sum  // sum为响应式
      }
    },
    data: {
      movies: ['电影1', '电影2', '电影3'],
      sum: 100,
      obj: {
        name: '张三',
        age: 24
      }
    },
    computed: {
      moviesLength() {
        return this.movies.length
      }
    },
    methods: {
      getAncestorSum() {
        console.log('祖先组件中的getAncestorSum方法被触发');
        this.sum++
      },
      updateAge() {
        this.obj.age = 28
      },
      addMovies() {
        this.movies.push('电影' + (this.movies.length + 1))
      }
    }
  });
</script>

[9]. 子组件访问父组件和根组件:$parent,$root

  • 尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。

  • 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。

  • 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。

  • 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护

    <div id="app">
       <cpn></cpn>
    </div>
    
    <template id="cpn">
        <div>
            <h2>我是子组件</h2>
            <button @click="getParent">获取父组件1</button>
            <ccpn></ccpn>
        </div>
    </template>
    
    <template id="ccpn">
        <div>
            <h2>我是孙组件</h2>
            <button @click="getParent2">获取父组件2</button>
        </div>
    </template>
    
    <script src="./js/vue.js"></script>
    <script>
        const app = new Vue({
            el:"#app",
            //创建子组件
            components:{
                cpn:{
                    template: '#cpn',
                    data() {
                        return {
                            name: '我是子组件的name'
                        }
                    },
                    methods:{
                        getParent(){
                            //获取的是vue实例
                            console.log(this.$parent);
                            console.log(this.$parent.name);
                            this.$parent.getParentFun()
                        },
                        getParentFun(){
                            console.log('我是子组件的方法');
                            
                        }
                    },
                    //创建一个孙组件
                    components:{
                        ccpn: {
                            template:"#ccpn",
                            methods:{
                                getParent2(){
                                    //获取的是VueComponent实例
                                    console.log(this.$parent);
                                    console.log(this.$parent.name);
                                    this.$parent.getParentFun();
                                    //获取跟组件,即vue实例
                                    console.log(this.$root);
                                    console.log(this.$root.name);
                                }
                            }
                        }
                        
                    }
                }
            },
            methods:{
                getParentFun(){
                    console.log('我是父组件的方法');
                }
            },
            data:{
                name:'我是父组件'
            }
            
        });
    </script>
    

[10]. 兄弟组件间的通信

①. 方式一:通过父组件进行兄弟组件之间通讯

让兄弟组件通过一个共同的父组件彼此通讯。
子组件1通过$emit将信息交给父组件,父组件通过props将信息传递给子组件2。

<div id="card"  style="border:1px solid green;">
    <div class="card-header">
        <h5 v-text="theCardTitle"></h5>
    </div>
    <div class="card-body">
        <brother-card :message-son="messageSon" @brother="messageDaughterFun"></brother-card>
        <sister-card :message-daughter="messageDaughter" @sister="messageSonFun"></sister-card>
    </div>
</div>


<template id="SisterCard">
    <div class="message"  style="border:1px solid blue; margin:2px;">
        <div class="message-header">
            <h5 v-text="theCardTitle"></h5>
        </div>
        <div class="message-body">
            <p class="message-text">我是Sister组件</p>
            <button @click="messageBrother" class="btn">给哥哥发消息</button>
            <div v-if="messageDaughter" class="alert" v-html="messageDaughter"></div>
        </div>
    </div>
</template>

<template id="BrotherCard">
    <div class="message" style="border:1px solid red; margin:2px;">
        <div class="message-header">
            <h5 v-text="theCardTitle"></h5>
        </div>
        <div class="message-body">
            <p class="message-text">我是Brother组件</p>
            <button @click="messageSister" class="btn">给妹妹发消息</button>
            <div v-if="messageSon" class="alert" v-html="messageSon"></div>
        </div>
    </div>
</template>


<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const SisterCard = {
        template: "#SisterCard",
        props: ['messageDaughter'],
        data: () => ({
            theCardTitle: '子组件2'
        }),
        methods: {
            messageBrother() {
                this.$emit('sister', '妹妹发送给哥哥的消息')
            }
        }
    };
    
    const BrotherCard = {
        template: "#BrotherCard",
        props: ['messageSon'],
        data: () => ({
            theCardTitle: '子组件1'
        }),
        methods: {
            messageSister() {
                this.$emit('brother', '哥哥发送给妹妹的消息')
            }
        }
    };

    const app = new Vue({
        el: "#card",
        data: () => ({
            theCardTitle: '父组件',
            messageDaughter: '',
            messageSon: ''
        }),
        components: {
            BrotherCard,
            SisterCard
        },
        methods: {
            messageDaughterFun(message) {
                this.messageDaughter = message;
            },
            messageSonFun(message) {
                this.messageSon = message;
            }
        }
    });
</script>

②. 方式二:通过事件总线进行兄弟间组件通讯

  • 一种组件间通信的方式,适用于任意组件间通信

  • 总线就是在Vue的prototype上添加一个Vue / VueComponent实例。

  • 可以使用入口Vue自身当做总线(推荐、标准)

    new Vue({
      el: '#app',
      beforeCreate() {
      	//安装全局事件总线,将当前的Vue实例(this)给Vue原型对象$bus,充当中转站
        Vue.prototype.$bus = this 
      },
    })
    
  • 可以使用一个Vue实例当做总线

    Vue.prototype.$bus = new Vue();//给$bus赋值一个Vue实例
    
  • 可以使用组件VueComponent实例当作总线

    let Dome = Vue.extend({});
    Vue.prototype.$bus = new Dome();//给$bus赋值一个组件实例(VueComponent实例)
    
<div id="app">
 <cpn></cpn>
  <hr />
  <cpn2></cpn2>
</div>

<template id="cpn">
  <div>
    <p>{{info}}</p>
    <p style="color: red;">{{getCpn2Msg}}</p>
    <button @click="senCpn2Msg">向组件2发送信息</button>
  </div>
</template>

<template id="cpn2">
  <div>
    <p>{{info}}</p>
    <p style="color: green;">{{getCpnMsg}}</p>
    <button @click="senCpnMsg">向组件1发送信息</button>
  </div>

</template>

<script src="./vue.js"></script>
<script>
  var cpn = {
    template: '#cpn',
    data() {
      return {
        info: '我是组件1',
        getCpn2Msg: '' //获取组件2给的消息
      }
    },
    methods: {
      senCpn2Msg() {
        //因为VueComponent.prototype.__proto__ = Vue.prototype
        //所以组件可以找到绑定在Vue上$bus
        this.$bus.$emit('listenCpnSend', '组件1给组件2发送的信息')
      }
    },
    mounted() {
      //要使用箭头函数 确保this的指向
      this.$bus.$on('listenCpn2Send', (msg) => {
        this.getCpn2Msg = msg;
      });
    },
    beforeDestroy() {
      // 销毁当前组件时,要解绑该组件绑定在总线上的自定义事件
      //注意:如果$off()没有参数则会解绑总线$bus上的所有自定义事件
      this.$bus.$off('listenCpn2Send');
    },
  };

  var cpn2 = {
    template: '#cpn2',
    data() {
      return {
        info: '我是组件2',
        getCpnMsg: ''  //获取组件1给的消息
      }
    },
    methods: {
      senCpnMsg() {
        this.$bus.$emit('listenCpn2Send', '组件2给组件1发送的信息')
      },
      getCpnInfo(msg){
        this.getCpnMsg = msg;
      }
    },
    mounted() {
      this.$bus.$on('listenCpnSend', this.getCpnInfo);
    },
    beforeDestroy() {
      this.$bus.$off('listenCpnSend');
    },
  };


  new Vue({
    el: '#app',
    components: {
      cpn,
      cpn2
    },
    beforeCreate() {
    	//安装全局事件总线,将当前的Vue实例(this)给Vue原型对象$bus,充当中转站
      Vue.prototype.$bus = this 
    },
  })
</script>

[11]. 组件的 $el获取组件内的html元素

每个组件都有$el属性,用于获取该组件内的html元素,在mounted生命周期中才有效。

<div id="app">
    <button @click="getEl">获取el</button>
    <button @click="getChildrenEl">获取cpn的el</button>
    <cpn ref="cpn"></cpn>
</div>

<template id="cpn">
    <p>cpn</p>
</template>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
 const cpn = {
      template:"#cpn",
  }
  new Vue({
     el: "#app",
      components: {
          cpn
      },
      methods: {
          getEl(){
              console.log(this.$el);//<div id="app">...</div>
          },
          getChildrenEl(){
              console.log(this.$refs.cpn.$el); //<p>cpn</p>
          }
      }
  });
</script>

[14]. 解绑自定义事件$off

  • 使用$off([参数]),可以解绑自定义的事件,参数为自定义事件名字符串,则表示解绑一个自定义事件,如果为自定义事件名数组,则表示解绑多个自定义事件,如果没有参数,则表示解绑所有自定义事件。

  • 组件被销毁(destroy钩子函数被触发)则该组件自动解绑所有自定义事件。该组件的所有子组件的自定义事件也自动解绑。(组件销毁后,组件和子组件的原生事件(如click)是不会解绑的)

    <div id="app">
     <button @click="killSelf">销毁父组件</button>
      <cpn @custom1="listenCpn" @custom2="listenCpn" @custom2="listenCpn"></cpn>
    </div>
    
    <template id="cpn">
      <div>
        <button @click="clickFn1">自定义事件1</button>
        <button @click="clickFn2">自定义事件2</button>
        <button @click="clickFn3">自定义事件3</button><br />
        <button @click="offOne">解绑事件1</button>
        <button @click="offTwo">解绑事件2和事件3</button>
        <button @click="killThis">销毁子组件</button>
      </div>
    </template>
    <script src="./vue.js"></script>
    <script>
      var cpn = {
        template: '#cpn',
        methods: {
          clickFn1() { //只解绑自定义事件,该原生方法(click)还会触发
            console.log('原生click事件不会失效');
            this.$emit('custom1', 1)
          },
          clickFn2() {
            this.$emit('custom2', 2)
          },
          clickFn3() {
            this.$emit('custom3', 3)
          },
          offOne() {
            this.$off('custom1')
          },
          offTwo() {
            this.$off(['custom2', 'custom3'])
          },
          killThis() {
            //所有自定义事件失效
            this.$destroy();
          }
        },
      }
    
    
      new Vue({
        el: '#app',
        components: {
          cpn
        },
        methods: {
          listenCpn(id) {
            console.log('自定义事件' + id);
          },
          killSelf() {
            this.$destroy(); //子组件自定义事件失效
          },
    
        },
      })
    </script>
    

[15]. 动态组件 component

https://cn.vuejs.org/v2/guide/components.html#%E5%8A%A8%E6%80%81%E7%BB%84%E4%BB%B6

当多个组件组成一个大页面时,如果组件类型/顺序不确定时,可以使用动态组件。

<div id="app">
   <ul>
        <li v-for="item in cpns">
            <component v-bind:is="item"></component>
        </li>
    </ul>
</div>

<template id="cpn">
    <p>组件1</p>
</template>

<template id="cpn2">
    <p>组件2</p>
</template>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
   const cpn = {
       template: "#cpn"
   }
   const cpn2 = {
       template: "#cpn2"
   }

    const app = new Vue({
        el: "#app",
        data: {
            cpns: [
                'cpn',
                'cpn2',
                'cpn'
            ]
        },
        components: {
            cpn,
            cpn2,
        }
    });
</script>

[16]. 缓存组件 keep-alive

① 用例

  • keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染(如果没有keep-alive组件是会被销毁和重新创建,添加keep-alive是不会被销毁的)。

  • keep-alive实例:

    <div id="app">
    <button @click="currentCpn = 'dateCpn'">btn1</button>
    <button @click="currentCpn = 'otherDateCpn'">btn2</button>
    <!-- 可以看看去掉keep-alive的效果 -->
    <keep-alive>
      <component :is="currentCpn"></component>
    </keep-alive>
    </div>
    
    <template id="cpn">
    <p>dateTime: {{dateTime}}</p>
    </template>
    
    <template id="cpn2">
    <p>otherCpn: {{dateTime}}</p>
    </template>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    const dateCpn = {
      template: "#cpn",
      data() {
        return {
          dateTime: new Date() //可以实现响应式改变
        }
      },
      mounted() {
        console.log('cpn mounted');
      },
      destroyed() {
        console.log('cpn destroyed');
      }
    }
    
    const otherDateCpn = {
      template: "#cpn2",
      data() {
        return {
          dateTime: new Date() //可以实现响应式改变
        }
      },
      mounted() {
        console.log('cpn2 mounted');
      },
      destroyed() {
        console.log('cpn2 destroyed');
      }
    }
    
    const app = new Vue({
      el: "#app",
      data: {
        currentCpn: 'dateCpn'
      },
      components: {
        dateCpn,
        otherDateCpn
      }
    });
    </script>
    

②. include 、 exclude 和 max

匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。

  • include - 字符串或正则表达,只有匹配的组件会被缓存
    <!-- Detail是组件的name属性的值 -->
    <!-- 缓存多个 可以使用 :include="['dateCpn', 'otherDateCpn']" -->
     <keep-alive include="dateCpn">
      <component :is="currentCpn"></component>
    </keep-alive>
    
  • exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
    <!-- 不缓存dateCpn(组件的name的值) -->
     <keep-alive exclude="dateCpn">
      <component :is="currentCpn"></component>
    </keep-alive>
    
  • max - 最多可以缓存多少组件实例。一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。

③. activated 和 deactivated 生命周期钩子

  • 对于缓存的组件来说,再次进入时,我们是不会执行 createdmounted 等生命周期
  • 当组件在<keep-alive> 内被切换,它的 activateddeactivated 这两个生命周期钩子函数将会被对应执行。
  • 在 2.2.0 及其更高版本中,activateddeactivated 将会在 <keep-alive> 树内的所有嵌套组件中触发。

[17]. 异步组件(异步加载组件)

https://cn.vuejs.org/v2/guide/components-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6

当使用局部注册的时候,你也可以直接提供一个返回 Promise 的函数:

new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

[18]. 组件构造函数VueComponent

  1. 组件(如上边的my-component)本质是一个名为VueComponent的构造函数,由Vue.extend生成。
  2. 当使用组件时(如<my-component></my-component>),Vue解析时会帮我们创建my-component组件的实例对象,即Vue帮我们执行:new VueComponent(option)。
  3. 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent,生成全新的实例对象。
  4. 关于this指向:
    (1). 组件配置中:
    data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是【VueComponent的实例对象】
    (2). new Vue(option)配置中:
    data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是【Vue的实例对象】
  5. VueComponent 和 Vue的关系:VueComponent.prototype.__proto__ = Vue.prototype
    请添加图片描述

(二) 组件化高级

[1].插槽 slot

  • 组件的插槽是为了让我们封装的组件具有更好的扩展性

  • 插槽的基础使用

    <div id="app">
        <!-- 2.组件中没有定义其他标签将使用slot默认值 -->
        <cpn></cpn>
        <!-- 3.组建中使用一个标签将替换slot默认值 -->
        <cpn>
          <p>我是p标签</p>
        </cpn>
        <!-- 4.组件中有多个标签将替换slot默认值 -->
        <cpn>
          <p>我是p标签</p>
          <i>我是i标签</i>
        </cpn>
    </div>
    
    <template id="cpn">
        <div>
          <h2>我是子组件</h2>
          <!-- 1.slot可以有默认值也可以没有,这里使用默认值button -->
          <slot><button>sloat</button></slot>
        </div>
    </template>
    
    <script src="./vue.js"></script>
    <script>
    const app = new Vue({
      el: "#app",
      components: {
        cpn: {
          template: '#cpn',
        }
      }
    });
    </script>
    

[2].具名插槽 slot

①. vue2.6.0 - 使用具名插槽

v-slot 指令自 Vue 2.6.0 起被引入,提供更好的支持 slot 和 slot-scope attribute 的 API 替代方案。在接下来所有的 2.x 版本中 slot 和 slot-scope attribute 仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。

<div id="app">
   <!-- 1.替换没有命名的slot -->
    <cpn>
        <button>替换没有命名的</button>
    </cpn>
    <!-- 2.替换中间的slot -->
    <cpn>
        <span slot="middle">替换中间</span>
    </cpn>
</div>

<template id="cpn">
    <div>
        <slot name="left"><span>左边</span></slot>
        <slot name="middle"><span>中间</span></slot>
        <slot name="right"><span>右边</span></slot>
        <slot><p>被替换</p></slot>
    </div>
    
</template>

<script src="./js/vue.js"></script>
<script>
    const app = new Vue({
        el :"#app",
        data:{
            message:"hello world!"
        },
        components:{
            cpn: {
                template: "#cpn"
            }
        }
        
    });
</script>

②. vue2.6.0 + 使用具名插槽

<div id="app">
    <cpn>
       <template v-slot:btn>
        	<button>替换btn</button>
       </template>
        <!-- #btn2是v-slot:btn2的缩写 -->
        <template #btn2>
            <p>btn2被替换掉</p>
        </template>
    </cpn>
</div>

<template id="cpn">
    <div>
        <slot name="btn">
          <button>默认btn</button>
        </slot>
        <slot name="btn2">
            <button>具名插槽的btn2</button>
        </slot>
    </div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const cpn = {
        template: '#cpn',

    }
    const app = new Vue({
        el: "#app",
        components: {
            cpn
        }
    });
</script>

[3].编译作用域

  • 父组件模板的所有东西会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
    <div id="app">
        <!-- 这里的isShow使用的是父组件中的 -->
        <cpn v-show="isShow"></cpn>
    </div>
    
    <template id="cpn">
        <div>
          <p>文字文字文字文字文字文字</p>
          <p>文字文字文字文字文字文字</p>
        </div>
    </template>
    
    <script src="./vue.js"></script>
    <script>
        const cpn = {
          template: '#cpn',
          data() {
            return {
              isShow: false
            }
          }
        }
    
    
        new Vue({
          el: "#app",
          data: {
            isShow: true
          },
          components: {
            cpn
          }
    
        });
    </script>
    

[4].作用域插槽

②. 2.6.0 - 使用作用域插槽

  • 父组件替换子组件插槽的标签,但是内容还是由子组件提供。

  • slot-scope="mySlot"对应的mySlot有一个属性 .row 通过mySlot.row可以获取到当前(行)的所有数据

  • 自 2.6.0 起已废弃的使用 slot-scope,被 v-slot 代替

    <div id="app">
        <Cpn>
          <!-- 2.通过slot-scope获取到绑定在子组件上的books数据存放在mySlot变量上 -->
          <template slot-scope="mySlot">
            <ul>
              <li v-for="book in mySlot.bs">{{book}}</li>
            </ul>
          </template>
        </Cpn>
    </div>
    
    <template id="cpn">
        <div>
          <!-- 1.将子组件的books数据绑定在自定义的bs变量上 -->
          <slot :bs="books">
          </slot>
        </div>
    </template>
    
    <script src="./vue.js"></script>
    <script>
        const Cpn = {
          template: "#cpn",
          data() {
            return {
              books: ['C语言编程', "C#一站式编程", "C指针", "PHP 基础"],
            }
          }
        }
        
        const vm = new Vue({
          el: "#app",
          data: {
            message: "hello world!"
          },
          components: {
            Cpn
          }
    
        });
    </script>
    

②. 2.6.0 + 使用作用域插槽

  • 绑定在<slot> 元素上的 attribute 被称为插槽 prop。

  • 具名作用域插槽

    <div id="app">
        <cpn>
            <!-- 2.通过v-slot获取到绑定在子组件上的bs数据,并存放在slotProps变量上 -->
            <!-- slotProps是自己命名的 -->
            <template v-slot:default="slotProps">
                <ul>
                    <li v-for="book in slotProps.bs">{{book}}</li>
                </ul>
            </template>
    
            <template v-slot:movieslot="slotMovies">
                <ul>
                    <li v-for="movie in slotMovies.ms">{{movie}}</li>
                </ul>
            </template>
        </cpn>
    </div>
    
    <template id="cpn">
        <div>
         	<!-- 1.将子组件的books数据绑定在自定义的bs变量上 -->
            <slot :bs="books"></slot>
            <slot name="movieslot" :ms="movies"></slot>
        </div>
    
    </template>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const cpn = {
            template: '#cpn',
            data() {
                return {
                    books: ['C语言编程', "C#一站式编程", "C指针", "PHP 基础"],
                    movies: ['泰坦尼克号', '红日', '钢铁侠', '建党伟业']
                }
            }
        }
        const vm = new Vue({
            el: "#app",
            components: {
                cpn
            }
        });
    </script>
    

[5]. 动态插槽名

  • 下边的代码运行有问题,仅供参考
  • 在 vue-cli 中使用动态插槽名是可以的
    <div id="app">
        {{slotName}}
        <cpn>
          <template v-slot:[slotName]>
            <span style="color:red;">一个新值</span>
          </template>
        </cpn>
        <button @click="checkSlotName">切换插槽名称</button>
      </div>
    
      <template id="soncpn">
        <div>
          <slot name="left"><span>左边</span></slot>
          <slot name="middle"><span>中间</span></slot>
          <slot name="right"><span>右边</span></slot>
          <slot><p>被替换</p></slot>
        </div>
      </template>
    
      <script src="https://lib.baomitu.com/vue/2.7.7/vue.js"></script>
      <script>
        const cpn = {
          template: '#soncpn',
          data() {
            return {
            }
          }
        };
    
        new Vue({
          el: "#app",
          data: {
            count: 0,
            slotNameList: ['default', 'left', 'middle', 'right']
          },
          components: {
            cpn
          },
          methods: {
            checkSlotName() {
              this.count++
            }
          },
          computed: {
            slotName() {
              return this.slotNameList[this.count % 4]
            }
          }
        });
      </script>
    

[6]. 混入 mixin

https://cn.vuejs.org/v2/guide/mixins.html#%E5%9F%BA%E7%A1%80

  1. mixin用来更高效的实现组件内容的复用。

  2. 一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

  3. 全局混合(所有的组件自动添加混合中的内容):Vue.mixin();

  4. Mixin 的合并规则

    • 情况一: 如果是 data 函数的返回值对象

      • 返回值对象默认情况下会进行合并;
      • 如果 data 返回值对象的的属性发生了冲突,那么会保留组件自身的数据
    • 情况二: 如果是生命周期钩子函数:

      • 生命周期的钩子函数会被合并到数组中,都会被调用
    • 情况三: 值为对象的选项,例如 methods、components 和 directives, 将被合并为同一个对象

      • 比如都有 methods 选项,并且都定义了方法,那么他们都会生效;
      • 但是如果对象的key相同,那么会取组件对象的键值对
  5. mixin并不是完美的解决方案,会有一些问题:

    • 变量来源不明确,不利于阅读
    • 多mixin可能会造成命名冲突
    • mixin和组件可能出现多对多的关系,复杂度较高。
<div id="app">
    <cmp></cmp>
    <cmp2></cmp2>
 </div>

 <template id="cmp">
    <div>
      <ul>
        <li v-for="item in arr">
          {{item}}
        </li>
      </ul>

      <ul>
        <li v-for="(item,key) in obj">
          {{key}}:{{item}}
        </li>
      </ul>

      <p> {{foo}}</p>
      <p> {{bar}}</p>
      <hr>
    </div>
 </template>

 <template id="cmp2">
    <div>
      <p>{{global}}</p>
      <ul>
        <li v-for="item in arr">
          {{item}}
        </li>
      </ul>

      <ul>
        <li v-for="(item,key) in obj">
          {{key}}:{{item}}
        </li>
      </ul>
      
      <p>{{foo}}</p>
    </div>
 </template>


 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
 <script>
   // 全局混入
   Vue.mixin({
     data() {
       return {
         global: '全局mixin'
       }
     },
     mounted() {
       console.log('global mounted 被触发');
     }
   })

   //定义一个局部混入对象
   var myMixin = {
     data() {
       return {
         arr: ['数据mix', '数据mix2'], //被覆盖掉
         obj: { //进行混合
           id: 1,
           msg: '对象mix' //被覆盖掉
         },
         foo: 'abc'
       }
     },
     methods: {
       hello: function () {
         console.log('hello from mixin!')
       },
       message() { //被覆盖掉了
         console.log('mixin message');
       }
     },
     mounted: function () { //被同时调用
       this.hello();
       this.message();
     },
   }

   const cmp = {
     template: '#cmp',
     mixins: [myMixin], //可以添加多个局部mixin,会自动合并起来
     data() {
       return {
         arr: ['数据1'],
         obj: {
           name: '对象',
           msg: '对象app'
         },
         bar: 'def'
       }
     },
     methods: {
       message() {
         console.log('message');
       }
     },
     mounted() { //被同时调用
       this.message();
       console.log(this.$data);
     }
   };

   const cmp2 = {
     template: '#cmp2',
     mixins: [myMixin], //可以添加多个局部mixin,会自动合并起来
   };

   const vm = new Vue({
     el: "#app",
     components:{cmp, cmp2}
   });
 </script>

(三) 生命周期

[1]. 单个组件

https://www.jianshu.com/p/410b6099be69

vm指vue实例let vm = new Vue({...})

①. 挂载阶段

从beforeCreate 、created、beforeMount、mounted

②. 更新阶段

beforeUpdate和updated

③. 销毁阶段

beforeDestroy和destroyed

[2]. 父子组件

①. 挂载阶段

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

②. 子组件更新过程

父beforeUpdate->子beforeUpdate->子updated->父updated

③. 父组件更新过程

父beforeUpdate->父updated

④. 父组件销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值