目录
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 导航守卫
小知识点
组件化模式,提高代码复用率,更易于维护
声明式编码,让编码人员无需直接操作DOM,提高开发效率
使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点
M: Model,模型,对应data中的数据
V: View,视图,对应模板
VM: ViewModel,视图模型,Vue的实例对象
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);
使用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内部操作(读/写)对应的 属性
事件修饰符:
prevent:阻止默认事件
stop:阻止事件冒泡
once:事件只触发一次
capture:使用事件的捕获方式
self:只有event.target是当前操作的元素时才触发事件
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.自定义键名 = 键码,来定制按键别名
enter => 回车
delete =>删除/退格
esc => 退出
space => 空格
tab => 换行(比较特殊,必须配合keydown使用
up => 上
down => 下
left => 左
right => 右
Vue会监测data里所有层次的数据
如何监测对象中的数据:
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,vue默认不做响应式处理
(2).若需要给后添加的属性做响应式,可使用如下API:
Vue.set(target,propertyName/index,value)或 vm.$set(target,propertyName/index,value)
如何监测数组中的数据:
通过包裹数组更新元素的方法实现,本质是做了两件事:
(1).调用原生对应的方法对数组进行更新
(2).重新解析模板,更新页面
Vue.set(target,propertyName/index,value)或 vm.$set(target,propertyName/index,value)
在vue修改数组中的某个元素一定要使用如下几个方法:
(1). push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2).vue.set() 或 vm.$set()
注意:Vue.set()和vm.$stet() ,不能给vm或vm的根数据对象(如data)添加属性!
全局事件总线示例
可用于任意组件间的通信
安装全局事件总线:
/* 全局事件总线 */
//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解绑当前组件用到的事件。
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)
取消订阅。
方法一:在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
},
},
优点:可以配置多个代理,且可以灵活控制请求是否走代理。
缺点:配置较为繁琐,请求资源时必须加前缀
初始化:生命周期、事件,但数据代理还未开始。
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渲染后执行里面的回调函数)
一般用法:函数形式,有返回值
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
只会调用一次。
computed:
{{fullname}}
{{fullname}}
{{fullname}}
{{fullname}}
methods:
{{getFullname()}}
{{getFullname()}}
{{getFullname()}}
{{getFullname()}}
当我们的应用遇到多个组件共享状态时:
多个视图依赖于同一状态。
来自不同视图的行为需要变更同一状态。
和路由一样,新建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的计算属性。
用法:函数参数有两个,state
和getters
,只用到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)
首先在vuex中引入import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
1. mapState方法:用于帮助我们映射state
中的数据为计算属性
2. mapGetters方法:用于帮助我们映射getters
中的数据为计算属性
3. mapActions方法:用于帮助我们生成与actions
对话的方法,即:$store.dispatch(xxx)
的函数
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'])
}
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,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 的状态
默认情况下,模块内部的 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']
}
}
}
}
}
})
当使用 mapState
, mapGetters
, mapActions
和 mapMutations
这些函数来绑定带命名空间的模块时,写起来可能比较繁琐:
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()
])
}
路由:一组key-value的对应关系
多个路由,需要经过路由器管理
路由组件通常存放在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' //设置激活的样式(所有激活状态都是这个样式)
})
a
b
使用children配置项:注意:children是一个数组,并且子路由不用写“/”
routes: [
{
path:'/home',
component:home,
children:[
{
path:'aaa',
component:aaa
},
{
path:'bbb',
component:bbb
}
]
},
跳转时要写完整路由:
a
b
query参数(路径是http://localhost:8080/#/home/aaa?id=003&name=消息3
样式的)
传递参数:to的字符串写法和对象写法(对象写法可以使用name或者path)
-
{{m.name}}
接收参数:使用$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写法)
-
{{m.title}}
接收参数:使用$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}
}
}
路由模式:
默认默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。(即路径中有#这种),兼容性好
路由的 history 模式充分利用 了history.pushState
API 来完成 URL 跳转而无须重新加载页面。可在router中配置:mode:'history'
,兼容性略差,应用部署上线时需要后端解决刷新页面服务端404的问题
router-link
的replace属性:可以控制路由跳转时操作浏览器历史记录的模式。 浏览器历史记录有两种写入方式:push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转默认为push
。开启replace
模式:
编程式导航
除了使用
创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
使用$router.xxx
实现,可用于按钮的点击事件中。
$router.push(...)
:这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到 之前的 URL。当点击
时,这个方法会在内部调用,所以说,点击
等同于调用 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的组件都缓存。
keep-alive包在router-view外面。
若include包含多个组件,可写成数组形式(:include="['News','Message']"
)
两个生命周期钩子:vue-router独有的两个生命周期函数,activated()
和deactivated()
表示组件的激活状态(是否活跃/隐藏),可用于希望在切换路由,同时组件不被销毁的状态下决定部分数据是否缓存,搭配
使用
路由元信息:定义路由的时候可以配置 meta
字段来给路由添加属性信息。
全局前置守卫 router.beforeEach((to, from, next) => {})
其中:to是即将要进入的目标的路由对象,from是当前导航正要离开的路由。
next: Function
: 一定要调用该方法来 resolve这个钩子。执行效果依赖 next
方法的调用参数。
next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed(确认的)。
next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from
路由对应的地址。
next('/')
或者 next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next
传递任意位置对象,且允许设置诸如 replace: true
、name: 'home'
之类的选项以及任何用在 router-link
的 to
、prop
或 router.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`
}
}
导航解析流程:
导航被触发。
在失活的组件里调用 beforeRouteLeave
守卫。
调用全局的 beforeEach
守卫。
在重用的组件里调用 beforeRouteUpdate
守卫 。
在路由配置里调用 beforeEnter
。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter
。
调用全局的 beforeResolve
守卫 。
导航被确认。
调用全局的 afterEach
钩子。
触发 DOM 更新。
调用 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老师视频和尚硅谷的视频整理,也参考了其他老师的视频笔记以及博客等 ,很多地方官网讲的也比较清楚就直接引用了,日后学习的时候再看看。有错误欢迎指出哈,大家一起交流~~