-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
复习笔记 #7
Comments
工程化 webpack打包过程
babel编译+webpack打包 副作用:一个函数会或者可能会对函数外部产生影响的行为 loader和plugin区别:转译器和扩展器 |
React hooks
真实的dom的js对象抽象表示,配合不同的渲染工具使跨平台渲染成为可能。通过事物处理机制可以将多次DOM修改的结果一次性更新到页面上,减少页面的渲染次数,提高渲染性能。 每次数据发生变化时,虚拟dom都会缓存一份,变化之时,现在的虚拟dom都会与缓存的虚拟dom进行对比,在vue或react封装了diff算法,通过这个算法来进行比较,渲染时修改改变的变化,原先没有发生改变的通过原先的数据进行渲染。 现代前端框架的一个基本要求就是无需手动操作dom,手动操作dom无法保证程序性能,多人开发可能出现性能较低的代码,另一方面省略手动dom操作可以大大提高开发效率 虚拟dom可以保证性能下限,在不进行手动优化的情况下,提供过得去的性能。 真实dom:html字符串+重建所有dom 虚拟dom另一个好处就是跨平台
diff算法的三个策略,分别基于树,组件、节点
可以通过key来标识元素是新创建的还是移动的,提高节点复用,从而减少不必要的diff和重渲染 react key主要用于追踪列表中的哪些元素被修改、被添加或被删除的辅助标识。 |
网络 tcp和udp最大区别 Http
http1.1:
http2.0
https https中间人攻击发生在哪个阶段? tls握手过程
http缓存
Cache-Control指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发送请求到服务器取数据,其设置的是一个相对时间。 Cache-Control:no-store表示没有缓存 Cache-control:private表示私有缓存,专用于某单个用户的,中间人(中间代理、cdn等缓存)不能缓存此响应
If-none-match、ETags If-none-match、ETags的优先级高于If-Modified-Since 、Last-Modified
|
树 // 多叉树遍历
function layerSum(root) {
function traverse(root) {
if (!root.children) return 0;
let sum = 0;
for (const item of root.children) {
sum += traverse(item) + 1;
}
return sum;
}
return traverse(root) + 1;
}
const res = layerSum({
value: 2,
children: [
{ value: 6, children: [{ value: 1 }] },
{ value: 3, children: [{ value: 2 }, { value: 3 }, { value: 4 }] },
{ value: 5, children: [{ value: 7 }, { value: 8 }] }
]
});
console.log(res); // 虚拟dom转真实dom
const vnode = {
tag: 'DIV',
attrs: { id: 'app' },
children: [{
tag: 'SPAN',
children: [{ tag: 'A', children: [] }]
},
{
tag: 'SPAN',
children: [
{ tag: 'A', children: [] },
{ tag: 'A', children: [] }
]
}
]
}
function render(vnode) {
const setAttr = (node, attrs) => {
if (!attrs) return;
const kv = Object.entries(attrs);
if (kv.length > 0) {
for (let item of kv) {
node.setAttribute([item[0]], item[1]);
}
}
}
const root = document.createElement(vnode.tag);
setAttr(root, vnode.attrs);
function buildTree(root, node) {
if (!node.children.length === 0) return;
for (let i = 0; i < node.children.length; i++) {
const element = document.createElement(node.children[i].tag);
setAttr(element, node.children[i].attrs);
root.appendChild(element);
buildTree(root.children[i], node.children[i]);
}
}
buildTree(root, vnode);
return root;
}
render(vnode) 实现new /**
* 创建一个空对象
* this指向
* 原型链完整
* 返回
*/
function myNew(fn,...args){
var obj=new Object();
fn.prototype = obj.__proto__;
const ret = fn.apply(obj,args)
return typeof ret==='object'?ret:obj;
} 实现call 实现compose const compose = (arr)=>{
if(arr.length===0) return arr[0];
return arr.reduce((pre,curr)=>{
return (...args)=>pre(curr(args))
})
} 实现防抖和节流 //防抖
const debounce = (fn)=>{
const timer= null;
return function(){
if(timer!==null){
clearTimeout(timer);
timer = null;
}
timer = setTimeout(()=>fn.apply(this,arguments),300);
}
}
// 节流
const throttle=(fn)=>{
const timer = null
return function(){
if(timer===null){
setTimeout(()=>{
fn.apply(this,arguments);
clearTimeout(timer);
timer=null;
},300)
}
}
} 实现promise.finally方法 /**
* 不管成功或者失败都调用
* finally不接收参数,但需要链式传递
*/
Promise.prototype._finally = function(fn){
return this.then(
data=>Promise.resolve(fn()).then(()=>data),
err=>Promise.reject(fn()).then(()=>throw err)
);
} 实现promise.all方法 /**
* 记录resolve次数
* 返回结果存入顺序
*/
const _all = (pList) => {
const ans = [];
let count = 0;
return new Promise((resolve, reject) => {
pList.forEach((item, i) => {
Promise.resolve(item).then((res) => {
ans[i] = res;
count++;
if (count === pList.length) resolve(ans);
}).catch(reject)
})
})
} 实现promise.race方法 const _race = (pList)=>{
return new Promise((resolve,reject)=>{
pList.forEach(fn=>{
Promise.resolve(fn).then(resolve,reject);
})
})
} 实现promise.allSettled方法 const _allSettled = (pList) => {
const ans = [];
let count = 0;
const valid = (callback, i, result) => {
count++;
ans[i] = result;
if (count === pList.length) callback(ans);
}
return new Promise((resolve, reject) => {
pList.forEach((item, i) => {
Promise.resolve(item)
.then((res) => valid(resolve, i, res))
.catch((err) => valid(reject, i, err));
})
})
} 请求(promise)并发 const scheduledPromise = async (maxLimit, pList, callback) => {
const ret = [];
const runPool = [];
for (const item of pList) {
const p = Promise.resolve().then(() => callback(item));
ret.push(p);
if (pList.length > maxLimit) {
const e = p.then(() => runPool.splice(runPool.indexOf(e), 1));
runPool.push(e);
if (runPool.length >= maxLimit) {
await Promise.race(runPool);
}
}
}
return Promise.all(ret);
} 请求(promise)重试 const promiseRetry = (fetchData,times)=>{
return new Promise((resolve,reject)=>{
const run = ()=>{
Promise.resolve(fetchData())
.then(resolve).catch(()=>{
--times==0 ? reject('重试次数达到上限') : run();
})
}
run();
})
} 请求(promise)超时 const promiseTimeout = (fetchData, time) => {
const timeout = new Promise((_, reject) => setTimeout(() => reject('超时了'), time));
const pList = [timeout, Promise.resolve(fetchData())];
return Promise.race(pList);
} 数组扁平化 /**
* 数组扁平化
*
* flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]);
*/
const flatter = (arr) => {
if (!arr.length) return;
return arr.reduce((pre, cur) => {
return Array.isArray(cur) ? [...pre, ...flatter(cur)] : [...pre, cur];
}, []);
}
// 迭代方式
const flatter2 = (arr) => {
if (!arr.length) return;
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
} // 深拷贝,忽略function、循环引用、undefined等
const deepClone = (obj) => {
if (typeof obj !== 'object' || obj === null) return obj;
const ans = Array.isArray(obj) ? [] : {};
for (let item in obj) {
if (typeof item !== 'object' || item === null) {
ans[item] = obj[item];
} else {
ans[item] = deepClone(obj[item]);
}
}
return ans;
}
const obj = { a: { b: { c: { d: 1 } } } }
const deepObj = deepClone(obj);
console.log(obj === deepObj, deepObj) |
js基础 var b = 10;
(function b(){
b = 20;
console.log(b);
})(); for (var i = 0; i< 10; i++){
setTimeout((i) => {
console.log(i);
}, 1000,i)
} 异步 Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
}; async-await:特性、串行并行、错误处理 async函数返回一个promise,如果返回值不是promise,则会用promise包装一下返回 async function fn(){return 1}
// 等价于
async function fn(){return Promise.resolve(1)} await表达式会暂停整个async函数的执行进程并让出其控制权,只有当异步返回成功或者失败才会恢复,如果在forEach里则不会被阻塞 async function fn(){await 1}
// 等价于
async function fn(){return Promise.resolve(1).then(()=>undefined)}
async function foo() {
const p1 = new Promise((resolve) => setTimeout(() => resolve("1"), 1000));
const p2 = new Promise((_, reject) => setTimeout(() => reject("2"), 500));
const results = [await p1, await p2]; // 不推荐使用这种方式,请使用 Promise.all 或者 Promise.allSettled
}
foo().catch(() => { }); // 捕捉所有的错误...
//
const next = (msg) => Promise.resolve().then(() => console.log(msg))
async function bar() {
// 串行
// await next(1)
// await next(2)
// 并行
const a = next(1);
const b = next(2);
await a;
await b;
}
// async 重写promise链
// 前
// async function getData(url) {
// return fetchData(url)
// .catch((e) => fallback(e))
// .then((v) => handleData(v))
// }
// 后
// async function getData(url) {
// let v;
// try {
// v = await fetchData(url);
// } catch (e) {
// v = fallback(e)
// }
// return handleData(v);
// } 输出代码顺序 async function async1() {
console.log('1');
await async2();
console.log('2');
}
async function async2() {
console.log('3');
}
console.log('4');
setTimeout(function() {
console.log('5');
}, 0);
async1();
new Promise(function(resolve) {
console.log('6');
resolve();
}).then(function() {
console.log('7');
});
console.log('8'); setTimeout和setInterval 为什么要用 settimeout 模拟实现 setinterval?setinterval 的缺陷是什么? |
用户时长统计上报 虚拟滚动
|
css BFC flex:1 |
vue
|
性能优化
页面加载过程
浏览器解析,查询缓存,dns查询,建立连接,服务器处理请求,服务器发送响应,客户端收到页面,解析html,构建渲染树,开始显示内容(白屏时间),首屏内容加载完成(首屏时间),用户可交互(DomContentLoaded),加载完成(load)
浏览器进程:主进程,GPU进程,渲染进程,网络进程
渲染进程包括js引擎线程、事件触发线程、定时器触发线程、异步http请求线程、GUI渲染线程
js引擎线程主要负责解析js并运行代码,一直等待任务队列中的任务的到来,然后加以处理。当js引擎执行时,GUI渲染线程就会被挂起,GUI更新会被保存在一个队列中等到js引擎空闲时立即被执行
GUI渲染线程主要负责渲染浏览器界面,解析html、css构建DOM树和RenderObject树,布局,绘制。需要重绘或者由于某种操作引起回流时该线程就会执行。
GUI渲染线程和js引擎线程互斥,为了防止渲染结果的不可预期
事件触发线程:用来控制事件循环,当事件满足触发条件时,将事件放入到js引擎所在的执行队列中
定时器触发线程:setTimeout和setInterval所在的线程,定时任务并不是由js引擎计时的,是由定时触发线程计时的,计时完毕后通知事件触发线程
异步http请求线程:浏览器有一个单独的线程用于处理ajax请求,当请求完成,若有回调函数,通知事件触发线程。
为什么js是单线程的?
多线程的复杂性,多线程操作需要加锁,编码复杂性升高
如果同时操作dom,在多线程不加锁的情况下,最终会导致DOM渲染结果不可预期
为什么GUI渲染线程和js引擎线程互斥?
由于js是可以操作dom的,如果同时修改元素属性并同时渲染界面,那么渲染线程前后获得的元素就可能不一致。因此为了防止不可预期的结果,浏览器设定GUI渲染线程和js引擎线程为互斥关系
脚本加载:动态加载、异步加载
白屏时间 = 页面开始展示时间点 - 开始请求的时间点
开始请求的时间点,performance Timing.navigation Start
页面开始展示的时间点,开始解析body的时间点就是页面开始展示的时间,可以通过在head标签尾部插入script标签来统计时间节点,或者通过performance Timing.domLoading Start(直接忽略head解析时间)
首屏时间 = 首屏内容渲染结束时间点 - 开始请求时间点
结束时间点可以通过以下几种方式获取
可交互时间 = 用户可以正常进行交互的时间点 - 开始请求的时间点
performance Timing.domInactive,代表dom结构结束解析的时间点,即docuemnt.readyState变为inactive
性能检测工具
chrome performance、lighthouse
火焰图:Main记录了渲染进程中主线程的执行记录
Summary:各指标时间统计表
bottom-up:事件时长排序列表
性能问题表现行为
首屏白屏,打包文件过大,首屏加载时间过长,可能原因js阻塞页面渲染
交互卡顿,js执行时间过长,占用主线程,阻塞任务执行,不能在16.7ms内完成任务而掉帧
相关知识点、
webpack工作过程
初始化,构建,生成
读取配置(webpack.config.js或者shell)参数创建一个编译器对象(compiler),初始化编译环境(注入内置插件、初始化ruleset集合、加载配置插件),compiler执行run方法,找出entry中所有入口,调用compilation.addEntry将入口文件转换为dependence对象
编译模块:根据entry对应的dependence创建module对象,调用loader将模块转译为标准的js内容,调用js解释器将内容转换为AST对象,从中找出该模块依赖的模块,再递归本步骤直到左右入口依赖文件都经过了本步骤的处理,处理完成之后得到了每个模块被翻译后的内容以及他们之间的依赖关系图
根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk,再把每个chunk转换成一个单独的文件加入到输出列表,这是最后修改文件内容的机会。在确定好输出内容之后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
babel编译+webpack打包
常用plugin有
terser-webpack-plugin(压缩代码)
html-webpack-plugin(创建html文件到输出目录,将webpack打包后的chunk自动引入到这个html中)
split-chunk-plugin:用来提取第三方库(react等)和公共代码(js、css),用于多页面应用程序,生成公共chunk,避免重复引用
常用loader:css-loader、style-loader
tree-shaking:是什么?有什么用?应用场景?有什么坑?怎么解决的?webpacke的tree-shaking和rollup的tree-shaking有何不同?
通过静态分析之后,给没有被使用的代码打上标识,然后通过压缩工具删除
副作用:一个函数会或者可能会对函数外部产生影响的行为
esm/import()
babel
压缩/terser,unglyfy
公共包复用,split-chunk-plugin
2. 网络
http https
http缓存
状态码
3. js异步
4. 页面重绘重排
5. 懒加载
less
变量和函数
嵌套和作用域
css隔离
运行时方案:BEM,命名规范, .block__element--modifier
编译时方案:
属性选择器+唯一的ID属性,vue的scoped,vue-loader支持
css-modules,css-loader支持
阶段划分
优化点
native loading与h5骨架屏
串行接口请求移动到服务端处理
接口拆分,页面懒渲染,非实时更新数据缓存
主接口预请求(prefetch)、请求失败重试
webpack打包体积优化方法
tree-shaking去除死代码
默认值
新的chunk可以被共享,或者模块来自于node_modules
新的chunk体积大于20k
按需加载chunks时,并行请求的最大数量<=30
加载初始化页面时,并发请求最大数量<=30
当满足最后两个条件时,最好使用较大的chunks
按需加载和路由懒加载
tree-shaking,用split-chunk-plugin(optimization的splitChunks)进行切割chunks,副作用和压缩工具terser协同处理死代码
渲染速度优化
逻辑代码优化
图片等静态资源优化
动画优化:对手机进行机型的等级划分为高端机、中端机和低端机,通过window.navigation.user
交互优化
构建速度优化
The text was updated successfully, but these errors were encountered: