一、Vue2核心

基本模板

<div id='root'> 
     name }} {{ cla.name }}
</div>

//创建容器
new Vue({
    el('#root', //el指定实例服务对象
    data:{ //数据名:值
        name:'111',
        cla: {
            name: '222'
        }
    }
})
  • 容器和实例是一一对应的关系,不能有一对多或多对一
  • 同名变量,后者会覆盖前者
  • 对于同名变量,可以创建一个子对象,然后调用时类似java的类一样去调用

1.1、语法

  1. 插值语法

    <div id='root'> 
        {{ name )
    </div>
    • 插值语法用于解析标签体内容
    • }}内部写的必须是js表达式,且可以读取data中所有属性(表达式是有值的语句)
  2. 指令语法

    <a v-blind(href = "url" ></a>
    //此时url是一个变量,可根据data里面的值进行改变
    
    <input type = "text" v-model:value = "123" ></a>
    • 对于标签里面的属性,使用v-blnd:进行绑定,同时v-blind可以简写为:

1.2、数据

  1. 数据绑定

    • v-blind是单向绑定,因此在页面中更改值时,data中的值不会变
    • v-model是双向绑定,页面中更改值时,data中的值也会变,但只能用于表单元素(input , select)。同时可以简写 v-model:value = '123'v-model = '123'
  2. data与el的2种写法

    • e1有2种写法

      • new Vue时候配置el属性
      • 先创建Vue实例const vm = new Vue(),随后再通过vm.$mount('#root')指定el的值
    • data有2种写法

      • 对象式
      • 函数式

        new Vue({
            el: '#root',
            data() {
                return {
                    name : 'NAME'
                }
            },
        );
  3. 数据代理

    • 回顾Object.defineproperty

      Object.defineproperty(对象 , 属性 , 参数)

      Object.defineproperty(person , 'age' , {
          value: 18,
          enumerable: true, //控制属性是否可以枚举,默认值是false
          writable: true, //控制属性是否可以被修改,默认值是false
          configurable: true, //控制属性是否可以被删除,默认值是false
      
          //当有人读取person的age属性时,get自动执行,且返回age的值
          get(){
              retrun '11'
          }
          //当有人修改person的age属性时,set自动执行,且收到修改的值
          set(value){
                 console.log(value)
          }
      })
      //为person添加 'age:18' 这一属性
    • 数据代理就是通过一个 对象代理 对另一个对象进行属性操作

      Vue中的数据代理是通过vm对象来代理data对象中属性的操作。内置Object.defineproperty()方法把源代码data所有元素都添加到vm的_data上,同时为每一个对象都用getter和setter方法去内部操作属性。

      image-20230211151356751
      image-20230211151356751

1.3、事件处理

  1. 基本使用

    //事件绑定
    <button v-on:click = 'show1()' >点击执行函数</button>
    <button @click = 'show2(12 , $event)' >点击执行函数</button>
    
    //事件回调
    const vm = new Vue({
        el: '#root',
        data: {
            name: 'zhang'
        },
        methods: {
            show1() {
                alert(12);
            },
            show2(number , e){
                console.log(number , e);
            }
        },
    });
    • 事件的绑定使用 v-on:@
    • methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象
    • $event 是事件对象
  2. 事件修饰符

    <!-- 阻止事件冒泡-->
    <a @click.stop="showInfo()"></a>
    
    <!-- 阻止默认事件 -->
    <a @click.prevent="showInfo()"></a>
    
    <!-- 修饰语可以使用链式书写 -->
    <a @click.stop.prevent="showInfo()"></a>
    
    <!-- 事件只执行一次 -->
    <button @click.once=""></button>
    
    <!-- 使用事件捕获模式 -->
    <button @click.captrue=""></button>
    
    <!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
    <!-- 例如:事件处理器不来自子元素 -->
    <div @click.self="showInfo()">...</div>
    
    <!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
    <!-- 这里则先进行事件回调,再触发button函数-->
    <button @click.passive="show()"></button>
  3. 键盘事件

    <!-- 键盘按下enter时触发函数 -->
    <inpuut @keyup.enter="show()">

    Vue 为一些常用的按键提供了别名:

    Vue文档

    Vue.config.keyCodes.自定义键名 = 键码 可以使用该方法自定义按键名

1.4、计算属性(compute)

计算属性的数据来源于data内,不能获取外部的值

底层原理还是Object.defineproperty方法

const vm = new Vue({
    el: '#root',
    data: {
        name: 'zhang'
    },
    compute: {
         //当有人调用fullname时,执行get并其返回的值为fullname的值
        fullname:{
            get(){
                return name
            }
        }
    },
});

get被调用的时机:

  • 初次被调用,此后值保存在缓存内,对此后所有的fullname应用
  • 当值被修改时,此时get再次被调用,缓存刷新

若要修改计算属性的值,那么需要写一个set()方法

计算属性简写(对于只读不改的情况才使用)

compute: {
    fullname(){
        return name
    }
}

调用时不用加括号 <span>{{ fullname )</span> (计算属性仍然是属性)

1.5、监视属性(watch)

const vm = new Vue({
    el: '#root',
    data: {
        name: 'zhang'
    },
    watch:{
        name:{
            immediate:true, //刷新时调用一下handler
            //当name发生改变时,调用handler
            handler(){
                console.log('修改了')
            }
        }
    }
});
  • handler()里面有两个参数 , handler(newvalue , oldervalue)

此外监视属性还有另一种写法

const vm = new Vue({
    el: '#root',
    data: {
        name: 'zhang'
    }
});

//以外部挂载的方式
vm.$watch('name' , {
    immediate:true, //刷新时调用一下handler
    //当name发生改变时,调用handler
    handler(){
        console.log('修改了')
    }
})
  • 两种写法都只能监视datacompute 里面的属性
  • 但仅有handler()时 可以简写为

    watch:{
        name(){
            console.log('修改了')
        }
    }
    //-----------------------------------------//
    vm.$watch('name' , function() {
        console.log('修改了')
    })

vue对象自身是可以监视多层级属性的 , 但vue的watch默认不监视多层级的变化 , 因此需要配置deep

const vm = new Vue({
    el: '#root',
    data: {
        num: {
            a:1,
            b:2
        }
    }
});

//以外部挂载的方式
vm.$watch('num' , {
    deep:true, //监视多级属性内所有属性的变化
    //当name发生改变时,调用handler
    handler(){
        console.log('修改了')
    }
})

如果要在watch里面使用不被Vue管理的函数 , 需要写成箭头函数的形式,这样this才是指向Vue对象

1.6、class和style绑定

  1. class绑定

    class绑定有三种写法: 字符串 、数组 、 对象

    <div class='basic' :class='change' ></div>
    data: {
       changeArr = ['1' , '2' , '3']
    
       changeObject: {
           a:false,
           b:true
       }
    },
    • 字符串写法,适用于:样式的类名不确定,需要动态指定
    • 数组写法,适用于:要绑定的样式个数不确定.名字也不确定
    • 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用
  2. style绑定

    <div class='basic' :style='change' ></div>
    • 写法有字符串 、 数组 、 对象三种(小驼峰写法)
    • 数组写法实际上是一个嵌套,在数组里面放置style对象(了解)

1.7、条件渲染与列表渲染

  1. 条件渲染

    • 使用v-show来隐藏元素,当值为false时隐藏

      <div v-show:'false'>div</div>

      此时该节点在dom中还存在( 在dom中是 display:none)

    • 使用v-if来彻底隐藏元素,当值为false时隐藏

      <div v-if:'false'>div</div>

      此时该节点在dom中不存在

      • 此外还有 v-elsev-else-if ,与其他语言作用一样。同时连续的条件指令视为一个配对组。v-if要最开始使用 切不允许被打断
      • v-if 中使用temple来进行包装会更好,实现对子元素的批量处理。同时在dom中不存在 “包装壳”

        <temple v-if:'true'>
            <h2>001</h2>
            <h2>002</h2>
            <h2>003</h2>
        </temple>
    • 高频率切换使用v-show更好
  2. 列表渲染

    v-for 起遍历作用

    <div v-for:'p in persons' :key='p.id'>
         p.name }} --- {{ p.age }}
    </div>
    data({
        persons:[
            {id:'001' , name:'张三' , age:18},
            {id:'002' , name:'李四' , age:18},
            {id:'003' , name:'王五' , age:18}
        ]
    }

    key 的值最好不用index来表示 ,如果index改变,会导致页面重新渲染

1.8、表单数据的收集

<input type="text"/> //则v-model收集的是value值,需要配置一个v-model

<input type="radio"/> //则v-model收集的是value值,且要给标签配置value值

<input type="checkbox"/>
//1.没有配置input的value属性,那么收集的就是checked(勾选or未勾选,是布尔值)
//2.配置input的value属性:
//(1)v-model的初始值是非数组,那么收集的就是checked(勾选or未勾选,是布尔值)
//(2)v-mode1的初始值是数组,那么收集的的就是va1ue组成的数组
  • v-model的三个修饰符:

    • lazy 失去焦点再收集数据
    • number 输入字符串转为有效的数字
    • trim 输入首尾空格过滤

1.9、内置指令

  1. v-textv-html

    • v-text 在所在节点内插入文本(会替换原有内容),相当于innerText
    • v-htmlv-text 作用相同 ,但其支持解析Html结构(该方法具有一定的网络安全性问题)
  2. v-cloak

    • 本质是一个特殊属性,vue实例创建完毕并接管容器并删去v-cloak属性
    • 使用css配合v-c1oak可以解决网速慢时页面展示出({xxx)的问题

      <style>
          /*---- 再其为未显示之前设置为隐藏 -----*/
          [v-cloak] {
              display: none
          }
      </style>
      
      <div v-cloak> name }}</div>
  3. v-once

    • v-once所在节点在初动态渲染后就视为静态内容,只渲染一次
  4. v-pre

    • 跳过该节点的解析过程,可以用于加快解析渲染,提高效率

1.10、自定义指令

  1. 函数式

    directives({
         fun(element ,binding){
    
         }
     }
  2. 指令式

    new Vue({
        ...
         directives:{
             fun:{
                // 绑定时触发
                bind(element ,binding){
    
                 },
                // 插入时触发
                inserted(element ,binding){
    
                   },
                // 数据更新时触发
                   update(element ,binding){
    
                   }
             }
         }
    })

总结

  • 自定义指令本质是对原生js的包装,element是绑定的元素 , binding是对绑定的
  • 例如要定义 fun 指令 , 使用时需要使用 v-fun ( <div v-fun></div> )
  • 自定义指令不需要设置返回值

1.11、生命周期

生命函数是Vue在关键时刻帮我们调用的一些特殊名称的函数

生命周期钩子分为 创建、挂载、更新、销毁四种

vue生命周期
vue生命周期

常用的生命周期钩子:

  • mounted 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等
  • beforeDestroy 清除定时器、解绑自定义事件、取消订阆消息等【收尾工作】

二、Vue组件化

2.1、非单文件组件

创建使用

//使用组件
<div id="root">
        <my></my>    
</div>

<script>
    // 创建组件
    const my = Vue.extend({
        template:`
        <div>
            <h2>姓名:{{ name )</h2>
            <h2>年龄: age }}</h2>
        </div>
        `,
        data() {
            return {
                name( 'zhang',
                age: 18
            }
        }
    })

    new Vue({
        el:'#root',
        // 注册组件 局部注册
        components:{
            my:my
        }
    })
</script>

组件的使用需要经历 创建 、 注册 、 使用 三步

Vue.extend(options)创建组件,其中配置项与new Vue几乎一样,使用temple进行组件结构的配置

也可以简写成consr ve = options

区别:

  1. data必须写成函数,避免组件被复用时,数据存在引用关系
  2. 没有el配置项

对于组件的注册:

  1. 局部注册:在new Vue的时候传入components选项
  2. 全局注册:靠Vue.component('组件名',组件)

使用组件时 ,可以用双标签(<my></my>) 也可以使用单闭合标签(<my/>)。但单闭合标签最好只在脚手架内使用

关于VueComponent

  1. school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
  2. 我们只需要写<school/><school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue执行new VueComponent(options) 且调用Vue.extend返回的都是一个全新的VueComponent
  3. 关于this指向

    • 组件配置中,this是【VueComponent实例对象】
    • new Vue配置中,this均是【Vue实例对象】

2.2、vue脚手架相关属性

  1. ref属性

    <!-- ref配置 -->
    <school ref='demo'></school>
    <div ref='div'></div>

    ref属性的作用:

    1. 被用来给元素域子组件注册引用信息(id的替代者)
    2. 应用在html标签上获取的是真实dom元素,应用在组件标签上是组件实例对象(vc)
    3. ref的获取:this.$refs.xxx
  2. props配置

    proos有三种写法

    <School name:'zhang' :age='18' sex='man'></School>
     new Vue() {
         ...
        props:['name' , 'age' , 'sex'] // 简单写法
    
         // 对象写法
        props:{
            name:String,
            age:Number,
            sex:String
        }
    
        // 完整写法
        props:{
            name:{
                type: String, //数据类型
                required: true //该数据是必要的
             },
            age:{
                type: String,
                default:18 //数据没值时默认
            },
            sex:{
                type: String, //数据类型
                required: true //该数据是必要的
            }
        }
    }
    • 年龄是一个Number类型的值,如果需要对值进行变化,需要进行v-bind绑定
    • props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告
    • 若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中
      的数据
  3. mixin

    • 定义混入

      在mixin.js里面写入并暴露

      const mixin ={
          data(){...},
          methods:{...}
      }
      export default mixin
    • 使用混入

      创建一个mixin.js文件 , 使用import导入。

      • 全局混入Vue.mixin(xxx)
      • 局部混入mixins:['xxx']

2.3、scope属性

在style属性内使用scope,将样式设置为局部样式,避免与其他组件冲突

<style scope>
    ...
<style>

2.4、TodoList案例总结

  1. 组件化编码流程:

    • 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
    • 实现动态组件:考虑好数的存放位置,数据是一个组件在用,还是一些组件在用:

      • 一个组件在用:放在组件自身即可
      • 一些组件在用:放在他们共同的父组件上(状态提升)
    • 实现交互:从绑定事件开始
  2. propsi适用于:

    • 父组件=>子组件通信
    • 子组件==>父组件通信(要求父先给子一个函数】
  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props
    是不可以修改的!
  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,在不推
    荐这样做

2.5、自定义触发事件

子组件想给父组件传数据,那么就要在父组件中给子组件绑定自定义事件

  1. 绑定方法

    • 第一种方法:在父组件中使用v-on 绑定
<!--- 父组件School --->
<!--- 事件绑定在组件上 --->
<Student @tapstu='getSchoolName'/>
methods:{
    getSchoolName(){console.log('被调用了')}
}
<!--- 子组件Student --->
<button @click='getName'></button>
methods:{
    getName(){
        this.$emit('tapstu')
    }
}
  • 第二种方法:在父组件中挂载this.$refs.demo.$on('事件',方法)
<!--- 父组件 --->
<Student ref='student'></Student>
methods:{
    getStudentName(){console.log('被调用了')}
},
mounted(){
    this.$refs.student.$on('tapstu',this.getStudentName)
}

<!--- 子组件Student --->
<button @click='getName'></button>
methods:{
    getName(){
        this.$emit('tapstu')
    }
}
  1. 解绑自定义事件this.$off('事件名')

    • 对于多个解绑,使用数组this.$off(['事件1','事件2'])
  2. 组件可以绑定原生DOM事件,需要使用native修饰符 @click.native="show"
  3. 上面绑定自定义事件,即使绑定的是原生事件也会被认为是自定义的,需要加native,加了后就将此事件给组件的根元素
  4. 注意:以this.$refs.xxx.$on('事件名',回调函数)绑定自定义事件时,回调函数要么配置在methods中,要么用箭头函数,否则 this 指向会出问题。(对于自定义事件。谁触发,谁就是this指向;但是如果this.函数,该普通函数配置在当前vc的methods内,则会改变其指向本身)

2.6、全局事件总线

在兄弟组件或者多层级组件进行信息传递时,使用独立于组件之外的全局事件总线进行串联

  1. 创建全局事件总线

    new Vue({
        ...
        beforeCreate(){
            Vue.prototype.$bus = this;  // $bus就是全局事件总线
        },
    })
  2. 发送方创建methods

    sendAge(){
        this.$bus.$emit('sendAge',this.age)
    }
  3. 接收方挂载和销毁

    mounted() {
        this.$bus.$on("sendAge", (age) => {
          console.log(age);
        });
    },
    beforeDestroy() {
        this.$bus.$off("sendAge");
    }

2.7、$nextTick

  1. 用法:this.$nextTick(回调函数)
  2. 作用:在下一次DOM更新结束后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

2.8、动画与效果

  1. 作用:在更新dom元素时会给元素添加合适的类名
  2. 写法:

    • 元素进入:

      • v-enter 进入的起点
      • v-enter-active 进入的过程
      • v-enter-to 进入的终点
    • 元素离开:

      • v-leave 离开的起点
      • v-leave-active 离开的过程
      • v-leave-to 离开的终点
  3. 使用时用transition包裹要过度的元素,并为其配置name属性

    <transition name='hello' appear>
        <h1 v-show:'isShiow'></h1>
    </transition>
    <style>
        .hello-enter,.hello-leave-to{
            ...
        },
        .hello-leave,.hello-enter-to{
            ...
        },
        .hello-leave-active,.hello-enter-active{
            ...
        }
    </style>
    • 如果要打开页面时就启用动画,添加appear属性
    • 不一定需要配置每一个属性,可以用CSS写一个@keyfrrames动画效果,然后在v-enter-activev-leave-active 中调用
    • 如果多个元素需要使用动画则使用transition-group包裹起来,并为每一个子元素添加key

三、Vue与ajax

3.1、Vue-cli 配置代理

  1. 简单模式

    在vue配置文件里添加

    devServer:{
        proxy:"url"
    }
  2. 复杂模式

    devServer: {
        proxy: {
            // 配置所有以/api开头的请求
          '/api': {
            target: 'http://127.0.0.1:5000',
            changeOrigin: true,
            ws: true,
            pathRewrite: {
              '^/api': ''
            }
          }
        }
      }
    • target里面配置代理服务器
    • 使用pathRewrite 将修改后的请求服务器url复原

四、Vuex

vuexpng
vuexpng

4.1、Vuex使用

  1. 在main.js内导入store

    import Vue from 'vue'
    import App from './App.vue'
    import store from './store';
    
    Vue.config.productionTip = false
    
    new Vue({
      store,
      render: h => h(App),
    }).$mount('#app')
  2. 在src目录下创建store文件夹,文件夹内再创建index.js文件
  3. 在index.js内写入内容

    import Vue from 'vue';
    //导入vuex
    import vuex from 'vuex';
    //安装vuex
    Vue.use(vuex);
    
    //创建store对象
    const actions = {
     jia(context , value){
         context.commit('JIA',value)
     }
    };
    const mutations = {
     JIA(state , value){
         state.sum += value
     }
    };
    const state = {
     sum:0,
    };
    //导出store对象
    export default new vuex.Store({
     actions,
     mutations,
     state,
    });
  4. 使用

    <template>
     <div class="show">
     <h1>MyTest</h1>
     <h3>结果为:{{$store.state.sum)</h3>
     <button @click="addNum">add</button>
     </div>
    </template>
    
    <script>
    export default {
     name: "List",
     data() {
     return {
       n:1
     };
     },
     methods: {
     addNum(n) {
       this.$store.dispatch('jia',this.n)
     },
     },
    };
    </script>
    • 组件中使用数据:$store.state.xxx

4.2、getters配置

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工
  2. 在store.js中追加getters配置

    ......
    const gettrts = {
        sum(){
            ......
        }
    }
    
    //导出store对象
    export default new vuex.Store({
        ......
        getters
    });
    • 组件中使用数据:$store.getters.xxx

4.3、map映射函数

(以mapstate为例)

  1. 原理:mapstate是一个包装函数自动生成this.$store.state.xxx的回调函数
  2. 用途:映射state中的数据为计算属性
  3. 用法

    // 导入
    import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
      export default {
         // mapState,mapGetters写在computed里面
        computed: {
          // 第一种写法:对象写法
          ...mapState({sum:'sum',count:'count'})
          // 第二种写法:数组写法
          ...mapState(['sum',count']) 
        },
        // 这两个必须写在methods里面
        methods:{
            ...mapActions(['jia']),
            ...mapMutations(['JIA'])
        }
     }
    <!--页面调用--->
    <div>sum}}</div>
    <div>{{count}}</div>
    <button @click='JIA'></button>
    • 对于数组写法,属性名和属性值必须相同,同时必须加上引号
    • 对于mapMutations , mapActions,如果需要传参,则需要在使用的位置也传一份,否则默认为event

4.4、Vuex模块化

  1. store index.js内配置模块化导出

    import Vue from 'vue';
    import vuex from 'vuex';
    
    Vue.use(vuex);
    
    // 模块化
    const countOptions = {
     // 命名空间
     namespaced( true,
     state: {
         sum:1101
     },
     actions: {
         jia(context , value){
             context.commit('JIA',value)
         }
     },
     mutations: {
         JIA(state , value){
             state.sum += value
         }
     }
    }
    
    //导出store对象
    export default new vuex.Store({
     // 模块化
     modules:{
         countOptions
     }
    });
  2. template内容

    <template>
        <div>
            <h3>结果为:{{sum)</h3>
            <!--传参,否则默认为event-->
            <button @click="JIA(n)">+</button>
        </div>
    </template>
  3. Vue文件内使用-map方式

    import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
    export default {
     name: 'Count',
     data() {
         return {
             n: 1
         }
     },
     computed: {
         // 模块名 + 模块内的state名
         ...mapState('countOptions',['sum']),
     },
     methods:{
         // 模块名 + 模块内的actions名、mutations名
         ...mapActions('countOptions',['JIA']),
         ...mapMutations('countOptions',['JIA'])
     }
    }
  4. 第二种使用方式:直接读取

    import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
    export default {
     name: 'Count',
     data() {
         return {
             n: 1
         }
     },
     computed:{
         // 直接读取state
         countSum(){
             return this.$store.state.countOptions.sum
         },
         // 直接读取getters
         countOddOrEven(){
             return this.$store.getters['countOptions/oddOrEven']
         },
     },
     methods:{
         // 直接调用actions
         countJia(){
             this.$store.dispatch('countOptions/JIA',this.n)
         },
         // 直接调用mutations
         countJian(){
             this.$store.commit('countOptions/JIAN',this.n)
         },
     }
    }

五、路由

路由是一组组key:value的组合,key为路径,value可能是function或component:

使用路由制作单页面应用,做到局部刷新的效果,优化使用和加载

5.1、路由使用

  1. npm安装vue-router,在src下创建router/index.js

    // 导入VueRouter
    import VueRouter from "vue-router";
    // 导入组件
    import About from "../views/About";
    import Home from "../views/Home";
    
    export default new VueRouter({
        routes: [
            {
                // 路由路径
                path: "/about",
                // 路由组件
                component:About
            },
            {
                path: "/home",
                component:Home
            }
        ]
    })
  2. main.js内导入使用

    import Vue from 'vue'
    import App from './App.vue'
    // 导入使用
    import VueRouter from 'vue-router'
    import router from './router'
    Vue.use(VueRouter)
    
    Vue.config.productionTip = false
    
    new Vue({
      router,
      render: h => h(App),
    }).$mount('#app')
  3. 使用

    <router-link active-class="active" to="/about">About</router-link>
    <router-view></router-view>
    • <router-link></router-link>浏览器会被替换为a标签
    • active-class可配置高亮样式
    • <router-view></router-view>内容展示区
  4. 注意

    • 一般把路由组件放在src/views 或者 src/pages 内,和一般组件区分开
    • 每一个路由组件都有自己的$route,但对于路由器全局共用一个$router
    • 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载

5.2、多级路由

  1. 使用children配置项

    routes:[
        {
            path:'/about',
            component:About,
        },
        {
            path:'/home',
            component:Home,
            // 通过children配置子级路由
            children:[                     
                {
                    path:'news',         // 此处不要带斜杠
                    component:News
                },
                {
                    path:'message',    // 此处不要写斜杆
                    component:Message
                }
            ]
        }
    ]
  2. 使用

    <router-link to="/home/news">News</router-link>
  3. 注意

    • 子级路由不需要加斜杆
    • 使用时需要写好完整的路径

5.3、路由query参数

  1. 传参

    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">跳转</router-link>
                    
    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link 
        :to="{
            path:'/home/message/detail',
            query:{
               id: m.id,
           title: m.title
            }
        }"
    >跳转</router-link>
  2. 接收参数

    • $route.query.xxx 的形式

5.4、路由其他参数

  1. 路由命名

    • 为具体的路由配置name属性来简化路由的跳转

      routes:[
          {
              name:'guanyu'
              path:'/about',
              component:About,
          }
      ]
    • 使用

      <!-- 字符串写法 -->
      <router-link :to="{name: guanyu}">跳转</router-link>
                      
      <!-- 对象写法 -->
      <router-link 
          :to="{
              name:'guanyu'
              query:{
                 id: 12
              }
          }"
      >跳转</router-link>
  2. params参数

    • 配置路由,声明接收params参数

      routes:[
          {
              path:'/about/:id/:title',    //使用占位符声明接收params参数
              component:About,
          }
      ]
    • 传递参数
      路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

      <!-- 跳转并携带params参数,to的字符串写法 -->
      <router-link :to="/about/666/你好">跳转</router-link>
                      
      <!-- 跳转并携带params参数,to的对象写法 -->
      <router-link 
          :to="{
              path:'/about'
              params:{
                    id:666,
                     title:'你好'
              }
          }"
      >跳转</router-link>
    • 参数的接收:$route.params.xxx
  3. 路由的 props 配置

    启用路由props配置后,需要在组件的props内接收参数,然后才能使用

    {
        path:'detail/:id',
        component:Detail,
    
        //第一种写法:props值为对象,以key-value的组合传递给组件
        // props:{a:900}
    
        //第二种写法:props值为布尔值,为true时,把路由收到的params参数传递给组件本身的props
        // props:true
        
        //第三种写法:props值为函数,函数返回的对象中每一组key-value传递给组件
        props($route){
            return {
                id: $route.query.id,
                title: $route.query.title
            }
        }
    }

5.5、replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为push和replace。push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 使用:<router-link replace> News </router-link>

5.6、编程式路由导航

不借助<router-link>实现路由跳转,调用的是this.$router内置的函数

  • this.$router.push({})this.$router.replace({}) :内传的对象与<router-link>中的to相同
  • this.$router.forward() :前进
  • this.$router.back() :后退
  • this.$router.go(n) :可前进也可后退,n为正数前进n步,为负数后退n步

5.7、缓存路由组件

让不展示的路由组件保持挂载,不被销毁

// 缓存一个路由组件
<keep-alive include="News"> // include中写想要缓存的组件名,不写表示全部缓存
    <router-view></router-view>
</keep-alive>

// 缓存多个路由组件
<keep-alive :include="['News','Message']"> 
    <router-view></router-view>
</keep-alive>

5.8、路由守卫

activateddeactivated是路由组件所独有的两个钩子,仅在keep-alive包裹的情况下起效用

activated路由组件被激活时触发,deactivated路由组件失活时触发

路由守卫对路由进行权限控制

  1. 全局路由守卫

    路由的使用需要在创建路由后对其进行配置,最后再单独暴露出来

    const router = new VueRouter({
        routes:[
            {
                name:'guanyv',
                path:'/about',
                component:About,
                meta:{title:'关于',isAuth='true'}    //meta可以为每个路由配置自定义内容
            }
        ]
    })
    ...
    export default router

    前置路由守卫(跳转前调用)

    router.beforeEach((to,from,next) => {
        if(to.meta.isAuth){    //判断元素的meta属性内的isauth自定义配置
            if(localStorage.getItem('school')==='atguigu'){
                next()    //next函数对内容进行放行
            }else{
                alert('学校名不对,无权限查看!')
            }
        }else{
            next()
        }
    })

    后置路由守卫(跳转后调用)

    router.afterEach(function(to,from) {
        ...
    })
  2. 独享路由守卫(使用beforeEnter函数)

    const router = new VueRouter({
        routes:[
            {
                name:'guanyv',
                path:'/about',
                component:About,
                meta:{title:'关于',isAuth='true'},    //meta可以为每个路由配置自定义内容
                beforeEnter(to,from,next){    //beforeEnter配置独享
                    if(localStorage.getItem('school') === 'atguigu'){
                        next()
                    }else{
                        alert('暂无权限查看')
                    }
                }
            }
        ]
    })
    ...
    export default router
    • 独享路由没有后置守卫,但是可以与全局后置守卫混用
  3. 组件内路由守卫

    <template>
        <h2>About组件</h2>
    </template>
    
    <script>
        export default {
            name:'About',
            ...
            // 进入该组件时被调用
            beforeRouteEnter (to, from, next) {
                console.log('about路由被调用',to,from)
                if(localStorage.getItem('school')==='atguigu'){
                    next()
                }else{
                    alert('学校名不对,无权限查看!')
                }
            },
            // 离开该组件时被调用
            beforeRouteLeave (to, from, next) {
                console.log('About离开',to,from)
                next()
            }
        }
    </script>

5.9、hash与history模式

url中的hash值:\#及其后面的内容就是hash值

hash值的内容不会传给服务器

  1. hash模式:以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法;兼容性较好
  2. history模式:兼容性和hash模式相比略差;应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

使用

const router =  new VueRouter({
    mode:'history',
    ...
})

export default router