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 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
???? 资料 1:标注图 + 部分举例聊聊 Vue 生命周期???? 资料 2:关于 Vue 实例的生命周期 created 和 mounted 的区别
vue 指令
内置指令
1.v-text:更新元素的 textContent2.v-html:更新元素的 innerHTML3.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(语法糖 :)当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM8.v-model:表单元素实现双向数据绑定
修饰符:
.trim – 去除两边空格
.number – 输入内容转换为 number 类型
.lazy – 当焦点离开文本框时,属性值发生了变化并与文本框内容保持一致
???? 资料 1:Vue.js 入门教程 - 指令
自定义指令
通过 directive 就可以在 Vue 上注册一个自定义指令(全局注册、局部注册)
???? 举例注册一个自定义指令,实现页面加载自动聚焦到元素
// 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>
???? 资料 1:【译】vue 自定义指令的魅力???? 资料 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 可以通过监测数据来响应数据的变化,可在数据变化时需要执行异步或开销较大的操作时使用。它是一个对象,键是需要观察的表达式,值是对应回调函数,回调函数接收两个参数分别是新值和旧值。
⚠️ 注意: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
对象 – 不能检测对象某个(在 data 中不存在)属性的添加或删除
以上三种情况下均数据发生改变时,watch 都不能检测出来,解决办法如下:
对于新增或删除等操作使用 vue 提供的方法,如 $set()、$delete()等
克隆,如 Object.assign()、JSON.parse(JSON.stringify(obj))、{…obj}、[…arr]等
???? 资料 1:Vue watch 选项???? 资料 2:Vue 的 computed 和 watch 的细节全面分析???? 资料 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>
???? 资料: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>
???? 资料一:vue 组件的那点事???? 资料二:vue 组件通信全揭秘(共 7 章)???? 资料三:Vue.js 父子组件通信的十种方式
===================== 分割线 ====================
vue-router
vue-router 的使用流程(基于 vue-cli):
step1: 安装并注册 vue-router
step2: 定义路由组件
step3: 配置路由
step4: 创建 router 实例,将配置好的路由传入
step5: 创建并挂载根实例,注入路由
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’);
???? 资料: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’)
???? 资料 1:Vue2.0 探索之路——vue-router 入门教程和总结???? 资料 2:vue-router 一些容易被忽略的知识点???? 资料 3:关于 vue-router 的 beforeEach 无限循环的问题???? 资料 4:Vue 的钩子函数[路由导航守卫、keep-alive、生命周期钩子