1. 写在前面
有时候一些其他领域的小伙伴,会私信问一些非博主领域相关触及知识盲区的问题,什么C++,C#,大前端…
其中有很多朋友问面试相关的,我之前的文章内有一篇关于Python领域面经汇总的置顶文章,那篇文章在我有空的时候,有价值的问题也会持续更新收录进去,感兴趣的可以去阅读:
极具参考价值的Python面试题!从创业公司到一线大厂的真实面经汇总(持续更新)
最近有几位朋友问前端的面试题目,说实话,我很难从专业的角度去给出总结,但是既然大家有需要,我还是在周末之余通过咨询相关领域的朋友跟目前比较主流的一些问题进行了一个汇总,于是有了这么一篇文章,希望它能够帮到你
前端天花板确实低,VP很少见?尽头小组长?撒一把米放键盘上?对于前端的梗很多,但是这都不重要!深耕自己的领域,你就是专家!
2. 前端面试汇总
2.0.1. 如何提⾼webpack的打包速度
-
优化Loader
对于 Loader 来说,影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST,然后对 AST 继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。当然了,这是可以优化的
首先优化Loader的文件搜索范围:
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /\.js$/,
loader: 'babel-loader',
// 只在 src 文件夹下查找
include: [resolve('src')],
// 不会去查找的路径
exclude: /node_modules/
}
]
}
}
对于 Babel 来说,希望只作用在 JS 代码上的,然后 node_modules 中使用的代码都是编译过的,所以完全没有必要再去处理一遍
当然这样做还不够,还可以将 Babel 编译过的文件缓存起来,下次只需要编译更改过的代码文件即可,这样可以大幅度加快打包时间
2.0.2. 数组去重
遍历旧数组,然后拿着旧数组元素去查询新数组,如果该元素在新数组里面没有出现过,我们就添加,否则不添加。先封装一个函数,unique:
function unique(arr){
var newArr =\[]
for(var i =0; i< arr.length; i++){
if (newArr.indexOf(arr\[i] === -1){
newArr.push(arr\[i])
}
}
return newArr
}
var demo = unique(\[])
console.log(demo)
2.0.3. 前端有几种缓存方式?
-
sessionStorage
但当页面关闭后,sessionStorage 中的数据就会被清空。 indexdb、 50mb 允许储存大量数据,提供查找接口,支持异步操作,还能建立索引。indexDB缺点是:不支持DO操作;不能跨域 -
localStorage
5M大小只能存储字符串格式的数据,所以最好在每次存储时把数据转换成json格式, 取出的时候再转换回来 生命周期是永久的, 除非主动删除数据,否则数据永远不会消失 -
cookiec
当在客户端的浏览器上设置Cookie时,它可以持续数天,数月甚至数年。这样可以轻松保存用户首选项和访问信息 不需要任何服务器资源,并存储在用户的计算机上,因此不会给服务器带来额外的负担。 确实非常小,它的大小限制为4KB左右,不能储存大数据且不易读取
2.0.4. nextTick描述一下?
异步方法,异步渲染最后一步,与JS事件循环联系紧密。主要使用了宏任务微任务(setTimeout、promise那些),定义了一个异步方法,多次调用nextTick会将方法存入队列,通过异步方法清空当前队列
2.0.5. Webpack层面的优化?
Webpack 对图片进行压缩
减少 ES6 转为 ES5 的冗余代码
提取公共代码
模板预编译
提取组件的 CSS
优化 SourceMap
构建结果输出分析
vue 项目的编译优化
2.0.6. 代码层面的优化?
v-if 和 v-show 区分使用场景
***puted 和 watch 区分使用场景
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
长列表性能优化
事件的销毁
图片资源懒加载
路由懒加载
第三方插件的按需引入
优化无限列表性能
服务端渲染 SSR or 预渲染
2.0.7. Web 技术的优化?
开启gzip压缩、浏览器缓存、CDN使用、使用Chrome Performance查找性能瓶颈
2.0.8. diff算法的优化策略?
四种命中查找,四个指针
1、旧前与新前(先比开头,后插入和删除节点的这种情况)
2、旧后与新后(比结尾,前插入或删除的情况)
3、旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
4、旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)
2.0.9. 事件循环?它在JS中作用?
事件循环是JavaScript中处理异步代码执行的机制。它负责管理调度和执行异步任务,并将它们添加到执行队列中。
在JavaScript中,事件循环的作用是确保异步任务按照正确的顺序执行,并且不会阻塞主线程。它通过不断地从执行队列中取出任务并执行,以实现非阻塞的异步操作
2.1.0. 浏览器缓存及作用?
浏览器缓存是浏览器在本地存储Web页面和资源的副本,以便在后续访问时可以快速加载。它的作用是减少对服务器的请求次数和网络传输量,提高页面加载速度和用户体验。
浏览器缓存通过在首次请求时将资源保存到本地,并在后续请求时检查资源是否已经存在并且没有过期来工作。如果资源已经存在且未过期,浏览器会直接从缓存中加载资源,而不是从服务器重新下载
2.1.1. Web Workers作用?
Web Workers是一种浏览器提供的JavaScript API,用于在后台线程中执行耗时的计算任务,以避免阻塞主线程。
Web Workers的作用是提高浏览器的响应性能,使得在执行复杂计算或处理大量数据时,不会影响用户界面的流畅性。
Web Workers通过将任务委托给后台线程来实现并行处理,从而充分利用多核处理器的能力。它们可以与主线程进行通信,但不能直接访问DOM或执行UI相关的操作
2.1.2. 浏览器垃圾回收机制?
浏览器的垃圾回收机制是一种自动管理内存的机制,用于检测和回收不再使用的对象,以释放内存资源。
垃圾回收机制通过标记-清除算法实现。它的工作原理如下:
标记阶段:垃圾回收器会从根对象(如全局对象)开始,递归遍历所有对象,并标记仍然可访问的对象。
清除阶段:垃圾回收器会扫描堆内存,清除未被标记的对象,并回收它们所占用的内存空间。
垃圾回收机制的目标是识别和回收不再使用的对象,以避免内存泄漏和提高内存利用率
2.1.3. 虚拟DOM实现原理?
虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象
状态变更时,记录新树和旧树的差异
最后把差异更新到真正的dom中
3. CSS相关汇总
3.0.1. CSS中的层叠顺序是如何工作的?
层叠顺序(z-index)用于控制元素在垂直方向上的堆叠顺序。具有较高层叠顺序值的元素将显示在较低层叠顺序值的元素之上。默认情况下,层叠顺序值为auto
3.0.2. CSS中的伪类和伪元素的区别?示例?
伪类用于向选择器添加特殊的状态,如:hover、:active等。伪元素用于向选择器添加特殊的元素,如::before、::after
/* 伪类示例 */
a:hover {
color: red;
}
/* 伪元素示例 */
p::before {
content: "前缀";
}
3.0.3. v-if和v-for哪个优先级更高
在vue2中,v-for的优先级是高于v-if
vue3中则完全相反,v-if的优先级高于v-for
3.0.4. CSS中的BFC它的作用是什么?
BFC(块级格式化上下文)是CSS中的一种渲染模式,它创建了一个独立的渲染环境,其中的元素按照一定的规则进行布局和定位。BFC的作用包括:清除浮动、防止外边距重叠
3.0.5. CSS中的flexbox布局是什么,它有什么优势?
flexbox布局是一种用于创建灵活的、响应式的布局的CSS模块。它通过flex容器和flex项目的组合来实现强大的布局能力。其优势包括简单易用、自适应性强、对齐和分布控制灵活
3.0.6. CSS的盒模型是什么,描述一下?
CSS的盒模型是用于布局和定位元素的概念。它由内容区域、内边距、边框和外边距组成,这些部分依次包裹在元素周围
3.0.7. 描述一下CSS中的选择器及其优先级?
CSS选择器用于选择要应用样式的HTML元素。选择器的优先级规则是:内联样式 > ID选择器 > 类选择器、属性选择器、伪类选择器 > 元素选择器 > 通用选择器。同时,使用!important可以提升样式的优先级
4. Vue相关汇总
4.0.1. v-if和v-for哪个优先级更高?
在vue2中,v-for的优先级是高于v-if,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费;另外需要注意的是在vue3中则完全相反,v-if的优先级高于v-for,所以v-if执行时,它调用的变量还不存在,就会导致异常
4.0.2. 3.0特性描述一下?
-
监测机制的改变
3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:(只能监测属性,不能监测对象、检测属性的添加和删除、检测数组索引和长度的变更、支持 Map、Set、WeakMap 和 WeakSet) -
模板
模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom -
对象式的组件声明方式
vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。
此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要 -
其它方面的更改
(1)支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式
(2)支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理
(3)基于 treeshaking 优化,提供了更多的内置功能
4.0.3. vue 中使用了哪些设计模式?
-
工厂模式:虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode
-
单例模式:vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉
-
发布-订阅模式 (vue 事件机制)
-
观察者模式 (响应式数据原理)
-
装饰模式: (@装饰器的用法)
-
策略模式:指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案-比如选项的合并策略
4.0.4. 模板编译原理
Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步:
1、将模板字符串 转换成 element ASTs(解析器)
2、对AST进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)
3、使用 element ASTs 生成 render 函数代码字符串(代码生成器)
export function ***pileToFunctions(template) {
// 我们需要把html字符串变成render函数
// 1.把html代码转成ast语法树 ast用来描述代码本身形成树结构 不仅可以描述html 也能描述css以及js语法
// 很多库都运用到了ast 比如 webpack babel eslint等等
let ast = parse(template);
// 2.优化静态节点
// 这个有兴趣的可以去看源码 不影响核心功能就不实现了
// if (options.optimize !== false) {
// optimize(ast, options);
// }
// 3.通过ast 重新生成代码
// 我们最后生成的代码需要和render函数一样
// 类似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world"))))
// _c代表创建元素 _v代表创建文本 _s代表文Json.stringify--把对象解析成文本
let code = generate(ast);
// 使用with语法改变作用域为this 之后调用render函数可以使用call改变this 方便code里面的变量取值
let renderFn = new Function(`with(this){return ${code}}`);
return renderFn;
}
4.0.5. 响应式原理区别?
Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法
import { mutableHandlers } from "./baseHandlers"; // 代理相关逻辑
import { isObject } from "./util"; // 工具方法
export function reactive(target) {
// 根据不同参数创建不同响应式对象
return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {
if (!isObject(target)) {
return target;
}
const observed = new Proxy(target, baseHandler);
return observed;
}
const get = createGetter();
const set = createSetter();
function createGetter() {
return function get(target, key, receiver) {
// 对获取的值进行放射
const res = Reflect.get(target, key, receiver);
console.log("属性获取", key);
if (isObject(res)) {
// 如果获取的值是对象类型,则返回当前对象的代理对象
return reactive(res);
}
return res;
};
}
function createSetter() {
return function set(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);
} else if (hasChanged(value, oldValue)) {
console.log("属性值被修改", key, value);
}
return result;
};
}
export const mutableHandlers = {
get, // 当获取属性时调用此方法
set, // 当修改属性时调用此方法
};
4.0.6. 双向数据绑定如何实现的?
Vue中的双向数据绑定是通过v-model指令实现的。v-model可以在表单元素(如、、)上创建双向数据绑定。当用户输入改变表单元素的值时,数据模型会自动更新;反之,当数据模型的值改变时,表单元素也会自动更新
4.0.7. 生命周期钩子?执行顺序?
Vue中的生命周期钩子包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed。它们的执行顺序如下:
beforeCreate、created beforeMount、mounted、beforeUpdate、updated beforeDestroy、destroyed
4.0.8. 路由如何实现的?
Vue中的路由是通过Vue Router实现的。Vue Router是Vue.js官方提供的路由管理器,它允许开发者在Vue应用中实现单页面应用(SPA)。Vue Router通过配置路由映射关系,将URL路径与组件进行关联,并提供导航功能,使用户可以在不刷新页面的情况下切换视图
4.0.9. watch和***puted区别是什么?
watch和***puted都可以用于监听数据的变化,但它们的用法和实现方式略有不同。watch用于监听指定的数据变化,并在数据变化时执行相应的操作。***puted用于根据依赖的数据动态计算得出一个新的值,并将该值缓存起来,只有在依赖的数据发生变化时才会重新计算
4.1.0. 依赖注入是什么?它的应用场景?
依赖注入是一种设计模式,用于将依赖关系从一个组件传递到另一个组件。在Vue中,依赖注入通过provide和inject选项实现。父组件通过provide提供数据,然后子组件通过inject注入这些数据。它在跨多个层级的组件通信中非常有用
4.1.1. action和mutation的区别?
mutation中的操作是一系列的同步函数,用于修改state中的变量的的状态。当使用vuex时需要通过***mit来提交需要操作的内容。mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
state.count++ // 变更状态
}
}
})
4.1.2. router和route的区别?
router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,包含了所有的路由包含了许多关键的对象和属性。
route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query
4.1.3. 插槽是什么?具有命名插槽和作用域插槽的示例?
<!-- 父组件 -->
<template>
<div>
<slot name="header"></slot>
<slot :data="data"></slot>
</div>
</template>
<!-- 子组件 -->
<template>
<div>
<slot name="header">默认标题</slot>
<slot :data="***putedData">{{ ***putedData }}</slot>
</div>
</template>
4.1.4. 动画系统是如何工作?简单示例?
Vue.js的动画系统通过CSS过渡和动画类实现。通过在元素上添加过渡类或动画类,可以触发相应的过渡效果或动画效果。示例:
<transition name="fade">
<div v-if="show">显示内容</div>
</transition>
<!-- CSS样式 -->
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
4.1.5. 错误处理机制?如何捕获?
Vue.js提供了全局的错误处理机制和组件级别的错误处理机制。全局错误处理可以通过errorCaptured钩子函数捕获和处理错误。组件级别的错误处理可以通过errorCaptured钩子函数或errorHandler选项捕获和处理错误
4.1.6. 响应式数组有哪些限制?如何解决?
Vue.js的响应式系统对于数组的变异方法(如push、pop、splice等)是无法追踪的。为了解决这个限制,Vue提供了一些特殊的方法,如Vue.set、vm.$set和Array.prototype.splice。这些方法可以用于更新数组并保持响应式
4.1.7. ***position API与Options API区别?
***position API是Vue.js 3中引入的一种新的组织组件逻辑的方式。它允许开发者通过函数的方式组织和重用逻辑,而不是通过选项对象。相比之下,Options API是Vue.js 2中常用的组织组件逻辑的方式,通过选项对象中的属性来定义组件的数据、方法
4.1.8. Suspense是什么?它的作用?
Suspense是Vue.js 3中引入的一种机制,用于处理异步组件的加载状态。它可以在异步组件加载完成之前显示一个占位符,并在加载完成后渲染异步组件的内容。这样可以更好地处理异步组件的加载过程,提供更好的用户体验
4.1.9. provide和inject作用?请给出示例?
provide和inject用于实现组件之间的依赖注入。通过在父组件中使用provide提供数据,然后在子组件中使用inject注入这些数据。示例:
// 父组件
const Parent = {
provide: {
message: 'Hello Spider!'
},
// ...
}
// 子组件
const Child = {
inject: ['message'],
created() {
console.log(this.message); // 输出:Hello Spider!
},
// ...
}
4.2.0. watchEffect和watch区别?如何使用?
watchEffect用于监听响应式数据的变化,并在回调函数中执行相应的操作。它会自动追踪依赖,并在依赖变化时重新运行回调函数。watch用于监听指定的响应式数据,并在其变化时执行相应的操作。它可以精确地指定要监听的数据,并提供更多的配置选项。一般来说,如果只需要监听一个响应式数据的变化并执行相应操作,可以使用watchEffect;如果需要更细粒度的控制,可以使用watch
5. React相关汇总
5.0.1. 核心概念是什么?
React是一个用于构建用户界面的JavaScript库。它的核心概念是组件化和声明式编程。React将用户界面拆分为独立的可重用组件,并使用声明式语法描述组件的状态和UI的关系,使得构建复杂的UI变得简单和可维护
5.0.2. 生命周期方法?列举一些?
React生命周期方法是在组件不同阶段执行的特定方法。以下是一些常用的React生命周期方法:
**
***ponentDidMount:组件挂载后立即调用
***ponentDidUpdate:组件更新后调用
***ponentWillUnmount:组件卸载前调用
should***ponentUpdate:决定组件是否需要重新渲染
getDerivedStateFromProps:根据props的变化来更新状态**
5.0.3. React Context?作用?
React Context是一种用于在组件树中共享数据的机制。它可以避免通过props一层层传递数据,使得跨组件的数据共享变得更加简单和高效。React Context提供了一个Provider和Consumer组件,用于提供和消费共享的数据
5.0.4. React Router?作用?
React Router是React中用于处理路由的库。它提供了一种在单页面应用中实现导航和路由功能的方式。React Router可以帮助开发者实现页面之间的切换、URL参数的传递、嵌套路由等功能
5.0.5. React Hooks?作用?
React Hooks是React 16.8版本引入的一种特性,用于在函数组件中使用状态和其他React特性。Hooks提供了一种无需编写类组件的方式来管理状态和处理副作用,使得函数组件具有类组件的能力
好了,到这里又到了跟大家说再见的时候了。创作不易,帮忙点个赞再走吧。你的支持是我创作的动力,希望能带给大家更多优质的文章