文章目录
- 前言
 - 一、全局组件
 
- 子组件应用
 - 组件优点:组件的可复用性
 - 全局组件缺点描述
 
- 二、局部组件
 
- 创建与注册局部组件(认识components属性)
 - 局部组件的默认规定(组件名称首字母全都大写)
 
- 三、父子组件的传值(静态、动态)
 
- 3.1、静态传值(name="技小胖")
 - 3.2、动态传值(v-bind:xx="xx"配合子组件中的props数组接收)
 
- 动态传递值
 - 动态传递函数
 
- 3.3、三种校验方式
 
- ①类型校验(props需要写成{}形式)
 - ②必填校验与默认值设置(required与default)
 - ③精准校验(validator)
 
- 四、单项数据流
 
- 4.1、父子组件传递属性补充
 
- ①以对象形式传递多个参数
 - ②xx-xx=''传递,props中xxXx接收
 
- 4.2、认识单项数据流
 - 4.3、解决方案:实现在子组件中修改父组件传递过来的参数
 - 4.4、设计单向数据流原因
 
- 五、No-Props属性
 
- 5.1、不使用props传递时效果(默认会继承到子组件中,仅仅是单标签情况)
 - 5.2、认识$attrs
 
- 六、父子组件通信(子组件来发出指示让父组件执行指定函数)
 
- 6.1、$emit来向外触发父组件函数(无参与有参)
 - 6.2、emits属性(方便管理向外触发的函数名称,有警告效果,数组以及对象形式)
 - 6.3、v-model配合$emit():实现非调用父组件函数来修改父组件对象属性值
 
- 6.3.1、v-model="xxx"默认值接收
 - 6.3.2、v-model:属性="xxx"(设置别名接收)
 
- 七、slot插槽(5个知识点)
 - 八、动态组件(切换组件)
 
- 8.1、原生切换组件方式(借助v-if、v-show,keep-alive标签保存input状态)
 - 8.2、动态组件(component标签以及:is)
 
- 九、异步组件(实现异步加载组件)
 - 十、补充知识点
 
- 10.1、v-once(让某个元素标签只渲染一次)
 - 10.2、$refs(定位获取到指定设置ref的dom元素)
 - 10.3、provide属性以及inject属性(嵌套组件中传递值)
 
- 10.3.1、引出provide以及使用
 - 10.3.2、注意点provide属性绑定inject属性并不是双向绑定!!!
 
前言
本篇博客是在学习技术胖-Vue3.x从零开始学-第一季 基础语法篇过程中记录整理的笔记,若文章中出现相关问题,请指出!
- 当前该链接失效了,有需要的小伙伴可以去b站或者技术胖官网去找相应的视频与笔记。
 
所有博客文件目录索引:博客目录索引(持续更新)
一、全局组件
子组件应用
<body>
  <div id="app"></div>
</body>
<script>
  //根组件(全局组件)
  const app = Vue.createApp({
    //直接应用两个自组件
    template: `
        <website />
        <describe />
    `
  });
  //子组件
  app.component('website', {
    template: ` <h2>JSPang.com</h2>`
  })
  app.component('describe', {
    template: ` <h2>技术胖的博客</h2>`
  })
  app.mount("#app");
</script>
组件优点:组件的可复用性
优点:组件与组件之间互不干扰,相互独立。
<body>
  <div id="app"></div>
</body>
<script>
  //根组件
  const app = Vue.createApp({
    //复用多个组件
    template: `
        <m-count />
        <m-count />
        <m-count />
    `
  });
  //子组件(全局组件)
  app.component('m-count', {
    data() {
      return {
        count: 0
      }
    },
    methods: {
      addFun() {
        this.count++;
      }
    },
    template: `
      <sapn>{{count}}</sapn>
      <button @click="addFun()">增加</button>
      <br/>
    `
  })
  app.mount("#app");
</script>
全局组件缺点描述
一旦定义了,就会占用系统资源,它是一直存在的,你在任何地方都可以使用这个全局组件,对性能产生影响。
全局组件(通过vue实例调用component())的概括:只要定义了,处处可以使用,性能不高,但是使用起来简单。
二、局部组件
创建与注册局部组件(认识components属性)
创建局部组件:直接使用一个对象{}来表示。
注册局部组件:需要配合传入到components对象属性中。
示例:
<body>
  <div id="app"></div>
