今天是六一儿童节,祝各位掘友们六一节快乐🎉🎉🎉
昨天进行了数字马力的二面,问了一些八股,也问了一些值得思考的问题,总体面试体验还可以吧,不过就结果而言应该是挂了。虽然死的很惨,但是复盘工作还是要做好,毕竟人不能在同样的地方跌倒第二次。
有没有写过webpack的自定义loader
答案肯定是没写过,咱搞业务代码的哪知道这个。不过因为面试要造火箭,所以还是得学习下怎么写。
首先我们要明确下loader做了什么事情:由于webpack只认识js,只能对js代码进行处理(当然这样说也不准确,在webpack5中增加了资源模块的特性,能对一些常见的静态资源文件进行处理),所以对于一些特殊的文件如.vue、.ts、tsx、.less等就需要用loader来处理。
下面我们来实现一个loader,把传入的文本文件(.txt)中的所有空格替换成-:
-
配置文件中增加
loader:const path = require('path'); module.exports = { // ... module: { rules: [ { test: /\.txt$/, use: [ { loader: path.resolve('loaders/txt-loader.js'), }, ], }, ], }, }; -
编写
loader代码:module.exports = function(source) { return source.replace(/\s/g, '-'); }
从上面的代码我们可以看到,loader的编写其实就是对传入的source进行处理,之后把得到的结果作为返回值,这个返回值可能是最终结果,也可能会被配置文件指定的下一个loader进行处理。
关于loader,还有一个值得我们注意的点。我们在配置loader的时候,总是从后往前配置,即先执行的loader放后面,这是因为在实际(从右到左)执行 loader 之前,会先从左到右调用loader上的pitch 方法。这部分的源码实现可见:loader-runner/LoaderRunner.js at main · webpack/loader-runner · GitHub。
为什么使用webpack进行打包,如果不进行打包会怎样
关于这个问题,我仅仅回答了面试官关于webpack的作用,实际上,如果不用打包工具会是怎样的问题,之前还真的没思考过,所以没答上来,也是比较尴尬。
于是我在网上搜索了一番,发现在前端真的需要打包工具吗?-知乎中的回答还是比较全面的。根据这个回答,并结合我自己的理解,有了以下几个分析问题的角度:
-
模块化:在现代化前端开发中,我们通常会将代码按照功能模块进行拆分,这样可以提高代码的可维护性和可读性。我们比较常用的两种模块化方案是
cjs和esm,当然cjs在浏览器环境不可用,利用打包工具我们可以进行兼容性处理(如webpack会对cjs引入的代码进行处理,在runtime中实现一套模块化的方案) -
代码的转译和产物优化处理:代码转译在现代前端中算是逃不开的话题了,因为有诸如
JSX语法、TypeScript、LESS/SASS等特殊语法或预编译语言等。如果不用打包工具,靠人为的去做代码转译的工作是一件很蠢的事情。这时候我们去使用打包工具,以webpack为例,我们只需要配置各种loader对对应的文件进行处理就行了。 -
产物优化:诸如代码压缩丑化、代码分割、
TreeShaking等产物优化的需求,通过打包工具来做是非常方便的 -
devServer:除了打包之外,构建工具还往往提供devServer的功能,允许我们在开发阶段对代码更改进行实时预览。个人认为相比于不用打包工具,使用诸如webpack和vite的打包工具在开发效率上的提升是非常明显的。
泛型的作用,对泛型的理解,平时怎么去用
我举了一个Promise的例子,因为在平时开发的时候,我们需要进行网络请求,并对返回的数据进行使用,我们可以定义请求返回内容的type或interface,并将其注入Promise预留的泛型中,便可以获得对then中返回数据的类型提示能力。当然,因为我对这个东西的认知仅仅是使用层面,说不出啥概念性的东西,面试官并不是很满意。
首先是关于泛型的概念:泛型是一种在定义函数、接口或类时使用类型变量的方式,它可以让我们在不确定具体类型的情况下编写代码,从而提高代码的可复用性和可读性。
使用泛型的好处如下:在不确定传入类型的情况下编写代码,在使用时才传入类型,传入的类型可以是多样的。从这个角度看待的话,泛型可以让我们编写更加通用的代码,从而提高代码的可复用性和可读性。
其实说到平时怎么用,场景就很多了。举一个简单的例子,使用React的useState,我们可以在useState预留的泛型接口中传入类型,如:
const [a, setA] = useState<number>(0)
TS中常见的高级类型
这也是我回答的比较差的一个问题,因为之前没怎么复习这块,忘记了。所以这一块的知识也是需要加以重视的地方。
下面是常用到的高级类型以及实现:
-
Record:用于创建索引类型的方法,如:type keys = 'name' | 'sex' | 'age' type people = Record<keys, any>源码如下:
/** * Construct a type with a set of properties K of type T */ type Record<K extends keyof any, T> = { [P in K]: T; }; -
Exclude:在一个联合类型T中排除联合类型U中的项,源码如下:/** * Exclude from T those types that are assignable to U */ type Exclude<T, U> = T extends U ? never : T; -
Omit:在一个索引类型T内部排除索引满足K的项:/** * Construct a type with the properties of T except for those in type K. */ type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; -
ReturnType:获取函数类型的返回值,如:type request<T> = (options: any) => Promise<T> type requestReturnType<T> = ReturnType<request<T>>源码如下:
/** * Obtain the return type of a function type */ type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; -
Partical:把索引类型的所有属性变成可选,源码如下:/** * Make all properties in T optional */ type Partial<T> = { [P in keyof T]?: T[P]; };
篇幅有限,这里就不再列举了,这里推荐一个项目:type-challenges,可以用来练习类型体操,还有对应配套的vscode插件,可以说是非常方便了。
如何评价后端全用post的现象
这个问题就见仁见智了,我是觉得都可以,毕竟:
不过我觉得这样回答可能有失专业性,所以顺带提了下Restful API的诸多好处,当然答得也并不是特别准确。下面是chatgpt老师给出的关于Restful API的介绍:
RESTful API是一种基于HTTP协议设计的Web API,它的核心思想是将资源和行为统一抽象为资源和HTTP动词,通过URI来标识资源,通过HTTP动词来定义对资源的操作。举一个生动的例子,假设我们正在开发一个博客系统,我们可以使用
RESTful API来设计博客文章的API。具体来说,我们可以定义以下资源和HTTP动词:
资源:文章(
Article)
HTTP动词:GET、POST、PUT、DELETE基于这些资源和
HTTP动词,我们可以设计以下RESTful API:
获取文章列表:
GET /articles获取指定文章:
GET /articles/{id}创建文章:
POST /articles更新指定文章:
PUT /articles/{id}删除指定文章:
DELETE /articles/{id}例如,我们可以使用以下
URI来获取指定ID为1的文章:GET /articles/1。这个URI中的/articles表示文章资源,/1表示文章的ID,而GET表示获取文章的操作。通过这种方式,我们可以使用统一的方式来定义和管理博客文章的API,使得API的设计更加清晰和易于理解。
在网络上关于这个问题的争论也是多种多样,这个是我比较认同的回答:公司规定所有接口都用 post 请求,这是为什么? - 程墨Morgan的回答 - 知乎。
如何获取一个元素在视口中的定位信息
这个问题属于是知识盲区了,完全不知道怎么回答...
可以使用Element.getBoundingClientRect()来获取元素在视口中的定位信息。该方法的返回值为DOMRect类型的对象,这个对象里面有如下关于定位的信息:
-
元素相对视口的定位(相对于视口左上角计算):
left、top、right、bottom、x、y -
元素本身的宽高:
width和height
下面是一个简单的例子:
<div id="parent" style="position: relative;">
<div id="child" style="margin-top: 10px; margin-left: 20px;"></div>
</div>
现在我们想要获取child元素相对于其父元素的定位信息,可以这么做:
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
const rect = child.getBoundingClientRect();
const parentRect = parent.getBoundingClientRect();
const top = rect.top - parentRect.top; // 10
const left = rect.left - parentRect.left; // 20
关于该API的其他用法可见:Element.getBoundingClientRect() - Web API 接口参考 | MDN。