持续更新面试经历
一面
简单自我介绍
简单说了一下各个公司的任职工作内容,很久没有面试了,说得有点磕磕巴巴的,建议可以提前自己先写一段出来,这样不会思路混乱。
项目经历
说说你觉得最值得说的项目是哪些?
啊巴啊巴啊巴
八股文
1、从输入一个URL到页面呈现经历哪些?
解析浏览器地址 => 强缓存判断 => 协商缓存判断 => DNS寻址,先从本地查找,再逐级网上 => 建立TCP/IP连接 => 发送HTTP请求 => 服务端返回HTML => 浏览器解析HTML字符串 => 生成DOM树 => 生成styleSheets => 格式化Css => 根据DOM树与样式表生存布局树 => 根据层级关系生成分层树 => 绘制图层 => 图层栅格化,把图块转化为位图 => 渲染到浏览器
2、CDN加速原理
通过将源站内容分发至 最接近用户 的节点,从而 降低核心系统负载(系统、网络) ,使用户可就近取得所需内容,提高用户访问的响应速度
3、什么HTTP2,有哪些新特性
- 传输数据量大、量减少
- 二进制分帧
- 标头压缩
- 多路复用及相关功能
- 多路复用
- 优先级与依赖性
- 服务器消息推送
4、script标签里defer、async有什么区别?
defer
在执行到加了defer的script时会推入下载进程,然后再去执行下一个标签。待到这个script的外链下载完毕时,需要看DOM是否渲染完毕了,如果渲染完毕了则执行defer script的内容,然后触发DOMContentLoaded 的回调。多个defer等待所有defer下载完依次执行。
async
在执行到加了async的script时会先下载,然后再去执行下一个标签。待到这个script的外链下载完毕时,如果DOM正在渲染则暂停,执行async script的内容。多个async先下载完的先执行
5、什么是闭包?
闭包的定义:指有权访问另一个函数作用域中的变量的函数,一般情况就是在一个函数中包含另一个函数。
闭包的作用:访问函数内部变量、保持函数在环境中一直存在,不会被垃圾回收机制处理 函数内部声明的变量是局部的,只能在函数内部访问到,但是函数外部的变量是对函数内部可见的。 子级可以向父级查找变量,逐级查找,直到找到为止或全局作用域查找完毕。因此我们可以在函数内部再创建一个函数,这样对内部的函数来说,外层函数的变量都是可见的,然后我们就可以访问到他的变量了。
function foo() {
let value = 1;
function bar() {
console.log(value);
}
return bar;
}
const baz = foo();
// 这就是闭包的作用,调用 foo 函数,就会执行里面的 bar 函数,foo 函数这时就会访问函数外层的变量
baz();
6、模块化有哪些?
CMD、AMD、CommonJS、ES6 Module、UMD
CommonJS
function myModule() {
this.hello = function() {
return 'hello!';
}
this.goodbye = function() {
return 'goodbye!';
}
}
module.exports = myModule;
var myModule = require('myModule');
Node.js默认的模块化规范,CommonJS以服务器优先的方式来同步载入模块,如果引入多个模块,这些模块会被一个一个载入。在服务端用起来没有什么问题,但是在浏览器上就不太高效了。
AMD
define(['myModule', 'myOtherModule'], function(myModule, myOtherModule) {
console.log(myModule.hello());
});
AMD的诞生是为了弥补CommonJS同步载入模块所带来的问题,所以他的全称叫Asynchronous Moudle Defintion(异步模块定义规范)。
除了异步加载以外,AMD 的另一个优点是你可以在模块里使用对象、函数、构造函数、字符串、JSON 或者别的数据类型,而 CommonJS 只支持对象。
CMD
CMD专门服务于浏览器端,整合了CommonJS和AMD的优点,模块是异步加载的,同时模块使用时才会加载执行。
//定义没有依赖的模块
define(function(require, exports, module){
exports.xxx = value
module.exports = value
})
//定义有依赖的模块
define(function(require, exports, module){
//引入依赖模块(同步)
var module2 = require('./module2')
//引入依赖模块(异步)
require.async('./module3', function (m3) {
console.log(m3)
})
//暴露模块
exports.xxx = value
})
ES6 Moudle
- ESModule设计理念是希望在编译的时就确定模块的依赖关系及输入输出
- CommonJS和AMD都只能在运行时才能确定依赖和输入、输出
// ES6模块
import { stat, exists, readFile } from 'fs';
UMD UMD全称为Universal Module Definition,也就是通用模块定义,为什么叫通用呢,我们怎么描述一个模块是通用的呢?举个例子,假如现在我的项目使用的是AMD模块规范,那么现在我引入了一个用CommonJS规范写的模块,能正常运行吗?肯定不行的,而UMD就是解决了这个问题。
在模块中去判断全局是否存在exports和define,如果存在exports,那么以CommonJS的方式暴露模块,如果存在define那么以AMD的方式暴露模块。
7、什么是单例、发布订阅模式
单例模式 单例模式,又被称为单体模式,是只允许实例化一次的对象类。其实闭包就可以看作是一个单例模式,外部无法修改内部变量
const options = () => {
let conf = {
MAX_NUM: 100,
MIN_NUM: 1,
COUNT: 1000
}
return {
get: (name) => { reutrn conf[name] ? config[name] : null },
set: (name,val) => { conf[name] = val }
}
}
const instanceConf = options()
console.log(instanceConf.get('MAX_NUM'))
单例模式里还有一种叫惰性单例,惰性单例是指需要的时候才去创建。
var Singleton = function (name) {
this.name = name
}
// 静态属性(类属性)
Singleton.instance = null
Singleton.prototype.getName = function () {
console.log(this.name)
}
// 静态
Singleton.getInstance = function (name) {
if(!this.instance){
this.instance = new Singleton(name)
}
return this.instance
}
var a = Singleton.getInstance('wang')
var b = Singleton.getInstance('Yi')
console.log(a.name) // wang
console.log(b.name) // wang
console.log(a === b ) // true
发布订阅模式
发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。
同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者存在。
class PubSub {
constructor() {
this.messages = {};
this.listeners = {};
}
// 添加发布者
publish(type, content) {
const existContent = this.messages[type];
if (!existContent) {
this.messages[type] = [];
}
this.messages[type].push(content);
}
// 添加订阅者
subscribe(type, cb) {
const existListener = this.listeners[type];
if (!existListener) {
this.listeners[type] = [];
}
this.listeners[type].push(cb);
}
// 通知
notify(type) {
const messages = this.messages[type];
const subscribers = this.listeners[type] || [];
subscribers.forEach((cb, index) => cb(messages[index]));
}
}
// 发布者
class Publisher {
constructor(name, context) {
this.name = name;
this.context = context;
}
publish(type, content) {
this.context.publish(type, content);
}
}
// 订阅者
class Subscriber {
constructor(name, context) {
this.name = name;
this.context = context;
}
subscribe(type, cb) {
this.context.subscribe(type, cb);
}
}
8、移动端适配
- 媒体查询
通过CSS里的@media来根据不同设备宽度来设置不同的CSS样式
@media only screen and (max-width: 750px) {
background: red
}
@media only screen and (max-width: 720px) {
background: green
}
-
动态rem、em
通过设置rem、em这种动态单位来自适应,单位由根元素字体大小决定。 -
Viewport
通过meta标签来固定布局视口宽度,与缩放比例
<meta name="viewport" content="width=720, user-scalable=no" />
9、Fiber架构
看这个吧,内容有点多:tsejx.github.io/react-guide…
10、页面加载优化
回答这个问题一定要理清楚思路,从开发 -> 打包 -> 发布 -> 请求 -> 访问 -> 呈现一步一步的说,不然说了几点过后就乱掉了忘了前面说的什么了
- 非页面初始化时的必要请求全部后置
- 大图片等资源文件可上OSS,CDN
- 使用React.memo lazy等懒加载方式
- 打包时可通过配置webpack 代码切割,代码按需加载
- 在移动端可以提取react等不常更新的包,打成dll先提前内置近宿主
- 开启Nginx的Gzip
- 把前端资源发布至CDN
- 开启浏览器缓存(强缓存、协商缓存)
- 在页面加载资源时,渲染时,最好能提前给用户呈现一个loading效果或者骨架屏,然后用户感官上觉得页面已经在加载中了。
11、react父子组件通信
- props
- Context
- ref、forwardRef
- useReducer
二面
G...
注:部分答案仅代表个人观点,可能存在一定的误差,若有错误各位大佬在评论区轻喷🙇,文章会持续更新