</body>
<script>
  //1、创建局部组件:直接在一个对象中设置参数
  const counter = {
    data() {
      return {
        count: 0
      }
    },
    template: `<div>{{count}}<button @click="count++">增加1</button></div>`
  };
  const app = Vue.createApp({
    //2、注册局部组件,其完整写法:components: { counter:counter },前面一个是设置的别名,后一个是实际局部组件名称
    //下面我演示别名的写法,若是不使用别名使用components: { counter }即可!!!
    components: { changlu: counter },
    template: `
        <h2>JSPang.com</h2>
        <changlu />
    `
  });
  const vm = app.mount("#app")
</script>
局部组件的默认规定(组件名称首字母全都大写)
对于组件的名称采用全部首字母大写(驼峰命名法),示例如下:
const XieDaJiao = {
    template: `<h2>谢大脚</h2>`
}三、父子组件的传值(静态、动态)
父子组件的传值,包括静态传值和动态传值。
3.1、静态传值(name=“技小胖”)
静态传值
核心:直接在标签中使用name="技小胖",该种方式vue会进行自动解析并传入到props数组中。
<body>
  <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    //1、通过属性name传入到子组件中
    template: `
        <h2>JSPang.com</h2>
        <Son name="技小胖" />
    `
  });
  app.component('Son', {
    //2、通过使用props数组形式根将值接收到name属性里
    props: ['name'],
    template: `<div>{{name}} div </div>`
  })
  const vm = app.mount("#app");
</script>
效果:动态传值使用props属性接收后,对应的标签中不会有该键值了!
3.2、动态传值(v-bind:xx="xx"配合子组件中的props数组接收)
动态传递值
动态传值
将这个可改变的动态值放置在data对象里,之后使用v-bind动态从data对象中找到属性值并赋值给指定属性:
<body>
  <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        //在data对象中包含一个可修改的属性值(动态的)
        name: "cl"
      }
    },
    //1、绑定data对象中的name值也就是cl赋值到name参数并传递到全局组件中
    template: `
        <h2>JSPang.com</h2>
        <Son v-bind:name="name" />
    `
  })
  app.component('Son', {
    //2、同样使用name属性来进行接收这个属性值
    props: ['name'],
    template: `
        <h2>JSPang.com</h2>
        <div>{{name}}</div>
    `
  })
  const vm = app.mount("#app");
</script>
注意点:静态传值一定传的是字符串string,而动态传值是根据data对象中的属性来决定!!!
动态传递函数
动态传递函数:本质与上面传递值相同,都是传递的data对象里的值。
示例:
<body>
  <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        appFun: () => alert("我是根组件的appFun()方法")
      }
    },
    //1、还是老样子,编写v-bind:appFun = "appFun"来绑定对应data对象中的属性进行动态传函数
    template: `
        <h2>JSPang.com</h2>
        <Son :appFun="appFun" />
    `
  })
  app.component('Son', {
    //2、接收该函数
    props: ['appFun'],
    methods: {
      divClick() {
        //3、调用传入进行的函数
        this.appFun();
      }
    },
    //为标签添加点击事件来间接调用动态传入的父组件的函数
    template: `
        <h2>JSPang.com</h2>
        <div @click="divClick()">点我试试</div>
    `
  })
  const vm = app.mount("#app");
</script>
3.3、三种校验方式
①类型校验(props需要写成{}形式)
此时props需要写成{}形式,key为得到的数据,value表示需要校验的值:这种方式只是会在console中报出黄色警告信息,依旧还是将值传递过来的!
- Vue支持的校验类型包括:String、Boolean、Array、Object、Function和Symbol。
 
