路由模式(2种)
在vue-router路由对象中,路由有两种模式:hash和history,而默认的是hash模式.
hash路由
onhashchange事件,可以在windows对象上监听这个事件:
windows.onhashchange = function(event){
console.log(event.oldURL,event.newURL);
let hash = location.hasg //通过location对象来获取hash地址
console.log(hash) // #/notebooks/123456/list 地址从#号开始
}
因为hash发生变化的url都会被浏览器记录下来,从而会发现浏览器的前进后退都可以用
history路由
随着 history api的到来,前端路由开始进化了,前面的hashchange,你只能改变#后面的url片段,而history api则给了前端完全的自由,history api可以分为两大部分,切换和修改
切换历史状态
包括 back,forward,go三个方法,对应浏览器的前进,后退,跳转操作。很多小伙伴只知道浏览器有前进和后退,其实在前进后退上长按鼠标,会弹出历史记录,从而完成跳转.
history.go(-2); //后退两次
history.go(2);//前进两次
history.back();//后退
history.forward();前进
修改历史状态
包括了pushState,replaceState两个方法,这两个方法接受三个参数:stateObj,title,url
history.pushState({color:'red','red','red'})
history.back();
通过pushstate把页面的状态保存在state对象中,当页面的url再变回这个url时,可以通过event.state取到这个state对象.
history 模式的补充
对于vue开发的单页面应用,我们在切换不同的页面的时候,可以发现html永远只有一个,这也真是称之为单页面的原因,而vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。而当开启history模式的时候,如果后端不进行任何处理,在Dev阶段一切都是正常的,可是打包之后,访问路径则会返回404,因此要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是app 依赖的页面。
Vue.nextTick 的原理和用途
JS 运行机制
JS 执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件(根据放置先后时间顺序决定事件执行顺序)。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
<img src="../assets/event-loop.png"/>
主线程的执行过程就是一个 tick,而所有的异步结果都是通过 “任务队列” 来调度被调度。 消息队列中存放的是一个个的任务(task)。 规范中规定 task 分为两大类,分别是 macro task 和 micro task,并且每个 macro task 结束后,都要清空所有的 micro task。
关于 macro task 和 micro task 的概念,这里不会细讲,简单通过一段代码演示他们的执行顺序:
在浏览器环境中,常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate;常见的 micro task 有 MutationObsever 和 Promise.then。
Vue.nextTick(callback) 使用原理:
原因是,Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
当你设置 vm.someData = ‘new value’,DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
定义:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
所以就衍生出了这个获取更新后的DOM的Vue方法。所以放在Vue.nextTick()回调函数中的执行的应该是会对DOM进行操作的 js代码;
理解:nextTick(),是将回调函数延迟在下一次dom更新数据后调用,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数,
nextTick属于微任务
同步代码执行 -> 查找异步队列,推入执行栈,执行Vue.nextTick[事件循环1] ->查找异步队列,推入执行栈,执行Vue.nextTick[事件循环2]
在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM,示例如下:
<template>
<div class="nextTick">
<div ref="msgDiv">{{msg}}</div>
<button @click="changeMsg">
Change the Message
</button>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: "nextTick",
data() {
return {
msg: "Hello Vue."
};
},
methods: {
changeMsg() {
this.msg = "Hello World.";
setTimeout(() => { // 属于宏任务
console.log(this.$refs.msgDiv.innerHTML); // Hello World.
}, 0);
this.$nextTick(() => { // 属于微任务
console.log(this.$refs.msgDiv.innerHTML); // Hello World.
});
console.log(this.$refs.msgDiv.innerHTML); // Hello Vue.(没有改变)
}
}
};
</script>
点击按钮之后,发现控制台中this.$nextTick()和setTimeout()都发生了改变
可以发现this.$nextTick和setTimeout的效果是一样的,都是将回调方法放入异步队列中
this.$nextTick是当dom发生变化更新后执行的回调。setTimeout只是一个异步延迟执行
什么时候需要用的Vue.nextTick()?
- Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。
<template>
<div class="nextTick">
<div ref="msgDiv">{{msg}}</div>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: "nextTick",
data() {
return {
msg: "Hello Vue."
};
},
created() {
// this.$refs.msgDiv.innerHTML = "更改了内容"; // 报错, 此时DOM并没有渲染
this.$nextTick(function () {
this.$refs.msgDiv.innerHTML = "更改了内容"; // 成功修改
})
}
};
</script>
- 当项目中你想在改变DOM元素的数据后基于新的dom做点什么,对新DOM一系列的js操作都需要放进Vue.nextTick()的回调函数中;通俗的理解是:更改数据后当你想立即使用js操作新的视图的时候需要使用它,如上面一开始的例子
官网等着你:https://cn.vuejs.org/v2/api/#Vue-nextTick