本人写了五年的后台管理。每次面试前就会头疼,因为写的页面除了表单就是表格。抱怨过苦恼过也后悔过(虽然我现在已经心安理得的摆烂),但是站在现在的时间点回想以前,发现有很多事情可以做的更好,于是有了这篇文章。
function formatSizeUnits(kb) { let units = ['KB', 'MB', 'GB', 'TB', 'PB']; let unitIndex = 0; while (kb >= 1024 && unitIndex < units.length - 1) { kb /= 1024; unitIndex++; } return `${kb.toFixed(2)} ${units[unitIndex]}`; }而在此之前,是一个后端同学写的代码(说起来,后台管理系统前端人力不够后端帮忙写好像是常规操作,后端总觉得写前端简单,而我只能说你们那代码写的,一言难尽……),如下:
function formatSizeUnits(kb) { var result = ''; if (kb < 1024) { result = kb + ' KB'; } else if (kb < 1024 * 1024) { result = (kb / 1024).toFixed(2) + ' MB'; } else if (kb < 1024 * 1024 * 1024) { result = (kb / 1024 / 1024).toFixed(2) + ' GB'; } else { result = (kb / 1024 / 1024 / 1024).toFixed(2) + ' TB'; } return result; }
虽然功能都一样,但是我写在面试的时候写了上面的代码得到了面试官的赞扬,也因此非常顺利的通过了面试。而如果我写成了下面的样子,估计不会有这种效果。
// 提问:写一个工具函数,对文件大小的单位进行转换,加注释 // 回答:下面是一个JavaScript工具函数,它接受文件大小、初始单位、目标单位和小数位数,然后返回目标单位下的文件大小。 /** * 将文件大小从一个单位转换为另一个单位。 * * @param {number} size 文件大小。 * @param {string} fromUnit 初始单位('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')。 * @param {string} toUnit 目标单位('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')。 * @param {number} [decimalPoint=2] 结果保留的小数位数,默认为2。 * @return {string} 转换后的文件大小,带单位。 */ function convertFileSize(size, fromUnit, toUnit, decimalPoint = 2) { // 定义单位与字节之间的转换关系 const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; // 获取初始单位和目标单位的索引 const fromIndex = units.indexOf(fromUnit); const toIndex = units.indexOf(toUnit); // 如果单位不在列表中,抛出错误 if (fromIndex === -1 || toIndex === -1) { throw new Error('Invalid units'); } // 计算初始单位与目标单位之间的转换系数 const exponent = toIndex - fromIndex; // 计算结果大小 const resultSize = size / Math.pow(1024, exponent); // 返回格式化后的结果 return parseFloat(resultSize.toFixed(decimalPoint)) + ' ' + toUnit; } // 堆代码 duidaima.com // 示例使用 console.log(convertFileSize(1, 'GB', 'MB')); // 输出: 1024.00 MB console.log(convertFileSize(1, 'MB', 'KB')); // 输出: 1024.00 KB console.log(convertFileSize(1, 'KB', 'B')); // 输出: 1024.00 B console.log(convertFileSize(1, 'MB', 'GB', 5)); // 输出: 0.00098 GB
还能说什么,我只能说 NB!(经评论区提示,此段代码可能有问题,建议大家在使用AI工具时,重点是借鉴思路,直接使用代码需要谨慎~)我现在每次写大段逻辑之后都习惯拿给 AI 看看,有什么更好的实现方式,或者用什么设计模式。AI 是非常低成本且高效提升代码质量的工具。
一个功能用到了好多次,为什么不封装成组件?一个组件用到了好几个项目,为什么不单独写个npm包?差不多的项目创建了好几个,为什么不封装成脚手架?你说,没时间,没必要,复制粘贴反而更快。那你就完全没理解,这么做不一定是为了让工作更快完成,而是可以让你在年年终述职时更有话说(你就算写了一百个表单表格没有写一个脚手架更值得炫耀),如果不会写可以问问 AI。
而当你真正开始封装组件,开始写工具库了,你会发现你需要思考的确实比之前多了。
对于前端业务重要吗?相比于后端来说,前端一般不会太关注业务。就算出了问题大部分也是后端的问题。但是就我找工作的经验,业务非常重要!如果你做的工作很有技术含量,比如你在做低代码,你可以面试时讲一个小时的技术难点。但是你只是一个破写后台管理,你什么都没有的说。这个时候,了解业务就成为了你的亮点。
然后我就挂了………………
1.每次接需求的时候,都要了解需求背景,并主动去理解
我们写一个表格简简单单,把数据展示出来就好,但是表格中的数据是什么意思呢?比如我之前写一个 kafka 管理平台,里面有表格表单,涉及什么 cluster controller topic broker partition…… 我真的完全不了解,很后悔我几年时间也没有耐下心来去了解。
说到源码, Vue,React 这些基本是每次必问,也有太多大佬们的总结我就不多说了。除此之外,我想大家每次面试应该都会被问,你写了什么亮点的项目,困难的项目,你会说什么?哦,都是表单表格,都用 Element UI 实现的,没什么难度。那估计你面试也就到这里了。那说什么,后台管理就这些啊?!
如果你用了 Element UI 你可以说写了什么自定义 Element UI 表单组件,Element UI 表单源码是什么原理,怎么通信的?用了 Axios 可以说 Axios 的原理,它的拦截器怎么实现的?用了编辑器 diff 那你知道 diff 算法是什么吗?用了 ECharts 那你知道他底层是用什么绘制的吗?用了 husky ?husky的原理是什么?用了Vite,它为什么快?是怎么打包的?写过插件吗?
Axios 的拦截器实现基于 Axios 的核心原理,即 Axios 实例是一个包含请求和响应拦截器堆栈的对象。当发出请求或接收响应时,Axios 会遍历这些拦截器,并按照添加的顺序执行请求拦截器,以及按照相反的顺序执行响应拦截器。在 Axios 的源码中,拦截器是通过一个 AxiosInterceptorManager 实例来管理的,它维护了一个拦截器数组。每个拦截器都是一个包含 fulfilled 和 rejected 函数的对象。这两个函数分别对应于拦截器成功处理和拦截器处理出错的情况。
class InterceptorManager { constructor() { this.handlers = []; // 存储拦截器的数组 } use(fulfilled, rejected) { this.handlers.push({ fulfilled: fulfilled, rejected: rejected }); return this.handlers.length - 1; // 返回拦截器的ID } eject(id) { if (this.handlers[id]) { this.handlers[id] = null; // 移除拦截器 } } forEach(fn) { this.handlers.forEach((h) => { if (h !== null) { fn(h); } }); } }在发送请求或接收响应时,Axios 会创建一个 promise 链,并通过 forEach 方法将拦截器中的 fulfilled 和 rejected 函数添加到这个链中。这样,每个拦截器都可以对请求或响应进行处理,然后将结果传递到链的下一个拦截器,或者在出错时结束链的执行。
axios.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); axios.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); });在 Axios 的完整实现中,这个拦截器机制被集成到了 Axios 的请求发送和响应处理流程中。通过这种方式,Axios 可以在发送请求之前和接收响应之后,但在用户定义的 .then 或 .catch 执行之前,插入自定义的逻辑。请注意,这里提供的代码只是为了说明 Axios 拦截器的实现原理,并不是 Axios 源码的完整复制。如果你对 Axios 的拦截器实现细节感兴趣,建议查看 Axios 的官方 GitHub 仓库中的源码。
当我们工作时间久了面试难免会遇到这些问题,前端工程化,前端监控,工作流,部署,性能等等。其实我们在工作中绝大部分时间都在写代码,对于这些不是所有人都有机会接触到,不过这些和所做的业务无关,是我们提升自己很好的一个思路。
总体而言,对于技术选型,依赖于我们对所有可选项的理解,做选择可能很容易,给出合理的理由还是需要花费一些精力的。
对于实现方案,无论使用第三方库还是自己实现,重要的都是理解实现原理。
对于错误监控,可以了解一下 Sentry,原理简单来说就是通过 window.onerror 和 window.addEventListener('unhandledrejection', ...) 去分别捕获同步和异步错误,然后通过错误信息和 sourceMap 来定位到源码。
最后,收集到信息之后,还要考虑数据上报的方案,比如使用 navigator.sendBeacon 还是 Fetch、AJAX?是批量上报,实时上报,还是延迟上报?上报的数据格式等等。
CI/CD
这个可能还是比较依赖信息收集能力,虽然我个人觉得很烦,但好像很多领导级别的面试很愿意问。比如近几年很火的低代码,很多面试官都会问,你用过就问你细节,你没用过也会问你有什么设计思路。还有最近的两年爆火的 AI,又或者 Vue React的最新功能,WebAssembly,还有一些新的打包工具 Vite Bun 什么的,还有鸿蒙开发……虽然不可能学完每一项新技术,但是可以多去了解下。