Vue-5

(一)动态组件

  • 动态组件就是动态切换组件的显示和隐藏:vue提供了一个内置的<component>组件,专门用来实现动态组件的渲染
 <!-- 1.component标签是vue内置的,作用:组件的占位符 -->
 <!-- 2.is属性的值,表示要渲染的组件的名字 -->
 <!-- 3.is属性的值,应该是组件在components节点下注册的名称 -->
  <component :is="comName"></component>
  • keep-alive缓存组件
<!-- keep-alive可以把内部的组件进行缓存,而不是销毁组件 -->
<!-- keep-alive对应的生命周期 -->
<!-- 当组件被缓存时,会自动触发组件的deactivted生命周期函数 -->
<!-- 当组价被激活时,会自动触发组件的activated生命周期函数 -->
<!-- keep-active属性用来指定:只有名称匹配的组件会被缓存,多个组件名之间用英文的逗号,分隔 --> <keep-alive include="Left"> <component :is="comName"></component> </keep-alive>
  • 组件的name
export default{
        //当提供了name属性后,组件的名字就是name属性的值
        //对比:
        //1.组件的“注册名称”的主要应用场景,以标签的形式,把注册好的组件,渲染和使用到页面结构之中
        //2.组件声明时候的“name”名称的主要应用场景,结合<keep-alive>标签实现组件的缓存功能,以及在测试工具中看到的组件name名称
        name: 'MyLeft',
}
  • 案例
    • App.vue
<template>
  <div class="app-container">
      <h1>App根组件</h1>
      <hr>
      <button @click='comName="Left"'>显示Left组件</button>
      <button @click="comName='Right'">显示right组件</button>
      <hr>
      <div class="box">
          <!-- keep-alive可以把内部的组件进行缓存,而不是销毁组件 -->
          <!-- keep-alive对应的生命周期 -->
          <!-- 当组件被缓存时,会自动触发组件的deactivted生命周期函数 -->
          <!-- 当组价被激活时,会自动触发组件的activated生命周期函数 -->
          <!-- keep-active属性用来指定:只有名称匹配的组件会被缓存,多个组件名之间用英文的逗号,分隔 -->
          <!-- keep-alive属性还包含exclude指定不会被缓存的组件 -->
          <!-- 注意:incude和exclude只能包含一个,不能同时使用 -->
          <keep-alive include="Left">
              <component :is="comName"></component>
          </keep-alive>
      </div>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
    data(){
        return {
            // 要展示的组件的名字comName
            comName: 'Left'
        }
    },
    methods: {
    },
    components: {
        Left,
        Right
    }
}
</script>

<style lang="less">
    .app-container {
        overflow: hidden;
        padding-left: 10px;
        padding-right: 10px;
        background-color:#ECECEC;
    }
</style>
    • Left.vue
<template>
    <div class="left">
        <h4>Left组件---{{count}}</h4>
        <button @click="count++">count+1</button>
    </div>
</template>

<script>
    export default{
        data(){
            return {
                count: 0
            }
        },
        created() {
            console.log("Left组件被创建了");
        },
        // 当组件第一次被创建时,既会执行created生命周期,又会执行activated生命周期
        // 但是,当组件被激活时,只会触发activated生命周期,不再触发created
        activated(){
            console.log("组件被激活了");
        },
        deactivated() {
            console.log("组件被缓存了");
        },
        destroyed(){
            console.log("Left组件被销毁了");
        }
    }
</script>

<style>
    .left {
        height: 300px;
        padding-left: 10px;
        background-color: orange;
        overflow: hidden;
    }
</style>
    • Right.vue
<template>
    <div class="right">
        <h4>Right组件</h4>
    </div>
</template>

<script>
    export default{
        
    }
</script>

<style>
    .right {
        height: 300px;
        background-color: royalblue;
        overflow: hidden;
    }
    
    h4 {
        margin-left: 10px;
    }
</style>