效果:仅仅有警告信息,传递依旧会执行。
app.component('Son', {
    //校验name是否为string类型
    props: {
        name: String
    },
    template: `<div>{{name}} div </div>`
})
②必填校验与默认值设置(required与default)
required
required(布尔值true,false):必须传递参数,若是设置了类型参数就必须要校验指定传递的类型。
效果:会报黄色警告信息,传递空值也就是阻止传递,程序也会依旧运行!
注意:与default同时存在时,default失效!
示例:
const app = Vue.createApp({
    data() {
        return {
            name: 123
        }
    },
    //进行传递值,只是传递的是number类型
    template: `
        <h2>JSPang.com</h2>
        <Son :name="name"/>
     `
})
app.component('Son', {
    //包含requred属性进行校验是否为String类型,不是就会阻止传递进来
    props: {
        name: {
            type: String,
            // required: false
            default: 'i am changlu'
        }
    },
    template: `<div>{{name}} div </div>`
})default
default(默认值):没有传递指定参数会设置默认值给指定参数。
效果:当没有传递过来指定值时,就会使用默认参数(没有required情况下)。
示例:
const app = Vue.createApp({
    data() {
        return {
            name: 123
        }
    },
    //没有进行传递值
    template: `
        <h2>JSPang.com</h2>
        <Son />
     `
})
app.component('Son', {
    //由于name没有接受到传递值并且没有required,就会使用default默认值
    props: {
        name: {
            type: String,
            default: 'i am changlu'
        }
    },
    template: `<div>{{name}} div </div>`
})
③精准校验(validator)
validator:精准校验是一个函数:通过布尔类型返回值来表示校验失败还是通过。
- true:表示通过校验。
 - false:表示没有通过校验,仅仅在console上报警告信息,传值同样成功!
 
示例:
<body>
  <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        name: 'xxxSPan'
      }
    },
    template: `
            <h2>JSPang.com</h2>
            <Son :name="name"/>
        `
  })
  app.component('Son', {
    props: {
      name: {
        type: String,
        //validator属性是一个函数用于进行简单校验,根据返回值来表示是否通过
        //与default不会有任何联系,记住default只是没有传递值的时候进行设置默认值的
        validator: function (value) {
          console.log(value.search("JSPang"))
          return value.search("JSPang") != -1
        },
        default: 'JSPang.com'
      }
    },
    template: `<div>{{name}} div </div>`
  })
  const vm = app.mount("#app")
</script>
四、单项数据流
4.1、父子组件传递属性补充
①以对象形式传递多个参数
之前仅仅学习了单个值单个值传递,若是参数有多个在父组件标签中需要写大量的绑定传值,造成冗余。
效果:将要传递的多个参数封装到一个对象中传递即可!

示例:
<body>
  <div id="app">
  </div>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          //1、将多个参数绑定至一个对象
          params: {
            content: 'xxx内容',
            a: 'a',
            b: 'b'
          }
        }
      },
      //2、使用v-bind='xx',来绑定某个对象进行传值
      template: `
          <exerTab v-bind="params" />
          `
    });
    app.component('exerTab', {
      //3、可直接使用对象内部的属性名来接收
      props: ['content', 'a', 'b'],
      template: `
          <span>{{ content }}-{{ a }}-{{ b }}</span>
          `
    });
    app.mount("#app");
  </script>
</body>
②xx-xx=''传递,props中xxXx接收
静态传递带有-号的属性:content-aa='xxx内容',需要使用props: ['contentAa']接收。
- 
content-aa =>contentAa,去掉-,并且之后字母大写拼接来接收! 
示例:
<body>
  <div id="app">
  </div>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
  <script>
    const app = Vue.createApp({
      //1、传递带有-的参数:content-aa
      template: `
          <exerTab content-aa='xxx内容' />
          `
    });
    app.component('exerTab', {
      //2、对应该类参数props接收,需要去掉-以及其后大写:contentAa
      props: ['contentAa'],
      template: `
          <span>{{ contentAa }}</span>
          `
    });
    app.mount("#app");
  </script>
</body>4.2、认识单项数据流
单项数据流:子组件可以使用父组件传递过来的数据,但是决定不能修改传递过来的数据!(可以想成调用函数时传递过来的是基本类型,而非地址引用传递)
效果:若是在子组件中修改父组件传递过来的值时就会报出warning提示并且会阻止修改该值!
<body>
  <div id="app"></div>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          content: '父组件中的content'
        }
      },
      template: `
          <exerTab :content='content' />
          `
    });
    app.component('exerTab', {
      props: ['content'],
      methods: {
        modifyContent() {
          this.content = '子组件修改了content';
        }
      },
      //绑定单击事件来尝试修改
      template: `
          <span @click="modifyContent()">{{ content }}</span>
          `
    });
    app.mount("#app");
  </script>
