最后:
总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。
面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
observer.observe(textNode, {
characterData: true,
});
timerFunc = () => {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
} elseif (typeof setImmediate !== “undefined”) {
// 如果前面都不支持 判断setImmediate
timerFunc = () => {
setImmediate(flushCallbacks);
};
} else {
// 最后降级采用setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0);
};
}
exportfunctionnextTick(cb) {
// 除了渲染watcher 还有用户自己手动调用的nextTick 一起被收集到数组
callbacks.push(cb);
if (!pending) {
// 如果多次调用nextTick 只会执行一次异步 等异步队列清空之后再把标志变为false
pending = true;
timerFunc();
}
}
数据更新的时候内部会调用nextTick
// src/core/observer/scheduler.jsexportfunctionqueueWatcher (watcher: Watcher) {
const id = watcher.idif (has[id] == null) {
has[id] = trueif (!flushing) {
queue.push(watcher)
} else {
// if already flushing, splice the watcher based on its id// if already past its id, it will be run next immediately.let i = queue.length - 1while (i > index && queue[i].id > watcher.id) {
i–
}
queue.splice(i + 1, 0, watcher)
}
// queue the flushif (!waiting) {
waiting = trueif (process.env.NODE_ENV !== ‘production’ && !config.async) {
flushSchedulerQueue()
return
}
// 把更新方法放到数组中维护[nextTick回调函数,更新函数flushSchedulerQueue]/**
* vm.a = ‘test’; // 修改了数据后并不会马上更新视图
vm.KaTeX parse error: Expected '}', got 'EOF' at end of input: …console.log(vm.el.innerHTML)
})
*/nextTick(flushSchedulerQueue)
}
}
}
#### 那vue中是如何检测数组变化的呢?
数组就是使用 object.defineProperty 重新定义数组的每一项,那能引起数组变化的方法我们都是知道的, pop 、 push 、 shift 、 unshift 、 splice 、 sort 、 reverse 这七种,只要这些方法执行改了数组内容,我就更新内容就好了,是不是很好理解。
1. 是用来函数劫持的方式,重写了数组方法,具体呢就是更改了数组的原型,更改成自己的,用户调数组的一些方法的时候,走的就是自己的方法,然后通知视图去更新。
2. 数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象才能进行观测,观测过的也不会进行观测)
vue3:改用 proxy ,可直接监听对象数组的变化。
#### 如何从真实DOM到虚拟DOM
涉及到Vue中的模板编译原理,主要过程:
1. 将模板转换成 ast 树, ast 用对象来描述真实的JS语法(将真实DOM转换成虚拟DOM)
2. 优化树
3. 将 ast 树生成代码
#### 虚拟DOM实现原理?
* 虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象
* 状态变更时,记录新树和旧树的差异
* 最后把差异更新到真正的dom中
参考
## 大厂面试题分享 面试题库
#### 前后端面试题库 (面试必备) 推荐:★★★★★
地址:[前端面试题库](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)
#### 为什么Vue采用异步渲染呢?
Vue 是组件级更新,如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能, Vue 会在本轮数据更新后,在异步更新视图。核心思想 nextTick 。
dep.notify() 通知 watcher进行更新, subs[i].update 依次调用 watcher 的 update , queueWatcher 将watcher 去重放入队列, nextTick( flushSchedulerQueue )在下一tick中刷新watcher队列(异步)。
#### Vue3.0 和 2.0 的响应式原理区别
Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。
相关代码如下
import { mutableHandlers } from"./baseHandlers"; // 代理相关逻辑import { isObject } from"./util"; // 工具方法exportfunctionreactive(target) {
// 根据不同参数创建不同响应式对象returncreateReactiveObject(target, mutableHandlers);
}
functioncreateReactiveObject(target, baseHandler) {
if (!isObject(target)) {
return target;
}
const observed = newProxy(target, baseHandler);
return observed;
}
const get = createGetter();
const set = createSetter();
functioncreateGetter() {
returnfunctionget(target, key, receiver) {
// 对获取的值进行放射const res = Reflect.get(target, key, receiver);
console.log(“属性获取”, key);
if (isObject(res)) {
// 如果获取的值是对象类型,则返回当前对象的代理对象returnreactive(res);
}
return res;
};
}
functioncreateSetter() {
returnfunctionset(target, key, value, receiver) {
const oldValue = target[key];
const hadKey = hasOwn(target, key);
const result = Reflect.set(target, key, value, receiver);
if (!hadKey) {
console.log(“属性新增”, key, value);
} elseif (hasChanged(value, oldValue)) {
console.log(“属性值被修改”, key, value);
}
return result;
};
}
exportconst mutableHandlers = {
get, // 当获取属性时调用此方法
set, // 当修改属性时调用此方法
};
#### Vue 的生命周期方法有哪些 一般在哪一步发请求
beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问
created 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。这里没有��,如果非要想与���进行交互,可以通过��.el,如果非要想与Dom进行交互,可以通过vm.nextTick 来访问 Dom
beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。
mounted 在挂载完成后发生,在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom 节点
beforeUpdate 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁(patch)之前。可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程
updated 发生在更新完成之后,当前阶段组件 Dom 已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新,该钩子在服务器端渲染期间不被调用。
beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用。我们可以在这时进行善后收尾工作,比如清除计时器。
destroyed Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。
activated keep-alive 专属,组件被激活时调用
deactivated keep-alive 专属,组件被销毁时调用
>
> 异步请求在哪一步发起?
>
可以在钩子函数 created、beforeMount、mounted 中进行异步请求,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
如果异步请求不需要依赖 Dom 推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
* 能更快获取到服务端数据,减少页面 loading 时间;
* ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
#### Vue-router 路由有哪些模式?
一般有两种模式: (1)hash 模式:后面的 hash 值的变化,浏览器既不会向服务器发出请求,浏览器也不会刷新,每次 hash 值的变化会触发 hashchange 事件。 (2)history 模式:利用了 HTML5 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
#### 虚拟 DOM 的优缺点?
优点:
* 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
* 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
* 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
* 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
#### vue和react的区别
=> 相同点:
- 数据驱动页面,提供响应式的试图组件
- 都有virtual DOM,组件化的开发,通过props参数进行父子之间组件传递数据,都实现了webComponents规范
- 数据流动单向,都支持服务器的渲染SSR
- 都有支持native的方法,react有React native, vue有wexx
=> 不同点:
1.数据绑定:Vue实现了双向的数据绑定,react数据流动是单向的
2.数据渲染:大规模的数据渲染,react更快
3.使用场景:React配合Redux架构适合大规模多人协作复杂项目,Vue适合小快的项目
4.开发风格:react推荐做法jsx + inline style把html和css都写在js了
vue是采用webpack + vue-loader单文件组件格式,html, js, css同一个文件
#### v-model 的原理?
我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
* text 和 textarea 元素使用 value 属性和 input 事件;
* checkbox 和 radio 使用 checked 属性和 change 事件;
* select 字段将 value 作为 prop 并将 change 作为事件。
以 input 表单元素为例:
相当于
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件:
子组件:
props:{
value: String
},
methods: {
test1(){
this.$emit(‘input’, ‘小红’)
},
},
#### 什么是 MVVM?
Model–View–ViewModel (MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样也是 WPF 和 Silverlight 的架构师)于2005年在他的博客上发表
MVVM 源自于经典的 Model–View–Controller(MVC)模式 ,MVVM 的出现促进了前端开发与后端业务逻辑的分离,极大地提高了前端开发效率,MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用
(1)View 层
View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建 。
(2)Model 层
Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的 api 接口。
(3)ViewModel 层
ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。
MVVM 框架实现了双向绑定,这样 ViewModel 的内容会实时展现在 View 层,前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新。这样 View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。
我们以下通过一个 Vue 实例来说明 MVVM 的具体实现,有 Vue 开发经验的同学应该一目了然:
(1)View 层
{{message}}
(2)ViewModel 层
var app = newVue({
el: ‘#app’,
data: { // 用于描述视图状态 message: ‘Hello Vue!’,
},
methods: { // 用于描述视图行为 showMessage(){
let vm = this;
alert(vm.message);
}
},
created(){
let vm = this;
// Ajax 获取 Model 层的数据ajax({
url: ‘/your/server/data/api’,
success(res){
vm.message = res;
}
});
}
})
(3) Model 层
{
“url”: “/your/server/data/api”,
“res”: {
“success”: true,
“name”: “IoveC”,
“domain”: “www.cnblogs.com”
}
}
#### diff算法
时间复杂度: 个树的完全 diff 算法是一个时间复杂度为 O(n\*3) ,vue进行优化转化成 O(n) 。
理解:
* 最小量更新, key 很重要。这个可以是这个节点的唯一标识,告诉 diff 算法,在更改前后它们是同一个DOM节点
* 扩展 v-for 为什么要有 key ,没有 key 会暴力复用,举例子的话随便说一个比如移动节点或者增加节点(修改DOM),加 key 只会移动减少操作DOM。
* 只有是同一个虚拟节点才会进行精细化比较,否则就是暴力删除旧的,插入新的。
* 只进行同层比较,不会进行跨层比较。
diff算法的优化策略:四种命中查找,四个指针
1. 旧前与新前(先比开头,后插入和删除节点的这种情况)
2. 旧后与新后(比结尾,前插入或删除的情况)
3. 旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
4. 旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)
#### Vue 初始化页面闪动问题如何解决?
出现该问题是因为在 Vue 代码尚未被解析之前,尚无法控制页面中 DOM 的显示,所以会看见模板字符串等代码。 解决方案是,在 css 代码中添加 v-cloak 规则,同时在待编译的标签上添加 v-cloak 属性:
[v-cloak] { display: none; }
{{ message }} ```Vue 子组件和父组件执行顺序
加载渲染过程:
-
父组件 beforeCreate
-
父组件 created
-
父组件 beforeMount
-
子组件 beforeCreate
-
子组件 created
-
子组件 beforeMount
-
子组件 mounted
-
父组件 mounted
更新过程:
-
父组件 beforeUpdate
-
子组件 beforeUpdate
最后
文章到这里就结束了,如果觉得对你有帮助可以点个赞哦
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
one; }
{{ message }} ```Vue 子组件和父组件执行顺序
加载渲染过程:
-
父组件 beforeCreate
-
父组件 created
-
父组件 beforeMount
-
子组件 beforeCreate
-
子组件 created
-
子组件 beforeMount
-
子组件 mounted
-
父组件 mounted
更新过程:
-
父组件 beforeUpdate
-
子组件 beforeUpdate
最后
文章到这里就结束了,如果觉得对你有帮助可以点个赞哦
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
[外链图片转存中…(img-XFOq0utC-1715668782532)]