(二)插槽

   插槽(Slot)是vue为组件的封装者提供的能力,运行开发者在封装组件时,把不确定的,希望由用户指定的部分定义为插槽

  • 插槽的基本语法格式:v-slot的简写为#
  <!--子组件 Right.vue-->
  <div class="right">
        <h4>Right组件</h4>
        <!-- 声明一个插槽 -->
        <!-- vue官方规定每一个slot插槽,都要有一个name名称 -->
        <!-- 如果省略了slot的name属性,则有一个默认的名字叫做default -->
        <slot name="default">这是default插槽的默认内容</slot>
    </div>
  <!-- 父组件 App.vue -->
  <div class="box">
      <Right>
        <!-- 默认情况下,在使用的组件的时候提供的内容都会被填充到名字为default的插槽中 -->
        <!-- 1.如果要把内容填充到指定名称的插槽中,需要v-slot: 这个指令 -->
        <!-- 2.v-slot:后面要跟上插槽的名字 -->
        <!-- 3.v-slot:指令不能直接用在元素身上,必须用在template标签上 -->
        <!-- 4.template这个标签,它是一个虚拟的标签,只起到包裹性质的作用,但是,不会被渲染为任何实质性的html元素 -->
        <!-- 5.v-slot:指令的简写形式是# -->
        <template #default>
            <p>我放个p</p>
        </template>
      </Right>
   </div>
  • 具名插槽的定义和使用
<!-- 父组件App.vue -->
<template #content>
    <div>
        <p>锦瑟无端五十弦</p>
        <p>一弦一柱思华年</p>
    </div>
</template>
<!-- 子组件Article.vue -->
<div class="content-box">
    <slot name="content"></slot>
</div>
  • 作用域插槽的定义和使用
<!-- 子组件 -->
<template #content="{msg, user}">
    <div>
        <p>锦瑟无端五十弦</p>
        <p>一弦一柱思华年</p>
        <p>{{msg}}</p>
        <p>{{user.age}}</p>
    </div>
</template>
<!-- 父组件App.vue -->
<!-- 文章的内容 -->
    <div class="content-box">
    <!-- 在封装组件时,为预留的slot提供属性对应的值,这种用法叫做作用域插槽 -->
    <slot name="content" msg="只是当时已惘然" :user="userinfo"></slot>
</div>
<script>
    export default{
        data(){
            return{
                userinfo:{
                    name: '张三',
                    age: 20
                }
            }
        }
    }
</script>

(三)自定义指令

  • vue中的自定义指令分为两种
    • 私有自定义指令

      在每个vue组件中,可以在directives节点下声明私有自定义节点

      • bind函数
        //私有自定义指令的节点
    directives:{
        //定义名为color的自定义指令,指向一个配置对象
        color: {
            //当指令第一次被绑定到元素身上的时候,会立即触发bind函数,只调用一次,当DOM更新是并函数不会触发
            //形参中的el表示当前指令所绑定到的那个DOM对象
            bind(el,binding){
                el.style.color = binding.value;
                console.log(binding);
            }
        }
    }
      • update函数
//bind函数只调用一次,当指令第一次绑定到元素时调用,当DOM更新时bind函数不会被触发,
//update函数会在每次DOM更新时被调用
     update(el,binding){
          el.style.color = binding.value;
     }
      • 简写
     //如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式
        color(el,binding){
            el.style.color = binding.value;
        }
    • 全局自定义指令

      全局共享的自定义指令需要通过Vue.directive()进行声明

//main.js
//全局自定义属性
Vue.directive('color',{
    bind(el,binding){
        el.style.color = binding.value;
    },
    update(el,binding){
        el.style.color = binding.value;
    }
})

(四)eslint

序号 规则名字 规则约束/默认约束
1 quotes 默认:字符串需要使用单引号包裹
2 key-spacing 默认:对象的属性和值之间,需要有一个空格分割
3 comma-dangle 默认:对象或数组的末尾,不允许出现多余的逗号
4 no-multiple-empty-lines 不允许出现多个空行
5 no-trailing-spaces 不允许在行尾出现多余的空格
6 eol-last 默认:文件的末尾必须保留一个空行
7 spaced-comment 在注释中的//或/*后强制使用一致的间距
8 indent 强制一致的缩进
9 import/first import导入模块的语句必须声明在文件的顶部
10 space-before-function-paren 方法的形参之前是否需要保留一个空格

(五)将axios挂载到vue

//main.js
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'

Vue.config.productionTip = false

// 全局配置axios请求根路径
axios.defaults.baseURL = 'http://www.liulongbin.top:3006'
// 将axios挂载到Vue的原型对象上,供每个.vue实例直接使用
// 今后在每个vue组件中发起请求,直接调用this.$http
Vue.prototype.$http = axios