</body>
4.3、解决方案:实现在子组件中修改父组件传递过来的参数
方案:通过在data对象中设置一个新属性来接收传递来的参数,之后对该新属性进行赋值修改也能够达成目的!
示例:
<body>
    <div id="app"></div>
</body>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        },
        template: `
            <div>
                <changlu :count="count"/>
            </div>
        `
    });
    app.component('changlu', {
        props: ['count'],
        data() {
            return {
                //1、创建一个属性来进行代替count
                mycount: this.count
            }
        },
        methods: {
            //2、对新创建的属性进行赋值操作
            handlerClick() {
                this.mycount++;
            }
        },
        template: `
            <span @click="handlerClick()">{{mycount}}</span>
        `
    });
    app.mount("#app");
</script>
4.4、设计单向数据流原因

核心:通过父组件传递到子组件中的data对象里的值不能够随意更改否则就会影响到其他复用的组件了!!!
五、No-Props属性
5.1、不使用props传递时效果(默认会继承到子组件中,仅仅是单标签情况)
简而言之:就是不使用props属性来接收的属性!
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        },
        template: `
            <div>
                <changlu msg="hello,cl!"/>
            </div>
        `
    });
    app.component('changlu', {
        //若是不使用props接收默认就会放置在template的标签中
        template: `
            <span>changlu组件</span>
        `
    });
    app.mount("#app");
</script>
效果:可以看到对应的键值对继承到了子组件标签中。若是不想让键值对继承到子组件上,可以在组件对象中添加如下属性:
inheritAttrs: false
5.2、认识$attrs
若是不使用props属性接收且template模板有多个标签:不会生效

若此时有个需求就是让某个标签具有对应的属性:此时就使用到了$attrs对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5AmAOaDB-1652144855684)(C:\Users\93997\AppData\Roaming\Typora\typora-user-images\image-20210708223102983.png)]
实际应用$attrs
this.$attrs:一个代理对象,其中包含了所有父组件传递子组件时的键值对中,可以从这里获取!
示例:
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        },
        template: `
            <div>
                <changlu msg="hello,cl!" msg1="changlugege"/>
            </div>
        `
    });
    app.component('changlu', {
        methods: {
            //this.$attrs:是一个代理对象,该对象中包含父组件传值过来的所有属性
            handleClick() {
                console.log(this.$attrs);
            }
        },
        //v-bind:msg="$attrs.msg1" =>  绑定单个值
        //v-bind="$attrs" => 绑定所有父组件传递过来的值
        template: `
            <span @click="handleClick()">changlu组件</span>
            <span v-bind:msg="$attrs.msg1">changlu组件</span>
            <span v-bind="$attrs">changlu组件</span>
        `
    });
    app.mount("#app");
</script>

六、父子组件通信(子组件来发出指示让父组件执行指定函数)
目的:通过子组件来调用父组件的函数来改变父组件的data对象中的属性。
6.1、$emit来向外触发父组件函数(无参与有参)
无参向外触发
this.$emit('addOne');:意思是子组件向外发出了一个addOne的信号,外部可使用@add-one="handleAdd"来表示收到信息并执行父组件中的handleAdd()函数!
- 这里
@add-one并不是必须要使用的,xx-xx比较特殊对应的是xxXx,对于普通名称也是可以的。 
示例:
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        },
        methods: {
            //1、父组件定义方法函数来操控data对象中的属性
            handleAdd() {
                this.count++;
            }
        },
        //2、:count="count":父组件向子组件传递属性
        //   @add-one="handleAdd":其中addOne为子组件向外进行触发的动作,handleAdd为父组件中本身的方法
        template: `
            <div>
                <changlu :count="count" @add-one="handleAdd"/>
            </div>
        `
    });
    app.component('changlu', {
        //来接收传递过来的属性
        props: ['count'],
        methods: {
            handleClick() {
                //重要:向外触发父组件的函数(通过传递的方式)
                this.$emit('addOne');
            }
        },
        template: `
            <span @click="handleClick()">{{count}}</span>
        `
    });
    app.mount("#app");
