Skip to content

第二章 Vue2进阶使用 v1.0

第一部分 Vue自定义

一、自定义指令(directive)

通过directive定义自己的指令,封装一些dom操作,扩展额外功能
在对应使用范围内通过:<标签名 v-指令名>使用

1.全局注册

全局注册通常在main.js文件中进行,指令可以在任何组件中使用,无需额外的导入或声明。

javascript
Vue.directive('指令名', {
  "inserted" (el){  // 钩子函数,inserted在元素添加到页面时进行操作
    // 对el标签,扩展额外功能
  }
})
text
bind: 只调用一次,指令第一次绑定到元素时调用。
inserted: 被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)。
update: 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
componentUpdated: 所在组件的 VNode 及其子 VNode 更新后调用。
unbind: 只调用一次,指令与元素解绑时调用。
示例(插入元素时聚焦)
javascript
Vue.directive('focus', {
  "inserted" (el){
    el.focus()
  }
})
vue
<template>
  <input v-focus>
</template>

2.局部注册

局部注册的指令只能在定义它的组件内使用,通常在组件的directives选项中进行。

vue
<script>
  export default {
    directives: {
      "指令名": {
        inserted (el) {  //在元素添加到页面时进行操作
          //对el标签,扩展额外功能
        }
      }
    }
  }
</script>

3.自定义指令传值

  1. 绑定指令时,可以传入值为指令绑定具体参数值:<标签名 v-指令名="参数值">
  2. 在注册时可以通过binding.value拿到指令值,指令值修改会触发update函数
javascript
directives: {
  color: {
    inserted (e1, binding) {  // 在元素添加到页面时进行操作
      el.style.color = binding.value
    },
    update (el, binding) {    // 在指令值更新时进行操作
      el.style.color = binding.value
    }
  }
}
vue
<template>
  <input v-color="#000000">
</template>

4.使用示例(加载指令封装)

v-loading封装
text
场景:实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态,用户体验不好
分析:
1. 本质loading效果就是一个蒙层,盖在盒子上
2. 数据请求中开启loading状态,添加蒙层;请求结束关闭loading状态,移除蒙层
实现:
1. 准备一个loading类,通过伪元素定位设置宽高,实现蒙层
2. 开启关闭loading状态(添加移除蒙层),本质只需要添加移除类即可
3. 通过自定义指令语法进行封装复用
vue
<template>
  <div class="box" v-loading="isLoading"></div>
</template>
<script>
  import axios from 'axios'
  export default {
    data () {
      return {
        isLoading: false
      }
    },
    async created () {
      this.isLoading = true
      const res = await axios.get("http://...")
      this.list = res.data.data
      this.isLoading = false
    },
    directives: {
      loading: {
      	inserted(el,binding) {
          binding.value ? el.classList.add('loading') : el.classList.remove('loading')
        },
        update(el,binding) {
          binding.value ? el.classList.add('loading') : el.classList.remove('loading')
        }
      }
    }
  }
</script>
<style>
  loading:before{  /* 1.定义loading类实现蒙层 */
    content:'';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%; 
    background: #fff url('./loading.gif') no-repeat center;  //url提供gif作蒙层
  }
</style>

二、插槽

插槽可以让子组件内部的一些结构支持自定义,允许子组件定义可复用的布局,同时将一些区域保留为可填充的内容。

1.插槽的使用

vue
<template>
  <div>
    <slot></slot>  <!-- 在需要定制的地方指定插槽 -->
  </div>
</template>
vue
<template>
  <div>
    <MyDialog>
      插槽的内容
    </MyDialog>
  </div>
</template>
<script>
  import MyDialog from 'MyDialog.vue'
  export default {
    components: {
      MyDialog
    }
  }
</script>
vue
<!--封装组件时,可以为预留的插槽提供显示的后备内容(默认值),若传值则会替换默认内容-->
<template>
  <div>
    <slot>插槽的默认内容</slot>
  </div>
</template>

2.具名插槽

一个组件内有多处结构,需要外部传入标签,进行定制时需要用到具名插槽

