🚀🚀🚀衔远科技第一轮面试,全栈偏前端方向前面几个问题是上篇文中说到的地方,但是有几个问题我回答的更加详细了,下面只
前面几个问题是上篇文中说到的地方,但是有几个问题我回答的更加详细了,下面只提到几个回答的不一样的例子,其他的基本上在上篇提到了
说说js的事件冒泡
一上来有点懵,因为好久没涉及到这块了,我就三言两语将话题引到react了。
首先是冒泡阶段:在底层的dom
触发一个事件,这个事件会一层一层向根节点传递,
捕获阶段:会在父节点addEventListener
监听事件,如果监听到,则完成捕获,并触发回调函数。
在react中,则对事件冒泡机制做了封装,首先会在dom触发的事件代理到入口dom上,原dom
使用一个空函数代替之前的函数:noop () {}
。捕获也是在入口dom上,之前版本是代理在body
标签上,新版本则改成入口dom
了,这么做的好处是方便多入口的应用。
说说react和vue的编译和运行的区别
这次我学聪明了,将问题分为两个层面,
首先是数据驱动方面:
vue2使用Object.defineProperty
代理对象每个key
,然后在提供的get
中收集依赖,在set
中通过广播的方式,将新的值告诉每个使用这个值的地方,进行对应的更新操作,vue3
则使用了Proxy
,因为这样可以监听属性的新增和删除,包括数组的基本方法增删数组元素也可监听到。vue2
则是重写了数组的基本方法,在重写的方法里监听数组元素的变化。
而react则是通过链表的形式驱动数据变化,比如在函数组件中,首先会创建函数类型的Fiber
,这个节点有个hook属性,指向第一个hook,hook本身有next属性指向下一个hook,而每个hook都有几个属性,baseState
存放初始值。memoziedState
存放每次计算之后的值。action
指向hook
(useState
)对应的dispatchAction
, action
是个队列,存放的是每次调用的dispatchAction
。react就是通过循环链表和队列进行数据驱动的。
在UI渲染方面:
vue主推的是使用模板文件,因为可以更加精确的感知数据变化和用户行为,方便大量的优化。所以也导致vue是偏向于编译时的
react则使用jsx,虽然简洁,但是不方便优化,导致很多性能能问题丢给了开发者。但是react也进行了一些优化,这些优化导致react是重运行时的。
这个问题可以继续延伸到diff
算法。但是我还没来得及说,面试官就开始问下个问题了。
webpack
和vite
用到的哪个更多一点
我的回答是webpack
。因为公司主要是通过create-react-app
创建的项目。
问我为啥不自己手动创建,我说因为公司没有多余时间自己手动创建,但是我私底下创建过。面试官让我详细介绍下。
基础配置:需要提供entry
、output
、alias
、loader
、plugin
webpack
根据提供的entry
,开始解析文件的依赖的关系,构建依赖关系树,并且缓存依赖关系。然后处理特定类型的文件,将特定文件传给特定类型的loader
处理。还举了一些常见的loader
,包括css-loader``style-loader
,默认会将样式放入style
标签中,这时候会将样式抽离成单独的文件,然后引入页面,还有处理图片的loader
,将大图片压缩,小图片换为base64
格式访问。
另外,全局的loader
包括eslist
语法检测,babel
语法降级兼容处理等。
接着,plugin
会在webpack
特定的hook中监听文件变化,进行相应的处理,比如webpack4
不带有服务,需要配置插件启动本地服务,而且也不支持热启动HMR
,还需要配置对应的插件,进行模块热替换,我还解释了模块热替换的概念。
其实这里可以继续深入说下插件的运行原理,但是这时候面试官说回答的很详细,可以了。
webpack
的插件本质是个带有apply
方法的类,apply
方法中参数是complier
对象,可以通过这个对象提供的hook
监听特定文件的变化进行编译。
module.exports = class MyPlugin {
apply(compiler) {
compiler.hooks.done.tap('MyPlugin', (Compilation) => {
console.log('MyPlugin: Compilation finished!');
});
}
}
这些hook
底层使用的是tapable
实现的。
做过哪些优化
接着上面的话题又问我做过哪些优化
分为两部分:一部分是项目级别的优化:
基于webpack
的。开发优化,例如:使用缓存,包括eslint的缓存,babel的缓存,这些缓存都可在loader
中开启缓存。
资源加载优化:压缩css
、js
、img
降低分辨率,或者base64
访问。
引入cdn
,减少使用本地文件。但是有限制,有的时候无法使用。
打包优化:使用插件多进程打包。
另一部分是组件级别的,使用是在App.tsx中使用lazy方法引入组件,防止一个页面加载无关的js代码。
可能是全栈,后面要求写代码,难度中等偏上。
顺时针输出一个多维数组的值
这道题我说暂时没有思路,直接跳过了,但是面试结束我仔细想了想实现了一个。
function spiralOrder(matrix) {
if (matrix.length === 0) return [];
const result = [];
let top = 0, bottom = matrix.length - 1;
let left = 0, right = matrix[0].length - 1;
while (top <= bottom && left <= right) {
for (let i = left; i <= right; i++) {
result.push(matrix[top][i]);
}
top++;
for (let i = top; i <= bottom; i++) {
result.push(matrix[i][right]);
}
right--;
if (top <= bottom) {
for (let i = right; i >= left; i--) {
result.push(matrix[bottom][i]);
}
bottom--;
}
if (left <= right) {
for (let i = bottom; i >= top; i--) {
result.push(matrix[i][left]);
}
left++;
}
}
return result;
}
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(spiralOrder(matrix)); // Output: [1, 2, 3, 6, 9, 8, 7, 4, 5]
格式化列表
给定一个数组,转为树的形式
const arr = [
{
id: 1,
name: 'a',
parentId: 0,//parentid
},
{
id: 2,
name: 'b',
parentId: 1,
},
{
id: 3,
name: 'c',
parentId: 1,
},
{
id: 4,
name: 'd',
parentId: 2,
},
{
id: 5,
name: 'e',
parentId: 4,
},
]
// 输出为
[
{
"id": 1,
"name": "a",
"parentId": 0,
"children": [
{
"id": 2,
"name": "b",
"parentId": 1,
"children": [
{
"id": 4,
"name": "d",
"parentId": 2,
"children": [
{
"id": 5,
"name": "e",
"parentId": 4
}
]
}
]
},
{
"id": 3,
"name": "c",
"parentId": 1
}
]
}
]
这个我实现了,但是本地无法调试,导致最后不了了之,有点遗憾,因为这个问题我实习开发的时候遇到过。
function convert(list) {
let roots = list.filter(item => item.parentId === 0);
const findChildren = (list, parentId) => list.filter(item => item.parentId === parentId);
const convertArrToTree = (node) => {
const children = findChildren(list, node.id);
if (children.length) {
node.children = children.map(child => convertArrToTree(child));
}
return node;
};
roots.forEach(root => {
convertArrToTree(root);
});
return roots;
}
反问环节
我反问的问题是,后面会有几轮面试
面试官回答说四面,第一面前端,第二面后端,第三面领导面,第四面HR面
之后面试官让我等消息,可能会有后面的面试
转载自:https://juejin.cn/post/7416499669630468137