</script>
有参向外触发
方式:将需要向父组件传递的参数写在第二个参数中即可,如this.$emit(信号名,参数)。

6.2、emits属性(方便管理向外触发的函数名称,有警告效果,数组以及对象形式)
目的:配合$emit向外抛出的名称填写,用于方便查看该组件向外触发的函数!
使用方式:直接在组件对象中设置属性emits: ['xxx','xxx']来进行声明表示向外触发的函数!
效果:一旦使用了该emits属性,那么就会对内部向外发出($emit)的函数名称进行校验,若是向外触发的没有在数组中显示就会出现警告信息,依旧执行父组件函数的!
数组形式

对象形式
附加功能:使用对象形式你能够编写对应的一个校验函数,若是返回true表示校验通过不会有警告信息,若是没有通过就会有警告信息依旧向外触发执行!

6.3、v-model配合$emit():实现非调用父组件函数来修改父组件对象属性值
6.3.1、v-model="xxx"默认值接收
方式:原本需要先将指定值进行传递之后配合$emit()向外抛出执行命令来进行执行,使用了v-model直接让父组件的对象某属性与子组件进行绑定连通在子组件中即可对其进行修改只不过需要配合$emit使用`
示例如下:
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 0
            }
        },
        //1、直接使用v-model来进行绑定对应属性值
        template: `
            <div>
                <changlu v-model="count"/>
            </div>
        `
    });
    app.component('changlu', {
        //2、使用v-model传递的默认必须使用modelValue来接收
        props: ['modelValue'],
        methods: {
            handleClick() {
                //3、依旧调用$emit()函数来执行,其中第一参数必须为update:modelValue
                //参数一:必须为update:modelValue
                //参数二:对于modelValue的操作,也就是说可以直接对外部属性进行修改
                this.$emit('update:modelValue', this.modelValue + 3);
            }
        },
        template: `
            <span @click="handleClick()">{{modelValue}}</span>
        `
    });
    app.mount("#app");
</script>
6.3.2、v-model:属性=“xxx”(设置别名接收)
针对于6.3.1进行简单修改即可!
好处:不用再在外部组件专门写一个方法来让内部向外触发调用了以及通过使用v-bind:xxx来进行传参!这里的方式更加的简洁明了推荐使用!!!

七、slot插槽(5个知识点)
①目的。②引用父组件还是子组件值。③插槽默认值。④具名标签(区分传递指定插槽)。⑤作用域插槽(子组件传值到父组件要传递的插槽内容中)。
1、slot插槽目的是为了从父组件中传递指定标签到子组件中来进行使用。
2、父组件中的插槽内容若是使用了{{}}就是引用的父组件中的值,在子组件中使用{{}}就是引用的子组件值。
const app = Vue.createApp({
    data() {
        return {
            content: '我是父组件'
        }
    },
    //将传递的标签内容写在<子组件></子组件>中
    template: `
        <changlu>
            <span>{{content}}</span>
        </changlu>
    `
});
app.component('changlu', {
    //在对应的位置使用<slot></slot>即可插入
    template: `
        <div>
            <slot></slot>
        </div>
    `
});3、插槽默认值:若是父组件中没有传递指定插槽内容,slot标签就会失效。若是我们想要给插槽中添加一些默认值(可以是标签或值),直接写在slot标签中即可!但要注意一旦父组件传递了插槽内容就不会显示定义在插槽中的默认值了!
//<slot>标签中可以定义默认内容,若是父组件不传递就会使用默认内容!传递就使用传递过来的内容
template: `
    <div>
        <slot>
            <button @click="handleclick()">点我下试试</button>    
        </slot>
    </div>
`4、若是插槽中有多个标签,此时不同的标签我们想用在不同位置,就可以使用具名标签,也就是使用template标签包裹并使用v-slot:xxx来进行标识写在template中,可简写为#,在子组件中的slot标签里并配置name="xxx" 来进行配对父组件传递过来的
//父组件:将传递的内容模块化<template v-slot:xxx></template>
//v-slot:btn1 => #btn1,可进行简化
template: `
    <changlu>
        <template v-slot:btn1>
            <button >按钮1</button>
        </template>
        <template v-slot:btn2>
            <button >按钮2</button>
        </template>
    </changlu>
