前言
由于很多小伙伴看了 《普通人如何进大厂》这篇文章对我的笔记比较感兴趣,所以我花时间整理提炼了一下跟大家分享,不保证内容完全正确,仅供参考哈
必问面试题
- Js 基础:事件循环、Proxy、Promise
- Http:缓存、TCP 头部堵塞、HTTPS 原理
- Vue 原理:key 作用,diff 过程、双向绑定原理
- 性能优化:Webpack 核心、优化手段
- 项目亮点难点:工程化
面试笔记
其他知识
Node
require 加载机制
-
先计算模块路径
-
如果模块在缓存里面,取出缓存
-
加载模块
-
输出模块的exports属性
node导出模块有两种方式,一种是exports.xxx=xxx和Module.exports={},有什么区别吗
可以给exports添加属性
exports.hello = hello;
exports.greet = greet;
但不可以直接对exports赋值,代码可以执行,但是模块并没有输出任何变量
exports = { hello: hello, greet: greet };
原因:exports 最终返回 module.exports,后者直接改变了 exports 的指向,即 exports !== module.exports
虽然给exports 赋值,但是 module.exports 结果依然不变
var module = {
id: 'hello',
exports: {}
};
load()函数最终返回module.exports:
var load = function (exports, module) {
// hello.js的文件内容
...
// load函数返回:
return module.exports;
};
var exports = load(module.exports, module);
多线程工作
var fork = require('child_process').fork;
var cpus = require('os').cpus();
for(var i = 0; i < cpus.length; i++){
fork('./worker.js');
}
node还有一个模块叫作cluster,可以用它来创建集群实现负载均衡,cluster的创建进程的方法就是使用了child_process的fork,它用来处理文件模块
koa中间件原理
// 注意其中的compose函数,这个函数是实现中间件洋葱模型的关键
// 场景模拟
// 异步 promise 模拟
const delay = async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 2000);
});
}
// 中间间模拟
const fn1 = async (ctx, next) => {
console.log(1);
await next();
console.log(2);
}
const fn2 = async (ctx, next) => {
console.log(3);
await delay();
await next();
console.log(4);
}
const fn3 = async (ctx, next) => {
console.log(5);
}
const middlewares = [fn1, fn2, fn3];
// compose 实现洋葱模型
const compose = (middlewares, ctx) => {
const dispatch = (i) => {
let fn = middlewares[i];
if(!fn){ return Promise.resolve() }
return Promise.resolve(fn(ctx, () => {
return dispatch(i+1);
}));
}
return dispatch(0);
}
compose(middlewares, 1);
node 异步实现方式
- setTimeout :回调地狱
- promise -then:不优雅
- generator :复杂
- async await:完美
- 事件监听:跟代码执行顺序无关,取决某个事件是否发生
可暂停 stream
const videoCopy = (rootPath,index) => {
const extname = path.extname(rootPath)
const fileName = path.basename(rootPath, '.mp4')
const rs = fs.createReadStream(rootPath)
const ws = fs.createWriteStream(`${fileName}_副本${extname}`)
let data = 0
rs.on('data', (chunk) => {
data += chunk
if (ws.write(chunk) === false) {
rs.pause()
}
})
rs.on('error', () => {
console.log('拷贝出错', error)
})
rs.on('end', () => {
console.log(`第${index}个视频拷贝完成`)
ws.end()
})
ws.on('drain', () => {
rs.resume()
})
}
Babel
一般编译器 Compiler 是指高级语言到低级语言的转换工具,对于高级语言到高级语言的转换工具,被叫做转译器 (Transpiler)。
babel 就是一个 Javascript Transpiler(转译器)。
babel 编译流程
babel 是 source to source 的转换,整体编译流程分为三步:
- parse:通过 parser 把源码转成抽象语法树(AST)
- transform:遍历 AST,调用各种 transform 插件对 AST 进行增删改
- generate:把转换后的 AST 打印成目标代码,并生成 sourcemap
parse 流程
parse 阶段的目的是把源码字符串转换成机器能够理解的 AST,这个过程分为词法分析、语法分析。
比如 let name = 'guang'; 这样一段源码,我们要先把它分成一个个不能细分的单词(token),也就是 let, name, =, 'guang',这个过程是词法分析,按照单词的构成规则来拆分字符串成单词。
之后要把 token 进行递归的组装,生成 AST,这个过程是语法分析,按照不同的语法结构,来把一组单词组合成对象。
transform
transform 阶段是对 parse 生成的 AST 的处理,会进行 AST 的遍历,遍历的过程中处理到不同的 AST 节点会调用注册的相应的 visitor 函数,visitor 函数里可以对 AST 节点进行增删改,返回新的 AST(可以指定是否继续遍历新生成的 AST)。这样遍历完一遍 AST 之后就完成了对代码的修改。
generate
generate 阶段会把 AST 打印成目标代码字符串,并且会生成 sourcemap。不同的 AST 对应的不同结构的字符串。比如 IfStatement 就可以打印成 if(test) {} 格式的代码。这样从 AST 根节点进行递归打印,就可以生成目标代码的字符串。
性能优化
web 性能优化
优化宗旨
- 文件少加载
- 代码少执行
优化思路
性能监控
- lighthouse 插件和 performance 工具检测性能瓶颈
http 层面优化
- 减少http请求次数-(本地存储,缓存)
- 本地存储(web storage、indexdb,cookies)
- 浏览器缓存(http缓存,service worker cache )
- 减少http请求文件的体积
-
图片优化(压缩、使用jpg、webp、base64、svg)
-
资源打包压缩(MiniCssExtractPlugin插件:对CSS进行分离和压缩,optimization.splitChunks:抽离公共JS文件,optimization.minimizer :文件压缩)
-
服务端开启gzip压缩
- 加快http请求速度(cdn)
渲染层面优化
DOM 优化
- 减少DOM操作
-
使用虚拟DOM
-
使用事件委托
-
使用DOM Fragment
-
缓存DOM结点
js 优化
-
使用防抖节流
-
避免使用全局变量
-
避免内存泄漏
-
删除多余代码
CSS 优化
- 有效使用选择器,避免使用calc表达式、CSS滤镜
- 减少重排和重绘
- 布局优先使用flex
- 尽量避免使用getBoundingClientRect,getComputedStyle等属性
- 触发渲染层(transform: translateZ(0);backface-visibility: hidden;)
首屏渲染
-
内联首屏关键CSS
-
异步加载CSS(动态插入)
-
JS放body 底部,或者异步加载js(defer、sync)
defer、sync 都不会阻塞页面渲染,他们的区别是sync编译完成立刻执行,defer编译完成同时还要等整个文档解析完成、DOMContentLoaded 事件即将被触发才执行
- 预加载 preload
- 页面loading动画
- 骨架图
使用page-skeleton-webpack-plugin插件
vue 层面优化
Vue 应用运行时性能优化措施
-
引入生产环境的 Vue 文件
-
使用单文件组件预编译模板
-
提取组件的 CSS 到单独到文件
-
利用Object.freeze()提升性能
-
扁平化 Store 数据结构
-
组件懒加载
-
路由懒加载
-
虚拟滚动
Vue 应用加载性能优化措施
- 服务端渲染 / 预渲染
- 组件懒加载
react 层面优化
使用 shouldComponentsUpdate 和 React.useMemo 钩子
首屏渲染优化
进度条 loading 方式
- 进度条 loading 动画(计算图片资源加载构造进度条)
非 loading 方式
- 内嵌关键 css
- 占位图模糊图
- 懒加载
图片优化
- 首屏图片优先加载,等首屏图片加载完全后再去加载非首屏图片。
- 对大部分图片,特别是轮播广告中的图片进行按设备尺寸裁剪,减少图片体积,减少网络开销,加快下载速率。
项目中做过哪些优化
- 找出瓶颈:首先我的突破点是会利用谷歌浏览器自带的lighthouse 插件和 performance 工具去检测性能瓶颈看是否是某个文件(js、图片,css)过大还是接口请求时间过长,还是某段代码执行过长
- 文件优化(即http请求优化):
-
减少请求(跟后端同学协商一下开启缓存,如果是接口比如是一些枚举类的直接本地缓存),
-
减少文件体积(webpack 分包,压缩,tree-shaking),服务端开启gzip压缩
-
加快请求速度:使用 cdn 方式引入各种库,使用 http2 协议
- 代码优化
-
对于长列表使用虚拟滚动或者object.freeze 冻结数据劫持
-
使用防抖节流减少代码的执行次数
-
细分功能函数,助于 esm tree-sharking
-
避免使用 getComputedStyle 和 css表达式减少回流和重绘
-
路由(组件)懒加载
-
开启组件缓存(keepalive)