YY

5月14日

面试部门:广州某部门

一面

面试形式:牛客网视频面试

面试时长:50分钟

面试感悟:YY和CVTE都是托树酱 (opens new window)小哥哥帮忙找的朋友内推,另外YY算第一个面的比较大的厂了吧,也是第一次在牛客网上视频面试,所以还是有些激动的。面试官稍微晚到了2分钟,不过问题不大

说一下你们项目动态表单的实现

说一下动态表格的实现

动态表格由两部分组成:

  • 一个是上面的索引内容(搜索条件),索引内容因为和动态表单很像,又有输入框,又有下拉框,所以上面的索引我也是基于动态表单UI组件库进行开发的
  • 二是下面具体的表格查询的内容,这个是基于Ant Design of Vue中的Table进行封装的,它的结构其实很简单,只要传入需要的表头字段列表,也就是columns,还有具体的数据进去就能显示出表格。不过这里因为表头字段的数量不确定,所以可能需要用到Vue的计算属性来进行一个计算,例如如果表头字段的数量是小于8的话则表格的x方向不需要滚动,否则整个x方向的长度就为数量 X 105px类似这样的值。以及还有涉及到是否分页,每个字段的排序呀等操作...

你们这个JSON树是在哪里生成的,前端会生成吗?

盘石中配置的,后台生成。

有了解过微前端吗?

我们后台有用到微服务,但是前端这边没有用到。

了解浏览器的哪些兼容问题

1、不同浏览器的标签默认的外补丁( margin )和内补丁(padding)不同 解决方案: css 里增加通配符 * { margin: 0; padding: 0; }

2、IE6双边距问题;在 IE6中设置了float , 同时又设置margin , 就会出现边距问题 解决方案:设置display:inline;

3、当标签的高度设置小于10px,在IE6、IE7中会超出自己设置的高度 解决方案:超出高度的标签设置overflow:hidden,或者设置line- height的值小于你的设置高度

4、图片默认有间距 解决方案:使用float 为img 布局

5、IE9以下下浏览器不能使用opacity 解决方案:

    div {
      opacity: 0.5;
      filter: alpha(opacity = 50);
      filter: progid:DXImageTransform.Microsoft.Alpha(style = 0, opacity = 50);
    }

6、边距重叠问题;当相邻两个元素都设置了margin 边距时,margin 将取最大值,舍弃最小值; 解决方案:为了不让边重叠,可以给子元素增加一个父级元素,并设置父级元素为overflow:hidden;

7、cursor:hand 显示手型在safari 上不支持 解决方案:统一使用 cursor:pointer

8、两个块级元素,父元素设置了overflow:auto;子元素设置了position:relative ;且高度大于父元素,在IE6、IE7会被隐藏而不是溢出 解决方案:父级元素设置position:relative

IE有哪些兼容性问题

同上

说一下EventLoop

必问...

在项目里有用过node吗

之前自己工作之余有玩过一些,但是没有实际运用在工作中。

如何实现一个findIndex

针对于这种要我们口述实现的问题,大家可以从这么几个方面去回答:

  • 需要实现的API的功能点
  • API的参数及返回值
  • 是否需要挂载在某个原型对象上(例如Array.prototype),或者就是一个单独的函数(例如实现new)
  • 具体的实现方式

根据MDN上的要求,这里贴一个简易版的实现方式。

需要满足的条件:

  1. findIndex方法对数组中的每个数组索引0..length-1(包括)执行一次callback函数,直到找到一个callback函数返回真实值(强制为true)的值。
  2. 如果找到这样的元素,findIndex会立即返回该元素的索引。
  3. 如果回调从不返回真值,或者数组的length为0,则findIndex返回-1。
  4. 回调函数调用时有三个参数:元素的值,元素的索引,以及被遍历的数组。
  5. 如果一个 thisArg 参数被提供给 findIndex, 它将会被当作this使用在每次回调函数被调用的时候。如果没有被提供,将会使用undefined (opens new window)
  6. findIndex不会修改所调用的数组。

