目录
2.v-if、v-show区别
3.vue-router 有哪些模式? 区别是什么? 具体是怎么实现的
14.diff算法
自定义指令(v-check、 v-focus)的方法有哪些?它有哪些钩子函数?
1.深入浅出Vue响应式原理
2.v-if、v-show区别
3.vue-router 有哪些模式? 区别是什么? 具体是怎么实现的
浅谈vue-router原理
https://www.cnblogs.com/goloving/p/9147551.html
https://blog.csdn.net/wang1006008051/article/details/81805932
history和hash的区别
- hash路由在地址栏URL上有#,用 window.location.hash 读取。而history路由没有会好看一点
- 我们进行回车刷新操作,hash路由会加载到地址栏对应的页面,而history路由一般就404报错了(刷新是网络请求,没有后端准备时会报错)。
- hash路由支持低版本的浏览器,而history路由是HTML5新增的API。
4.hash的特点在于它虽然出现在了URL中,但是不包括在http请求中,所以对于后端是没有一点影响的,所以改变hash不会重新加载页面,所以这也是单页面应用的必备。
5.history运用了浏览器的历史记录栈,之前有back,forward,go方法,之后在HTML5中新增了pushState()和replaceState()方法,它们提供了对历史记录进行修改的功能,不过在进行修改时,虽然改变了当前的URL,但是浏览器不会马上向后端发送请求。
6.history的这种模式需要后台配置支持。比如:当我们进行项目的主页的时候,一切正常,可以访问,但是当我们刷新页面或者直接访问路径的时候就会返回404,那是因为在history模式下,只是动态的通过js操作window.history来改变浏览器地址栏里的路径,并没有发起http请求,但是当我直接在浏览器里输入这个地址的时候,就一定要对服务器发起http请求,但是这个目标在服务器上又不存在,所以会返回404
总结
当然啦,history 也不是样样都好。SPA 虽然在浏览器里游刃有余,但真要通过 URL 向后端发起 HTTP 请求时,两者的差异就来了。尤其在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候。
- hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.abc.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
- history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
- 结合自身例子,对于一般的 Vue + Vue-Router + Webpack + XXX 形式的 Web 开发场景,用 history 模式即可,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。
4. vue-router导航守卫
https://zhuanlan.zhihu.com/p/54112006
应用场景
https://www.cnblogs.com/baifangzi/p/14283463.html
5. vue组件通信
6. diff算法
7. Vue3 proxy
8. vue.$set
原理
https://www.jb51.net/article/146580.htm
源码
export function set (target: Array<any> | Object, key: any, val: any): any {
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
} // 判断目标值是否为数组,并且key值是否为有效的数组索引
if (Array.isArray(target) && isValidArrayIndex(key)) { // 对比数组的key值和数组长度,取较大值设置为数组的长度
target.length = Math.max(target.length, key) // 替换目标值
target.splice(key, 1, val)
return val
} // 如果目标值是对象,并且key值是目标值存在的有效key值,并且不是原型上的key值
if (key in target && !(key in Object.prototype)) { // 直接更改目标值
target[key] = val
return val
}
const ob = (target: any).__ob__ // 判断目标值是否为响应式的
if (target._isVue || (ob && ob.vmCount)) { // 如果是vue根实例,就警告
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
if (!ob) { // 如果目标值不是响应式的,那么值需要给对应的key赋值
target[key] = val
return val
} // 其他情况,目标值是响应式的,就通过Object.defineProperty进行数据监听
defineReactive(ob.value, key, val) // 通知更新dom操作
ob.dep.notify()
return val
}
大概流程
判断目标值是否为有效值,不是有效值直接停止
判断是否为数组,并且key值是否为有效的key值
如果是数组,就选择数组的长度和key值取较大值作为数组的新的length值,并且替换目标值splice方法,重写了,所以执行splice,会双向数据绑定判断目标值是否为响应式的ob
如果是vue实例,直接不行
如果不是响应式的数据,就是普通的修改对象操作
如果是响应式数据,那就通过Object.defineProperty进行数据劫持通知dom更新
9. Vue是怎么重写数组的
// 缓存数组原型
const arrayProto = Array.prototype;
// 实现 arrayMethods.__proto__ === Array.prototype
export const arrayMethods = Object.create(arrayProto);
// 需要进行功能拓展的方法
const methodsToPatch = [
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function(method) {
// 缓存原生数组方法
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
// 执行并缓存原生数组功能
const result = original.apply(this, args);
// 响应式处理
const ob = this.__ob__;
let inserted;
switch (method) {
// push、unshift会新增索引,所以要手动observer
case "push":
case "unshift":
inserted = args;
break;
// splice方法,如果传入了第三个参数,也会有索引加入,也要手动observer。
case "splice":
inserted = args.slice(2);
break;
}
//
if (inserted) ob.observeArray(inserted);// 获取插入的值,并设置响应式监听
// notify change
ob.dep.notify();// 通知依赖更新
// 返回原生数组方法的执行结果
return result;
});
});
};
10. Vue.nextTick()
实现原理
/* @flow */
/* globals MutationObserver */
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'
export let isUsingMicroTask = false
const callbacks = []
let pending = false
/**
* 对所有callback进行遍历,然后指向响应的回调函数
* 使用 callbacks保证了可以在同一个tick内执行多次 nextTick,不会开启多个异步任务,而把这些异步任务都压成一个同步任务,在下一个 tick 执行完毕。
*/
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]( "i")
}
}
let timerFunc
/*
* timerFunc 实现的就是根据当前环境判断使用哪种方式实现
* 就是按照 Promise.then和 MutationObserver以及setImmediate的优先级来判断,支持哪个就用哪个,如果执行环境不支持,会采用setTimeout(fn, 0)代替;
*/
// 判断是否支持原生 Promise
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
// 不支持 Promise的话,再判断是否原生支持 MutationObserver
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// 新建一个 textNode的DOM对象,使用 MutationObserver 绑定该DOM并传入回调函数,在DOM发生变化的时候会触发回调,该回调会进入主线程(比任务队列优先执行)
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
// 此时便会触发回调
textNode.data = String(counter)
}
isUsingMicroTask = true
// 不支持的 MutationObserver 的话,再去判断是否原生支持 setImmediate
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// Promise,MutationObserver, setImmediate 都不支持的话,最后使用 setTimeout(fun, 0)
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
// 该函数的作用就是延迟 cb 到当前调用栈执行完成之后执行
export function nextTick (cb?: Function, ctx?: Object) {
// 传入的回调函数会在callbacks中存起来
let _resolve
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
// pending是一个状态标记,保证timerFunc在下一个tick之前只执行一次
if (!pending) {
pending = true
/**
* timerFunc 实现的就是根据当前环境判断使用哪种方式实现
* 就是按照 Promise.then和 MutationObserver以及setImmediate的优先级来判断,支持哪个就用哪个,如果执行环境不支持,会采用setTimeout(fn, 0)代替;
*/
timerFunc()
}
// 当nextTick不传参数的时候,提供一个Promise化的调用
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
- nextTick接受一个回调函数时(当不传参数的时候,提供一个Promise化的调用),传入的回调函数会在callbacks中存起来,根据一个状态标记 pending 来判断当前是否要执行 timerFunc();
- timerFunc() 是根据当前环境判断使用哪种方式实现,按照 Promise.then和 MutationObserver以及setImmediate的优先级来判断,支持哪个就用哪个,如果执行环境不支持,会采用setTimeout(fn, 0)代替;
- timerFunc()函数中会执行 flushCallbacks函数,flushCallbacks函数的作用就是对所有callback进行遍历,然后指向响应的回调函数
总体来说:就是先判断是否支持promise,如果支持promise。就通过Promise.resolve的方法,异步执行方法,如果不支持promise,就判断是否支持MutationObserver。如果支持,就通过MutationObserver(微异步)来异步执行方法,如果MutationObserver还不支持,就通过setTimeout来异步执行方法。
MutaionObserver通过创建新的节点,调用timerFunc方法,改变MutationObserver监听的节点变化,从而触发异步方法执行。
11. Key的作用
12. slot插槽
13. 为什么vue data属性必须是一个函数
14.diff算法
15. vue-router编程式导航
16. vue中输入框事件的使用——@input、@keyup.enter、@change、@blur
17. 自定义指令(v-check、 v-focus)的方法有哪些?它有哪些钩子函数?
vue2.0
vue3.0
2.0与3.0主要区别为3.0的钩子函数更全
2.0
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind
:只调用一次,指令与元素解绑时调用。
3.0
created
:在绑定元素的 attribute 或事件监听器被应用之前调用。在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用。beforeMount
:当指令第一次绑定到元素并且在挂载父组件之前调用。mounted
:在绑定元素的父组件被挂载后调用。beforeUpdate
:在更新包含组件的 VNode 之前调用。updated
:在包含组件的 VNode 及其子组件的 VNode 更新后调用。beforeUnmount
:在卸载绑定元素的父组件之前调用unmounted
:当指令与元素解除绑定且父组件已卸载时,只调用一次。
18. 自定义组件的v-model
19. vue-router router-link组件的active-class属性
20. vue- router嵌套路由
21. Vue中computed和method之间有什么不同点
VUE中methods watch和compute的区别和联系
22. 要发送一个异步请求,应该放在哪个生命周期
23. vue和原生js的优点分析
24. 如果要新增一个属性怎么实现响应式
this.$set(this.data,”key”,value’)
Vue.set(vm.items,2,"ling") : 表示 把vm.items 这个数组的下标为2 的元素,改为"ling"
Vue.set(vm.person,"age","26")
Vue.set()向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = 'hi')
区别在于Vue.set()是将set函数绑定在Vue构造函数上,this.$set()是将set函数绑定在Vue原型上。
Vue.set数组实现的原理:其实Vue.set()对于数组的处理其实就是调用了splice方法