阅读 64

组件通信、插槽、动态组件、异步组件

修饰符

  • lazy
    <!-- lazy修饰符是当点击回车的时候绑定的值才会改变 -->
 <input type="text" v-model.lazy='age'>
  • number
        <!-- number修饰符指的是age的类型一致设置成number类型(没有的话是设置成了字符串型) -->
    <input type="text" v-model.number='age'>
  • trim
    <!-- teim是过滤掉输入时两边的空格 -->
    <input type="text" v-model.trim='message'>

组件化开发

  • 思想:提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
  • 使用方法
    1.使用Vue.extend()创建出来组件构造器对象
    2.调用Vue.component()方法注册组件
    3.使用组件
<div id="app">
  <!-- 3.使用 -->
  <mycpn>
  </mycpn>
  <mycpn>
  </mycpn>
</div>

<body>
  <script src="../../node_modules/vue/dist/vue.js"></script>
  <script>
    // 1. 创建一个组件构造器对象
    const cpn = Vue.extend({
        template: `<div>
                                        <h2>欢迎使用组件化开发</h2>
                                        <p>这里是内容</p>
                                        <p>哈哈哈</p>
                                    </div>`
      })
      // 2.注册组件Vue.component('标签名', 组件构造器对象)
    Vue.component('mycpn', cpn)
    const app = new Vue({
      el: '#app',
      data: {

      }
    })
  </script>
</body>

综合写法,省略掉了Vue.extend(),但是component方法会自动调用extend函数

<div id="app">
  <!-- 3.使用 -->
  <mycpn>
  </mycpn>
</div>
    Vue.component('mycpn', {
      template: `<div>
                                        <h2>欢迎使用组件化开发</h2>
                                        <p>这里是内容</p>
                                        <p>哈哈哈</p>
                                    </div>`
    })

全局组件和局部组件

image.png

模板的两种写法

创建构造器&&注册组件

Vue.component('mycpn', {
      template: '#cpn'
    })
  • <script>标签
 <script type="text/x-template" id="cpn">
    <div>
      <h2>欢迎使用组件化开发</h2>
      <p>这里是内容</p>
      <p>哈哈哈</p>
    </div>
  </script>
  • <template>标签
 <template id="cpn">
        <div>
          <h2>欢迎使用组件化开发</h2>
      <p>这里是内容</p>
      <p>哈哈哈</p>
    </div>
    </template>

在模板里面添加数据

  • 模板里面的数据要存放在data函数当中
     <template id="cpn">
             <div>
          <h2>{{title}}</h2>
              <p>这里是内容</p>
              <p>哈哈哈</p>
            </div>
    </template>
    Vue.component('mycpn', {
      template: '#cpn',
      data() {
        return {
          title: '我是标题'
        }
      }
    })
  • 为什么data是一个函数,而不是一个对象? 因为Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。

组件通信

父到子通信
image.png
  • props的三种写法
 const cpn = {
      template: '#npm',
      // 1.数组类型写法
      // props: ['cmessage']
      props: {
        /* 2.类型限制
        cmessage: String */
        // 3.提供一些默认值
        cmessage: {
          type: String,
          default: 'aaaa',
          //如果没有传值,默认为aaaa,且报错
          required: true
        },
        cmovies: {
          type: Array,
          //type:object默认值必须是函数返回,目的是防止不要智指向 一个对象
          default () {
            return [111]
          }
        }
      }
    }
  • 更多写法


    image.png
  • 关于props里面驼峰命名时传参的方式


    image.png


在父组件中的自定义标签中,添加标签属性,那么在子标签中添加 v-bind="$attrs",这样子组件中就会添加父组件自定义标签里的属性


子到父的通信
  • 子到父数据的传递需要使用自定义事件
 <div id="app">
    <mycpn @item-click='cClick'></mycpn>
  </div>
  <template id="npm">
        <div>
            <button v-for='item of students' v-on:click='btnClick(item)'>{{item.name}}</button>
        </div>
    </template>
  <script src="../../node_modules/vue/dist/vue.js"></script>
  <script>
    //定义子组件
    const cpn = {
      template: '#npm',
      data() {
        return {
          students: [{
            id: 1,
            name: 'guo'
          }, {
            id: 2,
            name: 'zhang'
          }, {
            id: 3,
            name: 'liu'
          }, {
            id: 4,
            name: 'li'
          }]
        }
      },
      methods: {
        btnClick(item) {
          this.$emit('item-click', item)
        }
      }
    }
    const app = new Vue({
      el: '#app',
      data: {

      },
      components: {
        mycpn: cpn
      },
      methods: {
        cclick(item) {
          console.log('cClick', item);
        }
      }
    })
  </script>
image.png

父子间双向通信

image.png
<body>
  <div id="app">
    <cpn :pnum1='num1' :pnum2='num2' @cchange1='fchange1' @cchange2='fchange2' @changenum1='changenum1'></cpn>
  </div>
  <template id="tem">
<div>
    <h1>props:{{pnum1}}</h1>
    <h1>data:{{cnum1}}</h1>
    <input type="text" v-bind:value='cnum1' @input='cinputchange1'>
    <h1>props:{{pnum2}}</h1>
    <h1>data:{{cnum2}}</h1>
    <input type="text" v-bind:value='cnum2' @input='cinputchange2'>