vue
<!-- 使用多个slot和name属性区分名字 -->
<template>
  <div class="dialog-header">
    <slot name="header">header插槽的默认内容</slot>
  </div>
  <div class="dialog-content">
    <slot name="content">content插槽的默认内容</slot>
  </div>
  <div class="dialog-footer">
    <slot name="footer">footer插槽的默认内容</slot>
  </div>
</template>
vue
<!-- template标签配合 v-slot: 分发对应标签,v-slot: 可以简化为 # -->
<template>
  <MyDialog>
    <template v-slot:head>
      header
    </template>
    <template v-slot:content>
      content
    </template>
    <template #footer>  <!-- v-slot: 简化为 # -->
      footer
    </template>
  </MyDialog>
</template>

3.作用域插槽

定义slot插槽的同时,是可以传值的。插槽上可以绑定数据,将来使用组件时可以用。

  1. slot标签以属性方式传值:<slot :id="item.id" msg="element"></slot>
  2. 在template中通过#插槽名="obj"接收,默认插槽名为default
vue
<template>
  <MyTable :list="list">
    <template #default="obj">
      <button @click="del(obj.id)">删除</button>
    </template>
  </MyTable>
</template>
示例:列表管理
vue
<template>
  <div>
    <MyTable :data="list">
      <!-- 3. 通过template #插槽名="变量名" 接收子组件传递的对象 -->
      <template #default="obj">
        <button @click="del(obj.row.id)">
          删除
        </button>
      </template>
    </MyTable>
    <MyTable :data="list2">
      <template #default="{ row }">
        <button @click="show(row)">查看</button>
      </template>
    </MyTable>
  </div>
</template>

<script>
import MyTable from './components/MyTable.vue'
export default {
  data () {
    return {
      list: [
        { id: 1, name: '张小花', age: 18 },
        { id: 2, name: '孙大明', age: 19 },
        { id: 3, name: '刘德忠', age: 17 },
      ],
      list2: [
        { id: 1, name: '赵小云', age: 18 },
        { id: 2, name: '刘蓓蓓', age: 19 },
        { id: 3, name: '姜肖泰', age: 17 },
      ]
    }
  },
  methods: {
    del (id) {  //过滤(删除)对应元素
      this.list = this.list.filter(item => item.id !== id)
    },
    show (row) {
      alert(`姓名:${row.name}; 年纪:${row.age}`)
    }
  },
  components: {
    MyTable
  }
}
</script>
vue
<template>
  <table class="my-table">
    <thead>
      <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>年纪</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(item, index) in data" :key="item.id">
        <td>{{ index + 1 }}</td>
        <td>{{ item.name }}</td>
        <td>{{ item.age }}</td>
        <td>
          <!-- 1. 给slot标签,添加属性的方式传值 -->
          <slot :row="item"></slot>
          <!-- 2. 将所有的属性,添加到一个对象中 -->
          <!--{ row: { id: 2, name: '孙大明', age: 19 } }-->
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    data: Array
  }
}
</script>

<style scoped>
.my-table {
  width: 450px;
  text-align: center;
  border: 1px solid #ccc;
  font-size: 24px;
  margin: 30px auto;
}
.my-table thead {
  background-color: #1f74ff;
  color: #fff;
}
.my-table thead th {
  font-weight: normal;
}
.my-table thead tr {
  line-height: 40px;
}
.my-table th,
.my-table td {
  border-bottom: 1px solid #ccc;
  border-right: 1px solid #ccc;
}
.my-table td:last-child {
  border-right: none;
}
.my-table tr:last-child td {
  border-bottom: none;
}
.my-table button {
  width: 65px;
  height: 35px;
  font-size: 18px;
  border: 1px solid #ccc;
  outline: none;
  border-radius: 3px;
  cursor: pointer;
  background-color: #ffffff;
  margin-left: 5px;
}
</style>

第二部分 路由

一、应用程序

二、VueRouter

三、VueRouter导航

四、组件缓存

第三部分 Vuex