前言
本文主要用于记录本人的面试经验和总结,虽然并不全是大厂,但也是宝贵的经验。而且我想这可能才是更普遍的求职者会经历的过程。
第一场面试
1. 自我介绍 (知识点:表达能力)
自我介绍算是面试中的必做题了,这点上因为目前面试经历还不多,所以也没有机会问面试官对我的感受。不过还是从各处总结了一些小技巧给大家参考,如果有做面试官的朋友,也欢迎留言指出不足。
第一点:简单介绍自己的来历,工作年限。
这点比较简单,主要是给面试官一个整体印象,不管怎么说,自我介绍肯定要自报名字和来历吧? 此时如果说有过大厂或名校背板,肯定能拉高一些面试官的印象分,如果没有的话,就简单带过,或者用概括性的语言,比如:985高校,双一流高校,世界五百强企业,行业前十企业等。
第二点:重点,介绍自己为什么想应聘这个岗位,以及过往经历和岗位的需求匹配程度。
这个部分很大程度上决定了面试官对你的好感度和认可度,你想想,一个工作经历和岗位高度符合的求职者与工作经历完全不相关的求职者对比,面试官更喜欢哪一个呢? 这个环节我们主要是要说出,自己投递这个职位的原因是什么?并且根据自己以往的经历,选出其中符合该职位的成就。 举个简单的例子,如果我投递的,公司主要是面向移动端开发的,那么移动端开发常用的技术,像是微信公众号开发,小程序开发,原生app开发等,只要你曾经有过这些相关的开发经历,就都是可以拿出来说的点。
第三点:我们对于自己未来的职业规划。
这一点算是锦上添花的内容,但也是非常重要的一点。如果说求职者的未来规划和公司将来的发展方向不符,甚至相背而驰,那么对于公司来说,定然是不会录取的。相反的,如果求职者的未来规划与公司将来的发展方向非常符合,那对于面试官来说,这个人可培养的潜力就会大很多。
2.你了解哪些在开发H5中能垂直居中的方法?(知识点:CSS)
分析:
垂直居中作为面试中的考核CSS基础的高频题目,应当是面试初级前端前必须要准备的一道题。
当时被问到的时候,由于脑海中没有归纳总结,所以混在一起大概只说了三、四种方案,而且说得有些混乱,想来在面试官那边也减了一些分数。
因此在这里对常见的方法进行了一个总结归纳,希望大家能用得上。
垂直居中可以说是工作中最常用到的一个CSS方面的知识,但是由于各种情况的不同,垂直居中也有很多种实现方法。下面按照面临的不同情况,总结一些常见的方法。
干货:
| 适用方案 | 父元素定高 |
|---|---|
| 子元素定高 | 一、二、三 |
| 子元素高度不限制 | 四、五、六、七 |
方案一 相对定位+绝对定位
父元素:relative + 定高
子元素:absolute + 50%top + 一半高度的负margin + 定高
HTML
<div id="parent">
<div id="child">
</div>
</div>
CSS
#parent {
width: auto;
height: 300px;
background: #ddd;
position: relative;
}
#child {
width: 100px;/*只是为了撑开展示,如果child有内容,此处可以不设定*/
height: 100px;
background: orange;
position: absolute;
top: 50%;
margin-top: -50px;
}
方案二 相对定位+绝对定位
父元素:relative + 定高
子元素:absolute + top 0 + bottom 0 + margin:auto + 定高
HTML
<div id="parent">
<div id="child">
</div>
</div>
CSS
#parent {
width: auto;
height: 300px;
background: #ddd;
position: relative;
}
#child {
width: 100px;/*只是为了撑开展示,如果child有内容,此处可以不设定*/
height: 100px;/*如果不限定高度,高度会占满容器*/
background: orange;
position: absolute;
top: 0;
bottom: 0;
margin:auto;
}
总结: 方案一和方案二比较类似,都是利用relative+absolute来实现居中效果的,只是子元素的居中实现上稍有不同
方案三 引入第三方基准
父元素:定高
基准元素:父元素一半的高度
子元素:通过基准元素的排斥 + 负margin + 定高
HTML
<div id="parent">
<div id="base"></div>
<div id="child"></div>
</div>
CSS
#parent {
width: auto;
height: 300px;
background: rgb(206, 248, 171);
}
#base {
height: 50%;
background: orange;
}
#child {
height: 100px;
background: rgba(131, 224, 245, 0.9);
margin-top: -50px;/*自身高度的一半*/
}
总结: 方案三比较有趣,引入了第三方基准来将子元素挤下去,再利用负边框将子元素拉回一半从而达到居中效果。
方案四 绝对定位 + transform
父元素:relative + 定高
子元素:absolute + 不限制高度 + transform
HTML
<div id="parent">
<div id="child">看我居中</div>
</div>
CSS
#parent {
width: auto;
height: 300px;
background: rgb(159, 252, 216);
position: relative;
}
#child {
background: orange;
position: absolute;
top: 50%;
transform: translate(0, -50%);
}
总结: 方案四可以算得上是方案一和方案二的优化版,利用方案四的方法,即使不知道子元素的确切高度,也能够将子元素居中。此处的关键是利用了transform这个属性。我们在这里利用top属性将子元素向下平移了半个父元素的高度,再利用transform属性,将子元素平移了半个元素的高度。将这两个属性结合使用,就可以使自达到垂直居中的效果。
方案五 flex弹性盒子
父元素:flex
子元素:不限制
HTML
<div id="parent">看我居中</div>
CSS
#parent {
width: 300px;
height: 300px;
background: rgb(141, 252, 209);
display: flex;
align-items: center;
}
总结: 弹性盒子可以说是近两年最受欢迎的布局方式。通过将父元素设置为弹性盒子,就可以对子元素进行排列布局。使用方便简洁,唯一的缺点是,对于旧版的浏览器支持不太好。But,who cares?o(´^`)o
方案六 单行文本居中
父元素:定高
文本元素:与父元素高度相等的行高
HTML
<div id="parent">看我居中</div>
CSS
#parent5 {
width: 300px;
height: 100px;
background: rgb(141, 252, 209);
line-height:100px;
}
总结: 方案六也是文字居中最常用的方法,因为兼容性好,使用简便,受到大家的青睐。
方案七 table居中法
父元素:定高
子元素容器:不限制高度
HTML
<div id="parent">
<div id="child">
<!-- 子元素容器里可以是内联或块状元素 -->
<div id="child2"></div>
</div>
</div>
CSS
#parent {
width: 300px;
height: 300px;
background: #ddd;
display: table;
}
#child {
display: table-cell;
vertical-align: middle;
}
#child2{
width: 100px;
height: 100px;
background-color: antiquewhite;
}
总结: 现在使用table布局的已经很少了,但是使用table进行动态垂直居中布局仍然是一个可以优先考虑的选择,特别是这种实现方式的兼容性还很不错。
参考原文:CSS垂直居中的12种实现方式
3. 知道哪些前端的性能优化方案?(知识点:性能优化)
分析:
刚入行半年的前端小白哪里有时间去关注性能优化这个方面嘛?被问到的时候慌得一批,随便扯了几点。面完之后查资料,才发现自己连冰山一角都没有说到,有些还串了,根本就不属于这块的知识点。
但是作为一个有远大志向的前端,这块的知识是必不可少的。根据查询的资料消化总结一波,也算是一种进步了。
干货:
大致上可以从六个方向来进行Web性能的优化,此处仅列出一些关键信息,详细信息请看参考原文:
1. 页面内容
- 减少 HTTP 请求数
- 合并JS、CSS文件
- CSS Sprite
- 未来趋势:HTTP/2
- 合并JS、CSS文件
- 减少 DNS 查询
- DNS缓存机制
- 把资源分布到 2 个域名上(最多不超过 4 个)
- 缓存 Ajax 请求
- 缓存响应结果
- 减少 DOM 元素数量
- 消灭表格布局
- 减少仅用于布局的
<div> - 用伪元素代替某些元素
- 避免重定向
- URL 末尾添加
/
- URL 末尾添加
- 避免 404 错误
- 延迟加载
- 遵循「渐进增强」理念开发
- 延迟渲染
- 预先加载
- 无条件
- 有条件
- 基于某种目的
- 划分内容到不同域名
- 尽量减少 iframe 使用
- 将字符集定义在
<head>顶部
2. 服务器优化
- 使用CDN
- 添加 Expires 响应头(静态内容缓存)
- 添加 Cache-Control 响应头(动态内容条件请求)
- 启用 Gzip
- 配置 Etag
- Ajax 请求使用 GET 方法
- 避免图片 src 为空
3. Cookie
- 减少 Cookie 大小
- 静态资源使用无 Cookie 域名
4. CSS
- 把样式表放在
<head>中 - 不要使用 CSS 表达式(过时)
- 使用
<link>替代 @import - 不要使用 filter(过时)
5. JavaScript
- 把脚本放在页面底部
- 使用外部 JavaScript 和 CSS(可以缓存)
- 压缩 JavaScript 和 CSS
- 移除重复脚本
- 减少 DOM 操作
- 使用高效的事件处理
6. 图片
- 优化图片
- 优化 CSS Sprite
- 不要在 HTML 中缩放图片
- 使用体积小、可缓存的 favicon.ico
参考原文:CSSPod—前端性能优化最佳实践
4. 谈一谈你对CSS盒模型的认识(知识点:CSS)
分析:
盒模型作为一个常见的知识点,被考察的概率还是比较大的,不过在我半年的前端工作经历中其实涉及到的不多,可能也是因为小公司没有太多需要兼容的问题的原因吧,而且与盒模型相关的问题一般出在IE身上,但是连微软自己都已经使用chromium内核来做Edge了,那一部分老的IE浏览器用户也该随着时代的进步而淘汰了。
干货: 盒模型定义:盒模型可以说是网页布局时,每一个块的共同抽象模型,其本身由四部分组成:从外到内依次是:
marginborderpaddingcontent
在网页标准的发展过程中,IE和W3C各自出了一个不同的盒模型标准,现在人们通常称为怪异盒模型和标准盒模型。他们的区别就在于对width和height的定义。
-
W3C 标准盒模型(
box-sizing:content-box):在这种盒模型下,盒模型的width和height就等于content的width和height。
-
怪异盒模型(
box-sizing:border-box):在这种盒模型下盒模型的width和height除了content还包括盒模型的padding和border的宽度,即:
width = 左右border宽度 + 左右padding宽度 + 内容的宽度
height = 上下border宽度 + 上下padding宽度 + 内容的宽度
现代浏览器中,应该已经都是默认使用标准盒模型的了,但是CSS也提供box-sizing属性用来切换盒模型的模式,就我个人拙见,盒模型影响最大的应该就是布局时长宽的计算了吧,以及由此而带出的IE兼容性问题。
5. 你们的研发流程是怎样的?(知识点:软实力)
分析:
这个问题其实就是面试官想看看面试者平时是不是除了自己的事情,什么也不关心,也能在一定程度上窥得一点面试者的大局观。 下面是我结合自己的情况与一些资料给出的流程,会比较精简,可能一些大厂会更详细些。
干货:
需求提出-->产品需求文档评审-->产品设计-->接口定义&前后端开发&前后端联调-->代码review-->第一次验收-->发布计划确定-->产品第二次验收-->需求结束
这类问题大家根据自己的实际情况去说就好,其实各家公司的开发流程也不会差太远,所以这个环节更重要的应该是体现自己的亮点,相比较其他人做的更好的地方。如果不提前准备一下,可能就只是平平无奇的流程叙述,也就错失了一个展示自己的机会。
第一次面试也没有记录下问题,还有一些不太记得了,所以也只能作罢,将比较有印象的面试题写了一下。希望对大家有帮助。
第二场面试
1. 自我介绍 (知识点:表达能力)
请参考第一场面试内容。
2. 请说说,你会怎么实现一个饼图? (知识点:表达能力)
分析:
面试官问的这个问题,我是真没准备,原因来自于我简历里提到过的,使用Echarts做过数据可视化开发,然后自我介绍时也提到过,将来会向这个方向发展。所以想来面试官就以为我这个方面比较在行。所以说,引导面试官朝你擅长的方向提问是非常重要的。 以下总结主要来自一篇博客的参考:
干货:
SVG:
- SVG基于XML实现,所以它的每个DOM元素都可以添加事件处理器。
- SVG中每个被绘制图形都是一个独立的对象,因此浏览器能够侦测SVG对象的事件和属性,自动处理重绘,重现图形。
- SVG是矢量图形。
- 不适合游戏应用。
canvas:
- 通过JavaScript绘制2D图形
- 一旦绘制完成后如果需要变化,那么整个场景都需要重新绘制,且需要开发者手动处理。
- 不支持事件处理器。
- canvas是逐像素绘制的,与性能分辨率有很大关系。
- 能以.PNG或.JPG格式保存结果图像。
开发中的直观区别:
- canvas需要开发者处理重绘,而SVG则会在DOM被修改的时候自动重绘。
- canvas不负责监测鼠标/触摸事件发生在哪个图形元件上,而SVG可以。
- canvas效率更高。
| Canvas | SVG |
|---|---|
| 位图(像素,放大失真) | 矢量图(没单位,放大不失真) |
| 依赖js | 脱离js |
| 图形不能修改 | 图形可以修改 |
| 没有事件 | 有事件 |
| 性能很高 | 性能一般 |
| 适合:大型图表,游戏 | 适合:地图,交互频繁的图表,高保真文档等 |
参考原文:前端绘图方式Canvas和SVG
3.在Vue中,父组件有哪些向子组件传值的方式? (知识点:组件传值)
分析:
这个问题可以说是与Vue相关的问题中,被点中率数一数二的问题了,因为这个不仅仅是基础知识,也是实际工作中常用到的知识。可是没想到我却在这里又栽了下去,因为没有对父向子传值和子向父传值进行总结归纳,导致回答的时候时不时就讲串了。而且漏了很多, 场面一度非常尴尬。。这次在这个总结贴里也好好梳理下自己的知识。
干货:
| 序号 | 父传子 | 子传父 |
|---|---|---|
| 1 | props | $emit |
| 2 | $parent | $children |
| 3 | provide&inject | |
| 4 | ref | ref |
| 5 | eventBus | eventBus |
| 6 | Vuex | Vuex |
| 7 | localStorage/sessionStorage | |
| 8 | $attrs | $listeners |
总结:
- 1和2这两种方式仅能用于父子组件之间的传值,且1中的方法使用更加普遍,Vue官方也更推荐1中的方式。
- 3中的方法只用于父传子,但是无论子组件嵌套有多深,都能通过这种方式取到
- 4中的方式,父传子可以通过调用子组件实例的方法,将数据作为参数传递过去,子传父则可以通过父组件对实例属性的访问来取值。
- 5中eventBus只适用于轻量级的值传递,不仅可以用于父子传值,还能用于兄弟传值。
- 6中Vuex是一个专为 Vue.js 应用程序开发的状态管理模式,也是Vue全家桶之一。数据全局共享,共同维护。
- 7其实是一种缓存机制,通过本地缓存的存值取值来交换数据。但是这种方式比较混乱,不容易维护。可以结合Vuex使用。
- 8用于隔代通信。
参考原文:vue中8种组件通信方式, 值得收藏!
4.聊聊你对JS原型链的理解? (知识点:JS)
分析:
这个问题也可以说是基础中的基础了,但是事后复盘才发现自己当时回答的不仅思路不清晰,而且理解漏洞百出,难怪当时隔着话筒我都能感觉到面试官兴趣缺缺的感觉。
干货:
1. 起因
在遵循了OOP原则的高级语言中,继承是其中的一大特性。继承又分为两类:接口继承和实现继承。但是由于JS在诞生之初,只是一个10天内需要开发出来的脚本语言,要像JAVA但是又不能太像JAVA,于是创始人Brendan Eich混合了好几种语言的特性,开发出了JavaScript这一语言,其中的原型继承机制(prototype)就来自于Self语言。既然已经用了这种方式实现继承,那我们总还是得了解的。
2. 概念
在JS中,每个构造函数(
constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针(constructor),而实例(instance)都包含一个指向原型对象的内部指针(__proto__).从下面这段代码中可以看到,构造函数的原型对象(
Object.prototype)和实例的原型对象(obj.__proto__)是指代的物理意义上的同一个对象。由于JS中有这么一条规则:如果试图引用对象(实例instance)的某个属性,会首先在对象内部寻找该属性,直至找不到,然后才在该对象的原型(instance.
__proto__)里去找这个属性.由于原型对象在这条规则中起着链接的作用,因此我们把由于这种机制而形成的链型关系称为原型链。
// 已知有对象的初始构造函数Object
// 创建自定义的构造函数Person,Person2
function Person(name){
this.name = name
}
function Person2(name){
this.name = name
}
// 实例化对象
var obj = new Object()
var person = new Person()
var person2 = new Person2()
//下面代码表明构造函数的原型对象和实例的原型对象是同一个东西
obj.__proto__ === Object.prototype //true
person.__proto__ === Person.prototype //true
//下面代码表明所有开发者自定义的构造函数都是Object()的一个实例,且并非同一个实例
obj.__proto__ === Object.prototype //true
Person.prototype.__proto__ === Object.prototype //true
Person2.prototype.__proto__ === Object.prototype //true
Person.prototype === Person2.prototype //false
//所有构造函数的__proto__指向同一个函数,即所有构造函数的“原型”都是同一个函数
Person.__proto__ === Object.__proto__ //true
Person.__proto__ === Person2.__proto__ //true
typeof Object.__proto__ //"function"
//构造函数可以找到原型,原型也能找回构造函数
Person.prototype.constructor === Person //true
//实例没有prototype属性
console.log(obj.prototype,person.prototype,person2.prototype) // undefined,undefined,undefined
3. 应用
问题1: 当原型链中存在某个引用类型的原型时,它会被所有的实例共享。并且,在创建子类型的实例时,无法向父类的构造函数中传递参数。
解决的基本思想: 在子类型构造函数的内部通过call()、apply()之一调用父类构造函数,从而继承属性;再通过原型链的特性,将子类构造函数的原型指向父类的实例,从而继承父类定义在原型上的方法。通过两者结合达到OOP中继承的效果。
示例:
function Parent(mood){
this.num = [1,2,3];
this.mood = mood;
}
Parent.prototype.getMood = function(){
console.log(this.mood);
};
function Son(sonMood){
Parent.call(this,sonMood);//继承了Parent的属性,并且成功向父类型传递了参数,第一次调用Parent()
//Parent.apply(this,[sonMood]);//apply方式只接收数组参数
}
Son.prototype = new Parent();//继承父类方法,第二次调用Parent()
var son = new Son("happy");
son.num.push(4);
console.log(son.num);//"1,2,3,4"
console.log(son.mood);//"happy" 参数成功传递
console.log(son.getMood());//"happy" 继承了父类的方法
var son2 = new Son();
console.log(son2.num);//"1,2,3" 证明引用类型的值是独立的
console.log(son2.mood);//undefined 没有传参数,所以是undefined
5. 开发中你们对UI的还原度怎么样?有没有怎么都无法满足还原度要求的情况?(知识点:UI处理)
分析:
当时的我还是个不经世事的愣头青,听到这个问题,直接回答说我们是小公司,对UI的还原度要求没有那么高,遇到无法满足的就想办法在不影响功能的情况下妥协换掉。
现在想想,真的是想给我两巴掌。毕竟不管怎么说,面试官是肯定不希望听到你这种回答的。那如果真的不想欺骗面试官,又没有经验,要怎么办呢?
答案就是:表明现状的同时,给出自己的更优解。然后引申出自己了解或遇到的,如果要解决某些还原问题,应该怎么处理的情况。 比如:虽然我们现在由于人手原因,不会太抠细节,但是我自己平时会尽可能地向像素级还原。
在还原设计过程中,我遇到过设计的风格和现有组件库UI不一致的情况,这种情况下大部分情况一般会通过重写CSS来处理,而在移动端,虽然公司不会纠结一像素问题,但这个经典问题我也有了解过,他产生的原因是XXX,解决方法是XXX。
6. 相比较其他前端,你觉得你有什么优势?(知识点:软实力)
分析:
当时准备了,你有什么缺点? 的问题,却忘了准备这个对立的问题,结果当时临时答了一句,我的学习能力比较强,更乐于学习新的知识。 面试官紧接着就问到,那你最近有了解什么新的技术或者前端趋势吗? 这一下可把我给问懵了,我这还在打基础呢,咋就了解新技术了。。。于是没能想到什么拿得出手的答案,老实回答在学基础。于是便成了我的最后一个问题。暴风哭泣。
后来想想,我就算是说说HTML5,CSS3,ES6里面的新特性也行啊。为什么要说不了解,还在打基础。。。
所以说,对于大多数人来说,问题有准备和没准备完全是两种表现。大家一定要多模拟一下答题的场景,就算是自己对着镜子念也是好的。并且最好是从自己的答案延伸出来一些,不要一被追问就哑口无言了。
第三场面试 (阿里中台,无情暴击)
1. 自我介绍
请参考第一场面试内容。
2. 介绍一下你最自豪的项目
我想对于大多数的程序猿来说,可能自己做的项目都还达不到自豪的地步,毕竟除非是自己主导开发了一个完整的项目,不然大多数情况下都是在ctrl c 和 ctrl v中度过,那么如果是平时没有机会做大项目,应该怎么答好呢?
我个人的理解是,真诚地和面试官说明:平时的业务需求暂时还未达到让我自豪的程度,但是平时在开发时,针对每个需求都有认真对待,力求做到最好,在规定的时限内力求最优的实现。。。
如果平时有自己的项目或者有主导过大项目,那就好好答就可以了。
3. 性能优化是怎么做的
对于一个项目来说,性能优化有很多地方可以做,那么首先可以答出自己在实践过程中用过的优化方法,然后可以阐述自己了解的其他一些优化方法。 具体参考面试一第三点
4. 从网页解析到用户看到内容过程中,所发生的事情,重要节点即可
这个题目我在回答的时候由于惯性思维,以为面试官问的是URL到看到页面的过程,但是后来仔细想想不太对,应该是想考的更细节一些,是让我回答DOM树渲染的过程。在这简单阐述一下:
- 网页接收HTML文档并解析生成DOM Tree,同时将CSS解析生成CSSOM
- DOM Tree和CSSOM合成,生成Render Tree
- 布局阶段,在这一阶段为DOM Render Tree的每个节点分配在屏幕上出现的确切坐标。
- 绘制阶段,UI后端进行绘制并呈现页面效果
如果当时有回答准确的话,应该面试官还会有更深入的询问,这方面大家可以自行了解一下浏览器的渲染机制和渲染过程。
5. 怎么减少白屏等待时间?(更深入的层次)
首先要弄清楚白屏的过程中发生了什么:
- DNS解析
- 建立TCP连接
- 服务器处理请求并响应
- 客户端下载资源,解析并渲染页面,直至首屏被渲染
因此针对每个阶段,我们都有不同的优化方法。
1阶段:
- DNS缓存优化和预加载
- 寻找稳定可靠的服务器 2阶段:
- 花钱,这个目前没办法 3阶段:
- 针对服务器进行优化处理(偏后端),诸如Redis缓存、数据库优化、数据压缩等等
- 使用CDN减少服务器压力
- 按需引用组件库,减少传输文件体积 4阶段:
- 精简HTML代码,减少层叠结构
- 优化CSS文件
- 合理放置JS代码,以免阻塞渲染流程
由于这是白屏阶段,还未到渲染出之后,因此诸如骨架屏、懒加载这类还用不上,因此未写。
6. 跨域策略?
我们经常在开发中发送Ajax请求的时候会遇到跨域问题,这时候就需要用到一些跨域策略:
- JSONP
- JSONP主要就是利用了script标签,由于浏览器对于脚本的请求并没有同源限制,因此可以通过执行脚本来进行跨域请求。但也有限制,就是只能发送get请求。
- CORS
- CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。
这个标准需要浏览器和服务器的同时支持才能完成通信,由于该标准在浏览器端是完全自动完成,因此只要服务器实现了CORS接口,就可以跨域通信。- CORS方法有两种请求方式,一种是简单请求(HEAD、GET、POST以及限定字段的HTTP头)。一种是非简单请求,会发送预请求询问服务器是否允许,得到肯定后才会跨域通讯。
- iframe跨域
- iframe的加载是没有跨域限制的,因此我们可以使用iframe来进行跨域请求。
参考资料:跨域与同源策略探究
参考资料:
知识点 原文链接 垂直居中 CSS垂直居中的12种实现方式 性能优化 CSSPod—前端性能优化最佳实践 研发流程 《我们一起进大厂》系列-大厂需求研发/开发流程 SVG & Canvas 前端绘图方式Canvas和SVG 组件传值 vue中8种组件通信方式, 值得收藏! JS原型链 JS 原型与原型链 JS原型链 JS原型链与继承别再被问倒了
题外话:
这片文章断断续续写了近三周才写完。虽然中间因为一些私人原因导致拖沓了,但是写技术长文确实也很费神。不过想到可能能帮到和我一样迷茫的小伙伴也就觉得不那么累了,如果大家觉得有收获的话,点个赞给点鼓励吧(✪ω✪)。
To be continue...