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上的要求,这里贴一个简易版的实现方式。
需要满足的条件:
findIndex方法对数组中的每个数组索引0..length-1(包括)执行一次callback函数,直到找到一个callback函数返回真实值(强制为true)的值。- 如果找到这样的元素,
findIndex会立即返回该元素的索引。 - 如果回调从不返回真值,或者数组的
length为0,则findIndex返回-1。 - 回调函数调用时有三个参数:元素的值,元素的索引,以及被遍历的数组。
- 如果一个
thisArg参数被提供给findIndex, 它将会被当作this使用在每次回调函数被调用的时候。如果没有被提供,将会使用undefined(opens new window)。 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+remvw+px
...
如果一个移动端的项目要显示在PC端上保证结构稳定你会如何做?
这个没有唯一的答案,还希望有大佬能够补充一下,后期更新到文章中来。
这边有的一个方案是:
首先保证在移动端上显示正常,然后设置一个最大的max-width来限定死。
你自己的项目里有做过哪些webpack上的优化
参考文章: https://juejin.im/post/5ea528496fb9a03c576cceac#heading-2
先使用
webpack-bundle-analyzer分析打包后整个项目中的体积结构,既可以看到项目中用到的所有第三方包,又能看到各个模块在整个项目中的占比。Vue路由懒加载,使用() => import(xxx.vue)形式,打包会根据路由自动拆分打包。第三方库按需加载,例如
lodash库、UI组件库使用
purgecss-webpack-plugin和glob插件去除无用样式(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选项中的alias。alias创建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和allminSize: 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kbminChunks: 表示一个模块至少应被minChunks个chunk所包含才能分割,默认为1name: 设置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;
