一、js基础
1.cdn原理
CDN 的工作原理就是将源站资源缓存到位于全球各地的 CDN 节点上,用户请求资源时,就近返回,不需要每个请求从源站获取,避免网络拥塞、缓解源站压力,保证用户访问资源的速度
简单来说,CDN就是根据用户位置分配最近的资源
原理:
1.负载均衡系统
应用CDN后,DNS 返回的不再是 IP 地址,而是指向CDN的全局负载均衡,找就近、相同网络、负载较轻的节点,然后把这个节点返回给用户,用户就能够就近访问CDN的缓存代理
2.缓存代理
分成一级缓存节点和二级缓存节点。
一级缓存配置高一些,直连源站,二级缓存配置低一些,直连用户
回源的时候二级缓存只找一级缓存,一级缓存没有才回源站,可以有效地减少真正的回源
于是,用户在上网的时候不用直接访问源站,而是访问离他“最近的”一个 CDN 节点,术语叫「边缘节点」,其实就是缓存了源站内容的代理服务器。
2.es5和es6继承的区别、
https://www.mdnice.com/writing/d4a6f872b4484750916e72943baccf89
ES5 prototype 继承:
实质是先创造子类的实例对象this上,然后再将父类的方法添加到这个this上。类似使用:Father.apply(this)
S6 class 继承
通过class的extends + super实现继承。
子类没有自己的this对象,因此必须在 constructor 中通过 super 继承父类的 this 对象,而后对此this对象进行添加方法和属性。
super关键字在构造函数中表示父类的构造函数,用来新建父类的 this 对象。
内部实现机制上,ES6 的继承机制完全不同,实质是先创造父类的实例对象this---需要提前调用super方法,然后再用子类的构造函数修改this指针
二者区别:
答:不是完全一样的,主要有以下几个差异点:
1.写法不一样。class的继承通过extends关键字和super函数、super方法继承。(关于super实现继承的使用方式,具体我就不展开了)
2.类内部定义的方法都是不可枚举的,这个 ES5 不一样
3.类不存在变量提升,这一点与 ES5 完全不同
4.类相当于实例的原型,所有在类中定义的方法都会被实例继承。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就成为静态方法
内部实现机制不一样
因为实现机制不同,导致这两种继承在继承原生构造函数时有些差异:
es5的写法不能继承原生构造函数(比如Array、Number等)
因为es5的继承是先创造子类的实例对象this,再将父类原型的属性和方法重写到子类上,因为没法访问父类的内部属性,导致es5的继承方式无法继原生的构造函数。
es6允许继承构造函数生成子类。因为es6是先创建父类的实例对象this,然后再用子类的构造函数修饰,所以子类就可以继承父类的所有属性和方法。因此class可以继承并自定义原生构造函数的子类。extends不仅可以用来继承类,还能用来继承原生构造函数,因此也就可以在原生数据结构的基础上,构造自定义的数据结构
3.闭包(高频)
闭包是指有权访问另一个函数作用域中的变量的函数
闭包用途:
1.能够访问函数定义时所在的词法作用域(阻止其被回收)
2.私有化变量
3.模拟块级作用域
4.创建模块
闭包缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏
4.this指向、new关键字
this对象是是执行上下文中的一个属性,它指向最后一次调用这个方法的对象,在全局函数中,this等于window,而当函数被作为某个对象调用时,this等于那个对象。
在实际开发中,this 的指向可以通过四种调用模式来判断。
1.函数调用,当一个函数不是一个对象的属性时,直接作为函数来调用时,this指向全局对象。
2.方法调用,如果一个函数作为一个对象的方法来调用时,this指向这个对象。
3.构造函数调用,this指向这个用new新创建的对象。
- apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。apply接收参数的是数组,call接受参数列表,`` bind方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this指向除了使用new `时会被改变,其他情况下都不会改变。
new的创建过程:
首先创建了一个新的空对象
设置原型,将对象的原型设置为函数的prototype对象。
让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
5.Event Loop
执行顺序如下所示:
1)首先执行同步代码,这属于宏任务
2)当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
执行所有微任务
3)当执行完所有微任务后,如有必要会渲染页面
4)然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数
微任务包括 process.nextTick ,promise ,MutationObserver。
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。
6.http如何实现缓存
二.react
1.setState
只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。
setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次 setState , setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。
6.高阶组件
高阶组件是参数为组件,返回值为新组件的函数。HOC是纯函数,没有副作用。HOC在React的第三方库中很常见,例如Redux的connect组件。
高阶组件的作用:
1.代码复用,逻辑抽象,抽离底层准备(bootstrap)代码
2.渲染劫持
3.State 抽象和更改
4.Props 更改
三、计算机网络
1.http与https区别
1.HTTP
的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
2.HTTP
是不安全的,而 HTTPS 是安全的
3.HTTP
标准端口是80 ,而 HTTPS 的标准端口是443
4.在OSI
网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层
5.HTTP
无法加密,而HTTPS 对传输的数据进行加密
6.HTTP
无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书
2.安全防御
XSS避免方式:
1.url参数使用encodeURIComponent方法转义
2.尽量不是有InnerHtml插入HTML内容
3.使用特殊符号、标签转义符。
CSRF避免方式:
1.添加验证码
2.使用token:
1)服务端给用户生成一个token,加密后传递给用户
2)用户在提交请求时,需要携带这个token
3)服务端验证token是否正确
3.状态码
200响应成功
301永久重定向
302临时重定向
304资源缓存
403服务器禁止访问
404服务器资源未找到
500 502服务器内部错误
504 服务器繁忙
四、微前端
目前主流微前端方案:
1.iframe:基于iframe标签实现,技术难度低,隔离性和兼容性
2.基座模式:主要基于路由分发,qiankun和single-spa就是基于这种模式,监听路由来还在不同的应用,以实现应用间解耦
3.组合式集成,即组件单独打包和发布,然后在构建或运行时组合,类似npm包的形式
4.EMP,一种去中心化的微前端实现方案,它不仅能很好地隔离应用,还可以轻松实现应用间的资源共享和通信;
5.web components,它通过对组件进行更高程度的封装,来实现微前端,但是目前兼容性不够好,尚未普及。
qiankun基本原理:
将一个大型应用拆分成若干个更小、更简单,可独立开发、测试和部署的子应用,然后由一个基座应用根据路由进行应用切换。
基于sing-spa框架搭建的,在其基础上进行封装和增强,使其更加易用。
微前端的主要优势
1.技术兼容性好,各个子应用可基于不同技术架构
2.代码库更小、内聚性更高
3.便于独立编译、测试和部署,可靠性更高
4.耦合性更低,各个团队独立开发互不干扰
5.可维护性和扩展性更好,便于局部升级和增量升级
关于技术兼容性,由于在被基座应用加载前, 所有子应用已经编译成原生代码输出,所以基座应用可以加载各类技术栈编写的应用;
由于拆分后应用体积明显变小,并且每个应用只实现一个业务模块,因此其内聚性更强;
另外子应用本身也是完整的应用,所以它可以独立编译、测试和部署;
关于耦合性,由于各个子应用只负责各自的业务模块,所以耦合性很低,非常便于独立开发;
关于可维护性和扩展性,由于拆分出的应用都是完整的应用,因此专门升级某个功能模块就成为了可能,并且当需要增加模块时,只需要创建一个新应用,并修改基座应用的路由规则即可。
微前端缺点
1.子应用间的资源共享能力较差,使得项目总体积变大
2.需要对现有代码进行改造(指的是未按照微前端形式编写的旧工程)
qiankun与single-spa实现原理
微前端问题分为:
1.应用的加载与切换 :路由问题、应用入口、应用加载
2.应用的隔离与通信:js隔离、css样式隔离、应用间通信
single-spa很好地解决了路由和应用入口两个问题,但并没有解决应用加载问题,而是将该问题暴露出来由使用者实现(一般可以用system.js或原生script标签来实现);qiankun在此基础上封装了一个应用加载方案(即import-html-entry),并给出了js隔离、css样式隔离和应用间通信三个问题的解决方案,同时提供了预加载功能。
single-spa实现原理
1)路由问题
single-spa通过监听hashChange
和popState
这两个原生事件来检测路由变化的。
总的来看,当路由发生变化时,hashChange
或popState
会触发,这时single-spa会监听到,并触发urlReroute
;接着它会调用reroute
,该函数正确设置各个应用的状态后,直接通过调用应用所暴露出的生命周期钩子函数即可。
当某个应用被推送到appsToMount
后,它的mount函数会被调用,该应用就会被挂载;而推送到appsToUnmount
中的应用则会调用其unmount钩子进行卸载。
2)应用入口
single-spa采用的是协议入口,即只要实现了single-spa的入口协议规范,它就是可加载的应用。single-spa的规范要求应用入口必须暴露出以下三个生命周期钩子函数,且必须返回Promise,以保证single-spa可以注册回调函数:
1.bootstrap:用于应用引导,基座应用会在子应用挂载前调用它
2.mount: 用于应用挂载,就是一般应用中用于渲染的逻辑
3.unmount:用于应用卸载
3)应用加载
实际上single-spa并没有提供自己的解决方案,而是将它开放出来,由开发者提供。
qiankun原理
1)应用加载
针对上面我们谈到的几个弊端,qiankun进行了一次封装,给出了一个更完整的应用加载方案,qiankun的作者将其封装成了npm插件import-html-entry
该方案的主要思路是允许以html文件为应用入口,然后通过一个html解析器从文件中提取js和css依赖,并通过fetch下载依赖
2)js隔离
qiankun通过import-html-entry,可以对html入口进行解析,并获得一个可以执行脚本的方法execScripts。qiankun引入该接口后,首先为该应用生成一个window的代理对象,然后将代理对象作为参数传入接口,以保证应用内的js不会对全局window造成影响。由于IE11不支持proxy,所以qiankun通过快照策略来隔离js,缺点是无法支持多实例场景
3)css隔离
两种:一种是基于shadowDom的;另一种则是实验性的,思路类似于Vue中的scoped属性,给每个子应用的根节点添加一个特殊属性,用作对所有css选择器的约束
4)应用通信
qiankun思路是基于一个全局的globalState对象。这个对象由基座应用负责创建,内部包含一组用于通信的变量,以及两个分别用于修改变量值和监听变量变化的方法:setGlobalState和onGlobalStateChange。
1.解决的问题:
1)不同团队同时开发
2)每个团队开发的模块都可以独立开发,独立部署
3)实现增量迁移
基础:
1.flex布局
2.绝对布局和相对布局
3.作用域:全句作用域、函数作用域、块级作用域,自由变量的查找:函数定义的地方向上级查找
4.let const var????
var是es5语法,let const是es6语法,var有变量提升;
var和let是变量,可修改;const是常量,不可修改;(const指向的地址不可以更改,但内容可以改,所以const a={},a=1会报错,但a.b=1就不会报错)
let const有块级作用域(外面访问不到),var没有
5.this指向有哪几种?
1)普通函数:
1.new 方式:指向实例
2.非new方式:
1)fn() :指向window
2)obj.fn():指向obj
2)箭头函数:指向包裹箭头函数的第一个普通函数的this
3)call、bind、apply:this是第一个参数
6.深浅拷贝
浅拷贝拷贝的是地址,只能解决第一层的问题,如果下面的值有对象,想要用到深拷贝(拷贝的是值)
浅拷贝:object.assign()、...
深拷贝:JSON.parse(JSON.stringify(object))
//实现思路:先排除null和非object的情况,是数组返回[],是对象返回{},然后判断hasOwnproperty是不是自身属性,然后递归
7.原型:每个class都有自己的显式原型,每个实例都有自己的隐式原型,xiaoluo._proto = Student. prototype
8.继承:原型继承和class继承
1)原型继承:
a.组合继承:在子类的构造函数中通过Parent.call(this, value)继承父类的属性,然后改变子类的原型为 new Parent() 来继承父类的函数
function Child(value) {
Parent.call(this, value)
}
Child.prototype = new Parent()
缺点:子类的原型上会多出不需要的父类属性,内存上有点浪费
b.寄生组合继承:去掉Child.prototype = new Parent(),换成Object.create()即可
function Child(value) {
Parent.call(this, value)
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
}
})
2)class继承:expends+super(value)
注:super(value)就相当于Parent.call(this, value),所以必须调用
9模块化
1)立即执行函数()()
2)AMD、AMD
// AMD
define(['./a', './b'], function(a, b) {
// 加载模块完毕可以使用
a.do()
b.do()
})
// CMD
define(function(require, exports, module) {
// 加载模块
// 可以把 require 写在函数体的任意地方实现延迟加载
var a = require('./a')
a.doSomething()
})
3)CommonJS:常应用于服务器端、支持同步加载
require()方式引入、exports或者module.exports方式导出
4)es module:常用于浏览器端,支持异步加载
问题1:有commonjs,为什么还需要es
1)commonjs应用在服务器端,es一般用于浏览器,因为commonjs支持同步加载,而在浏览器端,由于网络延迟和文件大小限制,使用同步加载会导致浏览器假死或者加载时间过长;
2)同时coomonjs和es的语法和运行机制不同,可能会出现语法和运行错误
问题2:commonjs和es的区别
1)CommonJS 支持动态导入,es暂不支持
2)CommonJS是同步加载,因为用于服务器,文件都在本地,卡住主线程影响也不大;但es是异步导入,用于浏览器,需要下载文件,如果采用同步导入会对渲染有影响
3)CommonJS导出时时值拷贝,如果值更新需要重新导入,而es module实时绑定,会更新、
4)es module会编译成require/exports来执行
10.proxy??
11.手写promise
12.event loop
顺序:
1)先执行同步代码,这属于宏任务
2)执行完所有同步代码后,执行栈为空,看是否有异步任务要执行
3)执行所有的微任务,
4)当执行完所有的微任务后,看下是否需要渲染
5)下一轮event loop,执行宏任务里面的异步代码,如setTimeout
微任务:promise、
宏任务:script、setTimeout、setInterval 、setImmediate 、rendening、I/O
13.call\apply\bind、instanceof
call所有参数,
apply传数组
bind返回一个新函数
14.为什么0.1+0.2 != 0.3
因为 JS 采用的浮点数标准却会裁剪掉我们的数字,导致精度丢失,0.100000000000000002===0.1//true
办法:parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true
15.垃圾回收机制
标记清除
16事件机制:捕获、触发、冒泡
阻止冒泡:
event.stopImmediatePropagation()、
event.stopPropagation()
17跨域
1)jsonp:利用script标签没有跨域限制的漏洞
通过 <script> 标签指向一个需要访问的地址并提供一个回调函数来接收数据当需要通讯时
<script src="http://domain/api?param1=a¶m2=b&callback=jsonp"></script>
2)cors:浏览器和后端同时支持,服务端设置服务端设置 Access-Control-Allow-Origin 就可以开启 CORS
3)document.domain
该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。
只需要给页面添加 document.domain = 'test.com' 表示二级域名都相同就可以实现跨域
4)postmessage
适用于获取嵌入页面中的第三方数据。一个页面发送数据,另一个页面判断来源并接收
18.存储
cookie,localStorage,sessionStorage,indexDB
区别:
1)生命周期:
cookie一般由服务器生成,可以设置过期时间、localStorage和indexDB除非被清理,否则一直存在、sessionStorage页面关闭就清理
2)数据存储大小:
cookie:4k
localStorage,sessionStorage:5m
indexDB:无限
3)是否与服务器通讯?
cookie:会,每次请求会携带
其他都不
cookie属性及作用:
1)value:用于保护用户登陆态,应加密
2)hoot-only:不能通过js访问cookie,减少xss攻击
3)secure:只能在协议为https的请求中携带
4)same-site:规定浏览器不能在跨域请求中携带cookie,减少ccsrf攻击
19.缓存
react
1.react的setstate怎么看是异步还是同步
2.合成事件
react中所有的事件不是绑定到dom上,而是在document上监听所有事件,当事件触发冒泡到document上,React将事件内容封装交给中间层 SyntheticEvent React将事件内容封装交给中间层 SyntheticEvent ,SyntheticEvent再对使用统一的分发函数 dispatchEvent 将指定函数执行
阻止冒泡事件:event.preventDefault
实现合成事件的目的是什么?有什么好处?
1)抹平浏览器之间的兼容问题
2)性能:对于原生浏览器事件而言,浏览器会给监听器创建一个事件对象。如果有多个,会造成高额的内存分配问题。
但对于合成事件来说,有一个事件池子来管理创建和销毁
3.前端路由原理?本质就是监听url的变化,然后匹配路由规则,显示相应的页面,并且无需刷新。目前有两种方式:hash模式、history模式
1)hash模式
当#后的哈希值变化,可以通过hashchange事件来监听,从而跳转页面
2)history模式
改变url:history.pushState 和 history.replaceState
后退:popState。如window.addEventListener('popstate', e => {})
两种模式对比:
1)hash模式只可以更改#后面的内容,history模式可以通过api设置任意的同源url
2)history模式可以通过api添加任意类型的数据到历史记录,hash模式只能更改哈希值
3)hash模式无需后端配置,兼容性好。history模式需要发起url请求,后端需要配置index.html页面用于匹配不到静态资源的时候
4.react-route实现原理
1)基于hash路由:监听hashchange事件,感知hash的变化(loaction.hash=xxx)
2)基于history路由:
改变 url 可以通过 history.pushState 和 resplaceState 等,会将URL压入堆栈,同时能够应用 history.go() 等 API;
监听 url 的变化可以通过自定义事件触发实现
5.react-route实现思想:
1)基于history实现上述不同客户端路由实现思想,保存历史记录等,磨平浏览器差异,上层无感知
2)通过维护的列表,在每次url发生变化的回收,通过配置的路由路径,匹配到对应component,并且redner
6.如何配置 React-Router 实现路由切换
1)使用<Route> 组件:
<Route path='/about' component={About}/>
2)结合使用 <Switch> 组件和 <Route> 组件
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
3)使用 <Link>、 <NavLink>、<Redirect> 组件
7.React-Router怎么设置重定向?
使用<Redirect>组件实现路由的重定向:
<Switch>
<Redirect from='/users/:id' to='/users/profile/:id'/>
<Route path='/users/profile/:id' component={Profile}/>
</Switch>
8.react生命周期?React 的异步请求应该放在哪里,为什么?
componentDidMount。从事件顺序上来看,也可以:
1)constructor:可以放,但不推荐,因为constructor主要用于初始化state与函数绑定,并不承载业务逻辑
2)componentWillMount:已废除
所以componentDidMount是最好的选择
9.React Fiber架构
10.调和过程中setState内部做了什么?
1)将传递给setState对象合并到组件的当前状态
2)启动和解(reconciliation):react构建一个新的react元素树,根据新的状态来更新UI
3)将新树与上一个元素树diff
11.setState异步还是同步?
异步:合成事件和钩子函数中
同步:原生和setTimeout中
主要通过isBatchingUpdates是否为true来决定是先存进队列还是直接更新
为true表示是在可以控制的地方,如生命周期和合成事件中;false则表示在react无法控制的地方,比如原生事件(如addEventListener 、setTimeout、setInterval)等事件
12.setState 的批量更新
在异步中如果对同一值进行多次setState,内部会对其覆盖,取最后一次执行
在react生命周期和合成事件执行前后都有相应的钩子,分别是pre钩子和post钩子,pre钩子会调用batchedUpdate方法将isBatchingUpdates变量置为true,开启批量更新,而post钩子会将isBatchingUpdates置为false
isBatchingUpdates变量置为true,则会走批量更新分支,setState的更新会被存入队列中,待同步代码执行完后,再执行队列中的state更新。 isBatchingUpdates为 true,则把当前组件(即调用了 setState的组件)放入 dirtyComponents 数组中;否则 batchUpdate 所有队列中的更新
而在原生事件和异步操作中,不会执行pre钩子,或者生命周期的中的异步操作之前执行了pre钩子,但是pos钩子也在异步操作之前执行完了,isBatchingUpdates必定为false,也就不会进行批量更新
13.事务机制
4.hooks是什么?
5.为什么不能在条件语句中写 hook
1.缓存:强缓存和协商缓存
1)强缓存:
表示缓存期间不需要请求,可以通过两种响应头实现:
Expires / Cache-Control(优先级高)
Expires: Wed, 22 Oct 2018 08:41:00 GMT
//表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT 后过期,需要再次请求。并且 Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效
Cache-control: max-age=30
//表示资源会在 30 秒后过期,需要再次请求
2)协商缓存
如果缓存过期,需要通过协商缓存解决。协商缓存需要请求,如果缓存有效会返回304,需要客户端和服务端共同实现,有两种方式:Last-Modified 和 ETag(优先级高)
a.Last-Modified 和 If-Modified-Since
Last-Modified表示本地文件最后修改日期,if-modified-since会将Last-Modified的值发送给服务器,查询在该日期后资源是否有更新,如果有就会将新资源更新
缺点:
1)如果在本地打开缓存文件,也会造成Last-Modified被修改
2)因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源
所以HTTP / 1.1 出现了 ETag
b.ETag 和 If-None-Match
ETag类似于文件指纹,If-None-Match会将ETag值发送给服务器,查询是否有改动,有的话就将新资源发送回来
怎么选择?
1)不需要缓存的资源:使用Cache-control: no-store ,表示该资源不需要缓存
2)频繁变动的资源:可以使用 Cache-Control: no-cache 并配合 ETag 使用,表示该资源已被缓存,但是每次都会发送请求询问资源是否更新
3)对于代码文件来说,通常使用 Cache-Control: max-age=31536000 并配合策略缓存使用,然后对文件进行指纹处理,一旦文件名变动就会立刻下载新的文件
2.图片优化
1)不用图片。用css代替
2)用cdn加载,计算适配屏幕的宽度,然后请求响应建材好的图片
3)小图用base64格式
4)雪碧图(多个图标整合到一张图片中)
5)选择正确的图片格式
a.能够显示 WebP 格式的浏览器尽量使用 WebP 格式
(较好的图像数据压缩算法,优化体积,图片质量几乎无差别,缺点就是兼容性不好)
b.小图用png,大部分图标可以用svg代替
c.照片使用jpeg格式
display:flex
1.方向属性:flex-direction
2.换行flex-nowrap
3.主轴方向排列justify-content
2)flex-end
4.space-around
5.space-between
4.竖轴方向align-items
5.行与行之间的排列align-content
6.单独排序order(数值小的靠前排)
7.剩余空间分配比例flex-grow
1:1
2:1
如何收缩flex-shrik:1
单独设置某个元素的位置align-self
-
auto
-
flex-start
-
flex-end
-
center
-
baseline
-
stretch
事件执行机制
回流:
怎么避免回流:
垃圾回收机制:
1.引用计数:
问题:位置不连贯,会有找位置问题
v8优化-标记算法:
根据是否频繁回收,小的需要频繁回收,大的不需要
老生代:
箭头函数
xxs攻击
enhanceHybird性能优化
css内容:
1.盒模型:标准盒子模型、IE盒子模型
区别:
1)标准盒子模型:margin、border、padding、content
2)IE盒子模型:margin、content(content+padding+border)
怎么转换
box-sizing:content-box //标准盒子模型
box-sizing:border-box //IE盒子模型
2.line-height和height区别?
line-height是每一行文字的高,如果文字换行整个div盒子高度会撑大(行数*行高)
height:固定值,即该盒子的高度
3.css选择符号有哪些?哪些可以继承
css选择符:通配(*)、
id选择器(#)、
类选择器(.)、
标签选择器(div、p、h1)、
相邻选择器(+)、
后代选择器(ul li)、
子元素选择器(>)、
属性选择器(a[href])
哪些可以继承:font-size、color、line-height、text-aligin
哪些不可以继承:border、padding、margin
4.css优先级
!important >内联(1000)>id(100)>class(10)>标签/伪元素选择器(1)>通配(0)
5.css画一个三角形(用边框画:在哪个位置就把哪个边框颜色保留,其他全置为透明)
eg:画一个“上”三角形
.div{
width:0;
height:0;
boder-top:100px solid #ccc;
boder-left:100px solid transparent;
boder-right:100px solid transparent;
boder-bottom:100px solid transparent;
}
6.水平居中,不给宽高
<div class='parent'>
<div class='children'>子</div>
</div>
方式一:flex,给父元素设置
.parent{
display:flex;
justify-content:center;
align-items:center;
}
方式二:绝对定位(absolute +left50%+right50%+translate-50%)
.parent{
position: relative;
}
.children{
position: absolute;
left:50%;
right:50%;
transform:translate(-50%, -50%);
}
7:display有哪些值?说明作用
none: 隐藏元素
block:把某元素转换成块元素
inline:把某元素转换成行内元素,让元素拥有行内元素的特性
inline-block:把某元素转换成行内块元素
flex:设置为弹性伸缩盒
8.BFC理解(块级格式化上下文)
1)bfc理解:页面上独立容器,容器的子元素不影响外面的元素
2)bfc原则:如果一个元素具有bfc,那么内部元素再怎么弄,都不会影响到外面的元素
3)如何触发:
float的值非none、
overflow的值非visible、
display的值为:inline-block、table-cell
position的值为:absolute、fixed
9.清除浮动方式?第三种常用
1)触发bfc :overflow:hidden
2)子元素新增一个div标签,clear:both
3):after增加内容:
父元素:after{
content:'';
display:none;
clear:both
}
10.position有几种定位?分别根据什么定位
static默认值,没有定位
fixed:根据浏览器窗口定位
relative:相对于自身定位,不脱离文档流
absolute:相对于第一个有relative的父元素,脱离文档流
ps:relative如果设置了left、right、top、bottom,生效的只有left、top;而absolute不影响
11.css reset用来重置默认样式
12.css雪碧图
1)是什么:把多个小图标合并成一张大图片
2)优缺点:
优点:减少http请求,提升性能
缺点:难维护
13.哪些方式可以隐藏元素?
opacity:0设置透明度
display:none彻底隐藏了元素,元素从文档流中消失,既不占据空间也不交互,也不影响布局
visibility:hidden隐藏了元素,占据空间,但是不可以交互了
overflow:hidden只隐藏元素溢出的部分,但是占据空间且不可交互
z-index:-9999: 原理是将层级放到底部,这样就被覆盖了,看起来隐藏了
transform:scale(0,0): 平面变换,将元素缩放为0,但是依然占据空间,但不可交互
14.display: none 与 visibility: hidden 的区别
1)重排重绘:display: none会造成文档重排,visibility只会重绘
2)是否读内容?读屏器只会读visibility:hidden内容
3)是否从渲染树消失?
display:none会消失,并且不占空间
visibility:hidden不会消失,会占空间,但内容不可见
4)是否为继承属性?
visibility:hidden是,子孙节点可通过visibility:visible使节点显式
display:none不是
待办:两栏布局、三栏布局
》〉》〉
css篇
1.块元素和行内元素
2.想让offsetwidth为100,需要设置border-box
3.bfc理解和应用?
概念:块级格式化上下文,一块独立渲染区域,内部元素的渲染不会影响边界以外的元素
触发条件:
1)float不是none
2)position是absolute或者fixed
3)overflow不是visible
4)display是flex、inline-block
应用:清除浮动
设置overflow:hidden
4.清除浮动
1)额外标签
父
<子:style={clear:both}></>
2)bfc
<父 style ={overflow:hidden}></>
3)伪类选择器+clearfloat
parent:after{
content:'',
display:block,
clear:both,
hight:0,
visibility:hidden
}
5.relative与absolute
relative:依据自身定位
absolute依据最近上一层定位
6.line-height如何继承
所以p标签的line-height的值为20*200%=40px
响应式
7.px、em、rem、%、vh/vw是什么?
px,是绝对长度单位,最常用
em,相对长度单位,相对于父元素,不常用
rem,相对长度单位,相对于根元素,常用于响应式
%,相对长度单位,相对于父元素
vh/vw,相对长度单位,相对于屏幕高度/屏幕宽度
8.响应式布局的常用方案
先设置苹果6/7/8的html字体大小为100px作为基准,然后换算苹果5小尺寸的,86/100 = 320/375,大尺寸的同理
eg:body可基于html父元素设置rem
[图片上传中...(image.png-27e93d-1708348213008-0)]
网页视口尺寸:
屏幕高度:window.screen.height
网页视口高度:window.innerHeight
body高度:window.body.clinetHeight
vh/vw,相对长度单位,相对于屏幕高度/屏幕宽度
vmax/vmin 两者最大值/最小值
js进阶
1.event loop
异步回调的实现原理
执行过程:
1)先执行同步代码,这属于红任务
2)执行完同步代码,看下执行栈是否为空,再执行异步代码
3)执行微任务
4)执行完所有的微任务,看是否需要渲染
5)进入下一轮event loop,执行宏任务中的异步代码,如setTimeout中的回调函数
//Hi Bye cb1
ps:从上到下执行,遇到同步代码console.log('Hi'),进入call back,所以打印"Hi",执行完就清空了,然后继续走遇到setTimeout定时器,这个属于web apis,所以进入web apis此时有一个定时器任务(等待5s后执行),时间没到之前继续往下走,遇到同步代码console.log('Bye'),然后就进入call back栈里执行console.log('Bye'),所以打印'Bye',执行完清空。
再继续往下走,发现没有可执行代码了,执行栈call stack为空,启动evevt loop,此时5s时间还没到,此时callback queue依旧为空,等到5s后,定时器里面的函数console.log('cb1')被推到callback queue里执行,执行后callback queue会推console.log('cb1')到执行栈call stack里执行,执行完清空
2.DOM和event loop
//Hi Bye button clicked
ps:从上到下执行,先打印"Hi",再执行click事件代码,但只能用户点击了才能出发,所以会放到web apis里等待触发,再继续执行'Bye'代码,此时执行栈中为空,启动event loop,发现还是没有什么可执行(这里如果用户还是没触发,就不会打印点击事件里面的代码),等到用户点击触发click事件,这个事件被推到callback queue里,从而打印'button clicked'
3.Promise
3.1 then和catch改变状态
then正常返回resolved,里面有报错则返回rejected、
catch正常返回resolved,里面有报错则返回rejected
.catch返回的是
3.async/await
用来解决回调地狱,Promise then catch链式调用,虽然也可以解决回调地狱,但也是基于回调函数实现的,而async/await是同步语法,彻底消灭回调函数
与Promise的关系
1)执行async函数,返回的是Promise对象
2)await相当于Promise的then
3)try...catch可捕获异常,代替了Promise的catch
4.宏任务与微任务
宏任务:setTimeout、setInterval、ajax、dom事件
微任务:Promise、async/await
微任务执行时机比宏任务早(微任务在dom渲染前触发,是es6语法规定的,宏任务在dom渲染后触发,是浏览器规定的)
场景题
async相当于返回一个Promise,await相当于.then()
所以,
左边打印: Promise resolve ,100
右边打印:start 100 200 报错后面都不执行(await相当于then(),但里面是reject,then接收不了)
hooks的坑:
1.useState初始化值,只有第一次有效
描述:父通过props传给子组件propssum,子组件用新变量sum保存,初始化的时候子组件会是传过来的值,但父组件更新了,子组件不会更新
解决:
hooks:通过useEffect的第二个参数监听某个poros值,然后set
import React, { useEffect, useState } from "react";
const ChildModal = (props) => {
console.log('props', props);
const [sum, setSum] = useState(-1);
//通过第二个参数更新
// useEffect(() => {
// setSum(props.sum);
// }, [props.sum])
return (
<div>子组件的数量:{sum}</div>
)
}
export default ChildModal
class组件:通过生命周期 componentWillReceiveProps(props) {
console.log(props)
this.setState({show: props.checked})
}
ps:
或者直接用props.sum渲染
const ChildModal = (props) => {
console.log('props', props);
return (
<div>子组件的数量:{props.sum}</div>
)
2.useEffect内部不能修改state,第二个参数需要是空的依赖[]
会出现闭包陷阱
function UseEffectChangeState() {
const [count, setCount] = useState(0)
// 模拟 DidMount
const countRef = useRef(0)
useEffect(() => {
console.log('useEffect...', count)
// 定时任务
const timer = setInterval(() => {
console.log('setInterval...', countRef.current) // 一直是0 闭包陷阱
// setCount(count + 1)
setCount(++countRef.current) // 解决方案使用useRef
}, 1000)
// 清除定时任务
return () => clearTimeout(timer)
}, []) // 依赖为 []
// 依赖为 [] 时: re-render 不会重新执行 effect 函数
// 没有依赖:re-render 会重新执行 effect 函数
return <div>count: {count}</div>
}
export default UseEffectChangeState
useEffect中第二个参数[]+返回函数,模拟的是conponmentDidMount这个生命周期,所以re-redenr的时候不会走进来,所以不会拿到最新的值,所以需要借助useRef来获取
3.useEffect可能出现死循环,依赖[]里面有对象、数组等引用类型,把引用类型拆解为值类型
useEffect(() => {
setSum(props.sum);
}, [props.sum,props ])
如果第二个参数有对象porps,会陷入死循环,因为会进行浅比较,Object.is({},{})//false
React揭秘:
前言:主浏览器更新pi
相较于 React15,React16 中新增了Scheduler(调度器)
React16 架构可以分为三层:
Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
Reconciler(协调器)—— 负责找出变化的组件
Renderer(渲染器)—— 负责将变化的组件渲染到页面上
1.Scheduler(调度器)
实现了功能更完备的requestIdleCallback polyfill,需要当浏览器有剩余时间的时候通知我们,并提供优先级任务
2.Reconciler(协调器)
React15 中Reconciler是递归处理虚拟 DOM 的;
React16 中Reconciler从递归变成了可以中断的循环过程。
那如何解决中断更新时 DOM 渲染不完全的问题?
16中,Reconciler与Renderer不再是交替工作的,而是当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟 DOM 打上代表增/删/更新的标记。只有当所有组件都完成Reconciler的工作,才会统一交给Renderer
3.Renderer(渲染器)
其中红框中的步骤随时可能由于以下原因被中断:
1)有其他更高优任务需要先更新
2)当前帧没有剩余时间
从React15到React16,协调器(Reconciler)重构的一大目的是:将老的同步更新的架构变为异步可中断更新
为什么没有用Generator来实现协调器?
因为Generator只能解决“单一优先级任务的中断与继续”情况下实现异步可中断更新,一旦有‘高优先级任务插队’就不能实现了(因为如果有a任务在执行,此时已经完成a任务的计算,高优先级任务b插队,此时参数变了,需要重新计算,如果通过全局变量保存之前执行的中间状态,又会引入新问题
)。因此React没有采用Generator实现协调器
二、Fiber
1.Fiber的含义:
1)之前React15的Reconciler采用递归的方式执行,数据保存在递归调用栈中,所以被称为stack Reconcile
2)作为静态的数据结构来说,每个Fiber节点对应一个React element,保存了该组件的类型(函数组件/类组件/原生组件...)、对应的DOM节点等信息
3)作为动态的工作单元来说,每个Fiber节点保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新...)
2.双缓存:在内存中构建并直接替换的技术
为什么要双缓存?
绘制页面时,每一帧绘制前都会先将上一帧的画面清除,当计算量比较大时,就会在两帧之间出现较长空隙,白屏。
所以为了解决该问题,可以现在内存中绘制当前帧,绘制完毕后直接替换,节省白屏时间
3.双缓存fiber树
1)current Fiber树:当前屏幕上显示内容对应的Fiber树
2)workInProgress Fiber树:正在内存中构建的Fiber树
当workInProgress Fiber树构建完成交给Renderer渲染在页面上后,应用根节点的current指针指向workInProgress Fiber树,此时workInProgress Fiber树就变为current Fiber树
每次状态更新都会产生新的workInProgress Fiber树,通过current与workInProgress的替换,完成DOM更新
4.mount时、update时的构建/替换流程
mount阶段:
1)首次执行ReactDOM.render会创建fiberRootNode(源码中叫fiberRoot)和rootFiber。
其中:
fiberRootNode是整个应用的根节点,
rootFiber是<App/>所在组件树的根节点。
fiberRootNode的current会指向当前页面上已渲染内容对应Fiber树,即current Fiber树
2)接下来render阶段,根据组件返回的JSX在内存中依次创建Fiber节点并连接在一起构建Fiber树,被称为workInProgress Fiber树。(下图中右侧为内存中构建的树,左侧为页面显示的树)
构建内存树时会复用current Fiber树中已有的Fiber节点内的属性,在首屏渲染时只有rootFiber存在对应的current fiber(即rootFiber.alternate)。
3)内存树替换更新到页面
fiberRootNode的current指针指向workInProgress Fiber树使其变为current Fiber 树
update时:
1)当触发状态改变时,开启新的render阶段,构建新内存树
和mount时一样,workInProgress fiber的创建可以复用(是否复用就是diff算法的过程)current Fiber树对应的节点数据
2)内存树在render阶段完成构建后进入commit阶段渲染到页面上。渲染完毕后,内存树(workInProgress Fiber 树)变成current Fiber 树
小结:上面介绍了Fiber树的构建与替换过程
问题:构建过程中每个fiber节点怎么创建?