Coding :

    Array.prototype.myFindIndex = function (cb, context = undefined) {
      if (typeof cb !== 'function') {
        throw new TypeError('cb must be a function');
      }
      var arr = [].slice.call(this);
      var len = arr.length >>> 0;
      let i = 0;
      while (i < len) {
        if (cb.call(context, arr[i], i, arr)) {
          return i;
        }
        i++;
      }
      return -1;
    }
    function isEven (num) {
      return num % 2 === 0;
    }
    console.log([3, 4, 5].myFindIndex(isEven)) // 1

具体的Polyfill可以看MDN上:[Array.prototype.findIndex() (opens new window)](https://developer.mozilla.org/zh- CN/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)

移动端布局有哪些方案?

  • flex + rem
  • vw + px

...

如果一个移动端的项目要显示在PC端上保证结构稳定你会如何做?

这个没有唯一的答案,还希望有大佬能够补充一下,后期更新到文章中来。

这边有的一个方案是:

首先保证在移动端上显示正常,然后设置一个最大的max-width来限定死。

你自己的项目里有做过哪些webpack上的优化

参考文章: https://juejin.im/post/5ea528496fb9a03c576cceac#heading-2

  • 先使用webpack-bundle-analyzer分析打包后整个项目中的体积结构,既可以看到项目中用到的所有第三方包,又能看到各个模块在整个项目中的占比。

  • Vue路由懒加载,使用() => import(xxx.vue)形式,打包会根据路由自动拆分打包。

  • 第三方库按需加载,例如lodash库、UI组件库

  • 使用purgecss-webpack-pluginglob插件去除无用样式(glob插件可以可以同步查找目录下的任意文件夹下的任意文件):

        new PurgecssWebpackPlugin({
        // paths表示指定要去解析的文件名数组路径
        // Purgecss会去解析这些文件然后把无用的样式移除
        paths: glob.sync('./src/**/*', {nodir: true})
        // glob.sync同步查找src目录下的任意文件夹下的任意文件
        // 返回一个数组,如['真实路径/src/css/style.css','真实路径/src/index.js',...]
    })
  • 文件解析优化:

    • babel-loader编译慢,可以通过配置exclude来去除一些不需要编译的文件夹,还可以通过设置cacheDirectory开启缓存,转译的结果会被缓存到文件系统中
    • 文件解析优化:通过配置resolve选项中的aliasalias创建import或者require的别名,加快webpack查找速度。
  • 使用webpack自带插件IgnorePlugin忽略moment目录下的locale文件夹使打包后体积减少278k

    • 问题原因:使用moment时发现会把整个locale语言包都打包进去导致打包体积过大,但是我们只需要用到中文包

    • webpack配置中使用webpack自带的插件IgnorePlugin忽略moment目录下的locale文件夹

    • 之后在项目中引入:

                // index.js
        // 利用IgnorePlugin把只需要的语言包导入使用就可以了,省去了一下子打包整个语言包
        import moment from 'moment';
        // 单独导入中文语言包
        import 'moment/locale/zh-cn';
* (或者不用这种方式,直接使用更加轻量的`Day.js`也可以)
  • 使用splitChunks进行拆包,抽离公共模块,一些常用配置项:

  • chunks:表示选择哪些 chunks 进行分割,可选值有:async,initial和all

    • minSize: 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb
    • minChunks: 表示一个模块至少应被minChunks个chunk所包含才能分割,默认为1
    • name: 设置chunk的文件名
    • cacheGroups: 可以配置多个组,每个组根据test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。默认将所有来自 node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。
  • DllPlugin动态链接库,将第三方库的代码和业务代码抽离:

    • 根目录下创建一个webpack.dll.js文件用来打包出dll文件。并在package.json中配置dll指令生成dll文件夹,里面就会有manifest.json以及生成的xxx.dll.js文件
    • 将打包的dll通过add-asset-html-webpack-plugin添加到html中,再通过DllReferencePlugin把dll引用到需要编译的依赖。

具体说一下splitChunksPlugin

有自己写过webpack插件吗

写过一些简单的插件。

一、File-list-plugin :

作用:在每次打包成功之后都会在最终的输出目录下生成一个fileList.md文件,记录了输出目录的所有文件信息。

实现原理:

  • 定义一个名为FileListPlugin的构造函数,重写它原型对象上的apply方法。

  • 在此方法中触发compiler'emit'钩子,也就是生成资源到output目录之前

  • 然后因为此钩子的回调函数中有一个compilation对象,所以可以通过compilation.assets获取到所有待生成的文件

  • 在构建一下md里的内容,也就是把将每一项文件的名称写入markdown文件内

  • 最后通过以下方式把markdown文件放到要输出的目录下:

        compilation.assets[fileListName] = {
    	source: function () {
    	  return content;
    	},
    	size: function () {
    	  return content.length;
    	}
    }

完整代码:

File-list-plugin.js :

    function FileListPlugin (options) {
      this.options = options || {};
      this.filename = this.options.filename || 'fileList.md'
    }
    
    FileListPlugin.prototype.apply = function (compiler) {
      // 1.
      compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, cb) => {
        // 2.
        const fileListName = this.filename;
        // 3.
        let len = Object.keys(compilation.assets).length;
        // 4.
        let content = `# 一共有${len}个文件\n\n`;
        // 5.
        for (let filename in compilation.assets) {
          content += `- ${filename}\n`
        }
        // 6.
        compilation.assets[fileListName] = {
          // 7.
          source: function () {
            return content;
          },
          // 8.
          size: function () {
            return content.length;
          }
        }
        // 9.
        cb();
      })
    }
    module.exports = FileListPlugin;

