一、HTML和CSS基础
1. BFC
BFC:块状格式化上下文,有个独立的渲染容器,不受外局影响,同样不会影响外界元素。
实现BFC的条件:float不为none;定位为absolute或fixed;overflow为hidden、outo、scroll。
BFC的作用常用于清楚浮动及避免margin重叠。
2. css文档流
css文档流通俗来讲是元素在HTML中从上到下、从左到右的布局。 脱离文档流的方式有float浮动、absolute绝对定位、fixed固定定位、sticky黏性定位以及z-index便签
3. 盒模型
盒模型可分为标准盒模型和怪异盒模型两种。
- 标准盒模型的box-sizing属性为content-box。整个盒模型分为content+padding+border+margin。
- 怪异盒模型又叫IE盒模型,它的属性为border-box。整个部分可分为width(content+padding+border)+margin。也就是说在怪异盒模型中,写padding、border该盒子的width不会发生变化。
4. 定位
- 相对定位(relative) 元素不会脱离文档流
- 绝对定位(absolute) 元素会脱离文档流
- 固定定位(fixed) 元素相对偏移参考可视窗口
- 黏性定位(sticky) 在超过某个阈值前与相对定位行为相同,超过后类似绝对定位
5. 水平垂直居中
- 绝对定位(无宽高)
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
- 绝对定位(有宽高)
position: absolute;
top: 50%;
left: 50%;
margin-left: -(1/2 width) px;
margin-top: -(1/2 height)px;
- flex
display: flex;
justify-content: center;
align-items: center;
6. 清除浮动
浮动定义:子元素设置浮动布局,脱离文档流,容器高度塌陷,影响布局
float的出现是为了解决文字环绕图片的排版功能
清除浮动方式:伪元素
.clearfix::after{
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
7. css常用选择器
- id选择器
- class选择器
- 标签选择器
- 通用选择器(*)
- 组合选择器(后代选择器,子选择器,兄弟选择器)
- 属性选择器
- 伪类/伪元素选择器
8. 超出隐藏显示省略号
- 单行
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
- 多行
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;//设置行数
-webkit-box-orient: vertical;
9. 回流与重绘
- 回流:影响页面布局,尺寸,隐藏的 导致需要重新render
- 重绘:元素的外观风格改变
10. flex
- flex-diretion: row column 设置主轴方向
- justify-content: flex-start center space-around 主轴对齐方式
- align-items: flex-start center 单行侧轴对齐方式
- flex-wrap:wrap 换行
- align-content: flex-start center space-between 多行情况下侧轴子元素的对齐方式
- flex:1 子元素占几份
11. H5新增
- 语义化标签 header、nav、aside、section、footer
- localstorage、sessionstorage
- video、audio
- webworker、websocket...
12. css3新增
- border-radius
- text-shadow
- transform
- animation
- 选择器 (nth-child(),:not())...
13. 精灵图
将一个页面涉及到的背景图片放到一个大图中,利用css background属性来定位
优点:减少http请求,提高页面性能
缺点:开发维护麻烦
二、JS相关
1. 对象方法(Object.keys(),in/hasOwnProperty)
- Object.keys() 遍历对象得到属性名组成的数组
var obj = {
key1:1,
key2:2
}
Object.keys(obj); // ['key1', 'key2']
- in/hasOwnProperty
两者区别在于hasOwnProperty判断是否是对象自身属性,in会到原型链上去找。
var obj={p:1}
'p' in obj //true
'toString' in obj // true
obj.hasOwnProperty('toString')//false
2. 伪数组转数组
var args = Array.prototype.slice.call(arguments);
3. 闭包
闭包是指有权访问另一个函数内部作用域中变量的函数。内部的函数存在外部作用域的引用就会导致闭包。
应用场景:返回一个函数,防抖节流。
闭包容易造成内存泄漏。
var a = 1
function foo(){
console.log(a)
}
4. 原型和原型链
const ob = {}
console.log(ob);
console.log(ob.__proto__ == Object.prototype); //每个实例对象都有一个隐式原型(__proto__)指向构造函数的显式原型(prototype)
console.log(Object.prototype.constructor == Object);//构造函数的显式原型又有一个constructor指向构造函数
原型链的定义
当试图得到一个对象的属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型即它的构造函数的显示原型中去找,如果显示原型还没有,这个显示原型也是个对象,那么这个对象的构造函数是object,找到这个对象的object.prototype。而object.prototype的隐式原型指向null。
5. 防抖与节流
- 防抖:持续触发时,当一段时间内没再触发,事件函数才会重新执行一次。 应用场景:按钮点击,输入框输入
function debounce(fn, delay) {
let timer = null;
return function () {
timer && clearTimeout(timer);
timer = setTimeout(fn, delay);
}
}
function handle() {
console.log(Math.random());
}
const btn = document.querySelector('#btn');
btn.addEventListener('click', debounce(handle, 1000));
- 节流:持续触发时,一定事件内只触发一次。应用场景:mouseover,scroll,scroll
function throttle(fn, delay) {
let timer = null;
return function () {
if (!timer) {
timer = setTimeout(() => {
fn();
timer = null;
}, delay);
}
}
}
function handle() {
console.log('滚动事件执行');
}
window.addEventListener('scroll', throttle(handle, 1000));
6. ES6
- let const
let 不存在声明提升,存在块级作用域
const 定义的变量不可更改(引用类型不能改变引用地址)
- 解构赋值
let [a,b] = [1,2]
- 模版字符串
`${a}hello`
-
扩展运算符 ...
-
Symbol 表示独一无二的值
const sym1 = Symbol()
const sym2 = Symbol()
console.log(sym1 === sym2) // false
- 箭头函数
箭头函数和普通函数的区别:
(1)箭头函数的this指向外层第一个普通函数的this,普通函数的this是谁调用指向谁
(2)箭头函数的this指向不可改变;普通函数通过call() apply(数组) bind(返回一个函数) 改变指向
(3)箭头函数没有prototype
- set
[...new Set[1,1,3,2,5,4,2]] 成员是唯一的
- Promise Generator async await
promise是js的一个原生对象,是异步编程的一种解决方案
promise的出现 是为了解决回调地狱
promise有三个状态 pending(进行中) fulfilled(已成功) rejected(已失败)
const promise = new Promise((resolve, reject) => {
reject(1)
})
promise.then(res => {
console.log(res, 'res');
}, err => {
console.log(err, 'err')
}).catch(err => {
console.log(err, 'catch');
}).finally(console.log('finally'))
Promise.resolve(11).then(res => {
console.log(res);
})
Promise.reject(new Error('1111'))
Promise.all([]) //当所有promise变成resolved,all方法变成resolved,有一个变成rejectd,all变成rejected
Promise.race([]) //返回最先执行完事件的值
Promise.any() //返回最快的结果,如果都失败就返回失败
7. 常用数组和字符串方法
数组方法
-
Array.isArray(arr1); //判断是否是数组 -
arr1.toString()//转成字符串 -
arr1.push(4) //在数组后面新增元素 原数组改变 -
arr1.pop()//删除数组最后一个元素原数组改变 -
arr1.unshift(0)//在数组前面添加元素 原数组改变 -
arr1.shift() //删除数组第一个元素 原数组改变 -
arr1.join(',');//1,2,3 将数组以指定分隔符以字符串形式返回 原数组不改变 -
arr1.concat([4, 5, 6])//数组合并 原数组不改变 -
arr1.reverse() //反转数组 原数组改变 -
arr.slice(start, end);//截取数组 一个参数就是从索引到最后,两个从start到end (不包括end位置上的元素本身) 返回新的数组,原数组不改变 -
arr.splice(start, count, addElement1, addElement2, ...) //方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。原数组改变 -
[1, 4, 2, 6, 0, 6, 2, 6].sort((a, b) => a - b) //sort排序 -
arr5.map(item => item + 1) //返回一个新数组 原数组不改变 -
arr6.filter(item => item > 3)//返回所有符合条件组成的数组 -
arr7.some(item => item > 3) //有一个符合条件就返回true -
arr7.every(item => item > 3)//全部符合条件才返回true -
const c = [1, 2, 3, 4, 5].reduce( ( a, // 累积变量,必须 b, // 当前变量,必须 i, // 当前位置,可选 arr // 原数组,可选 ) => { return a + b }, 0 //a的初始值 ) -
[1, 2, 3].indexOf(1)//返回元素第一次出现的位置,没有则返回-1 -
var users = [ { name: 'tom', email: 'tom@example.com' }, { name: 'peter', email: 'peter@example.com' }] users.map(function (user) { return user.email; }) .filter(function (email) { return /^t/.test(email); }) .forEach(function (email) { console.log(email); }); //链式调用
字符串方法
-
str1.charAt(1)==str1[1]//返回索引为1位置的元素 -
str1.concat('efg')//返回新的字符串 -
str1.slice(1, 2)//同数组方法 -
str1.substring(2, 1)//与slice基本上相同,会自动交换start与end -
str1.substr(1, 1)//str.substr(start,count) -
str1.indexOf('a')//返回元素第一次出现的位置,没有则返回-1 -
' hello world '.trim() //去除字符串两端的空格,返回一个新字符串 -
'\r\nabc \t'.trim()//去除的不仅是空格,还包括制表符(\t、\v)、换行符(\n)和回车符(\r) -
'Hello World'.toLowerCase() //转小写 -
'Hello World'.toUpperCase() //转大写 -
'cat, bat, sat, fat'.match('at') //正则匹配 -
'vaa'.replace('a', 'b')//将匹配到的第一个替换 -
'vaa'.replaceAll('a', 'b') // 将匹配到的所有的替换 -
'a,b,c'.split(',')//字符串分割成数组
8. 常用算法
判断类型
const type = (o) => {
return Object.prototype.toString.call(o).match(/\[object (.*?)\]/)[1].toLowerCase()
}
['Null',
'Undefined',
'Object',
'Array',
'String',
'Number',
'Boolean',
'Function',
'RegExp'
].forEach(function (t) {
type['is' + t] = function (o) {
return type(o) === t.toLowerCase();
};
});
console.log(type({}))
console.log(type([]))
console.log(type.isNull(null))
证明 a==1&&a==2&&a==3
const a = {
i: 1,
valueOf() {
return this.i++
}
}
console.log(a == 1 && a == 2 && a == 3)
数组去重
const arr = [1, 3, 2, 1, 5, 2]
//set
function unique1(arr) {
if (!Array.isArray(arr)) return
return [...new Set(arr)]
}
//indexOf
function unique(arr) {
if (!Array.isArray(arr)) return
const res = []
arr.forEach((item, index) => {
if (res.indexOf(item) == -1) {
res.push(item)
}
})
return res
}
//对象
function unique2(arr) {
if (!Array.isArray(arr)) return
const res = []
const obj = {}
arr.forEach(item => {
if (!obj[item]) {
res.push(item)
obj[item] = 1
}
else {
obj[item]++
}
})
console.log(res, obj);
return res
}
数组排序
const arr1 = [1, 5, 7, 3, 4, 0, 23, 11]
//sort
function sortArr(arr) {
return arr.sort((m, n) => m - n)
}
//冒泡排序
function bubbleSort(arr) {
const len = arr.length;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {// 相邻元素两两对比
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]// 元素交换
}
}
}
return arr;
}
console.log(bubbleSort(arr1));
递归完成1到100累加
function recursion(num) {
if (num == 1) return 1
return num + recursion(num - 1)
}
9. Token
token是访问资源的凭证
1.可以存到localstorage,调用接口的时候当作一个字段 会引起xss攻击
2.存到cookie 请求的时候放到请求头的Authorization字段 会引起CSRF攻击
cookie的漏洞
1.xss攻击
2.csrf
3.信息泄漏
4.越权漏洞
10. 深浅拷贝
-
浅拷贝:创建一个对象,这个对象拥有者原始对象属性值的一份精确拷贝 基本类型拷贝值,引用类型拷贝内存地址。 其中一个对象改变了这个地址,就会影响另一个对象 object.assigin() ...展开运算符
-
深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放对象,修改新对象不会影响原对象 JSON.stringfy()
-
赋值:基本类型和引用类型拷贝的都是地址
const deepClone = (obj = {}) => {
let newObj
if (typeof obj == 'object' && obj != null) { //引用类型 继续递归
newObj = Array.isArray(obj) ? [] : {}
for (let i in obj) {
newObj[i] = deepClone(obj[i])
}
}
else newobj = obj;
return newobj
}
11. ajax、fetch、axios区别
- ajax
异步的js和xml,对原生xhr的封装,可以实现异步数据交互,实现页面局部刷新,暴露了与服务端的细节 - fetch
fetch属于原生js,脱离了xhr,提供的api丰富 基于promise - axios
axios基于xhr,支持promise api ,拦截请求和响应,提供了一些并发请求的接口(方便了很多的操作),可以防止crsf攻击
12. 判断字符类型
- typeof null为object
typeof 1 //Number
- instanceof 只能判断引用类型 在原型链中能否找到该类型的原型链
const arr = []
arr instanceof Array // true
- constructor也是通过原型链
'1'.constructor === String //true
三、其他
1. 浏览器常见兼容性问题
- 兼容性问题
1.不同浏览器 margin padding 不同
2.css新属性加前缀兼容早期浏览器(border-radius,box-shadow,transform)
3.图片默认间距 使用float布局
4.事件绑定 如果不支持addEventListener,用attachEvent替代
5.event事件对象 var e = e || window.event
6.事件冒泡 事件默认行为
- margin塌陷、合并问题
margin塌陷:两个父子关系的div有上边距(margin-top)的时候以大的上边距为准
解决方案:触发BFC,给父元素写overflow:hidden
margin合并:两个并列的元素,一个有下边距,一个有上边距,两者的距离等于它们较大的边距
解决方案:只写其中一个元素的边距
2. 前端攻击
1.Xss(跨站脚本攻击) 攻击:恶意js代码注入(用户输入) 防御:转译用户输入,对输入进行过滤
2.CSRF(跨站请求伪造) 攻击:登录一个网站后,诱使访问恶意网站,冒充身份做一些操作。防御:验证http referer
3.DNS劫持 攻击:域名劫持。防御:https加密
3. 前端缓存
前端缓存可分为三大类。
- http缓存(强缓存、协商缓存)
- 浏览器缓存 小容量:localStorage、sessionStorage、cookie 大容量:websql、IndexDB
- 应用程序缓存 PWA
http缓存详解如下:
- 根据缓存类型区分
缓存类型分为强缓存和协商缓存 强缓存不需要发送http请求,协商缓存需要。
在发送http请求之前先检查强缓存,命中则使用,否则使用协商缓存
- 强缓存
HTTP/1.0时期使用的是Expires; HTTP/1.1使用的是Cache-Control
Expires 指的是一个具体时间
Cache-Control 支持多个属性 max-age(多长时间过期) public(客户端和代理服务器都可以缓存) no-cache(不进行强缓存验证)
Cache-control的优先级更高,不支持HTTP/1.1的情况下则使用Expires
- 协商缓存
浏览器会携带tag标识向服务器发送请求来决定是不是使用缓存
tag标示分为 Last-Modified【最后修改时间】和 ETag【唯一标识】
两者对比
性能上Last-Modified好,准确度上Etag好,Etag优先级高。
本地打开缓存文件会因为修改时间改变而导致缓存失败,Last - Modified是以秒来计时的, 若是某个文件在一秒内被修改了很多次, 那么这时候的 Last - Modified 并没有体现出修改了。
ETag存在性能上的不足,只要文件发生改变,ETag就会发生改变。ETag需要服务器通过算法来计算出一个hash值。
- 根据缓存位置区分
- Service Worker
运行在浏览器背后的独立线程, 无法直接访问dom, 能实现离线缓存、消息推送、网络代理等功能
- Memory Cache
内存中的缓存,存储页面上已经抓去到的资源 缓存时间段 细分为preloader和preload
preloader:一遍解析css/js,一遍请求下一个资源,请求来的资源会被放入Memory Cache
preload:显式指定预加载的资源,这些资源也会被放入Memory Cache
- Disk Cache
硬盘存储 持久缓存 容量大 时间长 比较大的js/css文件会被丢进硬盘存储,否则会被丢进内存存储 系统内容使用率较高的时候优先进入硬盘
- Push Cache
推送缓存 只在会话中存在 缓存时间很短
-
根据缓存过程区分
(1)浏览器第一次发送http请求没有发现强缓存和协商缓存
(2)于是向服务器发送http请求,获取缓存结果和规则
(3)浏览器把响应内容存入硬盘存储,响应内容的引用存入内存缓存
(4)如果Service Worker 的脚本调用了 cache.put(),将响应内容存入Service Worker
下一次调用相同资源, 调用Service Worker的fetch事件响应, 查看Memory Cache、Disk Cache
- 运用场景
不常变化的Cache - Control: max - age = 31536000; 常变化的Cache - Control: no - cache
4. 前端跨域
前端跨域的出现归结于同源策略的限制 (协议、域名、端口号)
1.jsonp跨域
利用script标签没有跨域限制的特性,通过script的src标签,发送带有callback函数的get请求,后端将数据拼接到callback中,返回给前端
2.cors跨域(跨域资源共享)
3.node正向代理(webpack中的proxy)
4.Nginx 反向代理
5.websocket 没有使用http响应头
5. 前端页面渲染过程
前端页面渲染过程
- 创建dom树和cssom树,遇见js先解析js
- 将css规则附着到dom树上,构建render树
- 布局和绘制
为什么要将css放在头部,js放在尾部
- 将css放在头部是因为cssom和dom是并行加载的 将css放在头部可以防止dom加载完毕的重绘
- 将js放在尾部是因为渲染过程中遇到js会进行js的加载,影响dom加载的时间。防止脚本加载时浏览器页面阻塞,妨碍用户的体验,应合理使用scipt标签的defer和async属性·
async和defer区别
- async是文档和js异步加载执行
- defer则是文档和js异步加载,js最后执行
6. 前端项目性能问题及优化
- react层面
1.react路由组件懒加载 @loadable/component
2.antd ui库 按需加载
3.避免不必要的重复计算和重复渲染
shouldComponentUpdate
PureComponent对类组件的Props和state的浅比较
React.meno (高阶组件,被React.memo包裹的组件在render前会对新旧props进行浅比较)
useMemo (把一个计算函数和依赖项数组作为参数,执行计算函数并返回一个缓存值(函数执行的结果),它仅会某个依赖项改变时才重新计算缓存值)
useCallback(把一个计算函数和依赖项数组作为参数,不会执行传入的函数,返回一个缓存的回调函数(函数的引用),它仅会某个依赖项改变时才重新计算缓存值)
通过 React.memo 包裹组件,可以避免组件的非必要重新渲染。
通过 useMemo,可以避免组件更新时所引发的重复计算。
通过 useCallback,可以避免由于函数引用变动所导致的组件重复渲染。
4.列表项使用key属性
- 页面层面
1.http请求过多 (减少http请求,雪碧图,base64,使用缓存)
2.资源加载堵塞(资源压缩合并)
3.页面回流和重绘 (减少dom操作)
4.代码不规范(遵守代码规范,对复杂逻辑代码进行重构,删除无用代码)
5.未进行优化(ssr服务端渲染,webpack优化,提取公共代码,按需加载,删除无用代码)
- webpack
1.使用高版本的webpack
2.多实例多进程构建 thread-loader
3.压缩代码/图片
4.缩小打包作用域
5.提取页面公共资源
7. 微前端
微前端:
将一个大型应用分为多个小型应用。每个应用独立开发、运行、部署,最后在整合成一个完成的应用。减少了项目之间的耦合,提升了项目扩展性。
类似于后端的微服务,本质上都是为了将复杂庞大的前端应用拆解成一个个可以独立开发的部署的功能模块,并通过暴露相关的接口和功能来将各个功能模块进行连接。
解决的痛点:
1.单页面应用变得庞大并且难以维护,微前端将其拆分,每个模块进行单独维护和部署,提升效率
2.支持新旧项目并行
方案:
1.iframe 实现简单,局限性太大
2.webComponents 每个子应用有独立的script和css 对于历史系统改造成本较高
3.组合式应用路由分发 体验好 需要解决变量对象污染样式冲突等问题
8. 移动端布局
移动端布局最佳实践
px+flex+vw
rem在多屏幕尺寸适配上与当前两大平台的设计哲学不一致。即大屏的出现到底是为了看得又大又清楚,还是为了看的更多的问题。
百分比是相对父元素来说的 当嵌套层级较深的时候会引起计算地狱
vw 相对于视窗的宽度,视窗总宽度为100vw
vh 相对于视窗的高度度,视窗总宽度为100vh
在全局的variable.scss中写函数,然后用webpack将这个文件用sass-resources-loader导入,即可在项目中全局使用该函数
@function vw($px) {
@return ($px /375) * 100vw;
}
// 代表375设计稿上为100px的某内容块
.content {
width: vw(100);
}
vw适合宽屏等比例放大的结构
vh适合距上下边缘一定百分比的结构
常见的padding、border、margin等最好都是用px做单位,利用元素的自动撑开和img的宽高比撑开,不使用vw和vh。
常见的移动端布局方案
1.流式布局(百分比布局)
2.flex布局
3.媒体查询
4.rem布局
9. CSR与SSR
SSR(服务端渲染) 网页上需要呈现的东西在服务端生成,浏览器拿到响应直接展示
CSR(客户端渲染) 业务逻辑需求复杂 加上ajax的出现 可以不刷新页面的情况下生成html 前后端分离
SSR利于SEO和首屏渲染,局限性是服务端压力大
CSR利于前后端分离,交互好,用户体验好,局限性是首屏慢,不利于SEO
10. Event Loop (事件循环机制)
js的用途主要是与用户互动,操作dom,这决定了它只能是单线程
任务分为两种,一是同步任务,二是异步任务。同步任务在主线程上排队,异步任务进入任务队列。异步任务又分为宏任务和微任务
eventloop
1.一整个脚本作为一个宏任务执行
2.执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
3.当前整个宏任务执行完,检查微任务列表,有则执行,直到执行完
4.然后执行本轮宏任务。
5.不断循环,执行宏任务和微任务列表为空
常见宏任务 整体script setTimeOut setInterval
常见微任务 Promise.then() Promise.catch() process.nextTick
11. 常用git指令
git clone 克隆
git init 初始化目录
git add . 提交到暂存区
git commit -m "描述信息" 将暂存区到内容提交到本地仓库
git branch 查看本地所有分支
git branch -a 查看本地和远程所有分支
git branch -d [本地分支] 删除本地分支
git checkout [branch] 切换分支
git checkout -b [branch] 创建并切换
git push origin [远程分支] 提交本地到远程
git merge [分支] 合并
git pull origin [分支] 拉取
git remote set-url origin 【仓库地址】
git fetch origin 远程分支:本地分支 拉去远程分支并创建本地分支
git diff 比较当前文件和暂存区文件
git diff 分支名称 分支名称 比较两个分支差异
git log 显示所有提交记录
git remote -v 查看远程仓库地址
git status 显示工作目录和暂存区的状态
git push --set-upstream origin branch 新建远程并push
12. http协议相关
- http协议:超文本传输协议
超文本:除文字外的图片、音频、视频、超链接
传输:一方请求 一方响应 进行信息传输
协议:计算机通信需要遵守的协议
-
web服务器:浏览器是http请求的发起方,web是http请求的应答方 (常见web服务器:Nginx)
-
CDN: 内容分发网络 通过内容存储和分开技术 使用户就近获取资源
-
TCP/IP: 一系列网络通信协议的统称
-
DNS: 域名系统 解析域名
-
https:在http基础上加上了ssl层,通过传输加密和身份认证保证传输的安全性
-
https响应过程
1.解析url
2.缓存判断
3.先DNS域名解析,找到ip
4.TCP三次握手(请求,应答,应答的确认) 三次是为了防止历史连接及避免资源浪费
5.客户端发送请求报文获取服务端静态资源
6.服务端发送响应报文
7.TCP四次挥手(客户端发起关闭Socket请求,服务端待定,服务端关闭,客户端回复已关闭)
8.浏览器解析文档资源并渲染页面
- 常见状态码
200 – 请求成功
301 – 资源(网页等)被永久转移到其它URL
404 – 请求的资源(网页等)不存在
500 – 服务器错误
- http协议由http请求和响应组成
请求:请求行、请求头、请求内容
响应:状态行、响应头、响应内容
- http和tcp的区别
1.http是应用层的协议,以tcp为基础。tcp是传输层的协议,以ip为基础
2.http是建立在tcp之上的,http通过tcp建立一个到服务器的通道
- http1.0、http1.1、http2.0的区别
http1.0和http1.1
1.0默认使用非持久连接,1.1默认使用持久连接
1.1在请求头引入了range头域,允许只请求资源的某个部分
1.1提供了更多的缓存头
1.1新增了host字段,指定服务器的域名
http1.1和http2.0
http2采用二进制协议 http1.1 报文的头信息必须是文本
http2 多路复用 客户端和服务器都可以同时请求或回应
http2对头信息做了压缩
允许服务器推送:允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送
12. 对称加密与非对称加密
1.对称加密中加密和解密使用的秘钥是同一个;非对称加密中采用两个密钥,一般使用公钥进行加密,私钥进行解密
2.对称加密解密的速度比较快,非对称加密和解密花费的时间长、速度相对较慢。
3.对称加密的安全性相对较低,非对称加密的安全性较高。
13. webpack
webpack是一个前端打包构建工具
- webpack打包原理
根据文件间的依赖关系对其进行静态分析,将这些模块按照指定规则生成静态资源,
当webpack处理程序时,它会递归构建一个依赖关系图,
其中包含每个应用程序所需的模块,将这些模块打包成一个或者多个bundle
- webpack构建流程
1.初始化:启动构建,读取配置参数,加载plugin,实例化Complier
2.编译:从Entey出发针对每个Module调用对应的loader去翻译内容,再找该Mudule依赖的Module,递归编译
3.将编译后的Module组合成chunk,将chunk转换成文件,输出到文件系统中
- 常见loader
css-loader: 加载css
style-loader: 通过一个JS脚本创建一个style标签,把css代码注入到style标签
postcss-loader: 扩展css语法
sass-loader: scss/sass 转为css
file-loader/url-loader: 将文件放到一个文件夹中 通过相对地址引用 (处理图片和字体)
image-loader: 加载并压缩图片
babel-loader:es6=>es5
eslint-loader:通过 ESLint 检查 JavaScript 代码
- 常见Plugin
define-plugin:定义环境变量
html-webpack-plugin:简化html文件创建
clean-webpack-plugin:修改配置重新打包删除之前的dist
define-plugin:定义环境变量
terser-webpack-plugin:压缩js
- loader于plugin的区别
loader本身是个函数,对接受到的内容进行转换,返回转换后的结果 在modules.rules配置
plugin就是插件,扩展webpack的功能 在plugin中配置
- 可以提升效率的plugin
webpack-dashboard:可以更友好的展示相关打包信息。
webpack-merge:提取公共配置,减少重复配置代码
speed-measure-webpack-plugin:简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。
size-plugin:监控资源体积变化,尽早发现问题
HotModuleReplacementPlugin:模块热替换
- source map
source map 是将编译、打包、压缩后的代码映射回源代码的过程 方便调试
- webpcak热更新原理
Hot Module Replacement (HMR)
用webpcak-dev-server启动一个服务后,浏览器和服务端通过websocket进行长连接,webpack内部实现的watch会监听文件的修改,
只要有修改webpack就会重新打包编译到内存中,然后webpcak-dev-server依赖中间件webpack-dev-middleware和webpack之间进行
交互,每次热更新都会请求一个携带hash值的json文件和js,websocker传递的也是hash值,内部机制通过hash值检查进行热更新
- 保证loader的执行顺序
可以使用 enforce 强制执行 loader 的作用顺序,pre 代表在所有正常 loader 之前执行,post 是所有 loader 之后执行。
- 优化webpack的构建速度
1.使用高版本的webpack
2.多实例多进程构建 thread-loader
3.压缩代码/图片
4.缩小打包作用域
5.提取页面公共资源
- 代码分割
打包文件太大,请求资源太慢,分割之后chunk会相应变小
将较大的第三方依赖库分割到一个chunk,有效利用缓存
- Babel原理
将代码转换为AST(抽象语法树),访问AST的节点进行变换操作产生新的AST,以新的AST为基础生成代码
14. react
- react hooks(hook 16.8) 原理
React会维护俩个链表,一个是currentHook,另外一个是WorkInProgressHook,每一个节点类型都是Hooks。
每当hooks函数被调用,react就会创建一个hooks对象,并挂在链表的尾部。
函数组件之所以能做一些类组件不能做的事儿,就是因为hook对象,函数组件的状态,计算值,缓存等都是交给hook去完成的,这样组件通过Fiber.memoizedState属性指向hook链表的头部来关联hook对象和当前组件,这样就发挥了hooks的作用。
每次调用hooksAPI的时候,就会首先调用createWorkInProgressHook函数。得到hooks的串联不是一个数组,而是一个链式结构,从根节点workInProgressHook向下通过next进行串联,这也是为什么Hooks不能嵌套使用,不能在条件判断中使用,不能在循环中使用,否则链式就会被破坏。
- react事件机制
react事件不是将事件绑定到真实的dom上,而是在document处监听了所有的事件,当事件发生并冒泡到document处时,react将事件内容封装并交由真正的函数运行。
这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件
react上的事件是合成事件,合成事件的目的:解决了浏览器兼容性问题,减少了内存的消耗
- react 高阶组件(HOC)
相当于一种设计模式
hoc是一个函数,该函数接受一个组件,返回一个组件 在该hoc内部处理一些逻辑
eg:connect(react-redux) withRouter(react-router-dom)
- react-fiber (v16 stack=>fiber)
先有fiber后有hooks。
react在render的时候会递归比对虚拟dom树,找出需要变动的节点同步更新它。此时react会占据浏览器资源,这时会导致用户触发的事件得不到响应。
react通过fiber架构,将浏览器的渲染、布局、事件响应通过某些调度策略合理分配cpu资源。
优点:可以让浏览器及时响应用户交互,分批操作dom。
fiber也称协程,本身没有并发能力,是一种控制流程让出的机制。
- react内部性能优化
(1)shouldComponentUpdate(nextProps,nextState){
return true //通过判断nextProps和nextState的变化,决定是否render
}
(2)PureComponent React.Memo 类似shouldComponentUpadate 进行浅比较 当组件更新时,判断组件的props或者state都没有改变
(3)useMemo 缓存值用的 当依赖变化 才会重新计算
const cachedValue = useMemo(function() {
return count + 1
}, [count])
(4)useCallback 缓存一个函数
const onChange = useCallback(e => {
console.log(e.target.value)
},[]} //子组件接收一个函数作为props,父组件更新子组件也会更新,将函数用usecallback包裹
useEffect与useMemo的区别:
useEffect没有返回值(在不考虑解绑的情况下),并且是在页面渲染之后才执行的,而useMemo有返回值,并且是在页面渲染的时候进行的
- react生命周期
(1)挂载阶段
constructor(初始化state,在this上绑定函数)=>getDerivedStateFromProps(组件创建和更新阶段都会执行,第一个参数为props,第二个为state,可以进行逻辑处理)=>render=>componentDidMount(调用接口,事件监听,此时组件已经完全挂载到网页上)
(2) 更新阶段
getDerivedStateFromProps=>shouldComponentUpdate=>render=>getSnapshotBeforeUpdate(获取组件更新前的一些信息)=>componentDidUpdate
(3)卸载阶段
componentWillUnmount
为什么要废弃(componentWillMount,componentWillReceiveProps,componentWillUpdate)
因为fiber的出现而导致高优先级任务打断现有任务导致它们被执行多次
- 有状态组件和无状态组件
有状态:类组件、可以使用生命周期、有自己的状态、容易触发钩子函数降低向能
无状态:没有自身状态、通过props进行render、有更高性能、可以是类组件或者函数组件
- react虚拟dom和diff算法
虚拟dom:其实就是js对象
bebel将jsx转换为React.createElement()的形式,React.createElement()函数将参数转换成js对象的形式,ReactDom.render将虚拟dom转换成真实dom插入指定容器中
diff算法
调和:将虚拟dom转换成真实dom最少的操作过程。diff算法就是调和的具体实现
策略1:tree diff dom操作跨层级移动很少,可以忽略不计,对树的每一层级遍历,组件不存在直接销毁 (通过css方法显示隐藏dom)
策略2:component diff 相同类的组件生成相同的树形结构 不同类的组件生成不同的树形结构 不同类型的组件直接替换 同类组件可以通过shouldComponentUpdate控制是否继续render
策略3:element diff 通过key值比较
- 常用hooks
hooks弥补无状态组件没有生命周期,没有state状态的缺陷,增加代码复用性、逻辑性
useEffect,useState,usecontext,usereducer,useRef,useImperativeHandle+forwardRef
- react调用setState之后发生了什么
在调用setState后,react会将当前参数对象与组件的状态进行合并,触发调和过程。react会以相对高效的方式构建react元素树并重新render页面
- setState是同步还是异步
在react可以控制的地方:生命周期和合成事件就是异步的 (获取多个更新,批量更新,性能优化、减少渲染次数)
react不可控的地方:原生事件 addEventLister setTimeout 就是同步
- history和hash
- hash
hash路由的路径结合#号拼接在真实url后面。路径发生变化的时候,浏览器不会重新发送请求,而是会触发onhashchange事件。
也就是说所有的页面的跳转都是在客户端操作,这不算一次http请求,不利于seo优化
- history
history模式开发的项目,需要服务端做处额外配置,否则会出现白屏(链接分享失效)。
需要把所有请求都拦截到index.html文件上
- mvc和mvvm
mvc:model,模型层,处理数据逻辑的部分;view,视图层,处理数据展示的部分;controller,控制层,处理用户交互的部分
mvvm:model,模型层,处理数据逻辑的部分;view,视图层,处理数据展示的部分;viewModel,视图模型层,作为model和view之间的桥梁
两者之间的区别:mvc总的controller变为viewModel;mvvm通过数据驱动更新视图层而不是dom操作;mvvm中model和view之间不直接打交道;
- reactv18新特性
createRoot: 新的挂载入口api
批处理:promise、setTimeout、原生事件 批处理
(setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// render1次
}, 1000);)
并发渲染:reactv16之前是同步,16之后是异步,18是并发,并发解决了异步渲染中中断废弃的问题
新hooks useId
- react-redux (使用场景:state被多组件共享,业务逻辑复杂抽离出来可以复用)
1.创建一个store
import {createStore} from 'redux';
const store = createStore(reducer);
2. 创建一个reducer, const reducer = (state,action)=>{} 将reducer放入createStore里
3. import {Provider} from 'react-redux';
<Provider store={store}> 用provider包裹,将store提供给内部的组件
<TodoList />
</Provider>
4.在组件内部用connect高阶组件包裹
export default connect(mapStateToProps,mapDispatchToProps)(TodoList);
connect接受两个参数 mapStateToProps 将store里的数据映射到props里 mapDispatchToProps把store的dispatch挂载到props上
- vue和react
相同点:(1)都使用了虚拟dom+diff算法(2)组件化思想 (3)数据驱动视图
不同点: (1)vue双向绑定,react单向
(2) vue diff采用双链表 边比对边更新dom react使用diff队列保存需要更新的dom,批量更新
(3)组件写法差异 react all in js ,vue template
- 为什么建议传递给 setState 的参数是一个 callback 而不是一个对象
state和props的更新是异步的,callback提供一个参数为之前的state进行计算
15. vue
- vue生命周期
beforeCreate=>created=>beforeMount=>mounted=>beforeUpdate=>updated=>beforeDestory=>destoryed
生命周期的作用是在vue生命周期的不同阶段通过对应的钩子函数来实现组件数据管理和dom渲染两大功能
created阶段数据初始化完成 不能获取dom
mounted阶段 vue实例初始化完成 可以操作dom
- 常用指令
v-bind,: 动态绑定
v-if v-else v-else-if
v-show
v-model 双向绑定
v-for
v-on @
- vue数据双向绑定原理
数据劫持结合发布者订阅模式,利用Object.defineProperty()方法劫持每个属性的getter和setter,在数据变动时发布消息给订阅者,从而触发响应回调更新视图
vue3 采用proxy
Object.defineProperty()只能对对象的属性值劫持,proxy可以劫持整个对象
- 组件通信
父传子:props
子传符:this.$emit()
ref
eventbus
vuex
- vuex
state:基本数据;
getters:相当于state的计算属性;
mutations:提交更新数据的方法(同步);
action:提交mutations,可以是异步;
modules:模块化vuex
- vue3
(1)双向绑定原理的更新 ProxyAPI 对数据代理
(2)setup 选项式api和组合式api ref 和 reactive响应式数据
(3)生命周期hook式引入
16. vite
- 打包原理
先启动服务器,然后通过ESModule请求模块时按需动态编译显示。
- 与webpack相比
webpack是先分析依赖,然后进行编译打包,最后交给本地服务器进行渲染
热更新方面vite是改动一个模块仅仅会重新请求该模块,webpack是所有模块全部分析依赖进行打包。
- 缺点
vite相关生态没有webpack完善
17. 单页面与多页面应用的区别
单页应用(spa):切换路由通过js将响应内容展现。切换页面快,seo差
多页应用(mpa):每次页面跳转都会返回一个新的html文档。多页应用首屏快,访问页面的时候只经历了一个http请求。
切换页面慢。seo效果好,搜索引擎识别html,多页应用每个页面内容都放在html文档中
18. SEO
seo定义是为了提升网页在搜索引擎中排名的优化
优化方式:
- TKD设置
- 标签语义化
- SSR...
19. 垃圾回收与内存泄漏
垃圾回收:当变量不再参与运行时,系统会回收被占用的内存空间
全局变量的周期会持续到页面卸载;局部变量持续到函数运行结束
内存泄漏:
1.闭包
2.定时器未取消、
3.使用未声明的全局变量
20. 常用设计模式
- 工厂模式
定义一个创建对象的接口,由子类决定实例化哪一个类
举例:jquery 通过操作符去获取元素,而不是new实例
- 单例模式
一个类只能有一个实例
举例:全局loading框 只能有一个存在
- 适配器模式
将不适合的转为适合的
举例:vue中的computed 通过计算属性重新计算出一个新的数据
- 观察者模式(发布订阅)
给属性绑定观察者,当发生变化时可以让他们更新自己
举例:vue definePropety
持续加载中,各位不要吝啬您的小手帮忙点个赞哦,您小小的赞是对作者大大的鼓励~