Vue2学习笔记

目录

1.1 Vue的特点

1.2 MVVM模型

1.3 Vue学习

1.3.1 Object.defineProperty

1.3.2 数据代理

1.3.3 修饰符

1.3.4 数据监测

1.3.5 全局事件总线

1.3.6 消息订阅与发布

1.3.7 配置代理

1.4 vue理解使用

1.4 .1生命周期

1.4.2 计算属性

1.5 vuex

1.5.1 环境搭建与使用:

1.5.2 map方法:

1.5.3 vuex模块化:

1.5.4 命名空间:

1.5.5 带命名空间的绑定函数:

1.6 路由

1.6.1 概念与基本使用

1.6.2 嵌套路由(多级路由)

1.6.3 路由传参

1.6.4 路由模式与导航

1.6.5 导航守卫

小知识点


1.1 Vue的特点

  1. 组件化模式,提高代码复用率,更易于维护

  2. 声明式编码,让编码人员无需直接操作DOM,提高开发效率

  3. 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点

1.2 MVVM模型

  1. M: Model,模型,对应data中的数据

  2. V: View,视图,对应模板

  3. VM: ViewModel,视图模型,Vue的实例对象

1.3 Vue学习

1.3.1 Object.defineProperty

let number=20
       let person = {
           name:"张三",
           sex:"男",
        //    age:18
       }

       //此方法添加的属性一些基本属性都是false,可以进行属性修改
       Object.defineProperty(person,'age',{
        //    value:19,
        //    enumerable:true,//控制属性是否可枚举,默认为false
        //    writable:true,//控制属性是否可修改,默认为false
        //    configurable:true,//控制属性是否可以被删除,默认值为false

           //当有人读取person的age属性时,get(getter)会被调用,返回值为age的值,
           //此方法可以绑定age的值为number的值,当number的值改变时,age会自动改变,不需要再次声明age=number
           get(){
               console.log("读取age属性");
               return number
           },

            //当有人修改person的age属性时,set(setter)会被调用,并且能收到修改的值
            set(n){
               console.log("修改age属性");
               number=n;
               return n
            }
       })
    //    console.log(Object.keys(person))
       console.log(person);

1.3.2 数据代理

使用Object.defineProperty方法可以进行数据代理

//数据代理:通过一个对象代理对另一个对象中的属性进行一系列操作(读/写)
      let obj={x:100};
      let obj2={y:200}
      Object.defineProperty(obj2,'x',{
          get(){
              return obj.x
          },
          set(n){
              obj.x=n
          }
      })

· Vue中的数据代理: 通过vm(Vue实例)对象来代理vm的data对象中的属性;

·Vue中的数据代理好处:可以更方便地操作data中的数据(不用通过{{data.name}}来获取name的值,

而是可以直接通 过{{name}}来获取)

·Vue中的数据代理的基本原理:通过Object.defineProperty()把data对象中的所有属性都添加到vm上,再为每一 个添加到vm上的属性都指定一个getter/setter,在getter/setter内部操作(读/写)对应的 属性

1.3.3 修饰符

事件修饰符:

  1. prevent:阻止默认事件

  2. stop:阻止事件冒泡

  3. once:事件只触发一次

  4. capture:使用事件的捕获方式

  5. self:只有event.target是当前操作的元素时才触发事件

  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕

键盘别名:

·Vue给一些常用的键提供了别名,对未提供别名的按键,可以使用按键原始的key值绑定,但要转为kebab-case(短横线命名),例如切换大小写按键的key值是CapsLock,使用时要改为caps-lock。

·系统的修饰键用法较为特殊:ctrl、alt、shift、meta(win键)

(1)配合keyup使用时,按下修饰键的同时再按下其他键,随后释放其他键,才会触发事件(若想只通过系统修饰键+指定按键触发事件,可在系统修饰键后加上.再加上指定按键,例如,只想通过ctrl+y来触发事件,可以在ctrl后加上.y,即@keyup.ctrl.y)

(2)配合keydown使用时,可以正常触发事件

·也可以使用keyCode来指定具体的按键(不推荐,不同键盘 键码可能不统一)