</div>
    </template>
  <script src="../../node_modules/vue/dist/vue.js"></script>
  <script>
    //子组件
    const cpn = {
        template: '#tem',
        props: {
          //只接受父级传递过来的参数,不能手动对其进行修改
          pnum1: Number,
          pnum2: Number
        },
        data() {
          //因为父级也会改变props里面的值,所以我们初始化出来一个变量,来接受父级的改变的变量
          return {
            cnum1: this.pnum1,
            cnum2: this.pnum2
          }
        },
        methods: {
          cinputchange1(event) {
            this.cnum1 = event.target.value
            this.$emit('cchange1', this.cnum1)
            this.cnum2 = this.cnum1 * 100
            this.$emit('changenum1', this.cnum1 * 100)
          },
          cinputchange2(event) {
            this.cnum2 = event.target.value
            this.$emit('cchange2', this.cnum2)
            this.cnum1 = this.cnum2 * 0.01
            this.$emit('changenum2', this.cnum1)
          }
        }
      }
      //父组件
    const app = new Vue({
      el: '#app',
      data: {
        num1: 1,
        num2: 2
      },
      components: {
        cpn
      },
      methods: {
        fchange1(pnum1) {
          this.num1 = pnum1 * 1
        },
        fchange2(pnum2) {
          this.num2 = pnum2 * 1
        },
        changenum1(num) {
          this.num2 = num
        },
        changenum1(num) {
          this.num1 = num
        }
      }
    })
  </script>
</body>
image.png

父子组件通信之$children(Vue3已经弃用了)

      methods: {
        btnClick() {
          //以数组的形式输出所有的组件
          console.log(this.$children);
          //调用子组件的函数
          this.$children[0].cfn()
            //由于组件之间可能会插入其他的组件,所以但用数组的形式选择相应的子模块可能会出问题
            //refs的用法
          console.log(this.$refs); //输出一个对象,对象里包含组件中所有包含ref的组件,
          console.log(this.$refs.aaa.message) //输出子数据

        }
      }

子访问父之 $parent $root $el(可以拿到子组件整个模板)

  • 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了
  • 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题
 const app = new Vue({
      el: '#app',
      data: {
        message: '我是父组件'
      },
      components: {
        cpn: {
          template: '#cpn',
          methods: {
            btnClick() {
              //访问父组件
              console.log(this.$parent);
              console.log(this.$parent.message);
              console.log(this.$root);
            }
          },
          components: {
            ccpn: {
              template: '#ccpn',
              methods: {
                cbtnClick() {
                  //访问根组件
                  console.log(this.$root.message);
                }
              }
            }
          }
        }
      }
    })
  • $refs能够拿到所有绑定ref的DOM元素
    image.png

非父子组件之间的访问(信息由高到低)

父组件添加新属性:

provide:{
name:"why"
age:18
}
  • provide里面的内容想要响应式处理需要用到computed,computed返回的是一个ref对象,需要取出其中的value来使用
  • 当前的this指向的是根组件本身,即this能够访问到data里面的值


    image.png

子组件也添加新属性

inject:["name","age"]

事件总线

image.png

在Vue2中

image.png

在Vue3中

如果想AB之间完成事件传递,那就要用到以下办法

  • 下载mittnpm install mitt
  • 封装一个工具
    image.png
  • 导入工具并在B里面定义事件
 btnClick() {
        console.log("about按钮的点击");
        emitter.emit("why", {name: "why", age: 18});
        // emitter.emit("kobe", {name: "kobe", age: 30});
      }
  • 在A里面监听函数
created() {
      emitter.on("why", (info) => {
        console.log("why:", info);
      });

      emitter.on("kobe", (info) => {
        console.log("kobe:", info);
      });
//所有的事件  type:事件名,info:传过来的信息
      emitter.on("*", (type, info) => {
        console.log("* listener:", type, info);
      })
    }
  • 取消事件


    image.png

插槽<slot></slot>

  • 模板中slot标签里没有内容的时候,可以通过自定义组件标签里添加内容插入
  • 模板中slot标签中有内容的时候,那就是默认插入的内容,若自定义组件标签里有内容,则会对其就行替换


    image.png

具名slot

  • 如果自定义模板标签里的内容没有写name属性,则会替换所有无名slot标签内容
    image.png

    第二种写法
  • v-slot:center 可以缩写成 #center

插槽作用域改变

image.png
 <div id="app">
    <cpn>
      <template slot-scope='info'>{{info.data.join(' - ')}}</template>
    </cpn>
  </div>
  <template id="cpn">
        <div>
            <slot :data='movies'></slot>
        </div>
    </template>

vue3中

image.png

动态组件

  • component 是内置组件,通过里面的is属性指定是哪个组件显示


    image.png

keep-alive

  • include:只有匹配到name属性的组件才会被缓存

  • exclude:匹配到任何之间的名称都不会被缓存

  • max:最多可以缓存的数目,超过这个数目之后,会把最久未使用的组件删除缓存


    image.png

在webpack打包的时候,将不同的函数分包

image.png

异步组件

  • 通过定义异步组件的方式引入组件,并注册组件之后再使用组件,那webpack在进行打包的时候会进行分包处理
  • 写法一:
  import Home from './Home.vue';
  const AsyncCategory = defineAsyncComponent(() => import("./AsyncCategory.vue"))
  • 写法二:
  const AsyncCategory = defineAsyncComponent({
    loader: () => import("./AsyncCategory.vue"),
    loadingComponent: Loading,
    // errorComponent,
    // 在显示loadingComponent组件之前, 等待多长时间
    delay: 2000,
    /**
     * err: 错误信息,
     * retry: 函数, 调用retry尝试重新加载
     * attempts: 记录尝试的次数
     */
    onError: function(err, retry, attempts) {

    }
  })

异步组件和Suspense

  • Suspense:内置的全局组件,它里面有两个插槽,一个名叫:default(默认显示),另一个是:fallback(default能显示就不会显示该组件)
  • 注册Suspense组件之后,使用组件,
    image.png

作者:大佬教我写程序

原文链接:https://www.jianshu.com/p/c32f584efcab

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