电子商务与网站建设论文,免费网页制作代码,网站论坛做斑竹,中国品牌加盟网官网2023-前端面试知识点总结面试题总览javascript相关一、js 代码的常用优化手段二、es5 构造函数与继承三、new 一个对象的过程四、防抖与节流五、promise/A规范概述六、实现一个柯里函数封装七、事件队列八、微任务是哪些宏任务是哪些九、执行js代码时#xff0c;同步任务、微任…
2023-前端面试知识点总结面试题总览javascript相关一、js 代码的常用优化手段二、es5 构造函数与继承三、new 一个对象的过程四、防抖与节流五、promise/A规范概述六、实现一个柯里函数封装七、事件队列八、微任务是哪些宏任务是哪些九、执行js代码时同步任务、微任务、宏任务、dom渲染的执行顺序十、js是单线程怎么实现异步任务十一、笔试-事件队列十二、笔试-文件上传通过头文件判断文件类型css相关一、css的常用优化手段二、link与import的区别三、响应式页面设计四、css实现三角形与扇形typescript相关一、typescript 类型对象key字符串值number的表示方式二、interface与type的区别三、四、ts中有哪些数据类型五、ts中定义function类型的方式http相关一、http基础超文本传输协议二、http1.0与http1.1的区别三、websocket三次握手(tcp三次握手)四、tcp四次挥手五、浏览器缓存http缓存-- 主要缓存静态资源六、浏览器本地存储vue相关一、vue项目如何进行部署、是否遇到过刷新404问题二、vue的template到生成html渲染的过程三、vue模板编译过程四、diff算法五、vue3中设置全局变量六、vuex数据持久化七、vue中\$router与\$route的区别八、vue3的响应式原理 组件v-model的实现九、vue2与vue3的区别总结十、组件传参方式十一、vue组件中定义一个变量a 1在页面显示使用for循环去改变100次变量的值页面会渲染多少次react相关一、react样式实现作用域二、组件传参方式浏览器相关一、为什么基本数据类型存放在栈中复杂数据类型存放在堆中二、js垃圾回收机制三、重绘与回流重排四、硬件加速gpu加速五、执行js同步代码时遇到一个dom的样式修改浏览器会怎么操作六、浏览器多线程七、浏览器组成八、浏览器地址栏输入地址获取数据的过程九、浏览器地址栏输入地址获取数据后将html渲染到页面的过程数据转视图十、v8引擎执行js代码十一、SSR服务端渲染、CSR客户端渲染与SSG预渲染webpack等构建工具一、Vite整个热更新过程二、vite了解三、webpack loader的作用四、webpack深入优化、使用前端单元测试写在最前面坐标成都3月16号上家公司离职目前已经入职新公司。总的来说现阶段前端岗位的招聘比不上以往金三银四那么活跃零零碎碎的面试机会加上行内人的内卷行为我***。 但是积极面试和准备面试的过程中可以捡起或是加深印象对于以往工作开发中遗忘的前端基础。下面是我根据个人面试过程中整理的一些被问及的前端知识点记录分享祝各位早日入坑 基本是自己键盘敲出来的文字内容可能存在错别字欢迎指出错误知识点。 面试题总览
vue2与vue3的区别
重绘重排回流
防抖节流
双向绑定原理
react样式实现作用域
组件传参方式
websocket三次握手
http三次握手、浏览器输入一个url到页面显示的过程描述
webpack loader作用
webpack深入优化、使用
new 一个对象的过程
js的优化、css的优化、项目界面优化
前端单元模块测试经历
es6与es7 es8
es6的新增特性
this指向分类深入了解
vue2与vue3的响应式原理es5的defineProperty与es6的proxy的区别对比
vuex实现数据持久的方法
文件上传组件请求头判断文件类型
垃圾回收机制async 中使用awiat 与.then的区别-- 阻塞
css 动画样式实现平移 旋转 缩放等
css 阴影 参数讲解
webpack loader使用 基本配置
vite与webpack
vue2 vue3的区别
typescript 类型对象key字符串值number的表示方式
interface type的区别
typescript 类型有哪些、泛型
map数据格式
函数闭包、内存溢出原因
垃圾回收机制
var let const 区别与实际应用
js作用域
promise
vue组件父子传参
vite冷启动
commonjs 与 esmodule浏览器http1.0 的缓存
promise原理
执行上下文
link与import css的区别
js浮点数丢失精度原因与解决方案flex布局属性
css实现三角形扇形
线程与进程
http缓存
自适应布局
cdn cdn解决更新缓存问题
递归函数的作用域是否一样 不一样 因为局部执行上下文是在执行function时才会被创建每次执行方法时都会创建一个局部执行上下文执行fun时有一个入栈操作执行完成后有一个出栈操作在栈底部时全局执行上下文这也是为什么基础数据存在栈上因为栈需要维护js运行时的执行上下文切换
递归栈溢出解决方案---尾递归
尾调用优化原理
微任务是哪些宏任务是哪些 事件队列
设计模式观察者模式、发布订阅模式
target currenttarget区别
点击事件发生的事件过程捕获冒泡
链表
vue的源码响应式、模板渲染
js是单线程怎么执行异步任务的
vue组件中定义一个变量a 1在页面显示使用for循环去改变100次变量的值页面会渲染多少次
执行js同步代码时遇到一个dom的样式修改浏览器会怎么操作直接渲染界面还是
vue定义全局组件的方法javascript相关
一、js 代码的常用优化手段
1、使用事件委托事件代理减少事件注册次数和内存占用提高性能。 2、避免使用全局变量在 JavaScript 中访问全局变量会非常慢因为如果在一个作用域中找不到变量就会一直向上查找到全局范围。 3、减少 DOM 操作DOM 操作非常消耗 CPU 和内存所以最好只有必要时才操作 DOM。 4、对象缓存如果你需要重复创建同一个对象那么最好将其缓存下来避免频繁创建销毁对象带来的性能损耗。 5、使用节流和防抖技术节流和防抖可以有效降低函数执行的频率。 6、减小作用域链长度作用域链越长变量的查找和访问时间也就越长。 7、避免重复计算例如一些常量、计算结果等应该缓存起来避免反复计算。 8、function 尾调用优化例如尾递归 关于尾递归 1、只有不再用到外层函数的内部变量不形成闭包内层函数的调用帧才会取代外层函数的调用帧 2、在函数内部尾调用是最后一条语句 3、尾调用的结果作为函数值返回 满足以上三点要求就会被js引擎自动优化否则无法识别尾调用优化
尾调用一直存在尾调用优化是在支持es6的js引擎如v8里添加的尾递归优化只在严格模式下生效。也可以在 es6 module 中使用因为 es6 module 默认是遵循严格模式的
尾递归优化-解决栈溢出问题
// 非尾调用 (计算n的阶乘最多需要保存n个调用记录复杂度O(n))
function factorial(n) {if (n 1) return 1;return n * factorial(n - 1);
}
// 尾调用 (只保留一个调用记录复杂度O1)
function factorial(n, total) {if (n 1) return total;return factorial(n - 1, n * total);
}二、es5 构造函数与继承
// 构造函数继承
// 1.能给父类传参 2.每个新实例引入的构造函数属性是私有的 3.不能继承父类原型上的东西即不能共用一些公共方法和属性
function A(name) {this.name name;this.age 1;this.say function () {console.log(this.name, this.age);};
}
A.prototype.sayOk function () {console.log(this.name, ok);
};
function B(name) {A.call(this, name); // *****this.age 2;// this.say function () {// console.log(this.name, this.age)// }
}const b new B(nb);
console.log(b.say());
console.log(b.sayOk()); // 不能继承原型上的属性方法// 原型继承
// 1.能够继承父类原型上的方法和属性 2.父类不能入参 3.所有子类实例会共享父类的引用类属性
function AA(name) {this.name name || AA;this.age 1;this.say function () {console.log(this.name, this.age);};
}
AA.prototype.sayOk function () {console.log(this.name, ok);
};function BB() {this.age 2;
}
BB.prototype new AA(); //**** child的原型指向parent的实例
BB.prototype.constructor BB; // 纠正child原型上的构造函数
const bb new BB(nbb);
bb.say();// 组合继承原型加借用构造函数
// 1.能继承父类的实例属性与方法以及父类原型上的属性与方法 2.父类能入参 3.子类实例引入的构造函数属性是私有的 4.调用两次父类构造函数
function Parent(name) {this.name name;this.arr [1];this.age 1;this.getAge function () {console.log(this.age);};
}
Parent.prototype.sayOk function () {console.log(this.name, say ok);
};function Child(name) {Parent.call(this, name);this.age 2;
}Child.prototype new Parent();
Child.prototype.constructor Child;Child.prototype.sayWhy function () {console.log(this.name say why);
};let c1 new Child(c1);
console.log(c1.__proto__.constructor Child);// 原型式继承
// 在已有一个对象的基础上再创建一个新对象
let obj {name: 已存在的对象,age: 100岁,friend: [1, 2, 3],
};
// 本质是一次浅拷贝
//
function object(o) {// 创建一个临时的构造函数function F() {}F.prototype o;return new F();
}
// 与原型继承相似 子类实力会共享父类的引用类属性 不需要单独创建构造函数
const o1 object(obj);
o1.friend.push(6);
o1.getNum function () {console.log(this.friend);
};
const o2 object(obj);
o2.getNum function () {console.log(this.friend);
};
o1.getNum();
o2.getNum();// 寄生式继承
// 在原型式的继承上加一个function包裹扩展新对象的效果和方法
// 不需要单独创建函数 通过该方式给对象扩展 难以复用。
function createAnother(o) {const _o object(o);_o.vue ok;_o.useVue function () {console.log(vue is this.vue);};return _o;
}const o3 createAnother(obj);
o3.useVue();// 寄生组合式继承
// 借用父类构造函数、把子类的原型指向通过寄生式继承父类原型的对象
function object(o) {// 创建一个临时的构造函数function F() {}F.prototype o;return new F();
}
function createAnother(o) {const _o object(o);return _o;
}
function People(name) {this.name name;this.age 100;this.eat function () {console.log(this.name eat....);};
}
People.prototype.bloodColoe red;
People.prototype.speek function (s) {console.log(this.name 说: s);
};function Man(name) {People.call(this, name);this.sex 男;
}Man.prototype createAnother(People.prototype);
// Man.prototype Object.create(People.prototype)
Man.prototype.constructor Man;// new 的过程// 1.创建一个空对象 2.将空对象的__proto__属性指向构造函数的原型对象 3.将构造函数中的this指向创建的空对象call apply并执行构造函数 4.如果构造函数没有返回对象不包括null则返回objthis
function T() {this.a 1;this.b 2;return {};
}
const t new T();
console.log(t);三、new 一个对象的过程
一、 1、创建一个空的简单 JavaScript 对象即 {} 2、为步骤 1 新创建的对象添加属性 proto将该属性链接至构造函数的原型对象 3、将步骤 1 新创建的对象作为 this 的上下文 4、如果该函数没有返回对象则返回 this。 二、 将目标对象Target的类型设置为函数构造函数 创建一个新的空对象将其绑定到 Target.prototype 上。 执行 Target 函数并将 this 设置为新创建的对象。也就是说这个函数可以在调用过程中使用 this 引用新对象。 如果返回值不为空则使用该值作为构造函数的结果。否则返回新创建的对象
四、防抖与节流
防抖和节流是处理 JavaScript 中频繁触发的函数的两种方法以优化页面性能。
防抖debounce 当一个事件被频繁触发时只有等到一定的时间间隔内没有再次触发该事件才会真正去执行该事件 实现原理 使用定时器和闭包来进行实现。每次触发事件都会先清除定时器只有事件停止触发的时候才会执行定时器中的回调函数
function debounce(fn, delay) {let timer null;return function () {if (timer) clearTimeout(timer);const self this;const args arguments;timer setTimeout(function () {fn.apply(self, args);}, delay);};
}节流throttle: 在一定时间间隔内只会执行一次函数相当于设置执行的最小时间间隔。 实现原理 记录上次执行的时间戳 lastTime在接下来的某个时刻 timestamps 内如果客户端重复触发事件则只返回上次调用的结果只有当时间大于 timestamps 时才重新计算并返回新的值。
// 1
function throttle(fn, delay) {let lastTime 0;return function () {const currentTime new Date();const self this;const args arguments;if (currentTime - lastTime delay) {fn.apply(self, args);lastTime currentTime;}};
}// 2
function throttle(fn, delay) {let timer null;return function () {const self this;const args arguments;if (!timer) {timer setTimeout(() {fn.apply(self, args);clearTimeout(timer);}, delay);}};
}五、promise/A规范概述
promise 有三个状态pendingfulfilledor rejected「规范 Promise/A 2.1」 new promise 时 需要传递一个 executor()执行器执行器立即执行 executor 接受两个参数分别是 resolve 和 reject promise 的默认状态是 pending promise 有一个 value 保存成功状态的值可以是 undefined/thenable/promise「规范 Promise/A 1.3」 promise 有一个 reason 保存失败状态的值「规范 Promise/A 1.5」 promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled状态一旦确认就不会再改变 promise 必须有一个 then 方法then 接收两个参数分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected「规范 Promise/A 2.2」 如果调用 then 时promise 已经成功则执行 onFulfilled参数是 promise 的 value 如果调用 then 时promise 已经失败那么执行 onRejected, 参数是 promise 的 reason 如果 then 中抛出了异常那么就会把这个异常作为参数传递给下一个 then 的失败的回调 onRejected then 的参数 onFulfilled 和 onRejected 可以缺省如果 onFulfilled 或者 onRejected 不是函数将其忽略且依旧可以在下面的 then 中获取到之前返回的值「规范 Promise/A 2.2.1、2.2.1.1、2.2.1.2」 promise 可以 then 多次每次执行完 promise.then 方法后返回的都是一个“新的 promise「规范 Promise/A 2.2.7」 如果 then 的返回值 x 是一个普通值那么就会把这个结果作为参数传递给下一个 then 的成功的回调中 如果 then 中抛出了异常那么就会把这个异常作为参数传递给下一个 then 的失败的回调中「规范 Promise/A 2.2.7.2」 如果 then 的返回值 x 是一个 promise那么会等这个 promise 执行完promise 如果成功就走下一个 then 的成功如果失败就走下一个 then 的失败如果抛出异常就走下一个 then 的失败「规范 Promise/A 2.2.7.3、2.2.7.4」 如果 then 的返回值 x 和 promise 是同一个引用对象造成循环引用则抛出异常把异常传递给下一个 then 的失败的回调中「规范 Promise/A 2.3.1」 如果 then 的返回值 x 是一个 promise且 x 同时调用 resolve 函数和 reject 函数则第一次调用优先其他所有调用被忽略「规范 Promise/A 2.3.3.3.3」
promise.then()里面如果返回一个非 promise 的值会在下一个.then()中获取该值如果返回的是一个 promise会根据这个 promise 的解析值来判断如果是 resolve 则会在下一个.then()中获取 resolve 的值如果是一个 reject 则会在下一个 catch 中获取值如果没有执行任何操作则不会触发后续的.then 或.catch 操作。promise.resolve() 返回一个成功解析的 promise 对象promise.reject()返回一个错误解析的 promise 对象
六、实现一个柯里函数封装
函数柯里化其实就是把多次调用的变量保存在闭包中每次调用都查看一下变量数和原函数的形参数量是否相等。不相等就继续递归。直到相等为止就处理了
* function的length属性表示该方法的形参个数
function curry(fn) {return function curryFn() {// 1. 一次性将所有参数传完// argumentsif (arguments.length fn.length) {// 2. 没有一次性传完var _arg Array.from(arguments);return function () {// 当这个匿名函数被调用时// 看这一传递进来的参数 上一次的参数 fn.lengthreturn curryFn(...Array.from(arguments).concat(_arg));};}return fn(...arguments);};
}// es6 rest参数 替代arguments类数组获取剩余参数并且本身就是数组类型。箭头函数替代函数声明式或函数表达式、匿名函数等
const _curry (fn) {const myCurry (...args) fn.length args.length ?fn(...args) :(...arg) myCurry(...args, ...arg)return myCurry
}
const _myCurry _curry(add)七、事件队列
1.执行全局Script同步代码形成一个执行栈
2.在执行代码时当遇到如上 异步任务时便会按上文所描述的将宏任务回调加入宏任务队列微任务回调加入微任务队列
3.然而回调函数放入任务队列后也不是立即执行会等待执行栈中的同步任务全部执行完清空了栈后引擎才能会去任务队列检查是否有任务如果有那便会将这些任务加入执行栈然后执行
4.执行栈清空后会先去检查微任务队列是否有任务逐一将其任务加入执行栈中执行期间如果又产生了微任务那继续将其加入到微任务队列末尾并在本周期内执行完,直到微任务队列的任务全部 清空执行栈也清空后再去检查宏任务队列是否有任务取到队列队头的任务放入到执行栈中执行其他可能又会产生微任务那当本次执行栈中的任务结果清空后又会去检查微任务队列…
5.引擎会循环执行如上步骤这就是Event Loop
八、微任务是哪些宏任务是哪些
微任务postMessage、MutationObserve、promise.then、process.nextTick 红任务setTimeout、setInterval、setImmediate、I/O
九、执行js代码时同步任务、微任务、宏任务、dom渲染的执行顺序
1、同步任务 2、微任务 3、dom渲染 4、宏任务
十、js是单线程怎么实现异步任务
javascript虽然是单线程但是js运行的依托浏览器是多线程提供了定时触发器线程、事件触发线程等来实现js的异步逻辑
十一、笔试-事件队列
// 题一
const p1 new Promise((resolve, reject) {console.log(p1)setTimeout(() {resolve(success p1)}, 0)
})
const p2 p1.then(() {console.log(p1-p2)return 818
})
const p3 p2.then(res {console.log(p3, res)// return Promise.resolve(p3 Promise)return Promise.resolve(new Promise((resolve, reject) {resolve(test)}))
})
const p4 p3.then(res {console.log(p4:, res)
})
setTimeout(() {console.log(setTimeout 10)
}, 10)
setTimeout(() {console.log(setTimeout 0)
}, 0)
console.log(just test)
/*
p1
just test
p1-p2
p3 818
p4: p3 Promise
setTimeout 0
setTimeout 10*/// 题二
const myPromise Promise.resolve(42);
myPromise.then(res {console.log(res)// return Promise.resolve(12).then(res {// return Promise.resolve(what)// })// return new Promise((res, rej) {})return Promise.reject(what)
}).then(res {console.log(res)
}).then(res {console.log(1)
}).then(res {console.log(2)
}).then(res {console.log(3)
}).then(res {throw new Error(hhh)console.log(4)
}).then(res {console.log(5)
}, err {console.log(errr 5)
}).catch(err {console.log(errrrr)
})
十二、笔试-文件上传通过头文件判断文件类型
通过input标签typefile实现上传文件功能判断文件类型
const allowedType {FFD8FFE0: jpg,89504E47: png,47494638: gif,52494646: webp
}
// 通过头文件 判断上传文件格式
function fileType(file) {const fileReader new FileReader()fileReader.readAsArrayBuffer(file)return new Promise((resolve, reject) {fileReader.onload (e) {try {let uint8Array new Uint8Array(e.target.result).slice(0, 4)// let key [...uint8Array].map(s s.toString(16).toUpperCase().padStart(2, 0)).join()let key Array.prototype.map.call(uint8Array, s s.toString(16).toUpperCase().padStart(2, 0)).join()console.log(key, allowedType[key])resolve(typeof allowedType[key] string)} catch (error) {reject(error)}}})
}function clickBtn () {const fileInput document.querySelector(#file)console.log(fileInput.files)fileType(fileInput.files[0]).then(res {console.log(res)})
}
css相关
一、css的常用优化手段
避免使用 import: 原因 import引用外部css会增加页面的请求次数和 css 文件的数量从而导致页面加载速度变慢。 解决方法: 使用 link 标签在 head 中引入 CSS。 压缩 CSS 文件: 原因删除不必要的代码、注释、空格等非必要字符可以减少文件大小提高文件下载速度。 解决方法: 使用 CSS 压缩工具例如UglifyCSS、CSSNano 等。 减少使用 Float 原因 float 的元素比较容易导致根元素高度塌陷从而影响其他内容的排布直接影响页面性能。 解决方法: 使用 Flexbox 或 Grid Layout 替代 Float 布局。 使用 CSS Sprites 原因 减少了 HTTP 请求使网页加载更快。 解决方法: 将几个小图片合并成为一张大图并使用 background-position 属性来显示所需的图像区域
二、link与import的区别
link是 HTML 标签而import是 CSS 提供的一种方式 link在页面载入时同时加载而import要等价CSS文件完全下载完才会加载 link支持使用 JS 操作 DOM 动态加载和改变样式而import不支持
三、响应式页面设计
原生的html元素在没有编写任何css时本身就是响应式的。响应式主要针对固定宽度或者影响布局的元素进行调整
首先需要启用meta标签 name“viewport” content属性值设置为widthdevice-width,initial-scale1.0。因为在移动端页面默认是对pc端页面进行缩放来展示内容的用户体验不友好需要放大查看内容设置device-width后网页的可视区域就会取移动设备屏幕实际的宽度设置initial-scale1.0之后就会使用正常的缩放比例即1:1不缩放
一、容器宽度的响应式 1.使用百分比宽度使其根据屏幕可视区域自动调整宽度尺寸 2.如果必须使用固定的宽度可以结合media媒体查询来查询可视区域的宽度然后根据不同的可视区域宽度设置容器宽度
二、布局的响应式横向 例如使用了flex布局 可以使用flex-wrap与flex属性来让flex子元素保持最小宽度并自动折行
如果使用的是grid布局默认是竖向排列的没有问题但是如果是多列布局设置了grid-template-columns: 1fr 1fr 1fr 并且每一列都是浮动宽度也会出现像flex布局那样的情况 解决方式1 grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)) 当容器最后的宽度不足以容纳列的最小宽度时折行显示 解决方式2 使用media根据可视宽度手动控制列数
三、图片的响应式 1.pc端使用百分比宽度设置但是在移动端时任然会有显示问题。 移动端时解决方案 1.结合使用img元素的属性srcset和sizes实现响应式sizes时通过media 查询可视区域宽度当满足条件时通过第二个值来指定图片的宽度srcset根据第二个参数指定的宽度来使用第一个参数指定的图片。浏览器会自动根据像素的密度和屏幕的宽度来加载图片
img srcxx.png srcsetxxx.png 1240w,xxx1.png 600w,xxx3.png 300wsizes(max-width: 400px) 300px, (max-width: 900px) 600px, 1240px /2.使用picture元素结合source元素指定图片的url和media query在不同可视宽度下加载不同的图片使用img元素加载保底默认的图片
picturesource media(max-width: 400px) srcsetxxxx.png /source media(max-width: 900px) srcsetxxxx1.png /source media(max-width: 1240px) srcsetxxxx2.png /img srcxxx.png /
/picture四、字体的响应式 1.使用media 2.使用浮动单位rem em vwvw可能会存在屏幕宽度太小字体也会过小显示可以结合calc2rem 2vw的方式 3.media与浮动单位结合
四、css实现三角形与扇形
1、使用border加内容h、w为0来实现三角形 2、使用伪类元素实现 3、使用clip或clip-path裁剪属性来实现 具体代码如下
div classsector/div
div classfan/divdiv classtriangle-up/div
!-- div classtriangle-down/divdiv classtriangle-left/divdiv classtriangle-right/div --
div classclip/div.triangle-up {width: 0;height: 0;border-left: 10px solid transparent;border-right: 10px solid transparent;border-bottom: 10px solid red;/* border-top: 10px solid transparent; */
}.triangle-down {width: 0;height: 0;border-left: 10px solid transparent;border-right: 10px solid transparent;border-top: 10px solid green;
}.triangle-left {width: 0;height: 0;border-top: 10px solid transparent;border-right: 10px solid brown;border-bottom: 10px solid transparent;
}.triangle-right {width: 0;height: 0;border-top: 10px solid transparent;border-left: 10px solid yellow;border-bottom: 10px solid transparent;
}.sector {width: 100px;height: 100px;background-color: red;border-radius: 50%;clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}.fan {width: 0;height: 0;border-radius: 100% 100% 0 0;border-style: solid;border-width: 100px 60px 0 60px;border-color: #555555 transparent transparent transparent;position: relative;
}.fan::before {content: ;position: absolute;left: -20px;top: 50%;width: 20px;height: 20px;background-color: #555555;border-radius: 50%;transform: translate(-50%, -50%);
}.fan::after {content: ;position: absolute;right: -20px;top: 50%;width: 20px;height: 20px;background-color: #555555;border-radius: 50%;transform: translate(50%, -50%);
}
.clip{width: 300px;height: 300px;background-color: red;position: absolute;clip: rect(0px, 120px, 160px, 20px);transform: rotate(45deg);
}typescript相关
一、typescript 类型对象key字符串值number的表示方式
interface {[key:string]: number
}二、interface与type的区别
在 TypeScript 中interface 和 type 都用来描述对象类型或其他类型。它们之间的主要区别是: interface 可以用于声明合并即两个同名 interface 会被自动合并为一个类型并集 type 可以用于声明联合类型与类型别名 当需要定义一个联合类型时应该使用 type 语句调整层次结构。而对于任何需要扩展、合并或实现某种类规范的类型定义都应该使用 interface
三、
在 TypeScript 中 表示交叉类型Intersection Types可以将多个类型合并为一个类型。
例如有两个接口User 和 Account可以使用 将它们合并成一个“全新”的接口
interface User {name: string;
}interface Account {id: number;
}type UserInfo User Account;const user: UserInfo {name: Tom,id: 123456,
}
四、ts中有哪些数据类型
Any: 表示任意类型即可以是任何类型的值。 Number: 表示数字类型包括整数和浮点数支持二进制、八进制和十六进制字面量。 String: 表示字符串类型包括单引号和双引号两种形式以及模板字符串使用反引号 。 Boolean: 表示布尔类型只有两个值true 和 false。 Array: 表示数组类型有两种定义方式number[] 表示只能存储数字类型的数组Arraynumber 表示同样只能存储数字类型的数组。 Tuple: 表示元组类型表示已知元素数量和每个元素类型的数组例如 [string, number] 表示一个由字符串和数字组成的元素序列。 Enum: 表示枚举类型用于为一组数值赋予友好的名字例如下方例子中代表星期几的变量名就赋予了相应的英文名称。 Void: 表示undefined 或 null 的类型。通常并不建议直接将其与声明变量使用但却是一些函数的返回值类型。 Null 和 Undefined: 分别表示空值和未定义的值这两种类型是所有类型的子类型 Object: 表示非原始类型即除 number、string、boolean、symbol、null 和 undefined 这六种类型之外的类型。使用 object 定义相当于其它语言中使用 class 的实例对象。 值得注意的是在 TypeScript 中变量的类型定义可以使用小写字符开头也可以使用大写字符开头。小写字符开头通常被称作 Primitive Type 基本类型而大写字符开头通常被称作 Object Type对象类型。
具体如下 基础类型number, boolean, string, null, undefined, symbol, void 对象类型Object, Function, Array, Tuple, Enum, Class, Interface, Type Alias 以上是 TypeScript 中的数据类型发现有一些类型是 JavaScript 中没有的比如 Tuple元组类型其他的基础类型都差不多还有一些特殊类型需要在后续学习过程中慢慢掌握。
五、ts中定义function类型的方式
函数声明使用function关键字来声明函数
function add(x: number, y: number): number {return x y;
}函数表达式将函数赋值给变量可以简化明确指出参数类型的定义。
const addFunc (x: number, y: number): number {return x y
}接口定义函数类型定义一个接口来描述此函数的签名。
interface Add {(x: number, y: number): number
}
const add: Add (x, y) x yType别名定义函数类型使用type别名为函数类型命名。
type Add (x: number, y: number) number
const add: Add (x, y) x yhttp相关
一、http基础超文本传输协议
http是一种 可扩展的传输协议、 应用层的协议、 client-server协议 HTTP 本质是无状态的使用 Cookie 可以创建有状态的会话。 可扩展主要体现在http1.0提出的header标头配置只要客户端与服务端标头语义一致例如cookie、缓存之类的
二、http1.0与http1.1的区别
一、tcp连接区别 http1.0 默认短连接 客户端发起一次请求时会建立tcp连接在服务端响应此次请求后就关闭tcp连接。每次请求都会建立一次tcp连接 1.0可以通过设置标头Connection:Keep-Alive来打开保持tcp连接持久化功能可以配合使用Keep-Alive:max5,timeout120表示此次连接最多为5个请求保持持久状态或者持久状态保持2分钟 http1.1 默认持久连接 1.1逐渐停止了对keep-alive连接的支持使用持久连接的改进版来取代它客户端通过配置标头Connection:close来关闭tcp连接服务端在报文正确等条件下才会保持持久连接否则服务端主动断开连接 1.1还有个管道化连接连接 二、缓存方式区别 http1.0使用expires绝对时间 http1.1新增cache-control头文件来设置强缓存max-age相对时间
三、websocket三次握手(tcp三次握手)
SYNACK报文。其中ACK报文是用来应答的SYN报文是用来同步的 本质是为了确认客户端与服务端双方的发生与接收能力是否ok
第一次握手客户端发送SYN包告诉服务器端请求建立连接。 第二次握手服务器端收到客户端的SYN包之后服务端确认客户端的发送能力ok发回一个SYN-ACK包以示传达确认信息表示收到了客户端的请求并同意建立连接。 第三次握手客户端收到服务器端的SYN-ACK包之后客户端确认服务端的发送与接收能力ok向服务器端发送一个确认应答包ACK表示确认服务器端已收到自己的请求并同意建立连接服务端确认客户端的接收能力ok此时TCP连接成功建立。 在三次握手完成之后客户端和服务器才能互相发送数据。当连接建立完毕之后如果需要终止连接则需要进行四次挥手。
四、tcp四次挥手
简述1、客户端发起关闭连接请求2、服务端收到关闭请求并返回客户端服务端已经收到关闭连接的请求 3、但是此时服务端可能还有未完成的报文传输所以会等服务端处理完后向客户端发起FIN报文 4、客户端收到服务端的FIN报文后发生一个ACK应答给服务端
五、浏览器缓存http缓存-- 主要缓存静态资源
浏览器缓存的优点减少服务器负担提高网站性能、加快客户端网页的加载速度、减少多余的网络数据传输、提高服务器的并发性能。
一、http请求时浏览器获取缓存数据或是重新请求后台大致过程 1.在第一次加载资源时服务器返回200浏览器从服务端下载资源文件并缓存资源文件以及响应头以供下一次请求数据时判断缓存时效性使用
2.下一次加载资源时由于强缓存检验时间过期来判断缓存可用性的优先级较高先比较本次请求与上一次服务端返回200请求的时间差如果没有超过标头里cache-control设置的max-age相对时间—http1.1版本支持则没有过期命中可以使用强缓存直接从本地获取资源。如果浏览器不支持http1.1则使用标头里的expires绝对时间来判断是否过期
3.如果缓存资源已经过期则表示强缓存没有被命中开始走协商缓存向服务器发送携带If-None-Match服务器没有与Etag匹配的资源才会去获取数据并返回200和If-Modified-Since在Last-Modified(响应标头里存放的上一次修改时间)之后期间有修改过资源才会去获取数据并返回200否则返回304客户端获取本地缓存的请求
4.服务器收到请求后优先根据Etag的值去判断被请求的资源是否被修改过结合条件式请求头If-None-Match存在Etag值一致的资源命中则表示没有修改过返回304客户端使用本地缓存不存在一致的Etag说明修改过资源则返回新资源并且响应头携带新的Etag返回200状态
5.如果请求头里没有Etag值则使用条件式请求头If-Modified-Since携带上一次请求的Last-Modified进行对比在Last-Modified之后资源是否被修改过没有修改命中协商缓存则返回304客户端使用本地缓存被修改过则返回新的资源并且在响应头里携带新的Last-Modified值返回200
很多网站的资源后面都加了版本号这样做的目的是每次升级了 JS 或 CSS 文件后为了防止浏览器进行缓存强制改变版本号客户端浏览器就会重新下载新的 JS 或 CSS 文件 以保证用户能够及时获得网站的最新更新。例如cdn之类的引入数据
二、浏览器资源缓存的位置 1.service worker 2.内存读取效率高、持续性短 3.硬盘容量大、持续性长、读取慢
六、浏览器本地存储
描述Cookie、LocalStorage、SessionStorage
vue相关
一、vue项目如何进行部署、是否遇到过刷新404问题
vue项目部署 只需将vue的打包文件放在服务器指定静态文件夹下即可然后使web容器运行起来即可访问项目。 一般使用nginx来实现配置完nginx启动即可刷新404问题 history路由模式的情况下会出现部署的包访问后404的情况因为history路由在浏览器访问路径时路径会被包括在 HTTP 请求中服务器会根据路径重新加载页面。 vue是单页面spa应用可以通过打包文件看出只有一个入口页面index.html在nginx配置中可以看出在浏览器打开服务器地址时会默认定向到index.html去 然而其他路径例如 xx.xx.com/login时 浏览器就会刷新并定向到/login的对应界面但是由于nignx的location没有相关路径的配置然后服务器又找不到对应的页面就是抛出一个404给浏览器 至于hash路由模式不存在这种问题因为hash路由是根据hash值来判断路由并且hash值#/login并不会被包括到http请求中去所以对服务端完全没有影响也就不会有页面重新加载的过程 即使没有配置location /login 也不会出现404的情况
解决方法 history模式下配置nginx try_files $uri $url/ /index.html。即配置重定向让路由始终定向到index.html页面路由相关全权交由前端处理
二、vue的template到生成html渲染的过程
我们知道 template/template 这个是模板不是真实的 HTML浏览器是不认识模板的所以我们需要把它编译成浏览器认识的原生的HTML这一块的主要流程如下 第一步通过正则等手段提取template里的标签元素原生html与非原生html、变量、属性等解析成抽象语法树AST 第二步经过一些处理如标记静态节点等优化生成render函数 第三步通过render函数生成vnode虚拟dom节点 第四步再经过patch过程diff算法-新旧虚拟dom比对找出需要渲染到视图的vnode 第五步根据vnode创建真实dom节点html渲染到视图中完成template的渲染
三、vue模板编译过程
模板编译就是生成html过程中的1-3步
第一步通过正则等手段提取template里的标签元素、变量、属性等解析成抽象语法树AST一个对象 第二步优化AST遍历AST并用static属性标记其中的静态节点与静态根节点 第三部根据AST生成对应的render函数
四、diff算法
diff算法在vue中用来比对生成的新旧vnode优化页面渲染比对后只渲染有改变的dom 特点
同层级比较不会跨层级比较。我们都知道虚拟dom与真实dom都是tree结构同层级vnode比对更合理比较的方法是 首尾比对法react是相邻比对
五、vue3中设置全局变量
使用config.globalProperties全局对象(vue2中使用的是Vue.prototype.xxx)使用provie/inject 全局注入
六、vuex数据持久化
使用localStorage或sessionStorage使用vue-along插件实质就是使用的localStorage或sessionStorage原理只是存取的过程组件帮我们完成了
七、vue中$router与$route的区别
$router是router的实例对象可以通过实例对象获取整个路由文件以及一些实例方法等 $route是当前激活的路由信息对象一个只读属性对象不能被监听可以获取当前激活路由的有关信息
八、vue3的响应式原理 组件v-model的实现
实现组件的v-model
// 父组件
templateChild v-modelmsg/Child
/template
script setupconst msg ref(xxx)
/script// 子组件
script setup
defineProps([modelValue])
const emit defineEmits([update:modelValue])
/script
templatediv clickemit(update:modelValue, xxxx){{ modelValue }}/div
/templatevue3中父组件通过v-model传入变量值子组件通过defineProps传入固定的参数modelValue获取props中v-model的变量值使用再通过defineEmits第一参数传入固定的标识update:modelValue,第二个参数传入改变的值来修改v-model的值来实现v-model的双向绑定 vue2中 父组件通过v-model传入变量值子组件中通过this.props获取v-model传入的值并通过$emit触发input等事件来修改v-model的值来实现v-model的双向绑定
vue3的响应式原理 在普通js执行中是不会有响应式的变化的需要对其进行一系列处理大致流程如下 1.创建副作用方法副作用主要是用于执行改变比如一个变量值改变与之业务上相关的其他变量值通过执行副作用来改变 2.创建一个mapweakMap或者set(weakSet)数据类型来存放需要执行的副作用 3.创建一个收集副作用的方法 4.创建一个执行对应副作用的方法 例如 a,b,c 业务逻辑是 a b c要想达到响应式效果即b与c改变时a的值也相应做出改变。就需要如下操作
let a 0, b 0, c 0
// 创建副作用
const effect () { a b c }
//创建一个set收集副作用
const effects new Set()
// 创建一个收集副作用的方法
const addEffect () { effects.add(effect) }
addEffect()
// 创建一个执行副作用的方法
const deal () {effects.forEach(effect { effect() })
}
b 1
deal()
console.log(a) // 1如上是一个实现响应式的基本流程最关键的点就在于实现自动收集副作用以及自动执行副作用在vue中 vue2通过es5的Object.defineProperty()方法针对单个属性监听get和set对数据进行劫持结合发布订阅模式来实现 vue3中通过es6的proxy API实现针对整个对象监听get和set代理对象。再使用reflect API反射对被代理对象的属性进行操作 相比较 proxy API 优势有 1.直接拦截对象进行监听包括数组 2.速度快不需要像Object.defineProperty方法那样定义的时候深度递归复杂对象属性可以省去for in 、闭包等内容来提升效率直接绑定整个对象即可 3.可以检测到代理对象的所有操作行为 缺点 es6语法 对一些低版本的浏览器不兼容 defineProperty优势es5语法 兼容性好 缺点 1.只能监听某个属性不能对全对象进行监听并且只能监听到get与set新增与删除时无法监听需要使用$set$delete方法来辅助 2.对数组类型的监听是通过重写更新数组的一些实例方法实现拦截的有明显的缺陷例如无法监听修改数组的下标 3.因为只能递归处理监听对象的单个属性所以在数据复杂层次深时就会有性能问题影响初始化速度
九、vue2与vue3的区别总结
1、声明周期 在声明周期方面vue3使用setup代替了beforeCreatedcreated其他大致相同只是在使用方式上不同比如mountedvue3中使用onMounted声明周期钩子函数
2、碎片化多根节点 vue2中template里只能支持单一的根节点vue3中支持多根节点
3、响应式原理 vue2Object.defineProperty() vue3proxy reflect
4、支持件组件移动到app根节点之外 vue3提供Teleport组件将组件传送到app根节点之外例如dialog
5、打包优化 tree-shaking模块打包webpackrollup等中的概念移除js上下文中未使用的代码主要依赖的esmodule中的import与export语句用来检测代码模块是否导出、导入、被使用。 vue3中针对tree-shaking的支持将全局以及内部的api进行了重构全局只能使用es模块构建的命名导出访问 删除未使用的代码以及依赖优化打包文件大小
6、编写风格 vue3使用的是组合式apihooks的味道vue2使用的是选项式api
7、声明响应式变量方式 vue3通过reactive与ref来声明响应式变量vue2中通过选项式api中的data方法返回一个对象进行响应式绑定
8、异步组件 vue3提供了完整的Suspense异步组件通过#default插槽来显示异步内容显示在异步未加载完成时会默认显示#callback插槽里的内容 vue2只是提出了实验性的Suspense异步组件去了解过没有使用过
9、事件缓存 vue3在第一次渲染后绑定的事件会被缓存不想vue2那样每次渲染都会重新给事件绑定新函数
10、虚拟dom与diff算法优化 vue3的vnode对象新增了patchFlag字段用来标记动态文本信息、静态节点等静态节点在渲染时直接引用而不需重新创建。在diff中根据patchFlag字段区分静态节点、以及不同类型的动态节点一定程度的减少节点本身以及属性的比对。
十、组件传参方式
1.props向下emit向上 2.使用vuex共享store 3.使用event bus 4.$attrs 和 $listeners 是 Vue 的实例属性。子组件存在插槽时使用$attrs分发未知的 attribute、与$attrs类似使用$listeners分发未知的事件 5.$refs可以直接访问子组件的内部数据 6.provide/inject
十一、vue组件中定义一个变量a 1在页面显示使用for循环去改变100次变量的值页面会渲染多少次
由于 Vue 是响应式框架当 a 的值发生变化时会自动更新 DOM因此循环100次更改变量的值时DOM 会重新渲染并更新100次因此在这个具体的例子中页面将渲染100次
react相关
一、react样式实现作用域
使用css modules css-in-jsstyled-components !-- import styles from ‘./App.module.css’; -- !-- styles.样式名使用 --
二、组件传参方式
1.通过props传递数据 2.通过context共享数据 3.redux 4.eventbus
浏览器相关
一、为什么基本数据类型存放在栈中复杂数据类型存放在堆中
在javascript中引擎通过栈空间来维持程序的执行上下文状态如果所有数据都存放在栈的空间内就会影响到执行上下文的切换效率从而导致整个程序的执行效率所以内存大的数据类型存放在堆上只在栈上标记一个指向堆上的指针
二、js垃圾回收机制
垃圾回收机制就是清理内存的方式
在js中的内存分配机制根据数据类型 基础数据类型存放在栈上引用数据类型存放在堆上。储存在栈上的基础数据类型值可以直接通过操作清除而在堆上的数据值大小不确定需要js引擎的垃圾回收机制进行处理 对于堆上的内存管理js是自动的每当我们创建函数、对象、数组的时候会自动的分配相应的内存空间当对象不再被引用的时候是垃圾对象不能从根上访问可达对象到时也是垃圾
垃圾回收机制的策略 1.标记-清除 2.引用计数
三、重绘与回流重排
浏览器渲染过程主要分三步dom解析、css解析、布局绘制 在dom与css发生改变时引起页面重新渲染时浏览器会对发生改变的部分进行布局回流或绘制重绘
重绘指的是元素的样式更改并不影响元素在文档流中的位置只需要重新将新样式绘制到屏幕上即可比如更改背景颜色、字体颜色等。重绘不会更改元素的大小以及位置
回流通常指元素的几何尺寸以及位置、布局等发生变更需要重新计算元素的大小、位置并重新构建渲染树和布局树。比如元素的增删改、wh修改以及位置等还包括元素样式例如动画等(transform转换不会影响回流)都会导致回流。
以下几个原因会触发页面的重绘与回流 页面初始化渲染时 浏览器窗口大小变化及缩放 dom元素的增删改 元素位置、大小、内容、样式等任何变化 css伪类before after 内嵌的iframe内容发生变化
优化方法 1.避免一次性操作多个dom元素应将多个操作合并成一个操作、使用documentFragment来优化大量的dom操作 2.避免频繁修改dom元素的样式例如el.style.top应使用class或styles来完成相关操作只触发一次回流或重绘 3.避免使用table布局每个单元格的大小取决于内容单元格大小的变化可能导致整个表格的重新布局。 4.使用flex布局代替传统的布局 5.尽量使用transform代替top、left等定位 6.对执行动画的元素尽量使其脱离文档流减少对其他元素的影响 7.对需要回流的元素先将元素隐藏再回流
四、硬件加速gpu加速
使用transform或opacity等css属性可以开启GPU加速用来处理一些动画
五、执行js同步代码时遇到一个dom的样式修改浏览器会怎么操作
当执行同步的javascript代码时浏览器会等待javascript代码执行完成后才会更新页面的渲染如果代码中有对dom样式进行修改则此更改不会立即更新到浏览器窗口。需要等js代码执行完成后重新绘制和呈现整个文档直接渲染界面来显示更新后的样式。 原因 在浏览器中js引擎与渲染引擎时互斥的即在执行js代码时会阻塞渲染引擎cpujs中遇到样式修改时浏览器会将该修改保存在渲染队列中只有当这个js的执行完成之后渲染引擎才会开始渲染修改后的文档
六、浏览器多线程
渲染引擎线程、js引擎主线程、定时触发器线程、事件触发线程任务队列、http请求线程、service worker
七、浏览器组成
用户界面- 出标签页以外的其他用户界面内容 浏览器引擎数据持久层 -用于在用户界面与渲染引擎之间传递数据 渲染引擎包含网络请求模块、js解析器等-渲染用户请求的页面内容 渲染引擎是浏览器核心浏览器内核谷歌、Edge以及opera使用的blink、以及safari使用的webkit
八、浏览器地址栏输入地址获取数据的过程 待补充 九、浏览器地址栏输入地址获取数据后将html渲染到页面的过程数据转视图
一、多进程结构的浏览器 进程是操作系统进行资源分配和调度的基本单元可以申请和拥有计算机资源进程是程序的基本执行实体占用独立的空间 线程是操作系统能够进行运算调度的最小单位一个进程中可以并发多个线程每条线程并行执行不同的任务线程是资源共享的
当我们启动某个程序时就会创建一个进程来执行任务代码同时会为该进程分配内存空间该应用程序的状态都保存在该内存空间里当应用关闭时该内存就会被回收。进程可以启动更多的进程来执行任务每个进程分配的内存空间时独立的如果两个进程间需要传递数据需要通过进程间的通信管道ipc来传递。 很多应用程序都是多进程的结构进程间是相互独立的这样是为了避免一个进程卡死影响到整个应用程序。 进程可以将任务分成更多细小的任务然后通过创建多个线程并行执行不同的的任务同一进程下的线程之间是可以直接通信和共享数据的 对于以前单线程的浏览器大致有页面线程来负责页面渲染和展示等js线程执行js代码以及其他各种功能的线程。单线程结构会引发很多问题一是不稳定一个线程卡死可能会导致整个进程出问题例如打开多个标签页其中一个标签页卡死可能会导致整个浏览器无法正常运行。二是不安全单进程的浏览器之间线程是可以共享数据的js线程就可以随意访问浏览器进程内的所有数据。三是不流畅一个进程需要负责太多的事情会导致运行效率问题。所以为了解决以上问题实现了多进程浏览器结构 多进程浏览器大致分为 浏览器进程负责控制除标签页以外的用户界面包括地址栏、书签、前进后退按钮等以及负责与浏览器其他进程协调工作 网络进程负责发起和接受网络请求 GPU进程负责整个浏览器界面的渲染 渲染器进程用来控制页面内容渲染 浏览器会在默认情况下可能会为每一个标签页创建一个进程谷歌浏览器启动方式-可以配置进程模型
正文 当在地址栏输入地址时浏览器进程的UI线程会捕捉你的输入内容如果访问的是网址则ui线程会启动一个网络线程来请求DNS进行域名解析获取IP地址接着开始连接服务器获取数据。如果输入内容不是网址而是一串关键词浏览器就会使用默认配置的搜索引擎来查询。 html数据获取成功后浏览器执行渲染过程如下 1、当网络线程获取到数据后会通过safeBrowsing来检查站点是否是恶意站点提示当前连接不安全浏览器阻止你的访问safeBrowsing是谷歌浏览器内部的一套站点安全系统例如查看ip是否在黑名单中。当返回数据准备完毕并且安全校验通过后网络线程会通知UI线程表示我已经准备好了该你了。然后UI线程通知渲染进程来渲染页面浏览器进程通过ipc管道将数据传递给渲染进程正式进入渲染流程。渲染器进程的核心任务就是把html、css、js、image等资源渲染成用户可以交互的web界面。
2、渲染器进程的主线程将html进行解析构造dom数据结构dom文档对象模型是浏览器对页面在其内部的表现形式可以通过js与之交互的数据结构和api。html首先经过Tokeniser标记化通过词法分析将输入的html内容解析成多个标记根据识别后的标记进行dom树构造在dom树构造的过程中会创建document对象以documnet为根节点的dom树不断进行修改向其中添加各种元素。
3、html中会插入一些额外的资源比如图片、css、js脚本等。这些资源需要通过网络下载或者从浏览器缓存中直接加载图片、css资源不会阻塞html的解析因此它们不会影响dom的生成。但是在html解析过程中遇到script标签就会停止html解析流程转而去加载解析并执行js代码因为js中可能存在一些对dom的修改操作比如document.write在页面加载完成后调用会覆盖整个html页面
4、解析完成html后得到一个dom tree但是此时还不知道dom tree每个节点的样式。主线程需要解析css并确定每个dom节点的计算样式即使没有提供css样式浏览器有自己默认的样式。在样式解析完成后此时知道了dom结构以及每个节点的样式。
5、接下来需要知道每个节点需要放在页面哪个位置也就是节点的坐标以及该节点需要占用多大的区域。这个阶段被称为layout布局主线程通过遍历dom和计算好的样式来生成layout tree layout tree上的每一个节点都记录了xy坐标和边框尺寸。需要注意的是dom tree与layout tree并不是一一对应的设置了display:none的节点不会出现在layout tree上而在伪类元素中添加了content值得元素content里的内容会出现在layout tree上不会出现在dom tree中。这是因为dom tree是通过html解析获得的并不关心样式而layout tree是根据dom tree与计算好的样式来生成的。layout tree和最后展示在屏幕上的节点是对应的
5、在得到layout tree后还需要知道页面以什么样的顺序绘制节点例如样式中的z-index会影响节点绘制的层级关系如果我们按照dom的层级结构来绘制页面则会导致错误的渲染。所以为了保证屏幕上显示正确的层级主线程遍历layout tree创建一个绘制记录表用来记录节点绘制的顺序。这个阶段被称为绘制。
6、到此渲染进程的解析过程就完成了接下来就是把这些解析出来的信息转化成像素点显示在屏幕上这种行为被称为栅格化-栅格化指将图块转换成位图。chrome最早使用的一种很简单的方式只栅格化用户可是区域的内容当用户滚动页面时再栅格化更多的内容来填充这样就会导致页面展示延迟。现在chrome使用的一种更复杂的栅格化流程叫做合成合成是一种将页面的各个部分分层多个图层分别对其进行栅格化并在合成器线程中单独进行合成页面的操作。简单来说就是页面的所有元素按照某种规则进行分图层并把图层栅格化然后只需要把可视区的内容组合成一帧展示给用户即可 主线程遍历layout tree生成layer tree当layer tree生成完毕和绘制顺序确定后主线程将这些信息传递给合成器线程合成器线程将每个图层栅格化由于一层可能像页面的整个长度一样大因此合成器线程将他们切分为许多图块然后将每个图块发送给栅格化线程栅格化图块后将他们存储在GPU内存中。
7、当图块栅格化完成后合成器线程将收集称为“draw quads”的图块信息记录了图块在内存中的位置和在页面中绘制的位置信息。根据这些信息合成器线程生成一个合成器帧然后这个合成器帧通过IPC传送给浏览器进程接着浏览器进程将合成器帧传送到GPU然后GPU渲染展示到屏幕上。
8、此时就可以在页面上看到内容的展示了。
当滚动页面或者操作一些改变页面的交互时都会生成一个新的合成器帧新的帧再传给GPU渲染到屏幕上
总结简述 浏览器进程中的网络线程请求获取到html数据后通过IPC将数据传给渲染器进程的主线程主线程将html解析构造dom树然后进行样式计算根据dom和计算好的样式生成layout tree布局树通过遍历layout tree生成绘制顺序表接着遍历layout tree生成layer tree然后主线程将layer tree和绘制顺序信息一起传给合成器线程合成器线程按照规则进行分图层并把图层分为更小的图块传给栅格化线程进行栅格化栅格化完成后合成器线程会获得栅格化线程传过来的“draw quads”图块信息根据这些信息合成器线程上合成了一个合成器帧然后将合成器帧通过IPC传回给浏览器进程浏览器进程再传给GPU渲染到页面上
重排回流与重绘 当我们改变一个元素的尺寸位置属性时会重新进行计算样式布局、绘制以及后面所有的流程。这种行为就是重排回流 当我们改变一个元素的颜色属性时不会重新触发布局但还是会触发样式计算和绘制。这个就是重绘 重排和重绘都会占用主线程同时js也是运行在主线程就会出现抢占时间的问题js引擎与渲染引擎是互斥的。如果写了一个不断导致重绘重排的动画浏览器则需要每一帧都运行样式计算布局和绘制的操作我们知道当页面以每秒60帧的刷新率时每帧16ms才不会让用户感觉到页面卡顿。如果在运行动画时还有大量的js任务需要执行因为样式计算、布局、绘制、js执行都是在主线程执行的当在一帧的时间内布局和绘制结束后还有剩余时间js就会拿到主线程的使用权如果js执行时间过长就会导致在下一帧开始时js没有及时归还主线程导致下一帧动画没有按时渲染就会出现页面动画的卡顿
优化方法 1.使用requestAnimationFrame()方法 requestAnimationFrame() 方法是由浏览器提供的一种API用于通过 JavaScript 运行动画。 相较于使用 setTimeout 定时器进行动画操作requestAnimationFrame的性能更加优化。因为它可以跟随显示器的刷新频率执行会在每一帧被调用而不是在固定时间间隔中运行代码 当页面处于非激活状态即你没有来到当前页面时requestAnimationFrame 不会被执行。 如果要停止requestAnimationFrame我们需要使用cancelAnimationFrame方法 2.通过上述的渲染流程可以看出栅格化的整个流程时不占用主线程的合成器线程与栅格化线程单独的线程运行意味着它无需和js抢夺主线程在反复重绘重排时可能会导致页面掉帧这是因为有可能js执行阻塞了主线程在css中有个动画属性-transform转换通过该属性实现的动画不会经过布局和绘制而是直接运行在合成器线程和栅格化线程中所以不会受到主线程中js执行的影响。更重要的是通过transform实现的动画由于不需要经过布局绘制样式计算等操作所以节省了很多运算时间。通过将元素分层并使用硬件加速业界称GPU硬件加速也可以使用opacity属性达到加速效果
十、v8引擎执行js代码
javascript动态类型语言js引擎采用执行时编译来编译js代码just in time compilation 简称JIT静态类型语言是采用AOT提前编译。js引擎的作用就是将js代码编译成机器能够识别的代码。
v8引擎是一个接收javascript代码编译代码然后执行的c程序编译后的代码可以在多种操作系统多种处理器上运行。 主要负责编译和执行js代码、处理调用栈、内存分配、垃圾回收等
一般的js引擎在编译和执行代码都会用到三个重要的组件----解析器parser、解释器interpreter、编译器compiler 解析器负责将js代码解析成抽象语法树AST、解释器负责ast抽象语法树解释成字节码bytecode同事解释器也有直接解释执行字节码的能力、编译器负责将字节码编译成运行更高效的机器代码
最新版v8引擎执行js代码的流程整体是字节码和优化后的机器码共存的架构模式
1.通过parser解析js代码生成抽象语法树 2.AST通过ignition基准解释器生成bytecode字节码此时ast就会被清除掉释放内存空间生成的bytecode直接被解释器执行同时生成的bytecode将会作为基准执行模型在不断执行过程中解释器会收集到很多可以用来优化代码的信息比如变量的类型以及哪些函数执行频率高。这些信息被发送给编译器 3.v8引擎新的编译器Turbofan根据解释器收集的信息和字节码来编译出经过优化的机器代码。在字节码不断执行过程中可能会生成更多的机器码 4.存在逆向还原成字节码的情况deoptimization这是因为js是一个动态语言会导致解释器ignition收集到错误信息比如一个sum方法如下
function sum(a, b) {return a b
}
在函数声明时js引擎并不知道参数ab是什么类型。当多次调用sum方法并且都是传入两个number整型时sum会被解释器标记为热点函数解释器将收集到的类型信息和该函数对应的bytecode发送给编译器编译器生成优化后的机器代码中就假定了sum函数的参数是int类型之后遇到该函数的调用就直接只用运行更快的机器代码。但是如果此时我们调用sum方法传入两个字符串机器代码不知道如何处理字符串的参数此时就需要进行deoptimization回退成bytecode由解释器来解释执行。所以说编程过程中尽量不要把一个变量类型变来变去的传入函数的参数也最好保持固定否则会给v8引擎带来一些影响损失一定性能。
v8引擎处理js代码的一些优化策略 1.如果函数只声明未调用不会解析成AST也就不会生成字节码 2.函数只调用一次bytecode直接被解释执行不会进行优化编译 3.如果函数被多次调用可能会被标记为热点函数可能会被编译成机器代码
注-字节码生成速度比编译成机器码快很多
十一、SSR服务端渲染、CSR客户端渲染与SSG预渲染 待完成 SSR目的 1、解决首屏加载缓慢 2、优化单页应用SPA的SEO搜索引擎
同构一套代码需要在客户端运行还要在服务端运行
vueNuxt.js react Next.js 原生node服务搭建ssr主要在于配置webpack对react与vue语法的识别例如.jsx、.vue等
注水与脱水操作 renderToString方法
webpack等构建工具
一、Vite整个热更新过程
1.创建websocket服务端和client文件启动服务 2.通过chokidar监听文件变更 3.当代码改变后服务端进行判断并推送到客户端 4.客户端根据推送的信息执行更新操作
二、vite了解
vite是一个基于esbuild与rollup依靠浏览器自身ESM编译功能实现极致开发体验的新一代构建工具
webpakc构建工具 构建工具一般运行过程打包 抓取-编译-构建整个应用的代码生成一份编译、优化后能良好兼容各个浏览器的生产环境代码 开发环境中的流程也大致一致先将整个应用构建打包在把打包后的代码交给dev server运行。随着前端业务复杂化js代码量呈指数增加导致打包构建时间越来越长在dev server中服务器启动以及热更新都到达了性能瓶颈已经没有什么优化的余地了。 vite就是在这种前景下生产出来的
vite相比较webpack拥有极致的开发体验在dev server中webpack采取的是先将整个应用构建打包在把打包后的代码交给dev server运行模式。而vite是直接将源码交给浏览器实现dev server秒开浏览器需要相关模块再向dev server发起请求服务器返回相应的模块给浏览器实现了真正意义的按需加载。
Vite主打的是开发环境的极致体验生产环境集成Rollup
高度集成开箱即用 基于ESM急速热更新无需打包编译 基于esbuild的依赖预处理 兼容Rollup庞大的插件机制插件开发更简洁 天然支持TS
三、webpack loader的作用
Webpack loader是一种特殊的模块用于处理应用中引入的非JS文件并将它们转换为Webpack可识别的有效模块。用来支持CSS、图片、JSON、XML等资源加载 例如style-loader和css-loaderstyle-loader和css-loader常常被一起使用来加载和处理CSS文件。 css-loader: 读取 CSS 文件然后让 Webpack 将其解析成 JS 的 module export 代码块。 style-loader: 接收 css-loader 模块所导出的 JS 模块并作为样式标签插入到 DOM 中
四、webpack深入优化、使用 待完成 前端单元测试
jest使用 待完成