`
//子组件:对应slot标签中设置name="xxx"来指定接收
template: `
    <div>
        <slot name="btn1"></slot>
        <div>content</div>
        <slot name="btn2"></slot>
    </div>
`5、作用域插槽:若是我们想要遍历父组件传递的插槽内容时,如何让插槽内容能够引用子组件里的data对象的值?
在插槽中使用:xx="xx"用来传递到父组件,在父组件使用子组件标签中使用v-slot="obj"来接收,传递来的值以键值对形式保存在obj对象里中,之后即可使用该对象中的属性进行{{}}显示操作了!!!
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                content: '我是父组件'
            }
        },
        //v-slot="{item}"来接收子组件传递过来的值,这里使用到了对象解构
        template: `
            <changlu v-slot="{item,dataArr}">
                <span>{{item}}</span>
            </changlu>
        `
    });
    app.component('changlu', {
        data() {
            return {
                dataArr: [1, 2, 3]
            }
        },
        //使用:item="item"来进行传值绑定
        //可以传多个值都是可以的,因为是通过对象的形式来接收的
        template: `
            <div>
                <slot v-for="item in dataArr" :item="item" :dataArr="dataArr"></slot>
            </div>
        `
    });
    app.mount("#app");
</script>八、动态组件(切换组件)
8.1、原生切换组件方式(借助v-if、v-show,keep-alive标签保存input状态)
原生切换自定义组件:
v-if来进行切换时input标签中的内容会清空,不会保存状态。若是想要保存状态使用<keep-alive>标签包裹。
v-show会保存状态。(原理是display:none,仅仅只是隐藏组件)
//父组件的template
data() {
    return {
        isSpan: true
    }
},
methods: {
    //改变isSpan的布尔值达到切换效果
    handleclick() {
        this.isSpan = this.isSpan ? false : true;
    }
},
template: `
    <m-span v-if="isSpan"></m-span>
    <keep-alive>
        <m-input v-if="!isSpan" ></m-input>
    </keep-alive>
    <button @click="handleclick">点我切换</button>