·可以通过Vue.config.keyCodes.自定义键名 = 键码,来定制按键别名

  1. enter => 回车

  2. delete =>删除/退格

  3. esc => 退出

  4. space => 空格

  5. tab => 换行(比较特殊,必须配合keydown使用

  6. up => 上

  7. down => 下

  8. left => 左

  9. right => 右

1.3.4 数据监测

  1. Vue会监测data里所有层次的数据

  2. 如何监测对象中的数据:

    通过setter实现监视,且要在new Vue时就传入要监测的数据。

    (1).对象中后追加的属性,vue默认不做响应式处理

    (2).若需要给后添加的属性做响应式,可使用如下API:

    Vue.set(target,propertyName/index,value)或 vm.$set(target,propertyName/index,value)

  3. 如何监测数组中的数据:

    通过包裹数组更新元素的方法实现,本质是做了两件事:

    (1).调用原生对应的方法对数组进行更新

    (2).重新解析模板,更新页面

    Vue.set(target,propertyName/index,value)或 vm.$set(target,propertyName/index,value)

  4. 在vue修改数组中的某个元素一定要使用如下几个方法:

    (1). push()、pop()、shift()、unshift()、splice()、sort()、reverse()

    (2).vue.set() 或 vm.$set()​​

    注意:Vue.set()和vm.$stet() ,不能给vm或vm的根数据对象(如data)添加属性!

1.3.5 全局事件总线

全局事件总线示例

  • 可用于任意组件间的通信

  • 安装全局事件总线:

/* 全局事件总线 */

//1.使用VueComponent当全局总线,
// const vc = Vue.extend({})
// Vue.prototype.x = new vc

//2.使用Vue当全局总线,在生命周期beforeCreate()创建
new Vue({
  el: '#app',
  router,
  render: h => h(App),
  //安装全局事件总线,在beforeCreate()创建
  beforeCreate() {
    // Vue.prototype.x=this  //将x改为$bus(标准命名)
    Vue.prototype.$bus=this  //$bus就是当前应用的vm
  },
})
  • 使用事件总线:

1.接收数据:A组件想接收数据,则在组件A中给$bus绑定自定义事件,事件的回调留着A组件自身

methods(){
    demo(data){...}
}
......
mounted(){
    this.$bus.$on('xxx',this.demo)
}

也可以直接将回调函数写成箭头函数:

export default {
  name:'School',
  data() {
    return {
      name:'bupt',
      address:'beijing',
      studentName:''
    }
  },
  mounted(){
    this.$bus.$on('hello',data=>{
      this.studentName=data
    })
  },
  //避免资源占用,页面销毁前应解绑自定义事件
  beforeDestroy(){
    this.$bus.$off('hello')
  }
}

2.提供数据 :

this.$bus.$emit('xxx',数据)

//或
 methods:{
   sentname(){
     this.$bus.$emit('hello',this.name)
   }
 }
  • 最好能在生命周期钩子beforeDestroy中,用$off解绑当前组件用到的事件

1.3.6 消息订阅与发布

Vue中用的不多,因为和全局事件总线差不多

第三方库:pubsub npm i pubsub-js

·一种组件间通信的方式,适用于任意组件间的通信

·import引入

·接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身

 mounted(){
    //订阅消息使用pubsub.subscribe()函数
    //注意pubsub.subscribe()有两个参数,第一个是消息名,第二个才是消息内容的回调函数
   this.pubId = pubsub.subscribe('hi',(MsgName,data)=>{
     console.log('@@@@',data)
     this.submeg=data;
     console.log('有人发布了hi消息,hi消息的回调执行了')
   })
 },
     
 beforeDestroy(){
   //取消订阅消息,需要订阅消息的id
   pubsub.unsubscribe(this.pubId)
 }

·提供数据:

methods:{
   //发布消息用pubsub.publish()函数
   pubmeg(){
     pubsub.publish('hi',this.meg)
   }
 }

·最好在生命周期钩子beforeDestroy中,用pubsub.unsubscribe(this.pubId)取消订阅

1.3.7 配置代理

方法一:在vue.config.js中添加配置(没有此文件需手动创建):

module.exports = {
  devServer: {
    proxy: 'http://localhost:4000'
  }
}

版本不同,可在config/index.js中找到proxyTable,添加配置(此时好像不能用方法一?):

 proxyTable: {
   '/stus': {
   	 target: 'http://localhost:5000',
   	 pathRewrite:{'^/stus':''} ,
  	 ws: true,//用于支持websocket
   	 changeOrigin: true, //用于控制请求头中的host值(即是否更改请求源信息),一般默认为true
   },
   '/cars': {
     target: 'http://localhost:5001',
     pathRewrite:{'^/cars':''} ,
     ws: true,//用于支持websocket
     changeOrigin: true, //用于控制请求头中的host值(即是否更改请求源信息),一般默认为true
   },
 },

优点:配置简单,请求资源时直接发给前端(8080)即可。

缺点:不能配置多个代理,且不能灵活的控制请求是否走代理。

工作方式:若按照上述配置代理,当请求了前端不存在的资源时,该请求会转发给服务器(优先匹配前端资源)

方法二:配置具体的代理规则:

 proxyTable: {
   '/stus': {//匹配所有以'/stus'开头的请求路径
   	 target: 'http://localhost:5000',//代理目标的基础路径
   	 pathRewrite:{'^/stus':''} ,
  	 ws: true,//用于支持websocket
   	 changeOrigin: true, //用于控制请求头中的host值(即是否更改请求源信息),一般默认为true
   },
   '/cars': {
     target: 'http://localhost:5001',
     pathRewrite:{'^/cars':''} ,
     ws: true,//用于支持websocket
     changeOrigin: true, //用于控制请求头中的host值(即是否更改请求源信息),一般默认为true
   },
 },

优点:可以配置多个代理,且可以灵活控制请求是否走代理。

缺点:配置较为繁琐,请求资源时必须加前缀

1.4 vue理解使用

1.4 .1生命周期

Vue 实例生命周期

初始化:生命周期、事件,但数据代理还未开始。

beforeCreate():此时,无法通过vm访问到data中存放的数据、methods中的方法。

初始化:数据监测、数据代理

created():此时,可以通过vm访问到data中存放的数据、methods中的方法。

此阶段Vue开始解析模板,生成虚拟DOM(内存中),页面还不能显示解析好的内容。

beforeMount():此时,1.页面呈现的是未经Vue编译的DOM结构 。2.所有对DOM的操作,最终的时候都不奏效

将内存中的虚拟DOM转化为真实DOM插入页面

mounted():此时,1.页面中呈现的是经过Vue编译的DOM。2.对DOM的操作均有效(但应尽可能避免)。

至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件 等初始化操作。

beforeUpdate():此时,数据是新的,但页面是旧的,即:页面尚未和数据保持同步。

根据新数据,生成新的虚拟DOM,随后与旧的虚拟DOM进行比较,最终完成页面解析,即:完成Mode==>View的更新。

updated():此时,数据是新的,页面也是新的,即:页面和数据保持同步。

beforeDestroy():此时,vm中所有的data、methods、指令等都处于可用状态,马上要执行销毁过程,一般在此 阶段:关闭定时器、取消订阅消息、解绑自定义事件等收尾操作。

destroyed():此时,页面摧毁

// 生命周期函数:
      beforeCreate() {
        console.log("beforeCreate")
        console.log(this)
        // debugger
      },
      created() {
        console.log("create")
        console.log(this)
        // debugger
      },
      beforeMount() {
        console.log("beforeMount")
        console.log(this)
        // debugger
      },
      mounted() {
        console.log("mounted")
        console.log(this)
        // debugger
      },
      beforeUpdate() {
        console.log("beforeUpdate")
        console.log(this)
        // debugger
      },
      updated() {
        console.log("updated")
        console.log(this)
        // debugger
      },

      //此时,数据和方法都能访问和调用,但不会更新
      //销毁后自定义事件会失效,但原生DOM事件依然有效
      beforeDestroy() {
        console.log("beforeDestroy")
        console.log(this)
        // debugger
      },
      destroyed() {
        console.log("destroyed")
        console.log(this)
        // debugger
      },

此外,还有几个特殊的生命周期函数:activated()deactivated()(vue路由独有的两个钩子);

nextTick()(DOM渲染后执行里面的回调函数)

1.4.2 计算属性

一般用法:函数形式,有返回值

fullName:function(){
    return this.firstname+" "+this.lastname
}

实际上,计算属性是一个对象,里面有两个属性:get()set(),上面的用法是get()的简写形式,set()一般不用,即一般没有set方法(只读属性),当属性发生改变时会调用set(),可以在里面写修改属性的函数语句

fullname:{
    get:function(){
         return this.firstname+" "+this.lastname
    },
    set:function(newvalue){
        const newname=newvalue.split(' ')
        this.firstname=newname[0]
        this.lastname=newname[1]
    }
}

计算属性与方法methods不同之处在于vue内部会对计算属性computed进行缓存,例如同样的属性在进行多次使用时,由于methods是调用函数,所有会在每一次使用时都调用一次函数,而computed只会调用一次。

  <div id="test">
    <p>computed:</p>
    {{fullname}}
    {{fullname}}
    {{fullname}}
    {{fullname}}
    <br>
    <p>methods:</p>
    {{getFullname()}}
    {{getFullname()}}
    {{getFullname()}}
    {{getFullname()}}
  </div>

  <script src="./vue.js"></script>
  <script>
    var vm = new Vue({
      el: "#test", //css选择器
      data: {
        firstname: "aa",
        lastname: "bb"
      },
      computed: {
        fullname: function () {
          console.log('computed---') //打印一次
          return this.firstname + " " + this.lastname
        }
      },
      methods: {
        getFullname() {
          console.log("methods---") //打印4次
          return this.firstname + " " + this.lastname
        }
      },
    })
  </script>

1.5 vuex

vuex

当我们的应用遇到多个组件共享状态时:

  • 多个视图依赖于同一状态。

  • 来自不同视图的行为需要变更同一状态。

1.5.1 环境搭建与使用:

和路由一样,新建vuex的文件,然后在main.js中引入import store from './store'

在store/index.js中开始配置使用:

import Vue from 'vue'
import vuex from 'vuex'

Vue.use(vuex)

//创建并暴露store
export default new vuex.Store({
  // 用于响应组件中的动作
  actions:{ },
  // 用于操作数据(state)
  mutations:{},
  // 用于存储数据
  state:{},
  //用于将state中的数据进行加工,(相当于全局计算属性,会缓存属性)
  getters:{}
})
  • state:Vuex 使用单一状态树,每个应用将仅仅包含一个 store 实例,state保存项目全局变量,通过this.$store.state 访问

  • getters:从 store 中的 state 中派生出一些状态并进行缓存,多个组件都会用到时会从缓存中取而不是频繁调用函数,相当于一个store的计算属性。

    • 用法:函数参数有两个,stategetters,只用到state可以不写getters

      getters: {
        // ...
        doneTodos: state => {
          return state.todos.filter(todo => todo.done)
        },
        // ...
        doneTodosCount: (state, getters) => {
          return getters.doneTodos.length
        },
        // ...
        //返回值为函数,可传递参数
        getTodoById: (state) => (id) => {
          return state.todos.find(todo => todo.id === id)
        }
      }
    • 访问:通过属性访问或通过方法访问(一般需要传递参数时),也可以通过mapGetters 辅助函数将 store 中的 getter 映射到局部的计算属性中进行使用

      //通过属性访问
      store.getters.doneTodos
      store.getters.doneTodosCount 
      //通过方法访问,可传递参数
      store.getters.getTodoById(2)
  • mutation:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,里面的函数参数有两个参数state和需要传递的参数。mutation 必须是同步函数,否则开发者工具devtools 状态不会改变,因为它 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。

    • 访问时使用store.commit('increment', 10)提交(一般提交)

    • 使用对象风格提交时,整个对象都作为载荷传给 mutation 函数,此时mutations的函数中第二个参数是此对象

    //一般提交
    store.commit('increment', 10)
    // store中:
    mutations: {
      increment (state, n) {
        state.count += n
      }
    }
    
    //对象提交
    store.commit({
      type: 'increment',
      amount: 10
    })
    // store中:
    mutations: {
      increment (state, payload) {
        state.count += payload.amount
      }
    }
  • action:执行异步操作,可与Promise一起使用。类似于 mutation,不同在于:

    • Action 提交的是 mutation,而不是直接变更状态。

    • Action 可以包含任意异步操作。

    mutations: {
      increment (state) {
        state.count++
      }
    },
    actions: {
      incrementAsync (context,payload) {
        setTimeout(()=>{
          context.commit('increment')
        },1000)
      }
    }
    
    //使用action:
    store.dispatch('incrementAsync',payload)

1.5.2 map方法:

首先在vuex中引入import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'

1. mapState方法:用于帮助我们映射state中的数据为计算属性

2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

3. mapActions方法:用于帮助我们生成与actions对话的方法,即:$store.dispatch(xxx)的函数

  1. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:$store.commit(xxx)的函数

    注意:如果 mapActions和mapMutations使用时,若要传递参数,需要在模板中绑定事件时传递好参数,否则参数就是事件对象了。

methods:{
    //借助mapMutations生成对应的方法,方法中会被调用commit去联系mutations(对象写法)
          ...mapMutations({increment:'increment',decrement:'decrement',incrementodd:'incrementodd',incrementwait:'incrementwait'})
  
    //借助mapMutations生成对应的方法,方法中会被调用commit去联系mutations(数组写法)
 ...mapMutations(['increment','decrement','incrementodd','incrementwait'])
},

computed:{
    //借助mapState生成计算属性,从state中提取数据(对象写法)
 ...mapState({sum:'sum',place:'province',name:'name'}),

    //借助mapState生成计算属性,从state中提取数据(数组写法),此时生成的计算属性名字和数据名要一样。
    // 数组中的元素(例如sum)两种用途:一种是指向生成的计算属性的 名字(sum),一种是state里的数据($store.state.sum)
 ...mapState(['sum','province','name']),

  ...mapGetters(['bigsum'])
  }

1.5.3 vuex模块化:

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:(模块也可以单独写到各自文件中,在index.js中引入即可。

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

1.5.4 命名空间:

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

如果希望模块具有更高的封装度和复用性,可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如:

const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模块内容(module assets)
      state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模块
      modules: {
        // 继承父模块的命名空间
        myPage: {
          state: () => ({ ... }),
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 进一步嵌套命名空间
        posts: {
          namespaced: true,

          state: () => ({ ... }),
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})

1.5.5 带命名空间的绑定函数:

当使用 mapState, mapGetters, mapActionsmapMutations 这些函数来绑定带命名空间的模块时,写起来可能比较繁琐:

computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])
}

