vue基础第六天

211 阅读3分钟

vue基础第六天

组件进阶

动态组件

多个组件使用同一个挂载点,并动态切换,这就是动态组件

使用vue内置的component组件 配合is属性实现相应功能(改变is的属性值为要显示的组件名即可)

<template>
  <div>
    <button @click="componentName = 'UserInfo'">个人信息</button>
    <button @click="componentName = 'UserName'">账号登录</button>
    <!-- vue内置标签 component 是动态组件挂载点, 会根据绑定的 is 属性显示名字对应的组件 -->
    <component :is="componentName" />
  </div>
</template>

<script>
import UserInfo from '@/components/UserInfo.vue'
import UserName from '@/components/UserName.vue'
export default {
  data() {
    return { 
      componentName: 'UserInfo'
    }
  },
  components: {
    UserInfo,
    UserName
  }
}
</script>

<style>

</style>

组件缓存

组件切换会导致组件被频繁销毁和重新创建, 性能不高

使用vue内置的keep-alive组件,可以让包裹的组件保存在内存中不被销毁,页面更快呈现 性能更高

    <!-- vue内置的keep-alive组件把要缓存的组件包起来  不会频繁的创建和销毁组件,页面呈现更快 -->
    <keep-alive>
      <!-- vue内置标签 component是动态组件挂载点  会根据绑定的 is 属性显示名字对应的组件 -->
      <component :is="isShow" />
    </keep-alive>
组件缓存生命周期:

被缓存的组件不再创建和销毁, 而是激活和非激活

  • activated - 激活
  • deactivated - 失去激活状态

keep-alive可以提高组件的性能, 内部包裹的标签不会被销毁和重新创建, 触发激活和非激活的生命周期方法

1653115469975.png

组件插槽

通过 slot 标签, 让组件内可以接收不同的标签结构显示

给组件插入什么标签, 组件就显示什么标签

语法口诀:

  1. 组件内用占位
  2. 使用组件时夹着的地方, 传入标签替换slot
插槽默认内容

如果外面不给传, 想给个默认显示内容

语法:

夹着内容默认显示内容, 如果不给插槽slot传东西, 则使用夹着的内容在原地显示

插槽与默认值示例:

子组件 - src/components/TopBar.vue

<template>
  <div class="box">
    <div class="left"></div>
    <div class="right">
      <!-- <TopBar/> -->
      <!--  <slot/>插槽占位符 -->
      <!-- 单<slot/>标签-->
      <!-- <slot /> -->
      <slot>
        <!--<slot></slot> 双标签 没有传入的替换标签 可设置在双标签内设置默认内容   -->
        <button>返回</button>
      </slot>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style lang="less" scoped>
.box {
  display: flex;
  justify-content: space-between;
  padding: 10px 30px;
  border: 1px solid #ccc;
}
button {
  border: none;
  background-color: orange;
  margin-left: 10px;
  padding: 5px 5px;
  color: #fff;
}
</style>

父组件 - app.vue:

<template>
  <div>
    <!-- <TopBar/> -->
    <!-- TopBar改用双标签  包裹要传入替换slot的标签 -->
    <h2>员工</h2>
    <TopBar>
      <button>新增</button>
      <button>查看</button>
    </TopBar>

    <h2>薪资</h2>
    <TopBar>
      <button>报表</button>
    </TopBar>

    <h2>无替换</h2>
    <TopBar/>
  </div>
</template>

<script>
import TopBar from "./components/TopBar.vue";
export default {
  components: {
    TopBar,
  },
};
</script>

<style>
</style>
具名插槽:

一个组件内有2处以上需要外部传入标签的地方

语法:

  • slot使用name属性区分名字

  • template配合v-slot:名字来分发对应标签

v-slot:可以简化成#

具名插槽示例:

子组件 - src/components/TopBar.vue

    <div class="left">
      <slot name="left"/>
    </div>
    <div class="right">
      <!-- <TopBar/> -->
      <!--  <slot/>插槽占位符 -->
      <!-- 单<slot/>标签-->
      <!-- <slot /> -->
      <slot>
      <slot name="right">
        <!--<slot></slot> 双标签 没有传入的替换标签 可设置在双标签内设置默认内容   -->
        <button>返回</button>
      </slot>
    </div>

父组件 - app.vue:

<template>
  <div>

    <!--  🚨🚨🚨v-solt: 可以简化成#  -->
    <TopBar>
      <template #left>
        员工概览
      </template>
      <template #right>
        <button>新增</button>
        <button>查看</button>
      </template>
    </TopBar>


    <TopBar>
      <template v-slot:left>
        薪资
      </template>
      <template v-slot:right>
        <button>报表</button>
      </template>
    </TopBar>

    <h2>无替换</h2>
    <TopBar>
      <!-- 不传内容,显示子组件默认设置 -->
    </TopBar>
  </div>
</template>
作用域插槽

使用插槽时, 想使用子组件内变量

口诀:

  • 子组件, 在slot上绑定属性和子组件内的值

  • 使用组件, 传入自定义标签, 用template和v-slot="自定义变量名"

  • scope变量名自动绑定slot上所有属性和值 scope = {row: defaultObj}

子组件 - src/components/TopBar.vue

      <slot :row="monthList" name="left">
        {{monthList.first}} 报表
      </slot>

