综合技术

vue、vue-router 知识梳理

微信扫一扫,分享到朋友圈

vue、vue-router 知识梳理
0

vue

vue生命周期

Vue 实例从创建到销毁的过程,就是生命周期。

同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会,利用各个钩子来完成我们的业务代码,钩子如下:

1. beforeCreate

实例初始化之后、创建实例之前被调用,此时数据观察和事件配置都还没好准备好

2. created

实例创建完成后被调用,此时data有了,dom还没有,挂载属性el还没生成

3. beforeMount

将编译完成的html挂载到对应的虚拟dom时调用,此时相关的render函数首次被执行,页面还没有内容,表达式{{ }}的值还未被替换

4. mounted

编译好的html挂载到页面完成后调用,此时页面已经被渲染出数据

5. beforeUpdate

数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。

6. update

由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

7. beforeDestroy

销毁前调用

8. destroyed

销毁后调用,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。

:leaves: 资料1: 标注图+部分举例聊聊Vue生命周期

:leaves: 资料2: 关于Vue实例的生命周期created和mounted的区别

vue指令

内置指令

1. v-text
:更新元素的 textContent

2. v-html
:更新元素的 innerHTML

3. v-show
:根据表达式之真假值,切换元素的 display CSS 属性

4. v-if
v-else-if
v-else
:条件判断

5. v-for
:基于源数据多次渲染元素或模板块

6. v-on
:(语法糖 @
)绑定事件

修饰符:

  • .stop – 阻止事件冒泡,调用 event.stopPropagation()
  • .prevent – 阻止默认行为,调用 event.preventDefault()
  • .capture – 添加事件侦听器时使用 capture 事件捕获模式
  • .self – 元素本身触发时才触发回调
  • .once – 只调用一次该事件

7. v-bind
(语法糖 :
)当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

8. v-model
:表单元素实现双向数据绑定

修饰符:

  • .trim – 去除两边空格
  • .number – 输入内容转换为number类型
  • .lazy – 当焦点离开文本框时,属性值发生了变化并与文本框内容保持一致

:leaves: 资料1:Vue.js入门教程-指令

自定义指令

通过 directive
就可以在 Vue
上注册一个自定义指令(全局注册、局部注册)

:chestnut: 举例注册一个自定义指令,实现页面加载自动聚焦到元素

// 1. 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

// 2. 注册一个局部自定义指令 `v-focus`
directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

// 使用
<input v-focus>

:leaves: 资料1: 【译】vue 自定义指令的魅力

:leaves: 资料2:vue 自定义指令

filter过滤器

过滤器(全局过滤器,局部过滤器)可以通过管道符号 |
实现文本数据的格式化,可以用在表达式 {{}}
bind

// 全局过滤器
Vue.filter('toTime', function(value) {
  //value 表示要过滤的内容
})

// 批量注册全局过滤器
import * as filters from "config/filters"

Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key]);
});

// 局部过滤器
var app = new Vue({
    el: '#app',
    data: {},
    filters: {
        toTime: function(value){
            //value 表示要过滤的内容
        }
    }
})

// 使用
<div :data-time="date | toTime">{{ date | toTime }}</div>

过滤器可以串联、接收参数,比如:

{{ message | filterA | filterB }}
{{ message | filterA('p1', 'p2') }}

computed 计算属性

使用方法:

<p>我的名字:{{ fullname }}</p>

var vm = new Vue({
  el: '#app',
  data: {
    firstname: 'jack',
    lastname: 'rose'
  },
  computed: {
    fullname() {
      return this.firstname + '.' + this.lastname
    }
  }
})

注意: computed
中的属性不能与 data
中的属性同名,否则会报错。

Q: {{ }}
computed
methods
里面的方法分别在什么情况下使用 ?

