专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

冲击BAT,寒冬逆袭必备面经:Vue必问题目

ins518 2024-10-04 01:52:33 技术文章 11 ℃ 0 评论

前言

这个面向中高级Web前端的面经系列文章,到此便接近尾声了。是不是还有继续出,这个要看接下来的时间安排了。

小团体有个朋友说,他准备更新一些android-kotlin从入门到上手写业务的文章。所以关注我的Web前端的朋友们,不着急,先休息休息,消化消化之前的文章,提前预祝跳槽的朋友,有个好东家~!

本系列文章链接如下:

“金三银四”,让我们愉快的开始准备Web面经吧:CSS篇

“金三银四”,让我们愉快的开始准备Web面经吧:JavaScript-上

中高端Web前端面试题,直击BAT:JavaScript篇

元宵节,猿宵节,写代码之余面经走一波:JavaScript篇

通往中高端Web前端:浏览器篇

正文

1. nextTick

在下次dom更新循环结束之后执行延迟回调,可用于获取更新后的dom状态

  • 新版本中默认是mincrotasks, v-on中会使用macrotasks
  • macrotasks任务的实现:
  • setImmediate / MessageChannel / setTimeout

2. 生命周期

  • _init_
  • initLifecycle/Event,往vm上挂载各种属性
  • callHook: beforeCreated: 实例刚创建
  • initInjection/initState: 初始化注入和 data 响应性
  • created: 创建完成,属性已经绑定, 但还未生成真实dom
  • 进行元素的挂载: $el / vm.$mount()
  • 是否有template: 解析成render function*.vue文件: vue-loader会将<template>编译成render function
  • beforeMount: 模板编译/挂载之前
  • 执行render function,生成真实的dom,并替换到dom tree中
  • mounted: 组件已挂载
  • update:
  • 执行diff算法,比对改变是否需要触发UI更新
  • flushScheduleQueuewatcher.before: 触发beforeUpdate钩子 - watcher.run(): 执行watcher中的 notify,通知所有依赖项更新UI
  • 触发updated钩子: 组件已更新
  • actived / deactivated(keep-alive): 不销毁,缓存,组件激活与失活
  • destroy:
  • beforeDestroy: 销毁开始
  • 销毁自身且递归销毁子组件以及事件监听
  • remove(): 删除节点
  • watcher.teardown(): 清空依赖
  • vm.$off(): 解绑监听
  • destroyed: 完成后触发钩子

上面是vue的声明周期的简单梳理,接下来我们直接以代码的形式来完成vue的初始化

new Vue({})
// 初始化Vue实例
function _init() {
	 // 挂载属性
 initLifeCycle(vm) 
 // 初始化事件系统,钩子函数等
 initEvent(vm) 
 // 编译slot、vnode
 initRender(vm) 
 // 触发钩子
 callHook(vm, 'beforeCreate')
 // 添加inject功能
 initInjection(vm)
 // 完成数据响应性 props/data/watch/computed/methods
 initState(vm)
 // 添加 provide 功能
 initProvide(vm)
 // 触发钩子
 callHook(vm, 'created')
		
	 // 挂载节点
 if (vm.$options.el) {
 vm.$mount(vm.$options.el)
 }
}
// 挂载节点实现
function mountComponent(vm) {
	 // 获取 render function
 if (!this.options.render) {
 // template to render
 // Vue.compile = compileToFunctions
 let { render } = compileToFunctions() 
 this.options.render = render
 }
 // 触发钩子
 callHook('beforeMounte')
 // 初始化观察者
 // render 渲染 vdom, 
 vdom = vm.render()
 // update: 根据 diff 出的 patchs 挂载成真实的 dom 
 vm._update(vdom)
 // 触发钩子 
 callHook(vm, 'mounted')
}
// 更新节点实现
funtion queueWatcher(watcher) {
	nextTick(flushScheduleQueue)
}
// 清空队列
function flushScheduleQueue() {
	 // 遍历队列中所有修改
 for(){
	 // beforeUpdate
 watcher.before()
 
 // 依赖局部更新节点
 watcher.update() 
 callHook('updated')
 }
}
// 销毁实例实现
Vue.prototype.$destory = function() {
	 // 触发钩子
 callHook(vm, 'beforeDestory')
 // 自身及子节点
 remove() 
 // 删除依赖
 watcher.teardown() 
 // 删除监听
 vm.$off() 
 // 触发钩子
 callHook(vm, 'destoryed')
}

3. 数据响应(数据劫持)

看完生命周期后,里面的watcher等内容其实是数据响应中的一部分。数据响应的实现由两部分构成: 观察者( watcher )依赖收集器( Dep ),其核心是 defineProperty这个方法,它可以 重写属性的 get 与 set 方法,从而完成监听数据的改变。

  • Observe (观察者)观察 props 与 state
  • 遍历 props 与 state,对每个属性创建独立的监听器( watcher )
  • 使用 defineProperty 重写每个属性的 get/set(defineReactive)
  • get: 收集依赖
  • Dep.depend()watcher.addDep()
  • set: 派发更新
  • Dep.notify()
  • watcher.update()
  • queenWatcher()
  • nextTick
  • flushScheduleQueue
  • watcher.run()
  • updateComponent()

大家可以先看下面的数据相应的代码实现后,理解后就比较容易看懂上面的简单脉络了。

let data = {a: 1}
// 数据响应性
observe(data)
// 初始化观察者
new Watcher(data, 'name', updateComponent)
data.a = 2
// 简单表示用于数据更新后的操作
function updateComponent() {
 vm._update() // patchs
}
// 监视对象
function observe(obj) {
	 // 遍历对象,使用 get/set 重新定义对象的每个属性值
 Object.keys(obj).map(key => {
 defineReactive(obj, key, obj[key])
 })
}
function defineReactive(obj, k, v) {
 // 递归子属性
 if (type(v) == 'object') observe(v)
 
 // 新建依赖收集器
 let dep = new Dep()
 // 定义get/set
 Object.defineProperty(obj, k, {
 enumerable: true,
 configurable: true,
 get: function reactiveGetter() {
 	 // 当有获取该属性时,证明依赖于该对象,因此被添加进收集器中
 if (Dep.target) {
 dep.addSub(Dep.target)
 }
 return v
 },
 // 重新设置值时,触发收集器的通知机制
 set: function reactiveSetter(nV) {
 v = nV
 dep.nofify()
 },
 })
}
// 依赖收集器
class Dep {
 constructor() {
 this.subs = []
 }
 addSub(sub) {
 this.subs.push(sub)
 }
 notify() {
 this.subs.map(sub => {
 sub.update()
 })
 }
}
Dep.target = null
// 观察者
class Watcher {
 constructor(obj, key, cb) {
 Dep.target = this
 this.cb = cb
 this.obj = obj
 this.key = key
 this.value = obj[key]
 Dep.target = null
 }
 addDep(Dep) {
 Dep.addSub(this)
 }
 update() {
 this.value = this.obj[this.key]
 this.cb(this.value)
 }
 before() {
 callHook('beforeUpdate')
 }
}

尾声

汗--!没想到才写一半就这么多内容了,为了阅读体验,决定分上下俩部分发。后续文章可以关注我,翻看历史文章~

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表