父组件 - app.vue:

      <template v-slot:left="scope">
       {{scope.row.second}} 报表
      </template>

总结:组件内变量绑定在slot上, 然后使用组件v-slot="变量" 变量上就会绑定slot身上属性和值

作用域插槽可以让组件更加灵活的适用于不同的场景和项目

子组件 -- MyTabe.vue:
<template>
  <table>
    <tr>
      <slot name="thead">
        <th>姓名</th>
        <th>年龄</th>
        <th>头像</th>
      </slot>
    </tr>
    <tr v-for="(item, index) in data" :key="index">
      <!-- 每一行有多少列,各自显示什么内容 对于一个灵活的表格组件来说,应该有使用者最终决定 -->
      <!-- ❗⚠️⚠️:row即v-bind:row  ❗⚠️⚠️:row="item"即 绑定属性=值 -->
      <slot :row="item" name="tbody">
        <td>{{ item.name }}</td>
        <td>{{ item.age }}</td>
        <td><img :src="item.headImgUrl" alt="" /></td>
      </slot>
      <!-- 由于遍历在子组件,item只有在这里能够拿到  但是具体渲染又在父组件,所以要将每一行的数据往父组件传  -->
    </tr>
  </table>
</template>

<script>
export default {
  props: ["data"],
};
</script>

<style>
table {
  border: 1px solid #000;
}
img {
  width: 100px;
}
tr,
td {
  border: 1px solid #000;
}
</style>
父组件 -- app.vue:
<template>
  <div>
    <MyTable :data="list">
      <!-- 🚨⚠️🚨组件标签 包住template标签 -->
      <!-- 🚨⚠️🚨具名插槽 的名是写在template标签上 -->
      <template #thead>
        <th>姓名</th>
        <th>年龄</th>
        <th>头像</th>
      </template>
      <!--  =后面的scope(名字可自定义)为接收到子组件传来的属性 scope = 子组件内slot标签上绑定的row属性 (row=item) -->
      <template v-slot:tbody="scope">
        <td>{{ scope.row.name }}</td>
        <td>{{ scope.row.age }}</td>
        <td><img :src="scope.row.headImgUrl" alt="" /></td>
      </template>
    </MyTable>
  </div>
</template>

<script>
import MyTable from "./components/MyTable.vue";
export default {
  data() {
    return {
      list: [
        {
          name: "小传同学",
          age: 18,
          headImgUrl:
            "http://yun.itheima.com/Upload/./Images/20210303/603f2d2153241.jpg",
        },
        {
          name: "小黑同学",
          age: 25,
          headImgUrl:
            "http://yun.itheima.com/Upload/./Images/20210304/6040b101a18ef.jpg",
        },
        {
          name: "智慧同学",
          age: 21,
          headImgUrl:
            "http://yun.itheima.com/Upload/./Images/20210302/603e0142e535f.jpg",
        },
      ],
    };
  },
  components: {
    MyTable,
  },
};
</script>

<style>
</style>

自定义指令

除了核心功能默认内置的指令 (v-modelv-show),Vue 也允许注册自定义指令。 v-xxx

自定义指令注册

在Vue内置指令满足不了需求时, 可以自己定义使用。获取标签, 扩展额外的功能

全局注册指令

在main.js用 Vue.directive()方法来进行注册, 以后随便哪个.vue文件里都可以直接用全局注册的指令

1653119729758.png

局部注册指令

只能在当前文件使用

1653119739269.png

注册和使用示例:

<template>
  <div>
    <!-- 使用自定义指令是跟普通指令一样,千万注意--🚨🚨🚨 要加上v-前缀!! -->
    <input v-showdom type="text" /> <br>
    <input type="text" /> <br>
    <input v-autoFocus type="text" />
  </div>
</template>

<script>
export default {
  //局部指令注册
  directives: {
    //指令名字(字符串)
    showdom: {
      //配置对象  可以指定使用这个指令的元素,在不同生命周期执行的函数
      //在这些钩子函数的形参中,默认第一可以获取元素本身,第二可以传参
      //自定义指令常用的钩子函数  inserted 和 update
      inserted(el) {
        console.log("显示了", el);
      },
    },
    autoFocus: {
      inserted(el) {
        //focus() 自动聚焦
        el.focus();
      },
    },
  },
};
</script>

<style>
</style>

自定义指令 - 传值

语法:

1653122796215.png

  • 在标签上使用v-指令名=" '值' " -- 传参

示例:

<template>
  <div>
    <!-- 使用自定义指令是跟普通指令一样,千万注意--🚨🚨🚨 要加上v-前缀!! -->
    <input v-showdom type="text" /> <br />
    <input type="text" /> <br />
    <input v-autoFocus="'orange'" type="text" />
  </div>
</template>

<script>
export default {
  directives: {
    showdom: {
      inserted(el) {
        console.log("显示了", el);
      },
    },
    autoFocus: {
      //第一个参数el 是元素本身。 第二个参数color是使用时传回的内容
      inserted(el, color) {
        //focus() 自动聚焦
        el.focus();
        console.log(color.value);
        el.style.backgroundColor = color.value
      },
    },
  },
};
</script>

<style>
</style>

🚨🚨🚨指令值变化触发自定义指令的update方法而非inserted方法