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 (el, 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:header>
      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" msg="测试文本"></slot>
           <!-- 2. 将所有的属性,添加到一个对象中 -->
           <!--
              {
                row: { id: 2, name: '孙大明', age: 19 },
                msg: '测试文本'
              }
            -->
        </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>

第二部分 路由 ​

一、应用程序 ​

1.单页与多页应用程序对比 ​

单页应用程序

多页应用程序

二、VueRouter ​

以Vue2 VueRouter v3.x为例

1.Vue路由简介 ​

路由是一种映射关系,而在Vue中,路由是指 路径 与 组件 之间的映射关系

2.VueRouter简介 ​

VueRouter是Vue官方的一个路由插件,可以在修改地址栏路径时,切换显示匹配的组件

官网:https://v3.router.vuejs.org/zh

3.使用步骤 ​

①定义路由(五步)

javascript
// 1.添加模块到当前工程:npm i vue-router
// 2.引入vue-router
import VueRouter from 'vue-router'
// 3.安装注册,VueRouter插件初始化
Vue.use(VueRouter)
// 4.创建路由对象
const router = new VueRouter()
// 5.注入,将对象注入new Vue实例中,建立关联
new Vue({
  render: h => h(App),
  router  //完整写法为 router: router ,当且仅当路由对象名为router时可简写为router
}).$mount('#app')

②创建需要的组件,配置路由规则

javascript
import Find from './views/Find.vue'
import My from './views/My.vue'
import Friend from './views/Friend.vue'

const router = new VueRouter({
  routes: [
    // {path: '地址栏路径', component: 视图组件, redirect: 重定向地址, children: 嵌套子路由 }
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ]
})

③配置导航,配置路由出口

html
<div>
  <a href="#/find">发现</a>
  <a href="#/my">我的</a>
  <a href="#/friend">朋友</a>
</div>
<div class="top">
  <router-view></router-view>
</div>

4.路由的封装抽离 ​

javascript
import Find from '@/views/Find'
import My from '@/views/My'
import Friend from '@/views/Friend'

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ]
})
// 导出路由对象
export default router
javascript
import Vue from 'vue'
import App from './App.vue'
import router from './router/index'

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

5.重定向 ​

语法:{ path: '/匹配路径', redirect: '/重定向路径' }

javascript
const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
  ]
})

6.404页面 ​

当路径找不到匹配时,给出提示页面,需要配置在路由最后

javascript
const router = new VueRouter({
  routes: [
    { path: '/home', component: Home },
    ... ,
    { path: '*', component: NotFound },
  ]
})

7.模式设置 ​

在Vue中,默认模式是hash路由模式,需要切换为真正路径模式可以通过改为history路由模式实现

  • hash路由(默认),例如: http://localhost:8080/#/home
  • history路由(常用),例如: http://localhost:8080/home (以后,上线需要服务器端支持)
javascript
const router = new VueRouter({
  mode: 'history',
  routes: []
})

三、VueRouter导航 ​

在VueRouter中提供了一个全局组件<router-link>用于取代<a>标签

优点:①能跳转,本质还是<a>标签 ②能高亮,默认提供高亮类名,用于设置高亮样式

标准写法:<router-link to="/path">显示文字</router-link>

对比<a>写法:<a href="#/path">显示文字</a>

高亮:<router-link>自动给当前导航添加两个高亮类名,需要在下面<style>添加对应属性

router-link-active:模糊匹配(使用更多),to="/my"可以匹配/my、/my/abc等

router-link-exact-active:精确匹配,to="/my"仅可以匹配/my

使用示例:.footer a.router-link-active { ... }

高亮配置:定制高亮类名,需要在router对象中进行配置

javascript
const router = new VueRouter({
  routes: [],
  linkActiveClass: "active",  //配置模糊匹配类名
  linkExactActiveClass: "exact-active",  //配置精确匹配类名
})

2.编程式导航——基本跳转 ​

①path路径跳转 ​
javascript
//简写
this.$router.push("路由路径")
//完整写法
this.$router.push({
  path: "路由路径",
  query: {  //可以替代查询参数传参
    参数名1: "参数值1",
    ...
  },
  params: { 参数名: "参数值" }  //可以替代动态路由传参
})
②name命名路由跳转 ​
javascript
this.$router.push({
  name: "路由名"  //需要定义路由指定name { name:"路由名", path:"/path", ... }
})
③其他跳转方式 ​
  1. 返回上一页:$router.back()

3.跳转传参 ​

①查询参数传参(适合多个参数) ​

标准语法:<router-link to="/path?参数名=值">显示文字</router-link>

获取对应页面组件接收传递的参数值:this.$route.query.参数名

②动态路径传参(适合单个参数) ​
  1. 配置动态路由
javascript
const router = new VueRouter({
  routes: [
    { path:'/search/:参数名', component: Search },  //使用 :参数名 指定动态路由参数(必选)
    { path:'/search/:参数名?', component: Search }  //使用 ? 表示参数可选
  ]
})
  1. 配置链接导航:<router-link to="/path/参数值">显示文字</router-link>
  2. 对应页面组件接受传递过来的值:this.$route.params.参数名

四、组件缓存 ​

路由跳转后,组件被销毁,返回之后组件又将重建,数据需要重新加载,此时需要用到组件缓存

1.组件缓存的优点 ​

在组件切换过程中把切换出去的组件保留在内存中,防止重复渲染,减少加载时间及性能消耗,提高用户体验性。

2.keep-alive简介 ​

keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

keep-alive是一个抽象组件:它自身不会渲染成一个DOM元素,也不会出现在父组件链中。

3.keep-alive使用 ​

vue
<keep-alive :include="['LayoutPage']">  <!-- 只缓存LayoutPage内容 -->
  <router-view></router-view>
</keep-alive>

4.keep-alive详解 ​

keep-alive有三个属性,可以通过指定不同的属性进行不同配置:

  1. include:组件名数组,只有匹配的组件会被缓存
  2. exclude:组件名数组,任何匹配的组件都不会被缓存
  3. max:最多可以缓存多少组件实例

keep-alive被缓存的组件会多两个生命周期钩子:actived(已激活) deactived(已失活)

第三部分 Vuex ​

一、简介 ​

1.Vuex ​

vuex是一个vue的状态管理插件,可以帮我们管理vue通用的数据(多组件共享的数据)

2.优点 ​

①共同维护一份数据,数据集中化管理

②响应式变化

③操作简洁(vuex提供了一些辅助函数)

二、使用 ​

1.创建仓库 ​

javascript
import Vue from 'vue'
import Vuex from 'vuex'

//使用插件
Vue.use(Vuex)

//创建仓库
const store = new Vuex.Store()

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

2.提供/访问数据 ​

Vuex数据流

Vuex使用