`
//两个子组件
app.component('m-span', {
    template: `
        <span>span标签显示</span>
    `
});
app.component('m-input', {
    template: `
        <input type="text"/>
    `
});8.2、动态组件(component标签以及:is)
好处:解放了之前切换组件时要将多个组件都写在template模板里,切换几个就需要写几个之后通过控制data对象里的布尔属性来进行静态显示!
方式:<component :is="自定义标签名"></component>,就是这么简单,我们只需要在data对象中定义对应的自定义标签名就能够实现动态切换不同的组件,牛逼!
- 特殊:对于
input标签,我们要想保存切换前的状态就需要在外面包裹<keep-alive>标签。 
示例:
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                //目标要显示的自定义组件名
                showlabel: 'm-span'
            }
        },
        methods: {
            handleclick() {
                //更改标签名
                this.showlabel = this.showlabel == 'm-span' ? 'm-input' : 'm-span';
            }
        },
        //动态组件实现:不需要通过v-if、v-show来进行判断显示,而是直接通过:is="组件名"的显示动态显示组件
        //<component :is="showlabel"></component>  => 动态组件
        //<keep-alive> => 保存input输入框状态
        template: `
            <keep-alive>
                <component :is="showlabel"></component>
            </keep-alive>
            <button @click="handleclick">点我切换</button>
        `
    });
    //自定义的两个组件
    app.component('m-span', {
        template: `
            <span>span标签显示</span>
        `
    });
    app.component('m-input', {
        template: `
            <input type="text"/>
        `
    });
    const vm = app.mount("#app");
</script>九、异步组件(实现异步加载组件)
原本之前我们直接定义的{},或者app.component('组件名',{})定义的组件都是同步组件。
异步组件目的:通过使用异步组件来动态的加载一些组件,可以将一些大型的项目拆分为小的js文件,在需要这些组件的时候就可以进行引入并进行使用组件
- 在父标签中若是使用了异步组件就会进行异步加载组件,不是同步加载了!
 
核心:
1、Vue.defineAsyncComponent(函数),需要传入一个函数。
2、函数要返回一个Promise,也就是异步,最终通过resolve({})或reject({})将对应的组件对象传递出去就称为异步组件!
//自定义异步组件
app.component('asynCom', Vue.defineAsyncComponent(() => {
    //返回一个Promise对象
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({
                template: `
                    <span>异步显示的span</span>
                `
            });//将对应的组件进行传递!!!
        }, 3000);
    })
}));示例:令组件延时3秒出现,可以看出异步的效果
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                item: 'synCom'
            }
        },
        template: `
            <asynCom/>
            <br/>
            <synCom/>
        `
    });
    //自定义的两个组件
    app.component('synCom', {
        template: `
            <span>span标签显示</span>
        `
    });
    //自定义异步组件
    app.component('asynCom', Vue.defineAsyncComponent(() => {
        //返回一个Promise对象
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve({
                    template: `
                        <span>异步显示的span</span>
                    `
                });//将对应的组件进行传递!!!
            }, 3000);
        })
    }));
    const vm = app.mount("#app");
</script>十、补充知识点
10.1、v-once(让某个元素标签只渲染一次)
v-once:只显示一次值在页面上,之后若是动态修改值也不会进行渲染。(实际值进行了修改)
示例:
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 1
            }
        },
        methods: {
            handleclick() {
                this.count += 1;
            }
        },
        //v-once="xxx"进行绑定
        template: `
            <span v-once="count">{{count}}</span>
            <button @click="handleclick">点我切换</button>
        `
    });
    const vm = app.mount("#app");
</script>10.2、$refs(定位获取到指定设置ref的dom元素)
方式:在某个标签中添加ref="xxx",之后可通过js中使用$refs.xxx来获取到指定标签!
应用:获取到某个结点之后可以对其中内容来进行一些操作,最好还是操作dom元素!
示例:点击按钮之后来更改按钮中的值(其实完全可以进行双向绑定修改值的)
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 1
            }
        },
        methods: {
            handleclick() {
                //2、获取到指定设置ref的dom元素
                console.log(this.$refs.mspan);
                //应用:插入新的内容
                this.$refs.mspan.innerHTML = "changlu";
            }
        },
        //1、在指定标签上定义ref="xxx"
        template: `
            <button ref="mspan" @click="handleclick">{{count}}</button>
        `
    });
    const vm = app.mount("#app");
</script>
10.3、provide属性以及inject属性(嵌套组件中传递值)
10.3.1、引出provide以及使用
引出provide以及inject属性
若是我们定义了两个子组件,在父组件中使用了第一个子组件,接着在第一个子组件中复用了第二个子组件,此时第二个子组件想要使用父组件中的值就需要一层一层借助通:xx="xx"+props属性来进行一层层往下传递。
弊端:若是有大量嵌套组件,需要重复进行v:bind以及props设置传参!
使用provide以及inject属性
方式:父组件定义provide: {}属性,孙子组件或其他子组件使用inject: ['xx']即可拿到!
注意:provide对象想要动态使用this.属性拿到父组件data对象里的值,需要写成如provide(){ return {xxx=this.xxx}}
//想要拿到data对象里的值,需要编写provide函数接着子啊返回对象里进行获取!
provide() {
    return {
        count: this.count
    }
},示例:
<body>
    <div id="app"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                count: 1
            }
        },
        //1、定义可供其他组件使用的provide属性
        provide: {
            count: 1
        },
        template: `
            <firstspan></firstspan>
        `
    });
    //子组件:嵌套了子组件
    app.component('firstspan', {
        template: `
            <secspan/>
        `
    });
    //子组件
    //2、想要获取到provide对象中的属性,需要使用inject数组设置对应属性名来进行接收
    app.component('secspan', {
        inject: ['count'],
        template: `
            <span>{{count}}</span>
        `
    })
    const vm = app.mount("#app");
</script>
10.3.2、注意点provide属性绑定inject属性并不是双向绑定!!!
使用了provide属性可以方便父组件与嵌套组件方便进行传值,但是需要注意的是provide属性绑定inject属性并不是双向绑定,父组件中的值发生改变并不会重新传值过去来让子组件进行修改。
解决方案:之后会使用高级的语法来进行解决!(笨方法就是:xxx绑定以及使用props进行接收)
我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
 欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
 Q群:851968786 我们可以一起探讨学习
 注明:转载可,需要附带上文章链接