new Vue({
  render: h => h(App),
  a() {}
}).$mount('#app')
  • 缺点:无法实现API接口的复用

(六)路由

  路由(router):就是对应关系(Hash地址与组件的对应关系)

  • SPA与前端路由
    • SPA指的是一个web网站只有唯一的一个HTML页面,所有组件的显示和切换都在这唯一的页面内完成。此时,不同组件之间的切换需要通过前端路由来实现。
  • 前端路由的工作方式
    • 用户点击了页面上的路由链接
    • 导致URL地址栏中的Hash值发生了变化
    • 前端路由监听到了Hash地址的变化
    • 前端路由把当前Hash地址对应的组件渲染到浏览器中
  • 手动模拟简单的前端路由
    • 通过<component>标签,结合comName动态渲染组件
<!-- 父组件 App.vue -->
<template>
  <div id="demoApp">
    <h1>App 根组件</h1>
    <hr />
    <div class="choose-bar">
      <a href="#/home">首页</a>
      <a href="#/movie">电影</a>
      <a href="#/about">关于</a>
    </div>
    <hr />
    <component :is="comName">
    </component>
  </div>
</template>

<script>
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'

export default {
  data() {
    return {
      // 在动态组件的位置要展示的组建的名字必须要是字符串
      comName: 'Home'
    }
  },
  components: {
    Home,
    Movie,
    About
  },
  created() {
    location.hash = '#/home'
    // 只要当前的app组件一被创建,就立即监听window对象的onhashchange事件
    window.onhashchange = () => {
      switch (location.hash) {
        case '#/home':
          this.comName = 'Home'
          break
        case '#/movie':
          this.comName = 'Movie'
          break
        case '#/about':
          this.comName = 'About'
          break
      }
    }
  }
}
</script>

<style>
#demoApp {
  min-height: 300px;
  padding: 0px 8px 8px 8px;
  background-color: #ececec;
  overflow: hidden;
}

.choose-bar a {
  display: inline-block;
  margin-right: 10px;
  height: 30px;
  line-height: 30px;
}
</style>
  • 安装和配置路由

    vue-router是由vue.js官方给出的路由解决方案,它只能结合vue项目进行使用,能够轻松管理SPA项目中组件的切换

    • vue-router安装和配置的步骤
      • 安装vue-router包:npm i vue-router@3.5.2 -s
      • src/router/index.js:创建路由模块
      • 导入并挂载路由模块
// src/router/index.js 就是当前项目的路由模块
// 1.导入vue和vue-router包
import vue from "vue"
import VueRouter from "vue-router"

// 2.调用Vue.use()函数,把VueRouter安装为Vue插件
vue.use(VueRouter)
// 3.创建路由的实例对象
const router = new VueRouter
// 4.向外共享路由的实例对象
export default router
      • 声明路由链接和占位符
// main.js
// 导入路由模块,拿到路由的实例对象
import router from './router/index.js'
new Vue({
  render: h => h(App),
  // 在Vue项目中,要想把路由用起来,必须把路由实例对象,通过下面的方式进行挂载
  // router:路由的实例对象
  router
}).$mount('#app')
    • 在路由模块中声明路由的对应关系
// src/router/index.js 就是当前项目的路由模块
// 1.导入vue和vue-router包
import vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../components/Home.vue'
import Movie from '../components/Movie.vue'
import About from '../components/About.vue'

// 2.调用Vue.use()函数,把VueRouter安装为Vue插件
vue.use(VueRouter)
// 3.创建路由的实例对象
const router = new VueRouter({
  // routes是一个数组,作用:定义hash定制与组件之间的对应关系
  routes: [
    { path: '/home', component: Home },
    { path: '/movie', component: Movie },
    { path: '/about', component: About }
  ]
})
// 4.向外共享路由的实例对象
export default router
    • 使用router-link代替a链接
   <!-- 当安装和配置vue-router之后,就可以使用router-link来替代普通的a链接了 -->
    <div class="choose-bar">
      <router-link to="/home">首页</router-link>
      <router-link to="/movie">电影</router-link>
      <router-link to="/about">关于</router-link>
    </div>
    • redirect重定向

      路由重定向指的是:用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。通过路由规则指定的redirect属性,制                                定一个新的路由地址,可以方便地设置路由的重定向