二、Watch-plugin

作用:观察者模式下,当每次资源变动了,将改动资源的个数以及改动资源的列表输出到控制台中

Watch-plugin.js :

    function WatcherPlugin (options) {
      this.options = options || {};
    }
    
    WatcherPlugin.prototype.apply = function (compiler) {
      compiler.hooks.watchRun.tapAsync('WatcherPlugin', (compiler, cb) => {
        console.log('我可是时刻监听着的 🚀🚀🚀')
        let mtimes = compiler.watchFileSystem.watcher.mtimes;
        let mtimesKeys = Object.keys(mtimes);
        if (mtimesKeys.length > 0) {
          console.log(`本次一共改动了${mtimesKeys.length}个文件,目录为:`)
          console.log(mtimesKeys)
          console.log('------------分割线-------------')
        }
        cb()
      })
      compiler.hooks.watchClose.tap('WatcherPlugin', () => {
        console.log('本次监听停止了哟~👋👋👋')
      })
    }
    module.exports = WatcherPlugin;

三、Clean-plugin

作用:每次打包之后,生成了新的文件,则需要把以前的文件给清理掉。

Clean-plugin.js :

    const recursiveReadSync = require("recursive-readdir-sync");
    const minimatch = require("minimatch");
    const path = require("path");
    const fs = require("fs");
    const union = require("lodash.union");
    function CleanPlugin (options) {
      this.options = options;
    }
    // 匹配文件
    function getUnmatchFiles(fromPath, exclude = []) {
      const unmatchFiles = recursiveReadSync(fromPath).filter(file =>
        exclude.every(
          excluded => {
            return !minimatch(path.relative(fromPath, file), path.join(excluded), {
              dot: true
            })
          }
        )
      );
      return unmatchFiles;
    }
    CleanPlugin.prototype.apply = function (compiler) {
      const outputPath = compiler.options.output.path;
      compiler.hooks.done.tap('CleanPlugin', stats => {
        if (compiler.outputFileSystem.constructor.name !== "NodeOutputFileSystem") {
          return;
        }
        const assets = stats.toJson().assets.map(asset => asset.name);
        // 多数组合并并且去重
        const newAssets = union(this.options.exclude, assets);
        // 获取未匹配文件
        const unmatchFiles = getUnmatchFiles(outputPath, newAssets);
        // 删除未匹配文件
        unmatchFiles.forEach(fs.unlinkSync);
      })
    }
    
    module.exports = CleanPlugin;

HTTP3.0.1和HTTP3.0.2的区别

说一下Vue-Router的实现原理

Vue-Router初始化是发生在什么时候

你觉得我还有哪些问题没有问到的,也就是你可以说一下你的加分项

Last Updated:
Contributors: leeguooooo