tip:
Dep
是一个用来负责收集Watcher
的类,Watcher
是一个封装了渲染视图逻辑的类,用于派发更新的。需要注意的是Watcher 是不能直接更新视图的还需要结合Vnode经过patch()中的diff算法才可以生成真正的DOM
-
每一个属性都有自己的
dep
属性,来存放依赖的Watcher
,属性发生变化后会通知Watcher
去更新。 -
在用户获取(
getter
) 数据时 Vue 给每一个属性都添加了dep
属性来(collect as Dependency)收集Watcher
。在用户setting
设置属性值时dep.notify()
通知收集的Watcher
重新渲染。详情见上面的defineReactive()
-
Dep依赖收集类
其和Watcher类
是多对多双向存储的关系 -
每一个属性都可以有多个
Watcher 类
,因为属性可能在不同的组件中被使用。 -
同时一个
Watcher 类
也可以对应多个属性。
Vue中模板编译:其实就是将
template
转化成render
函数。说白了就是将真实的DOM(模板)
编译成虚拟dom(Vnode)
-
第一步是将
template 模板
字符串转换成ast 语法树
(parser 解析器),这里使用了大量的正则来匹配标签的名称,属性,文本等。 -
第二步是对 AST 进行静态节点
static
标记,主要用来做虚拟 DOM 的渲染优化(optimize优化器),这里会遍历出所有的子节点也做静态标记 -
第三步是 使用
ast语法树
重新生成render 函数
代码字符串 code。(codeGen 代码生成器)
为什么要静态标记节点,如果是静态节点(没有绑定数据,前后不需要发生变化的节点)那么后续就不需要 diff 算法来作比较。
-
vue 中的生命周期钩子只是一个回调函数,在创建组件实例化的过程中会调用对应的钩子执行。
-
使用
Vue.mixin({})
混入的钩子或生命周期中定义了多个函数,vue 内部会调用mergeHook()
对钩子进行合并放入到队列中依次执行 -
扩展
// src\core\util\options.js
function mergeHook (
parentVal: ?Array,
childVal: ?Function | ?Array
): ?Array {
const res = childVal
? parentVal
-
? parentVal.concat(childVal) // 合并
-
Array.isArray(childVal)
? childVal
-
[childVal]
-
parentVal
return res
-
? dedupeHooks(res)
- res
}
复制代码
8.老生常谈之 vue 生命周期有哪些,一般在哪里发送请求?
-
beforeCreate
: 刚开始初始化 vue 实例,在数据观测observer
之前调用,还没有创建data/methods
等属性 -
created
: vue 实例初始化结束,所有的属性已经创建。 -
beforeMount
: 在 vue 挂载数据到页面上之前,触发这个钩子,render 函数此时被触发。 -
mounted
: el 被 创建的vm.$el
替换,vue 初始化的数据已经挂载到页面之上,这里可以访问到真实的 DOM。一般会在这里请求数据。 -
beforeUpdate
: 数据更新时调用,也就是在虚拟 dom 重新渲染之前。 -
updated
: 数据变化导致虚拟 dom 发生重新渲染之后发生。 -
beforeDestroy
: 实例销毁之前调用该钩子,此时实例还在。vm.$destroy
触发两个方法。 -
destroyed
: Vue 实例销毁之后调用。所有的事件监听都会被接触。
请求数据要看具体的业务需求决定在哪里发送
ajax
-
使用场景:用于抽离一个公共的业务逻辑实现复用。
-
实现原理:调用
mergeOptions()
方法采用策略模式针对不同的属性合并。混入的数据和组件的数据有冲突就采用组件本身的。 -
Vue.mixin({})
缺陷,1.可能会导致混入的属性名和组件属性名发生命名冲突;2. 数据依赖的来源问题 -
扩展
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
// some code
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
}
// 递归遍历合并组件和混入的属性
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
复制代码
10.老生常谈之 vue 组件中的data 为什么必须是一个函数?
-
这和 js 本身机制相关,
data
函数中返回的对象引用地址不同,就能保证不同组件之间的数据不相互污染。 -
Vue.mixin()
中如果混入data
属性,那么data
也必须是一个函数。因为Vue.mixin()
也可以多处使用。 -
实例中
data
可以是一个对象也可以是一个函数,因为我们一个页面一般只初始化一个Vue实例(单例)
11. 老生常谈之 vue 中 vm.$nextTick(cb)实现原理和场景
-
场景:
在 dom 更新循环结束后调用,用于获取更新后的 dom 数据
-
实现原理:
vm.$nextTick(cb)
是一个异步的方法为了兼容性做了很多降级处理依次有promise.then,MutationObserver,setImmediate,setTimeout
。在数据修改后不会马上更新视图,而是经过set
方法 notify 通知Watcher
更新,将需要更新的Watcher
放入到一个异步队列中,nexTick
的回调函数就放在Watcher
的后面,等待主线程中同步代码执行借宿然后依次清空队列中,所以vm.nextTick(callback)
是在dom
更新结束后执行的。
上面将对列中
Watcher
依次清空就是vue 异步批量更新的原理
。提一个小思考:为什么不直接使用setTimeout
代替?因为setTimeout
是一个宏任务,宏任务多性能也会差。
-
computed
内部就是根据Object.definedProperty()
实现的 -
computed
具备缓存功能,依赖的值不发生变化,就不会重新计算。 -
watch
是监控值的变化,值发生变化时会执行对应的回调函数。 -
computed
和watch
都是基于Watcher类
来执行的。
computed
缓存功能依靠一个变量dirty
,表示值是不是脏的默认是true
,取值后是false
,再次取值时dirty
还是false
直接将还是上一次的取值返回。
// src\core\instance\state.js computed 取值函数
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) { // 判断值是不是脏 dirty
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
// src\core\instance\state.js watch 实现
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
): Function {
const vm: Component = this
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true
// 实例化 watcher
const watcher = new Watcher(vm, expOrFn, cb, options)
if (options.immediate) {
const info = callback for immediate watcher "${watcher.expression}"
pushTarget()
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
popTarget()
}
return function unwatchFn () {
watcher.teardown()
}
}
===========================================================================
面试光看这12道题怎么够呢?小编把vue面试题整理成了一个集合共有105道题目,不多但经典哦。
-
vue.js的两个核心是什么?
-
vue 的双向绑定的原理是什么?
-
vue生命周期钩子函数有哪些?
-
请问 v-if 和 v-show 有什么区别?
-
vue常用的修饰符
-
nextTick
-
什么是vue生命周期
-
数据响应(数据劫持)
-
virtual dom 原理实现
-
Proxy 相比于 defineProperty 的优势
-
vuex
-
vue中 key 值的作用
-
Vue 组件中 data 为什么必须是函数?
-
v-for 与 v-if 的优先级
-
说出至少 4 种 vue 当中的指令和它的用法
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
滞不前!**
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-Sxe2KTR2-1710919552234)]
[外链图片转存中…(img-8ddyMpJO-1710919552234)]
[外链图片转存中…(img-fBqK3qWA-1710919552235)]
[外链图片转存中…(img-VBxYyxbc-1710919552236)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-avQOQAUm-1710919552236)]