基础
1. 原型继承实现方式
每个对象拥有一个原型对象,通过 `__proto__` 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 `null`
* 定义新的构造函数,并在内部用call()调用希望“继承”的构造函数,并绑定this(Parent.call(this, props);)
* 借助中间函数F实现原型链继承,最好通过封装的inherits函数完成;
* 继续在新的构造函数的原型上定义新方法
let F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
[原型继承](https://www.liaoxuefeng.com/wiki/1022910821149312/1023021997355072)
2. es5与es6继承区别
ES5继承的本质:先创建实例对象this,再将父类方法添加到this上面。
思路:创建父类 创建子类 建立关系 通用继承方法与super
ES6继承实质:先创造父类的实例对象this,用子类的构造函数修改this.
[ES5和ES6继承的区别](https://blog.csdn.net/wenmin1987/article/details/81223088)
3. 前端模块化
js模块化规范有commonJS、AMD、CMD以及ES6的模块系统
* commonJs用同步方式加载模块。主要应用于node
* AMD用异步方式加载模块,使用require.js实现。主要应用于前端(require.js在申明依赖的模块时会在第一时间加载并执行模块内的代码)
* CMD与AMD类似,不同的是AMD推崇依赖前置,提前执行,CMD推崇依赖就近,延后执行。(CMD规范是在sea.js推广中产生的)
* es6 module export import
es6与commonJs的差异
* es6输出的是值的引用,commonJs输入的是值的拷贝
* es6是编译时加载,commonJs是运行时加载(es6模块不是对象,是通过export显示输出的一段代码,commonJs模块是一个对象)
4. 深浅拷贝
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一内存空间。深拷贝不只是对指针的拷贝,还会对指针指向的内容进行拷贝,拷贝完成后,两个指针分别指向不同的地址。
深拷贝:
json的stringfy、parse实现; 遍历对象属性(不包含原型链上的属性)
var obj2 = $.extend(true, {}, obj1)
使用递归复制对象的所有层级属性
扩展符、Object.assign可实现一层拷贝
[深浅拷贝](jianshu.com/p/11acbe692e4c)
5. 闭包理解及应用
有权访问另一个函数变量的函数称为闭包。
闭包的意义:
a. 延长变量的生命周期(虽然变量对象所属的执行上下文已被销毁,但被其它作用域链所引用,导致无法被垃圾回收)
b. 创建私有作用域(vue的data方法是使用闭包设计的,以此确保各组件数据不会相互干扰)
* 闭包会常驻内存,所以慎用闭包
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
应用:
* for循环中,定时器延时打印问题(使用自执行函数或es6)
* 防抖函数
* 使用闭包设计单例模式
* 使用自执行函数
``` javascript
var person = (function() {
var name = 'pan';
var age = 18;
var instance = null;
function init(){
return{
getName: function() {
return name
},
getAge: function() {
return age
}
}
}
return {
getInstance: function() {
if(!instance){
instance = init();
}
return instance;
}
}
})();
```
6. 浏览器渲染过程
浏览器渲染进程是多线程的,分为**GUI渲染进程,js引擎线程,事件触发线程,定时器线程及异步http线程**。GUI渲染进程负责解析html与css,构建Dom树和render树,布局与绘制等等。
js分同步任务与异步任务。同步任务都在主线程执行,形成一个执行栈。主线程之外,事件触发线程管理着一个任务队列,异步任务执行有了结果,就会在任务队列中放入一个事件。执行栈中的同步内容执行完毕之后,就会执行任务队列中的内容
7. 输入URL到浏览器完成页面渲染的发生了什么
* DNS域名解析
* 建立TCP链接(三次握手)
* 浏览器发送http请求
* 服务器处理请求
* 返回响应结果(四次挥手)
* 浏览器解析html
* 浏览器布局渲染
[从浏览器多进程到JS单线程](https://segmentfault.com/a/1190000012925872)
[输入URL到浏览器完成页面渲染的发生了什么](https://juejin.im/post/6844903885404897294)
8. 性能优化
根据network面板(查看请示数量,请求排队时间,是否命中缓存),lighthouse面板(通过不同的指标给网站打分并给出优化建议)来分析网站性能。webpack-bundle-analyzer插件也可以分析页面。
* react优化
* webpack优化
* 减少http请求
* cdn
* 减少不必要的dom
* 减少重绘和回流
* 压缩css,js
9. spa单页面优缺点
优点:
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
- 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
- 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
- SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
10. 防抖与节流
防抖:某个函数在某个时间内,无论触发多少次,都只执行一次。如:input输入回调事件添加防抖函数后,只会在停止输入后触发一次
节流:高频事件在n秒内只执行一次,如resize,监听滚动事件添加节流函数后,每隔固定的一段时间执行一次
``` javascript
var fn = function(){
console.log('fn')
}
window.onresize = debounce(fn,500)
window.onresize = throttle(fn,500)
function debounce(fn){
var timer = null
return function(){
//timer第一次执行后会保存在内存里 永远都是执行器 直到最后被触发
if(timer){
clearTimeout(timer)
timer = null
}
timer = setTimeout(function(){
fn()
},100)
}
}
function debounce(fn) {
var timer = null
return function(){
if(!timer){
clearTimout(timer);
timer = null;
}
timer = setTimeout(function(){
fn.aplly(this, arguments)
}, 500)
}
}
function throttle(fn) {
var canRun = true;
return function() {
if(!canRun) return;
canRun = false
setTimeout(function(){
fn.apply(this, arguments);
canRun = true;
}, 500);
}
}
```
节流
11. 判断数据类型
typeof 判断基本数据类型
instanceof 判断构造函数的prototype是否出现在对象的原型链上(obj intanceof fn)
Object.prototype.toString.call() 判断全数据类型
12. Call,apply,bind区别
* 都是修改this指向
* 其中apply与call的区别是:apply接收参数数组,而call接收参数列表
* bind返回的是一个函数,其参数也是参数列表
13. 模拟实现apply与call, bind(还未实现)
```
Function.prototype.mycall = function(context){
var context = context || window;
var args = [];
for(var i=1; i<arguments.length; i++){
args.push(arguments[i])
}
context.fn = this;
context.fn(...args);
delete context.fn;
}
Function.prototype.myapply = function(context, args){
var context = context || window;
context.fn = this;
if(!args){
context.fn()
}else{
if(!(args instanceof Array))
thorw new Error("params must be array")
context.fn(...args);
}
delete context.fn;
}
var bar = {
a: 1,
b:function() {
console.log(this.a)
}
}
```
14. Html是啥?js是什么?
js是单线程动态语言。(动态语言是指在运行时检查数据类型,一般不会给变量指定类型,而静态语言是是在运行编译前检查数据类型)
Html超文本链接标示语言,WEB网页设计语言,XHTML是一个基于XML的置标语言
15. 基本数据类型
undefined null Boolen String Number Object Symbol(表示独一无二的值,由Symbol方法生成 let s = Symbol())
16. Mvvm是什么
mvvm实现了前后端分离,可各自独立开发 mvc则不可以
17. new的实现
```
function create() {
// 创建一个空的对象
var obj = new Object(),
// 获得构造函数,arguments中去除第一个参数
Con = [].shift.call(arguments);
// 链接到原型,obj 可以访问到构造函数原型中的属性
obj.__proto__ = Con.prototype;
// 绑定 this 实现继承,obj 可以访问到构造函数中的属性
Con.apply(obj, arguments);
// 返回对象
return obj;
};
```
. Reduce实现map
18. 创建对象方式
构造函数, 原型模式,组合模式
```
function People() {
this.name = 'ye';
}
People.prototype = {
constructor: People,
getName: function(){
return this.name;
}
}
var oPeople = new People();
```
19. 变量提升
同名的变量与函数,函数提升优于变量提升,同名的变量不会覆盖同名的函数,而同名的函数会覆盖同名的变量
* 变量提升只提升声明不提升赋值
* 有俩个同名方法,默认调用最后一个方法
* 只有函数声明才存在函数提升,函数表达式不存在函数提升
* 函数声明会置顶,变量声明也会置顶,函数声明比变量声明更置顶
* 函数与变量同名时,声明为函数
``` javascript
console.log(fn);
var fn = () => {}
//function fn() {}
var fn;
console.log(fn);
var fn = 2
console.log(fn);
解析为=>
var fn;
console.log(fn);
fn = () => {}
console.log(fn);
fn = 2
console.log(fn);
```
20. 重绘与回流
重绘是更改样式,回流是重新构建
回流一定会重绘,而重绘不一定会回流。只有颜色改变只会重绘,不会引起回流,
21. 前端框架选型
[前端框架选型](https://www.cnblogs.com/chengxs/p/10910004.html)
22. a==1 && a==2 && a==3
js的隐式转换(默认调用valueOf方法)及==强制类型转换
```
var a = {
num: 0,
valueOf: function() {
return this.num += 1;
}
}
console.log(a==1 && a==2 && a==3)
```
23. 点击li输出index值
* 给li添加点击事件,可使用let声明块级作用域或使用闭包或使用自执行函数
* 给ul添加点击事件,获取所有的li并转换为数组,结合indexOf使用
```
li添加事件
ul添加事件
var nodeList = document.getElementsByTagName('li') ,
arrNodes = Array.prototype.slice.call(nodeList) ,
nodeUls = document.getElementsByTagName('ul') ;
nodeUls[0].addEventListener("click",function(event){
var event = event || window.event;
var target = event.target || event.srcElement;
alert(arrNodes.indexOf(target))
},false);
```
[链接](https://www.jianshu.com/p/c410342de707)
24. 含有length属性的元素转换为数组
* Array.prototype.slice.call(oLi);
* Array.from(...new Set());
* for循环
25. addEventListener与click事件的区别
* click
* 是Dom0级规范,所有浏览器都支持
* 定义多个click事件,只执行最后声明的事件
* addEventListener
* 是Dom2级规范,只有支持Dom2级事件处理程序的浏览器(IE9,chrome,firefox,safari)才支持这个方法
* 可以为一个事件注册多个监听器,它们先后运行且不被覆盖
* 其第三参数表示是在捕获阶段还是在冒泡阶段处理事件(默认值为false,代表在冒泡阶段处理,一般会省略)
26. 判断属性是否存在
in 运算符(实例对象的属性或继承自对象的属性) 或是 hasOwnProperty方法来判断
‘count’ in obj || obj.hasOwnPrototype('count')
css
1. css盒子模型
盒子模型包括:content、padding、margin、border。可通过box-sizing设置
标准模型的宽高为content的宽高
IE模型的宽高包括border
js获取盒模型的宽高:
```javascript
dom.style.width/height:内联样式的宽高
dom.currentStyle.width/height:渲染后的最终宽高(IE)
window.getComputedStyle(dom).width/height:DOM标准,不支持IE
dom.getBoundingClientRect().width/height:计算元素的绝对位置(视窗左顶点为起点,含left/right/height/width)
```
2. BFC
BFC是什么?块级格式化上下文,可以当作一个容器,容器里面的元素不会影响容器外面的元素
BFC的特性:
* BFC是块级元素,块级元素在垂直方向上依次排列
* BFC是一个独立容器,容器中的子元素不会影响到外面的元素
* 属于同一个BFC的两个相邻元素的margin会重叠
* BFC容器的高度,浮动元素也会参加
创建BFC的方法:设置overflow/display/position
* overflow: hidden;
* display: flex;
* display: inline-flex;
* display: inline-block;
* position: absolute;
* position: fixed;
BFC的使用场景:
* 高度坍塌:父元素在子元素浮动后高度变为0
* margin边距合并,包括[父子边距](http://js.jirengu.com/nerisokuha/1/edit)
* 上下边距重叠
* 左边宽度固定,右边自适应的两栏布局(BFC区域不会与浮动区域重叠,添加overflow:hidden auto可触发BFC)[示例](http://js.jirengu.com/wunigorozu/1/edit)
3. 硬件加速
CSS3 硬件加速又叫做GPU加速(加速图形处理速度),是利用 GPU 进行渲染,减少CPU操作的一种优化方案。由于GPU中的transform3d等CSS属性不会触发repaint,所以能大大提高网页的性能。如下几个css属性可以触发硬件加速:
transform( translate3d、translateZ(0)等)
opacity
filter(滤镜:drop-shadow()、opacity(),函数与已有的box-shadow、opacity属性很相似;不同之处在于,通过滤镜,一些浏览器为了更好的性能会提供硬件加速)
will-change:哪一个属性即将发生变化,进而进行优化。
4. 垂直居中
* 使用absolute与margin实现
* 使用absolute与transform实现
* 使用flex实现:父元素flex,align-items:center;justify-content:center
5. 移动端兼容性问题
1. 1像素问题
2. input框在fix布局下,光标乱飞
3. 穿透问题
4. ios键盘弹起挡住输入框
6. rem:设置html的font-szie为(屏幕宽度/设计稿宽度)px
7. 移动端适配方案:rem+media, vw+vh
8. css3
translate, rotate, skew,border-radius,text-shadow
es6
1. promise
promise是异步编程的一种解决方案,比回调函数更加合理。状态:pending fufilled rejected,且状态改变不可逆。
promise链式调用,不如同步写法简洁,但是async也有缺点,就是如果请求超时,会阻塞之后的代码;promise不好捕获错误;
[promise实现步骤](https://www.jianshu.com/p/b4f0425b22a1)
2. promise与async/await
带 async 关键字的函数,它使得你的函数的返回值必定是 promise 对象
用await声明的Promise异步返回,必须“等待”到有返回值的时候,代码才继续执行下去。
3. 事件循环
JavaScript 主线程拥有一个**执行栈**(原因是js的用途就是与用户互动并操作dom) 以及一个**任务队列**,主线程会依次执行代码,执行完毕后执行微任务队列,当微任务队列中的内容都执行完毕后,再提取一个宏任务来执行,直到所有的宏任务都执行完毕。这就是事件循环
常用的微任务 Promises;宏任务:定时器,**script(整体代码块)**
[事件循环机制](http://www.ruanyifeng.com/blog/2014/10/event-loop.html)、
4. var、let、const
* var声明的变量会挂载在window上,而let和const声明的变量不会:
* var 存在变量(声明)提升,而let和const不会
* let const存在暂时性死区(在声明前不可使用)
* let const不可重复声明
* const用来声明常量,且声明后不可改变
5. 解构
```
let a = 1, b = 2;
通过解构实现变量交换
[a, b] = [b, a]
```
6. set使用场景
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
* 数组去重
* 求交集 [...a].filter(x => b.has(x))
* 求差集 [...a].filter(x => !b.has(x))
* 求并集 [...new Set([...a, ...b])]
http
1. http状态码
* 2** 请求成功 204,请求成功但返回响应报文无内容返回,206(进行范围请求)
* 3** 重定向 304未修改 301永久重定向,302临时重定向,307与302相同
* 4** 客户端问题 400请求报文存在语法错误,401未认证,403被服务器拒绝访问
* 5** 服务器错误 500服务器内部错误
2. http1.x http2.0区别
http1.0 是短链接,浏览器发送请求,服务器返回结果
http1.x request header中加入Connection: Keep-Alive,keep-alive的链接会保持一段时间不断开,后继请求复用该TCP。长链接复用,多个请求排队串联单线程处理。一旦有某请求超时等,后续请求只能被阻塞
http2.0 多路复用,通过更小的二进制帧构成多条数据流,交错的请求和响应可以并行传输而不被阻塞。而且h2有首部压缩功能,如果两个请求的首部相同,即可省去这一部分,减少请示体积
http1解析基于文本,文本有多样性,需要考虑多种场景,而http2基于二进制,实现方便且快速
在network中即可查看当前请求,哪个是http1, h2
参考: <https://juejin.cn/post/7001510315514937375>
https://juejin.cn/post/6966857691381645325#heading-5
3. 浏览器缓存
浏览器加载资源时,通过cache-control和expire判断是否命中了强缓存,如果命中,直接从缓存中读取数据(此时status是200)。如果未命中,一定会向服务器发送请求,通过判断Last-Modified/If-Modified-Since Etag/If-None-Match 来判断是否命中,命中后返回304,否则返回新的数据。
强缓存:浏览器不会向服务器发送任何请求,直接从缓存中读取数据。它的status是200。优先级高于协商缓存。header参数:Expires(http1.0), cache-control: max-age(http1.1)
协商缓存:向服务器发送请求,通过请求中的一些参数判断是否命中。命中协商缓存返回304,否则返回新的数据。header参数:Etag/If-None-Match Last-Modifed/If-Modified-Since
参考:<https://juejin.cn/post/6844903838768431118>
4. 跨域
原因:浏览器的同源策略
* 服务端设置返回请求中的字段Access-Control-Allow-Origin为 *
* CORS 跨域资源共享(
* 代理跨域:服务器端不会跨域
1. 接受客户端 **请求** 。
1. 将 **请求** 转发给服务器。
1. 拿到服务器 **响应** 数据。
1. 将 **响应** 转发给客户端。
* WINDOW.NAME
* html5的window.postmessage
* jsonp:script不受同源策略影响,在js中动态生成script标签,src为请求资源地址+获取函数字段名+
* iframe
5. http
* post: 新增一条数据
* put:新增或完整更新一条数据
* patch:局部更新,相当于更新某个字段
* delete:删除数据
* get:查询数据
* post, patch不是安全和幂等的,其它都是安全和幂等的,即多次请求不会产生副作用。
6. websocket
websocket是应用层协议的一种,建立在http协议之上,是一种双向通信的协议
WebSocket 作为构造函数,用于新建 websocket 实例
ws:表示websocket协议
wss:表示websocket加密协议
HTTP 只能由客户端来主动发起,如果有需要服务端主动通知的业务,就需要轮训。轮询的效率低,非常浪费资源。为了解决 Web 端即时通讯的需求就出现了 WebSocket。
为保持连接不断开,我们使用心跳机制,每隔2s发送一次心跳请求
react
1. vue和react的相同点和不同点
相同:
- 都使用了虚拟dom、
- 都使用了diff算法并对diff算法进行了优化
- 都提倡组件化(提高代码的复用率及开发效率)
- 实现了数据驱动、
- 都有router库实现url到组件库的映射、
- 都有状态管理、
- ...
区别:
- 组件化差异:react推荐html+css+js全部写入js中,即all in js,而vue推荐template文件,html+css+js写入同一个文件中
- 虚拟dom差异
- vue双向数据绑定,react是单向、
- vue有指令语法、
小结:react是all in js 和vue是 all in .vue
[个人理解Vue和React区别](https://juejin.cn/post/6844903668446134286 "https://juejin.cn/post/6844903668446134286")
[关于Vue和React的一些对比及个人思考(上)](https://juejin.cn/post/6844904040564785159 "https://juejin.cn/post/6844904040564785159")
[关于Vue和React的一些对比及个人思考中)](https://juejin.cn/post/6844904052812169229 "https://juejin.cn/post/6844904052812169229")
2. 虚拟dom
虚拟dom本身是一个js对象,是真实dom的抽象表现。当状态发生改变时,记录新树与旧树的差异,并更新到真实dom。
当每一次UI更新时,总会根据render重新生成最新的VNode,然后跟以前缓存起来老的VNode进行比对,再使用Diff算法(框架核心)去真正更新真实DOM(虚拟DOM是JS对象结构,同样在JS引擎中,而真实DOM在浏览器渲染引擎中,所以操作虚拟DOM比操作真实DOM开销要小的多)

- tag不同认为是不同节点
- 只比较同一层级,不跨级比较
- 同一层级的节点用key唯一标识,tag和key都相同则认为是同一节点
[React和Vue框架的区别](https://juejin.cn/post/7144648542472044558)
3. diff算法react15、16区别
react15递归处理虚拟dom,更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了16ms,用户交互就会卡顿。
react16采用了新的状态更新机制Fiber,可判断当前是否有空闲时间,还提供了多种优先级的任务设置,将老的同步更新改为异步更新。且更新时采用双缓存来实现fiber树的构建与替换(在内存中构建并直接替换)解决了偶尔出现白屏问题
**React Fiber的机制:** 利用浏览器 `requestIdleCallback` 将可中断的任务进行分片处理,每一个小片的运行时间很短,这样唯一的线程就不会被独占
其中每个任务更新单元为`React Element`对应的`Fiber节点`。
-
为何使用hook
-
Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook
- 状态逻辑复用
- 比
class组件更容易理解
类组件有state,生命周期,this,但是函数组件没有。但是函数组件却比类组件更友好直观,所以引入hook
-
常用hook
useEffect, useState, useContext(跨组件共享数据)
- useLayoutEffect 相比 useEffect,通过同步执行状态更新可解决一些特性场景下的页面闪烁问题。
- useEffect 可以满足百分之99的场景,而且 useLayoutEffect 会阻塞渲染,请谨慎使用。
useMemo和useCallback的共同点:
- 接收的参数都是一样的,第一个是回调函数,第二个是依赖的数据
- 它们都是当依赖的数据发生变化时才会重新计算结果,起到了缓存作用
useMemo和useCallback的区别:
- useMemo计算结果是return回来的值,通常用于缓存计算结果的值
- useCallback计算结果是一个函数,通常用于缓存函数
-
react生命周期
shouldComponentUpdate
已废弃生命周期: componentWillMount componentWillReceiveProps componentWillUpdate
废弃原因:新增的这两个新周期替代了三个旧的生命周期,原因是:旧的生命周期经常被误解和滥用,在异步渲染过程中可能会有更大的问题
新增两个生命周期:getDerivedStateFromProps,getSnapshotBeforeUpdate
getDerivedStateFromProps让组件在 props 变化时更新 state,它+componentDidUpdate可替代ComponentWillReceiveProps的所有用例
getSnapshotBeforeUpdate+ComponentDidUpdate可替代ComponentWillUpdate的所有用例(这个生命周期不是经常需要的,但可以用于在恢复期间手动保存滚动位置的情况。
(React16特性)[www.imooc.com/article/279…]
-
setState原理
为什么要异步?
- 保证内部的一致性:因为批量处理对性能是有好处的。如果SetState同步更新,页面还未渲染,props也不是最新的
- 同步导致render多次执行,浪费性能。
注:setState并不是真正的异步。若在react机制中,它为异步;除此之外,它是同步的,如在定时器、ajax、元素的原生事件。因为setState的实现方法中,有一个变量isBatchingUpdates,它的默认值是false,即表示setState是同步执行。同时还有一个方法batchedUpdates,更改isBatchingUpdates的为值为true.当React在调用事件处理函数和自身生命周期之前就会调用这个batchedUpdates
- pending:当前所有等待更新的state队列。
- isBatchingUpdates:React中用于标识当前是否处理批量更新状态,默认false。
- dirtyComponent:当前所有待更新state的组件队列。
React通过setState实现数据驱动视图,通过setState来引发一次组件的更新过程从而实现页面的重新渲染(除非shouldComponentUpdate返回false)。
- setState()首先将接收的第一个参数state存储在pending队列中;(state)
- 判断当前React是否处于批量更新状态,是的话就将需要更新state的组件添加到dirtyComponents中;(组件)
- 不是的话,它会遍历dirtyComponents的所有组件,调用updateComponent方法更新每个dirty组件(开启批量更新事务) setState原理
-
keys
用于追踪列表的元素是否被修改、被移动或是被删除的标记 -
react的事件机制
react并没有将click事件绑定到dom上,而是在document处监听所有事件。当事件冒泡到document时,将事件内容交给中间层。当事件触发时,用统一的分发函数dispatchEvent将指定函数执行
-
性能优化
- 重写shouldComponentUpdate来避免不必要的dom操作。
- 使用 production 版本的react.js
- 使用key来帮助react实现列表中的组件的变化
- webpack优化
- cdn加载静态资源/第三方js
- 减少http
- 减少重绘与重排
- 在需要的时候使用防抖、节流方法
- webpack按需引入第三方库(只引入需要的组件)
-
redux原理
redux解决的真正问题是React组件间的状态共享和状态管理问题,通过store, reducer,action, dispatch来监听状态的改变
-
纯组件
使用方法:class MyComponent extends PureComponent {...}
pureComponent:改变了shouldComponentUpdate(默认返回true)方法,它会自动检测是否需要调用render。只有当props或state发生变化时才调用会render.
pure组件是浅比较,即嵌套对象与数组是无法进行比较的。解决办法:重写shouldComponentUpdate或使用Immutable.js库
-
高阶组件
可以看作
React对装饰模式的一种实现,高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。- 高阶组件就是一个没有副作用的纯函数,各个高阶组件不会互相依赖耦合
- 高阶组件并不关心数据使用的方式和原因,而被包裹的组件也不关心数据来自何处。高阶组件的增加不会为原组件增加负担
- 高阶组件也有可能造成冲突,但我们可以在遵守约定的情况下避免这些行为
-
Context
Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
用法:在父组件上定义getChildContext方法,返回一个对象,然后它的子组件就可以通过this.context属性来获取
-
受控组件与非受控组件区别
是否将所有数据托管于react 非受控组件有ref属性,有回调方法
-
函数式组件
即无状态组件,没有任何生命周期。将数据展示与逻辑处理分开了
即相同的输入就会有相同的输出
-
如何解析jsx
调用react.createElement方法来创建对象
-
props.children.map函数来遍历会收到异常提示,为什么?应该如何遍历? this.props.children 的值有三种可能:
- 当前组件没有子节点,它就是 undefined;
- 有一个子节点,数据类型是 object ;
- 有多个子节点,数据类型就是 array 。 系统提供React.Children.map()方法安全的遍历子节点对象
vue
-
vue2 vue3区别
-
生命周期,修改了生命周期的名字,功能不变
-
使用Proxy代替了object.defindeProperty
-
diff算法优化
-
vue3.0打包体积变小
-
-
双向绑定:v-model
Object.definedProperty的作用是劫持一个对象的属性的getter和setter方法,在对象的属性发生变化时进行特定的操作.而 Proxy劫持的是整个对象。
Proxy会返回一个代理对象,我们只需要操作新对象即可,而Object.defineProperty只能遍历对象属性直接修改。修复了数组的深层次监听无法实现的问题
Object.definedProperty不支持数组的各种API,而Proxy可以支持数组的各种API
Object.defineProperty有诸多缺陷,但是其兼容性要好于Proxy
v-model是语法糖,实现了value与input,用于input表单
-
data方法为什么是一个函数,而不是对象
使用了闭包设计,每个组件都有自己的私有作用域,确保各组件数据不会相互干扰,而纯对象则会造成数据干扰
-
v-if v-show
v-if 不满足条件,不会渲染dom => 适用于单次操作
v-show 一定会渲染dom(通过设置css样式display属性来实现dom的展示与隐藏) => 适用于多次切换(不适用权限操作)
-
v-if v-for
v-for的优先级 > v-if 建议将v-if用到v-for的外面,没必要循环判断
-
侦听器实现
使用object.definePrototype可监听一个属性的变化,那就可以遍历object的所有属性并监听其变化,也就实现了侦听器的功能
-
computed watch区别
watch 属性监听 是一个对象,键是需要观察的属性,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,监听属性的变化,需要在数据变化时执行异步或开销较大的操作时使用
computed 计算属性 属性的结果会被缓存,当
computed中的函数所依赖的属性没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取。除非依赖的响应式属性变化时才会重新计算,主要当做属性来使用computed中的函数必须用return返回最终的结果computed更高效,优先使用使用场景
computed:当一个属性受多个属性影响的时候使用,例:购物车商品结算功能watch:当一条数据影响多条数据的时候使用,例:搜索数据 -
生命周期
每个
Vue实例在创建时都会经过一系列的初始化过程,vue的生命周期钩子,就是说在达到某一阶段或条件时去触发的函数,目的就是为了完成一些动作或者事件-
create阶段:vue实例被创建beforeCreate: 创建前,此时data和methods中的数据都还没有初始化created: 创建完毕,data中有值,未挂载 -
mount阶段: vue实例被挂载到真实DOM节点beforeMount:可以发起服务端请求,去数据mounted: 此时可以操作Dom -
update阶段:当vue实例里面的data数据变化时,触发组件的重新渲染beforeUpdateupdated -
destroy阶段:vue实例被销毁beforeDestroy:实例被销毁前,此时可以手动销毁一些方法destroyed
-
-
nextTick:Dom更新后的延迟回调
A组件调用B组件方法时,需要保证B组件已加载完成
$.nextTick(() => { this.$refs.b.fn() }) -
vue-router
- hash路由: 基于 location.hash 来实现,使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)
- history 路由:基于 location.pathname 来实现,pushState 和 repalceState 两个 API 在不进行刷新的情况下,操作浏览器的历史纪录。使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染) history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。
vue react router:
-
工作原理 都相同, 都是基于 window.history 实现的
-
都有 hash 模式和 history 模式
-
都支持嵌套路由 区别:
-
两者 页面渲染触发机制的不同,主要是由于 vue 和 react 两者 响应式实现的原理 不同。
使用 vue router 时,在 应用启动过程 中,会给 根 vue 实例 构建一个响应式属性 - route 属性 会维护一个 依赖列表 - deps,使用 router-view 的组件都会添加到 deps 中。当我们通过 push(replace) 跳转页面 或者 激活历史记录 时, 都会修改 $route, 然后通知 deps 中的 组件 更新, 重新渲染页面。
使用 react router 时, Router 组件 在渲染时会构建一个 组件实例 并初始化。 初始化过程中, 会定义 state.location 来保存 页面 url 信息。当我们通过 push(replace) 跳转页面 或者 激活历史记录 时, 会通过 setState 方法更新 state.location,然后 触发更新,重新渲染页面。
-
vue router 提供了 全局守卫、路由守卫、组件守卫 供我们实现 路由拦截。
react router 没有提供类似 vue router 的 守卫 供我们使用,不过我们可以 在组件渲染过程中自己实现路由拦截。 如果是 类组件, 我们可以在 componentWillMount 或者 getDerivedStateFromProps 中通过 props.history 实现 路由拦截; 如果是 函数式组件,在函数方法中通过 props.history 或者 useHistory 返回的 history 对象 实现 路由拦截。
-
vuex的实现原理
-
vue scope css
避免全局样式污染,但同时也会增加性能开销
Scope CSS 的本质是基于 HTML 和 CSS 选择器的属性,通过分别给 HTML 标签和 CSS 选择器添加
data-v-xxxx属性的方式实现。使用vue-loader进行处理更改引入项目里的组件样式时,可使用/deep/ 或 :global来实现
-
vue事件传值
- 父传子: props/inject/provide
- 子传父: 事件传值emit
- 非父子组件传值:eventBus, vuex
webpack
-
webpack优化
- 生产环境与开发环境不要使用同一套配置,会影响打包效率(开发环境需要:热更新,sourceMap,使用代理,代码规范检查。生产环境:提取公共代码,压缩混淆)
- 优化打包速度(使用happypack——利用多线程打包,减少构建时间;设置babel的cacheDirectory为true,缓存babel-loader的编译过程;使用增强代码压缩工具——可并行运行时间,减少构建时间)
- 优化打包体积(Tree-shaking消除无用模块)
- 优化sourcemap(正式环境不要加sourcemap,偶尔会引起低版本手机白屏)
- 开发环境启用热更新
- 提取第三方库,利用缓存加载
- 使用代码压缩插件
- 服务端启用gzip
-
loader与plugin
loader将所有资源转换为webpack可处理的模块,可以让webpack处理非js模块。几乎所有loader都需要安装
plugin,具有apply属性的js模块。用于处理loader无法处理的事情,几乎可以处理所有任务,从打包优化和压缩,直到重新定义环境变量。plugin不需要安装,但是需要在头部引入
算法
-
数组去重
- 使用includes/indexOf去重
- 使用es6的set方法去重
- 使用对象属性存在特性,若没有该属性就存入新数组
- 将原数组排序,然后相邻元素进行比较,如果没有相同,若不同则存入新数组
-
数组乱序 洗牌:将数组的最后一个值随机插入到前面
var arr = [1,2,3,4,5,6]; var len = arr.length; for(var i=len-1; i>=01; i--){ var random = parseInt(Math.random()*len); var tmp = arr[i]; arr[i] = arr[random]; arr[random] = tmp; } console.log(arr) var arr = [1,2,3,4,5,6]; var newArr = []; while(arr.length>0){ var random = parseInt(Math.random()*(arr.length-1)); newArr.push(arr[random]); arr.splice(random, 1) } console.log(newArr) -
快速排序
-
数组乱序 洗牌:将数组的最后一个值随机插入到前面
-
快速排序
设计模式
开放性问题
-
开发过程中遇到的问题
1.环境问题 症状:通常是框架或系统环境配置不对,造成,在代码中找半天,找死也解决不了。 解决办法:咨询架构师。
2.产品业务设计问题 病例:页面中的一次后置规则的触发,导致无限循环的生成后置规则新数据,找到晚上9点。 解决办法:确认代码没有问题,向领导反应后,指派给产品。
3.缓存问题 症状:代码没有问题,配置也没有问题,整理半天也没有找到问题。 解决办法:重启项目,重启浏览器,清理缓存,重新编译,不行再重启idea,不行再删除项目重拉取项目,不行再重启电脑。