routes: [
    { path: '/', redirect: Home }
]
  • 嵌套路由

   通过路由实现组件的嵌套展示,叫做嵌套路由

    • 点击父连接显示模板内容
    • 模板内容中又有子级路由链接
    • 点击子级路由链接显示子级模板内容
<!-- 子组件Movie.vue -->
<template>
  <div class="container-movie">
    <h4>Movie 组件</h4>
    <router-link to="/movie/bk">bambi</router-link>
    <router-link to="/movie/zw">deep</router-link>
    <hr>
    <router-view ></router-view>
  </div>
</template>
// index.js
const router = new VueRouter({
  // routes是一个数组,作用:定义hash定制与组件之间的对应关系
  routes: [
    { path: '/', redirect: Home },
    { path: '/home', component: Home },
    {
      path: '/movie',
      component: Movie,
      // 默认子路由
      // redirect: '/movie/bk',
      // Movie页面的路由规则(父级路由规则)
      // 通过声明children属性,嵌套声明子级路由规则
      children: [
        // 子路由规则
        // 默认子路由 如果 children数组中,某个路由规则的path为空字符串,则这条路由规则叫做默认子路由
        { path: '', component: baekHyun },
        { path: 'bk', component: baekHyun },
        { path: 'zw', component: zWei }
      ]
    },
    { path: '/about', component: About }
  ]
})
  • 动态路由

    动态路由指的是:把Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性。在vue-router中使用英文的冒号来定义路由的                    参数项

<!-- About.vue:包含不必要连续路由地址 -->
<div class="container-about">
   <!-- 获取路由地址的"参数对象" -->
   <!-- this.$route是路由的"参数对象" -->
   <!-- this.$router是路由的"导航对象" --> <h4>About 组件---{{this.$route.params.id}}</h4> <button @click="showThis">打印this</button> <br /> <router-link to="/about/1">1</router-link> <router-link to="/about/2">2</router-link> <router-link to="/about/3">3</router-link> </div>
  // index.js:统一定义模板路由链接对应关系
  {
      path: '/about',
      component: About,
      children: [
        { path: '/about/:id', component: About }
      ]
    }
    • 为路由开启props传参
// index.js
{
    path: '/about/:id',
    component: About,
    // 可以为路由规则开启props传参,从而方便地拿到动态参数的值
    props: true
}
<!-- About.vue -->
<template>
  <div class="container-about">
    <h4>About 组件---{{ id }}</h4>
    <button @click="showThis">打印this</button>
  </div>
</template>

<script>
export default {
  name: 'myAbout',
  // 接收props数据
  props: ['id'],
  methods: {
    showThis() {
      console.log(this)
    }
  }
}
</script>
    • 拓展query和fullPath
<!-- 父组件 App.vue -->
<template>
  <div id="demoApp">
    <h1>App 根组件</h1>
    <hr />
    <!-- 当安装和配置vue-router之后,就可以使用router-link来替代普通的a链接了 -->
    <div class="choose-bar">
      <!-- 注意1:在hash地址中,/后面的参数项叫做路径参数 -->
      <!-- 在路由“参数对象”中,需要使用this.$route来访问参数路径 -->
      <!-- 注意2:在hash地址中,?后面的参数项叫做查询参数 -->
      <!-- 在路由“参数对象”中,需要使用this.$route.query来访问查询参数 -->
      <!-- 注意3:在this.$route中,path只是路径部分,fullPath是完整的地址 -->
      <router-link to="/home">首页</router-link>
      <router-link to="/movie">电影</router-link>
      <router-link to="/about/1?name=bh&age=20">1</router-link>
      <router-link to="/about/2">2</router-link>
      <router-link to="/about/3">3</router-link>
    </div>
    <hr>
    <!-- 只要在项目中安装和配置了vue-router,就可以使用router-view这个组件 -->
    <!-- 它的作用很单纯:占位符 -->
    <router-view></router-view>
  </div>
</template>