对于这种情况,你可以将模块的空间名称字符串作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文。于是上面的例子可以简化为:

computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}

1.6 路由

1.6.1 概念与基本使用

  1. 路由:一组key-value的对应关系

  2. 多个路由,需要经过路由器管理

  • 路由组件通常存放在pages文件夹,一般组件存放在components文件夹

  • 通过切换,"隐藏"了的路由组件默认是被销毁的,需要时再挂载

  • 每个组件都有自己的$route属性,里面存放自己的路由信息

  • 整个应用只有一个router,可以通过组件的$router属性获取

使用:下载插件-->安装插件vue.use(插件)-->创建router-->注册router(在new Vue时记得注册)

  • 设置路由组件映射时记得导入组件const Home = () => import('../views/home/Home')

  • 路由组件映射关系设置默认路径时可以使用重定向(redirect

  • router-link默认a标签,通过tag属性可以更改

  • router-link默认使用history.pushState即可以返回前进,如果想改成history.replaceState,不让他后退前进,可以添加replace属性

  • 修改当前状态路由的按钮样式可以使用active-class="active"属性修改样式,若所有激活状态都是一个样式,可以在router中设置属性linkActiveClass

  • 使用函数代码实现路由跳转:可以使用this.$router.push('/home')this.$router.replace('/home')

  • this.$route表示当前活跃(激活)的路由,通过this.$route.params取出参数,例如this.$route.params.userid

// 1.安装插件
Vue.use(VueRouter)

const routes = [
  {
    path: '',
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]
// 2.创建router
const router = new VueRouter({
  //  创建路由组件映射关系
  routes,
  mode: 'history' //路由模式
  linkActiveClass:'active' //设置激活的样式(所有激活状态都是这个样式)
})
<router-link to="/home" tag='button' replace>a</router-link>
<router-link to="/about" tag='li'>b</router-link>
<router-view></router-view>

1.6.2 嵌套路由(多级路由)

使用children配置项:注意:children是一个数组,并且子路由不用写“/”

routes: [
   {
     path:'/home',
     component:home,
     children:[
       {
         path:'aaa',
         component:aaa
       },
       {
         path:'bbb',
         component:bbb
       }
     ]
   },

跳转时要写完整路由:

<router-link to="/home/aaa">a</router-link>
<router-link to="/home/bbb">b</router-link>
<router-view></router-view>

1.6.3 路由传参

  • query参数(路径是http://localhost:8080/#/home/aaa?id=003&name=消息3样式的)

传递参数:to的字符串写法和对象写法(对象写法可以使用name或者path)

<ul>
  <li v-for="m in meg" :key="m.id">
    <!-- 跳转路由并携带query参数,to的字符串写法 -->
    <!-- <router-link :to="`/home/aaa?id=${m.id}&name=${m.name}`">{{m.name}}</router-link> -->

    <!-- 跳转路由并携带query参数,to的对象写法 -->
    <router-link :to="{
      path:'/home/aaa',
      query:{id:m.id,name:m.name}
    }">{{m.name}}</router-link>
  </li>
</ul>

接收参数:使用$route.query.id$route.query.name接收参数

  • params参数(路径是http://localhost:8080/#/home/bbb/01/标题1样式的)

注意:配置路由时必须声明接收params参数(占位)path:'bbb/:num/:title'

{
  path:'bbb/:num/:title',//使用占位符声明接收params参数
  name:'detail',
  component:bbb
}

传递参数:to的字符串写法和对象写法(对象写法只能使用name写法)

<ul>
  <li v-for="m in megb" :key="m.num">
  	<!-- 跳转路由并携带params参数,to的字符串写法 -->
  	<!-- <router-link :to="`/home/bbb/${m.num}/${m.title}`">{{m.title}}</router-link> -->

  	<!-- 跳转路由并携带params参数,to的对象写法,注意只能用name写法,不能使用path -->
  	<router-link :to="{
    	name:'detail',
    	params:{num:m.num,title:m.title}
  	}">{{m.title}}</router-link>
  </li>
</ul>

接收参数:使用$route.params.num$route.params.title接收参数

  • props配置:让路由组件更加方便地收到参数,接收时通过props:['num','title']接收参数

注意:布尔写法只能传递收到的所有params参数,而query参数不能传递;函数写法都能传递

{
  path:'bbb/:num/:title',
  name:'detail',
  component:bbb,

  //props对象写法,此对象中所有的key-value都会以props的形式传给detail组件(当 props 是静态的时候有用)
  //  props:{a:1,b:'text'}

  //props布尔写法,若为真,会把该路由组件收到的所有params参数以props的形式传给detail组件(route.params 将会被设置为组件属性)
  //  props:true
        
  //props函数写法,函数返回的对象中每一组key-value都会通过props传给detail组件
  props($route){
    return{num:$route.params.num,title:$route.params.title}
  }
}

1.6.4 路由模式与导航

  • 路由模式:

默认默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。(即路径中有#这种),兼容性好

路由的 history 模式充分利用 了history.pushState API 来完成 URL 跳转而无须重新加载页面。可在router中配置:mode:'history',兼容性略差,应用部署上线时需要后端解决刷新页面服务端404的问题

router-link的replace属性:可以控制路由跳转时操作浏览器历史记录的模式。 浏览器历史记录有两种写入方式:pushreplace,push是追加历史记录,replace是替换当前记录。路由跳转默认为push。开启replace模式:<router-link replace ......>xxx</router-link>

  • 编程式导航

除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

使用$router.xxx实现,可用于按钮的点击事件中。

$router.push(...):这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到 之前的 URL。当点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)

$router.replace(...):这个方法和push方法一样,不过它不会向 history 添加新记录,而是 替换掉当前的 history 记录,即:浏览器返回不到原来的url。

上述两个方法中的参数(即...)可以是字符串路径也可以是描述地址的对象(同to的字符串写法和对象写法)。

$router.back():相当于浏览器的后退按钮,返回上一个存在的(未被replace的)url。

$router.forward():相当于浏览器的前进按钮,进入到下一个存在的url。

$router.go(n):这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步。例如:

// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)

// 后退一步记录,等同于 history.back()
router.go(-1)

// 前进 3 步记录
router.go(3)

// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
  • 缓存路由组件:让不展示的路由组件保持挂载,不被销毁,使用keep-alive标签名,

    属性:include:字符串或正则表达式,匹配的组件被缓存

    exclude:字符串或正则表达式,匹配的组件不会被缓存

    注意:1. include里添加的是组件名,如果不写include,会将所有的router-view的组件都缓存。

    1. keep-alive包在router-view外面。

    2. 若include包含多个组件,可写成数组形式(:include="['News','Message']"

<keep-alive include="News">
  <router-view></router-view>
</keep-alive>
  • 两个生命周期钩子:vue-router独有的两个生命周期函数,activated()deactivated()表示组件的激活状态(是否活跃/隐藏),可用于希望在切换路由,同时组件不被销毁的状态下决定部分数据是否缓存,搭配<keep-alive/>使用

1.6.5 导航守卫

  • 路由元信息:定义路由的时候可以配置 meta 字段来给路由添加属性信息。

  • 全局前置守卫 router.beforeEach((to, from, next) => {})

    其中:to是即将要进入的目标的路由对象,from是当前导航正要离开的路由。

    next: Function: 一定要调用该方法来 resolve这个钩子。执行效果依赖 next 方法的调用参数。

    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed(确认的)。

    • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

    • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 router-linktoproprouter.push 中的选项。

    • next(error): 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

  • 全局解析守卫 router.beforeResolve,和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

  • 全局后置钩子 router.afterEach((to, from) => {}),和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身。

  • 路由独享守卫 beforeEnter:(to, from, next) => {}(在路由配置上定义),与全局前置守卫的方法参数是一样的。

  • 组件内守卫 beforeRouteEnter(to, from, next) {}(路由组件内定义)

    beforeRouteUpdate(to, from, next) {}(路由组件内定义)

    beforeRouteLeave(to, from, next) {}(路由组件内定义)

    const Foo = {
      template: `...`,
      beforeRouteEnter(to, from, next) {
        // 在渲染该组件的对应路由被 confirm 前调用
        // 不!能!获取组件实例 `this`, 因为当守卫执行前,组件实例还没被创建
      },
      beforeRouteUpdate(to, from, next) {
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 可以访问组件实例 `this`
      },
      beforeRouteLeave(to, from, next) {
        // 导航离开该组件的对应路由时调用
        // 可以访问组件实例 `this`
      }
    }
  • 导航解析流程:

  1. 导航被触发。

  2. 在失活的组件里调用 beforeRouteLeave 守卫。

  3. 调用全局的 beforeEach 守卫。

  4. 在重用的组件里调用 beforeRouteUpdate 守卫 。

  5. 在路由配置里调用 beforeEnter

  6. 解析异步路由组件。

  7. 在被激活的组件里调用 beforeRouteEnter

  8. 调用全局的 beforeResolve 守卫 。

  9. 导航被确认。

  10. 调用全局的 afterEach 钩子。

  11. 触发 DOM 更新。

  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

小知识点

  • 自定义事件,使用$emit定义,解绑使用 $​​off​(),也可以使用$destroy()(或使用生命周期函数)销毁所有自定义事件,例如:

//自定义事件demo,传入参数this.name
this.$emit('demo',this.name);
this.$emit('demo2',this.age);
//解绑事件
this.$off('demo');//解绑一个自定义事件
this.$off(['demo','demo2']);//解绑多个自定义事件(用数组表示)
this.$off();//解绑所有自定义事件
//组件销毁
this.$destroy()//销毁组件实例,同时销毁所有的自定义事件
  • 组件内使用原生事件如@click等,需要在后面加.native,即:@click.native = "xxx"

  • 重要的内置关系:VueComponent.prototype.__proto__===Vue.prototype(让组件的实例对象可以访问到Vue原型上的属性、方法)

  • 对象给对象赋值,会完全变成另一个对象,可能会改变原对象的数据结构,例如:

    obj1 = {name:'小白粥',age:22,sex:"男"}
    obj2 = {name:'深深',age:18}
    obj1 = obj2
    console.log(obj1)//输出{name:'深深',age:18},只有name属性和age属性,丢失sex属性

    若想保留原对象中未改变的值,可使用...运算符,例如:

    obj1 = {name:'小白粥',age:22,sex:"男"}
    obj2 = {name:'深深',age:18}
    obj1 = {...obj1,...obj2}//obj1和obj2共有的属性,以后面的obj2为准,obj1存在而obj2没有的属性会被保留,obj2存在而obj1没有的属性会被添加到obj1
    console.log(obj1)//输出{name:'深深',age:18,sex:"男"}
  • $nextTick:方法会先执行完再对DOM进行操作,若想在执行了部分代码后更新DOM再执行后面的代码(回调函数),或者后半部分的代码(回调函数)是基于前半部分更新后的DOM进行操作,使用$nextTick方法。

    语法this.$nextTick(回调函数)

  • 折叠注释代码块:#region#endregion

       

通过b站coderwhy老师视频和尚硅谷的视频整理,也参考了其他老师的视频笔记以及博客等 ,很多地方官网讲的也比较清楚就直接引用了,日后学习的时候再看看。有错误欢迎指出哈,大家一起交流~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值