{{}}`常用于简单的运算,当过长或逻辑复杂时会变得难以维护。

`computed`常用语复杂逻辑运算,基于依赖缓存。
当计算属性所依赖的数据发生变化时,才会重新取值,当遍历大数组或做大量计算时使用计算属性比较好

`methods`里的方法只要重新渲染就会被调用,函数也会被执行。

watch 监测数据

监测属性 watch
可以通过监测数据来响应数据的变化,可在数据变化时需要执行异步或开销较大的操作时使用。

它是一个对象,键是需要观察的表达式,值是对应回调函数,回调函数接收两个参数分别是新值和旧值。

:warning: 注意: watcher
回调函数不可用箭头函数,箭头函数绑定了父级作用域的上下文,会改变 this
指向。

new Vue({
  data: { 
    a: 1,
    b: { 
      age: 10 
    } 
  },
  watch: {
    a: function(newVal, oldVal) {
      //如果a发生改变,这个函数就会运行
    },

    /**
     * 监听对象变化
     * deep - 深度监测,对象内部属性或方法发生改变,使用它才能监测到该对象的改变
     * immediate - 立即执行,初始化的时候 watch 是不会执行的,如果想在初始化的时候就执行,则需开启这个属性
     */
    b: {
      handler: function (val, oldVal) {
        //TODO ...
      },
      deep: true,
      immediate: true
    },

    /**
     * 监听对象具体某个属性的变化
     */
    'b.age': function (val, oldVal) {
      //TODO ...
    },
  }
})

Q: watch
在任何时候都可以监测的到数据变动吗?

vm.items[0] = {}
vm.items.length = 0

以上三种情况下均数据发生改变时, watch
都不能检测出来,解决办法如下:

  • 对于新增或删除等操作使用 vue
    提供的方法,如 $set()、$delete()等
  • 克隆,如 Object.assign()
    JSON.parse(JSON.stringify(obj))
    {...obj}
    [...arr]

:leaves: 资料1: Vue watch选项

:leaves: 资料2: Vue的computed和watch的细节全面分析

:leaves: 资料3: Vue中$watch的监听变化
& vue中正确的使用watch进行监听

组件注册

全局注册

方法:

使用 Vue.component(tagName, options)
可以注册一个全局的组件

全局组件在所有的vue实例中都可以使用

let myButton = Vue.extend({ 
  template: `<button>点击我</button>`,
  // 组件中的 data 必须是函数 并且函数的返回值必须是对象
  data() {
    return {}
  }
}) 

Vue.component('my-button', myButton)

语法糖:

Vue.component('my-button', { 
    template: `<button>点击我</button>`
})

使用:

<div id="app"> 
    <my-button /> 
</div>

局部注册

方法:

使用 Vue
实例 / 组件实例选项中 components
注册

局部注册的组件只可以应用在当前实例中。

let myButton = Vue.extend({ 
    template: `<button>点击我</button>` 
})

let app = new Vue({ 
    el: '#app',
    components: {
        'my-button':myButton
    }
})

语法糖:

let app = new Vue({ 
    el: '#app',
    components: {
        'my-button': {
            template: `<button>点击我</button>` 
        }
    }
})

使用:

<div id="app"> 
    <my-button /> 
</div>

:leaves: 资料: Vue.js入门教程-组件注册

组件通信

props:父 -> 子

1.
子组件可以通过 props
声明从父组件接收过来的数据, props
值分两种,一种是字符串数组,一种是对象 (需要对值验证可以用对象)。

2.
由于 html
特性不区分大小写,当使用 dom
模板时,驼峰命名的 props
名称要转为短横线分隔命名(字符串模板除外)

3.
如果传递的数据不是直接写死的,而是来自父级的动态数据,可以使用指令 v-bind
来动态绑定 props
的值,当父组件的数据变化时,也会传递给子组件 ,反之不行

4.
业务中经常会遇到两种需要改变 props
的情况:

  • 传递初始值进来,子组件将它作为初始值保存到data中,在自己作用域下可以随意使用和修改
  • prop作为需要被转变的原始值传入,这种情况下可以使用计算属性

但是,以上两种情况,针对于引入类型的数据,在子组件中修改值是会影响到父组件的值的,需要注意!

例子:基于 vue-cli

// 父组件 - 传递 msg1 的值到子组件

<template>
  <div class="parent-box">
    我是父组件
    <child :msg1="msg1"></child>
  </div>
</template>

<script>
import child from "./components/child";  
export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg1: "我是msg1 - 父组件传给子组件的第1个值", 
    };
  }
};
</script>
//子组件child - 接收来自父组件的 msg1 的值

<template>
  <div class="child-box">
    我是子组件
    {{msg1}}
  </div>
</template>

<script>
export default {
  name: "child",
  props: ["msg1"], //props 接收来自父组件传递过来的值
};
</script>

$emit:子 -> 父

当子组件需要像父组件传递数据时,就要用到自定义事件。

类似观察者模式,子组件用 $emit
来触发事件,父组件用 v-on
(语法糖 @
)来监听子组件触发的自定义事件。

用法:

step1
:父组件 – 定义一个方法,并通过 v-on
(语法糖 @
)绑定给子组件,如:

<child :msg1="msg1" @changeMsg1="changeMsg1"></child>

step2
:子组件 – 通过 $emit
触发已绑定的方法,如:

//参数1 eventName - 方法名
//参数2 [...arg] - 要返给被触发方法的参数
this.$emit("changeMsg1", '传给父组件的参数');

例子:

//父组件 - 传递给子组件一个函数 changeMsg1 

<template>
  <div class="parent-box">我是父组件
    <child :msg1="msg1" @changeMsg1="changeMsg1"></child>
  </div>
</template>

<script>
import child from "./components/child";
export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg1: "我是msg1 - 父组件传给子组件的第1个值",
    };
  },
  methods: {
    //参数 data 是子组件传递过来的参数
    changeMsg1(data) {
      this.msg1 = data;
    }
  }
};
</script>
//子组件child - 给 button 绑定 handleClick 方法,在 handleClick 中通过 $emit 去触发changeMsg1方法,并传参过去

<template>
  <div class="child-box">
    {{msg1}}
    <button @click="handleClick">btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  props: ["msg1"],
  methods: {
    handleClick() {
      this.$emit("changeMsg1", '传给父组件的参数'); 
    }
  }
};
</script>

$parent & $children、$refs

this.$parent
可以直接访问该组件父实例或组件(子 -> 父),父组件也可以通过 this.$children
访问所有子组件(数组)(父 -> 子),而且可以递归向上或向下无线访问,直到根实例或最内层组件。

业务中子组件应该应该尽可能避免依赖父组件数据,更不该主动修改父组件数据,这样会使父子组件耦合严重,只看父组件很难理解父组件状态,因为父组件的值可能被随意修改了。

这种方式适用于一个页面单纯的拆分成多个组件组合在一起的情况,被拆分的组件只有一个公用且确定了的父组件。

例子:

//父组件 - 通过 $children 改变子组件 tip 值
<template>
  <div class="parent-box">
    我是父组件
    <child :msg="msg"></child>
    <button @click="handleClick">父btn</button>
  </div>
</template>

<script>
import child from "./components/child";
export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg: "我是父组件 msg 的值"
    };
  },
  methods: {
    handleClick() {
      this.$children.map(i => {
        i.tip = "我是父组件,我用 $children 改变了子组件 child 中 tip的值";
      });
    }
  }
};
</script>
//子组件 - 通过 $parent 改变父组件 msg 值
<template>
  <div class="child-box">
    我是子组件
    {{tip}}
    {{msg}}
    <button @click="handleClick">子btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  props: ["msg"],
  data() {
    return {
      tip: "我是子组件的数据,谁能改变我?"
    };
  },
  methods: {
    handleClick() {
      this.$parent.msg = "我是子组件,我用 $parent 改变了父组件中 msg 的值";
    }
  }
};
</script>

当子组件很多时,很难通过 $children
遍历出来需要的组件实例,可以用 ref
来为子组件指定一个索引名称,然后用 this.$refs[name]
来查找

例子:

//父组件 - 用 ref 给子组件 child 设置索引名,通过 refs 去查找子组件
<template>
  <div class="parent-box">
    我是父组件
    <child ref="component1" :msg="msg"></child>
    <button @click="handleClick">父btn</button>
  </div>
</template>

<script>
import child from "./components/child";
export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg: "我是父组件 msg 的值"
    };
  },
  methods: {
    handleClick() {
      this.$refs.component1.tip = '我是父组件,我要通过 $refs 来查找子组件 child 并修改 它的 tip 值'
    }
  }
};
</script>

EventBus:非父子组件通讯

如果涉及到爷孙之间、兄弟之间或更深层次组件之间需要通信,可以声明一个 vue
实例,把公共方法和属性放在这个实例上面,让这个实例充当一条通讯桥梁。

范围:适用于简单场景,复杂场景请用 vuex

步骤:基于 vue-cli

step1:
创建一个 vue
实例,挂载到全局,比如:

//main.js 文件

Vue.prototype.$bus = new Vue();

//或

window.$bus = new Vue();

step2:
$on
$emit
完成通信

// $on 监听事件,处理回调
bus.$on('eventName', val => {
  //TODO...
})

// $emit 触发事件,返回参数
bus.$emit('eventName', val)

例子:让两个同级组件通讯

//main.js - 声明一个全局变量来保存 vue 实例

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

Vue.prototype.$bus = new Vue();   // $bus

new Vue({
  render: h => h(App),
}).$mount('#app')
// 根组件 - 包含两个子组件
<template>
  <div class="parent-box">
    我是父组件
    <child></child>
    <child2></child2>
  </div>
</template>

<script>
import child from "./components/child";
import child2 from "./components/child2";

export default {
  name: "app",
  components: { child, child2 }
};
</script>
//child 子组件 - $on 监听事件,如果有新参数进来立即替换 childMsg 值
<template>
  <div class="child-box">
    我是子组件 child
    {{childMsg}}
  </div>
</template>

<script>
export default {
  name: "child",
  data() {
    return {
      childMsg: "我是child的数据"
    };
  },
  created() {

    //$on 监听
    this.$bus.$on('changeMsg', data => {
      this.childMsg = data;
    })
  },

};
</script>
//child2 - $emit 触发事件,并传递参数到 changeMsg 方法中
<template>
  <div class="child-box">
    我是子组件 child2
    <button @click="handleClick">child2 btn</button>
  </div>
</template>

<script>
export default {
  name: "child2",
  methods: {
    handleClick() {

      //$emit 触发
      this.$bus.$emit('changeMsg', '我是 child2 ,我更改了 child 的 childMsg 值')
    }
  }
};
</script>

v-model:双向数据绑定 – 单个属性

v-model
可以在 表单控件
或者组件上创建双向绑定。

表单控价上的双向绑定,如:

<input v-model="msg">

//相当于:

<input :value="msg" @input="msg = $event.target.value">

如上,可以看出语法糖 v-model
的实现过程:将 input
标签用 v-bind
绑定 value
属性, v-on
绑定 input
事件。当输入框的文本发生改变时,自动将属性 value
的值替换成目标输入值。

那么,换到组件中该怎么实现呢?

父组件:

通过 v-model
绑定变量

<child v-model="msg"></child>

//相当于

<child :value="msg" @input="msg = arguments[0]"></child>

子组件:

  • 接收一个 value
    属性
  • 在有新的 value
    时触发 input
    事件

举个栗子:

// 父组件 - 用 v-model 绑定 msg
<template>
  <div class="parent-box">
    我是父组件
    这是我的 msg 值:{{msg}}
    <child v-model="msg"></child>
  </div>
</template>

<script>
import child from "./components/child";

export default {
  name: "app",
  components: { child },
  data() {
    return {
      msg: "我是父组件的 msg"
    };
  }
};
</script>
//子组件 - 通过 props 接收 value,用 $emit 触发 input 事件
<template>
  <div class="child-box">
    我是子组件 child
    <button @click="handleClick">child's btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  props: ["value"],
  methods: {
    handleClick() {
      this.$emit("input", "我是 child,这是我传给父组件的新值");
    }
  }
};
</script>

上例中,如果属性 value
被占用了,或者 input
事件冲突了,就会引起报错,所以最好 用 model 属性来定制 v-model

定制方法: model { prop?: string, event?: string }

上例中, child
组件重写为:

//child - 通过 model 定制 v-model
<template>
  <div class="child-box">
    我是子组件 child
    <button @click="handleClick">child's btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  model: {
    prop: 'msg',  //代替 value
    event: 'changeMsg' //代替input方法
  },
  props: ["msg"],
  methods: {
    handleClick() {
      this.$emit("changeMsg", "我是 child,这是我传给父组件的新值");
    }
  }
};
</script>

.sync:双向数据绑定 – 多个属性

2.3版本中重新引入的 .sync 修饰符
作为语法糖存在,它会被扩展为自动更新父组件属性的 v-on
监听器,如下:

<child :msg.sync="msg"></child>

//相当于

<child :msg="msg" @update:msg="val => msg = val"></child>

当子组件需要更新 msg
时,触发update方法,将旧值替换成新值,如下:

this.$emit('update:name', newVal)

一次性想传递多个属性时,可以结合 v-bind
一起使用,如下:

//父组件 - 传递一个对象到子组件,会分别为对象的每个属性分配一个 v-on 监听器

<template>
  <div class="parent-box">
    我是父组件
    {{info.msg1}}
    {{info.msg2}}
    <child v-bind.sync="info"></child>
  </div>
</template>

<script>
import child from "./components/child";

export default {
  name: "app",
  components: { child },
  data() {
    return {
      info: {
        msg1: 1111,
        msg2: 2222
      }
    };
  }
};
</script>
//子组件 - $emit 触发事件

<template>
  <div class="child-box">
    我是子组件 child
    <button @click="handleClick">child's btn</button>
  </div>
</template>

<script>
export default {
  name: "child",
  methods: {
    handleClick() {
      this.$emit("update:msg1", "33333");
      this.$emit("update:msg2", "44444");
    }
  }
};
</script>

$attrs & $listeners

1.在多级组件通信中, $attrs & $listeners
分别负责收集父组件中传递过来的属性和事件,其中 $attr
中收集的属性不包括组件中已通过 props
接受的属性, $listeners
中收集的事件不包括有 .native
修饰符的事件。属性通过 v-bind="$attrs"
一级级向下传递, 事件通过 v-on="$listeners"
一级级向下传递。

2. $attrs
中包含的属性,默认情况下将会作为普通的 HTML
属性应用在子组件的根元素上,可以通过在当前组件中设置 inheritAttrs: false
去掉( style
, class
除外),去掉默认行为不影响数据的使用。

例如:

未设置 inheritAttrs: false
时:

<div class="grandson-box" msg3="333" msg4="444"></div>

设置了 inheritAttrs: false
时:

<div class="grandson-box"></div>

3.适用范围:简单的多级组件通信

例子:

// 一级组件 - 通过 v-bind, v-on 传递属性和事件到下级组件

<template>
  <div class="parent-box">
    我是爷爷
    <son v-bind="msg" @event1="event1" @event2.native="event2" @event3="event3" @event4="event4"></son>
  </div>
</template>

<script>
import son from "./components/son";

export default {
  name: "app",
  components: { son },
  data() {
    return {
      msg: {
        msg1: 111,
        msg2: 222,
        msg3: 333,
        msg4: 444
      }
    };
  },
  methods: {
    event1() {
      console.log(1);
    },
    event2() {
      console.log(2);
    },
    event3(data) {
      console.log(3, data); //3, 我是孙子组件grandson传递到爷爷那去的参数
    },
    event4() {
      console.log(4);
    }
  }
};
</script>
// 二级组件 - 通过 v-bind="$attrs" v-on="$listeners" 传递属性和事件到下一级组件中

<template>
  <div class="son-box">
    我是儿子 {{$attrs}}
    <grandson v-bind="$attrs" v-on="$listeners"/>
  </div>
</template>

<script>
import grandson from "./grandson";
export default {
  name: "son",
  inheritAttrs: false, //组件根部屏蔽掉 $attrs 的属性,但是值仍然存在只是不展示在 html 上了
  components: { grandson },
  props: ["msg1", "msg2"], //通过 props 接收的属性 会被过滤,不存在 $attrs 中
  created() {
    console.log("----- son -------");
    //如果上级组件传递过来的事件含有 .native 修饰符,则该事件被过滤, 不存在 $listeners 中
    console.log(this.$listeners); //{event1: ƒ, event3: ƒ, event4: ƒ}
  }
};
</script>
// 三级组件

<template>
  <div class="grandson-box">
    我是孙子
    {{$attrs.msg3}}
  </div>
</template>

<script>
export default {
  name: "grandson",
  created() {
    console.log("------ grandson ------");
    console.log(this.$listeners); //{event1: ƒ, event3: ƒ, event4: ƒ}
    this.$listeners.event3("我是孙子组件grandson传递到爷爷那去的参数");
  }
};
</script>

:leaves: 资料一: vue组件的那点事

:leaves: 资料二: vue组件通信全揭秘(共7章)

:leaves: 资料三: Vue.js 父子组件通信的十种方式

===================== 分割线 ====================

vue-router

vue-router
的使用流程(基于 vue-cli
):

vue-router
router

step1: 安装并注册 vue-router

安装

yarn add vue-router -S

注册

1.引入 vue
vue-router

import Vue from 'vue'
import VueRouter from 'vue-router'

2.注册

Vue.use(VueRouter)

step2: 定义路由组件

基于 vue-cli
,我们在写好页面组件之后,通常会通过 import
require
引入到管理路由的文件中,普通引入方式如下:

import login from '../page/login';

//或者

const login = require('../page/login').default;

注意:用 require 引入组件时需要加 default,否则报错!

扩展:exports、module.exports 和 export、export default 到底是咋回事

这种普通的引入方式有一个缺点,就是在 npm run build
的时候,所有引入的文件最终会被打包成一个文件,加载的时候会加载整个文件,文件过大会导致页面加载缓慢,为了解决这个问题,我们通常在项目中将它分成多个小的代码块来加载,通常用以下三种方法:

第一种: 异步组件

当需要这个组件的时候,会异步加载过来,并将结果储存以供下次使用。

const login = resolve => require(['../page/login'], resolve)

第二种: 懒加载组件

结合 Vue
的异步组件和 Webpack
的代码分割功能,实现路由组件的懒加载。

const login = () => import('../page/login');

注意:如果使用 Babel,需要添加 syntax-dynamic-import 插件

如果想把某个路由下的所有组件都打包在同个异步块 ( chunk
) 中。可以通过命名 chunk
来实现,如下 login
index
都被打包到了 base
文件中:

const login = () => import(/* webpackChunkName: "base" */ '../page/login');
const index = () => import(/* webpackChunkName: "base" */ '../page/index');
const list = () => import( /* webpackChunkName: "list" */ '../page/list');

第三种: webpack 代码分割

webpack
打包时,会把其对应的文件拆分到一个单独的 chunk
中,此 chunk
会在需要的时候被异步加载。

//最后一个参数 'login' 可以指定打包出的 chunk 名称
const login = r => require.ensure([], () => r(require('../page/login')), 'login');

:leaves: 资料: vue项目实现按需加载的3种方式:vue异步组件、es提案的import()、webpack的require.ensure()

step3: 配置路由

举个例子:

const routes = [
    { path: '/', redirect: '/index' },
    { path: '/login', component: login, name: 'login', meta: { windowTitle: '登录' } },
    { path: '/index', component: index, name: 'index', meta: { windowTitle: '首页' } },
    { path: '/list/:id', component: list, name: 'list', meta: { windowTitle: '列表'}, props: true}
]

routes
常用配置:

path

格式: path: string

概述:当前页面的路径

项目中经常会遇到这种情况,比如有一个 产品列表 list 组件,对于所有 id
各不相同的产品,都要使用这个组件来渲染,可以通过配置动态路径参数 来匹配这种路径,例如: path:'/list/:id'

component

格式: component?: Component

概述:路由视图,通过懒加载等方式引入的组件(见 step2 内容)

name

格式: name?: string

概述: 命名路由
,可通过 name 名字跳转页面,两边 name 要保持一致,跳转方式如:

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

//或

router.push({ name: 'user', params: { userId: 123 }})

props:

格式: boolean | Object | Function

概述:组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。路径上的参数可通过配置 props: true
[将组件和路由解耦][27],不必再通过 $route.params 去访问参数例如:

{ path: '/user/:id', component: User, props: true },

//User - 设置了props:true 之后,route.params 将会被设置为组件属性。
const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>',
  //template: '<div>User {{ $route.params.id }}</div>'  //不需要这样获取值了
}

children

格式: Array<RouteConfig>

概述: 嵌套路由

step4: 创建 router
实例,将配置好的路由传入

常用配置如下:

const router = new VueRouter({

    //页面配置(见step3)
    routes, 
    
    /**
     * mode - 模式,默认 hash
     * hash,地址会变成有 # 号的那种,很丑
     * history,无 # 号的正常地址
     */
    mode: 'history', 
    strict: process.env.NODE_ENV !== "production",

    //滚动位置
    scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            //savedPosition - 在按下 后退/前进 按钮时,就会像浏览器的原生表现那样
            return savedPosition;
        } else {
            //返回到顶部
            return {
                x: 0,
                y: 0
            };
        }
    }
});

step5: 创建并挂载根实例,注入路由

import router from './router' //引入路由

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

完整示例:

router.js
文件配置

//step1 - 安装并注册`vue-router`
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

//step2: 定义路由组件
const login = r => require.ensure([], () => r(require('../page/login')), 'login'); //登录
const index = r => require.ensure([], () => r(require('../page/index')), 'index'); //首页
const list = r => require.ensure([], () => r(require('../page/list')), 'list'); //首页


//step3: 配置路由
const routes = [
    { path: '/', redirect: '/index' },
    { path: '/login', component: login, name: 'login', meta: { windowTitle: '登录' } },
    { path: '/index', component: index, name: 'index', meta: { windowTitle: '首页' } },
    {    path: '/list/:id',    component: list, name: 'list', meta: { windowTitle: '列表'}, props: true },
]

//step4: 创建`router`实例,将配置好的路由传入
const router = new VueRouter({
    routes,
    mode: 'hash', //模式
    strict: process.env.NODE_ENV !== "production",
    //滚动位置
    scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition;
        } else {
            return {
                x: 0,
                y: 0
            };
        }
    }
});

//路由守卫
router.beforeEach((to, from, next) => {
    const token = localStorage.token || '';
    const userArr = localStorage.userArr ? JSON.parse(localStorage.userArr) : null;
    if (to.meta.windowTitle) document.title = to.meta.windowTitle;

    if (!token && to.name !== 'login') {
        next({name: 'login', replace: true}); //没有token进入登录页面
    } else if (token && userArr && to.name == 'login') {
        next({name: 'index', replace: true}); //有token进入登录页面跳转首页
    } else if (to.name != 'login' && !userArr) {
        next({name: 'login', replace: true});
    } else {
        //TODO 如果没有登录信息 - 保存登录信息
        // if (!store.state.userArr) {
        //     store.commit('SAVE_USERARR', userArr);
        // }
        next();
    }
})

export default router;

main.js
文件配置

import Vue from 'vue'
import App from './App'
import router from './router' //引入路由

Vue.config.productionTip = false

//step5: 创建并挂载根实例,注入路由
new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

:leaves: 资料1: Vue2.0 探索之路——vue-router入门教程和总结

:leaves: 资料2: vue-router 一些容易被忽略的知识点

阅读原文...

微信扫一扫,分享到朋友圈

vue、vue-router 知识梳理
0
SegmentFault博客

用户运营如何做?产品经理从0到1全过程

上一篇

哈啰致信滴滴顺风车:垄断阻碍进步 一起促进良性竞争

下一篇

评论已经被关闭。

插入图片

热门分类

往期推荐

vue、vue-router 知识梳理

长按储存图像,分享给朋友