(七)导航

  • 声明式导航和编程式导航
    • 在浏览器中,点击链接实现导航的方式,叫做声明式导航。
      • 例如:普通网页点击a链接,vue项目中点击<router-link>都属于声明式导航
    • 在浏览器中,调用API实现导航的方式,叫做编程式导航
      • 例如:在普通网页调用location.href跳转到新页面的方式,属于编程式导航
  • vue-router中的编程式导航API
    • this.$router.push('hash地址')
      • 跳转到指定的hash地址,并增加一条历史记录
    • this.$router.replace('hash地址')
      • 跳转到指定的hash地址,并替换掉当前的历史记录
    • this.$router.go(数值n)
      • 可以在浏览历史中前进或后退
      • 便捷写法:
        • $router.back():后退
        • $router.forward():前进
<template>
  <div class="container-home">
    <h4>Home 组件</h4>
    <button @click="goToMovie">跳转到movie</button>
    <button @click="replaceToMovie">替换到Movie</button>
    <button @click="goNext">前进</button>
    <button @click="goPre">后退</button>
  </div>
</template>

<script>
export default {
  name: 'myHome',
  methods: {
    goToMovie() {
      // 通过编程式导航 API,导航跳转到指定的页面
      this.$router.push('/movie')
    },
    replaceToMovie() {
      this.$router.replace('/movie/zw')
    },
    goNext() {
      this.$router.go(1)
    },
    goPre() {
      this.$router.go(-1)
    }
  }
}
</script>

<style>
  .container-home {
    min-height: 200px;
    background-color: lightsalmon;
    overflow: hidden;
  }
</style>
  • 导航守卫

    导航守卫可以控制路由的访问权限

    • 全局前置守卫:每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限控制
// src/router/index.js 就是当前项目的路由模块
// 1.导入vue和vue-router包
import vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../components/Home.vue'
import Movie from '../components/Movie.vue'
import About from '../components/About.vue'
import baekHyun from '../components/baekHyun.vue'
import zWei from '../components/zWei.vue'
import Main from '../components/Main.vue'
import Login from '../components/Login.vue'
// 2.调用Vue.use()函数,把VueRouter安装为Vue插件
vue.use(VueRouter)
// 3.创建路由的实例对象
const router = new VueRouter({
  // routes是一个数组,作用:定义hash定制与组件之间的对应关系
  routes: [
    { path: '/', redirect: Home },
    { path: '/home', component: Home },
    {
      path: '/movie',
      component: Movie,
      // 默认子路由
      // redirect: '/movie/bk',
      // Movie页面的路由规则(父级路由规则)
      // 通过声明children属性,嵌套声明子级路由规则
      children: [
        // 子路由规则
        // 默认子路由 如果 children数组中,某个路由规则的path为空字符串,则这条路由规则叫做默认子路由
        { path: '', component: baekHyun },
        { path: 'bk', component: baekHyun },
        { path: 'zw', component: zWei }
      ]
    },
    {
      path: '/about/:id',
      component: About,
      // 可以为路由规则开启props传参,从而方便地拿到动态参数的值
      props: true
    },
    { path: '/login', component: Login },
    { path: '/main', component: Main }
  ]
})

// 调用路由实例对象的beforeEach方法,即可声明"全局前置守卫"
// 每次发生路由跳转的时候,都会自动触发fn这个回调函数
// 全局前置守卫的回调函数包含3个形参
router.beforeEach(function(to, from, next) {
  // to是将要访问的路由信息对象
  // from是将要离开的路由信息对象
  // next是一个函数,调用next()表示放行,允许这次路由导航
  console.log(to)
  console.log(from)
  // next的三种调用方式
  // 1.当用户拥有后台主页的访问权限,直接放行:next()
  // 2.当前用户没有后台主页的访问权限,强制跳转到登录页面:next('/login')
  // 3.当前用户没有后台主页的访问权限,不允许登录到后台主页:next(false)
  // 分析
  // 1.要拿到用户将要访问的hash地址
  // 2.判断hash地址是否等于/mian,如果等于/mian,证明需要登陆之后才能访问成功,如果不等于/mian,则不需要登录直接放行next()
  // 3.如果访问的地址是/mian,则要读取localStorage,如果有token则放行,如果没有token,则强制跳转到/login登录页面
  if (to.path === '/main') {
    // 要访问后台主页,需要判断是否有token
    const token = localStorage.getItem('token')
    if (token) {
      next()
    } else {
      next('/login')
    }
  } else {
    next()
  }
})
// 4.向外共享路由的实例对象
export default router 
posted @ 2022-05-11 20:43  Miraitowa56  阅读(308)  评论(0)    收藏  举报