likes
comments
collection
share

🚀🚀🚀衔远科技第一轮面试,全栈偏前端方向前面几个问题是上篇文中说到的地方,但是有几个问题我回答的更加详细了,下面只

作者站长头像
站长
· 阅读数 55

前面几个问题是上篇文中说到的地方,但是有几个问题我回答的更加详细了,下面只提到几个回答的不一样的例子,其他的基本上在上篇提到了

说说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算法。但是我还没来得及说,面试官就开始问下个问题了。

webpackvite用到的哪个更多一点

我的回答是webpack。因为公司主要是通过create-react-app创建的项目。

问我为啥不自己手动创建,我说因为公司没有多余时间自己手动创建,但是我私底下创建过。面试官让我详细介绍下。

基础配置:需要提供entryoutputaliasloaderplugin

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中开启缓存。

资源加载优化:压缩cssjsimg降低分辨率,或者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
评论
请登录