1.window.onload与$(document).ready(function(){})的区别
window.onlaod: 必须等到页面内的所有元素加载到浏览器后才可以执行,不能编写多个,最后一个会把之前的都覆盖掉。$(document).ready(function(){}):DOM结构加载完毕之后执行的,可以编写多个
2.盒模型
页面渲染时,DOM元素所采用的布局模型,可以通过box-sizing进行设置。
box-sizing:content-box;W3C标准盒模型 width = width + border + padding 为默认的
box-sizing:border-box;为IE盒模型 width = width
3.BFC
块级格式化上下文:是一个独立的渲染区域,让处于BFC内部的元素与外部元素之间相互隔离,使得内外元素的定位不会相互影响。
触发条件:
1.body根元素
2.float不为none的属性值
3.position:absolute、fixed
4.display:inline-block、table-cells、flex
5.overflow:除了visible以外的值(hidden\auto\scroll)
display:table本身是不产生BFC的,他产生一个匿名框,匿名框中包含了display:table-cell会产生BFC
属于同一个BFC内的两个相邻box的上下margin会发生重叠,计算BFC高度时,浮动元素也会参与计算。
4.层叠上下文
z-index => 行内元素 =>浮动元素 => 块级元素 => z-index为负 => background
5.水平垂直居中布局\优先级
flex: justify-content:center + align-items:center
absolute: transform:translate(-50%,-50%)
!important => 行内样式 => #id => .class => tag => *
6.如何去除浮动影响,防止父级的高度塌陷
1.增加清除浮动:after{clear:both} 2.设置父级BFC 3.父级设置高度
7.link与@import的区别
@import为CSS提供的语法,只能倒入样式表。link标签是HTML提供的。
link引入的css样式在加载页面时同时加载,@import在页面加载完毕后被加载。
可以通过JS操作DOM来插入link,无法用@import方式插入样式
8.flex
flex-direction:改变布局方向
flex-wrap: 排列的顺序
justify-content: center/space-between/space-around 中间行如何排列
align-items:center 垂直居中
flex-grow: 父级宽度减去子集宽度,剩余空间按比例分割
flex-shrink:子集宽度和减去父级宽度,剩余空间按比例缩小
align-self: 可覆盖align-items的属性
9.JS数据类型及判断方法
七大数据类型:String、Number、Boolean、Undefined、Null、Object、Symbol
基本数据类型:String\Number\Boolean\Undefined\Symbol
引用数据类型:Object
判断方法:typeof(Number\String\Boolean\undefined\Symbol)
用instanceof可以判断Arrary 与 object
最终方法:var getType = Object.proptype.toString
getType.call(目标数):返回[object String][object Number][object Boolean][object Undefined]
[object Null][object Arrary][object Function]
10.原型/构造函数/实例
prototype(显式原型)/proto:对象的爹
构造函数:可以通过new来创造一个对象的函数
实例:通过构造函数和new来创造初的对象
实例通过proto指向原型,通过constructor指向构造函数。prototype是函数才有的,proto所有的引用类型都有,所以只有构造函数拥有prototype
构造函数: Person, 实例: p,
p.proto 指向 Person.prototype
Person.prototype 指向 Person.prototype的constructor指向Person
Person.proto指向Function.prototype 的 proto指向Object.prototype 的 proto指向null
11.变量提升/函数提升
变量提升:把变量提升到函数的顶部,只会提升声明不会将变量的值也提升上去。
函数提升:函数声明式会将其提升至顶部。(没等声明就表达)
12.闭包
闭包是指有权访问另一个函数作用域中的变量函数。可以把闭包简单理解成“定义在一个函数内部的函数”。
13.对象的拷贝
浅拷贝:以赋值的形式拷贝引用对象,修改时原对象也会受到影响。Object.assign
深拷贝:完全拷贝一个新的对象,修改时原对象不再受影响。JSON.parse(JSON.stringify(obj)) lodash:cloneDeep
原因:基本数据类型放在栈当中,数据确定,可以直接取放,引用类型放在堆当中,栈的内存上只存储了一个指向堆内存地址的指针。
14.继承
继承分为原型继承和构造继承。每个函数都有一个原型对象,每一个原型对象都有一个指向构造函数的指针。
构造继承:
function A(a){
this.a = a;
}
A.prototype.getValue = function(){
console.log(this.a)
}
function B(aName){
A.call(this,aName)
}
var b = new B('kaixing');
b.getValue() // b.getValue is not function
原型链继承
function Person(){
this.head = 'ddd';
this.emotion = ['d','s']
}
Person.prototype.getEmotion = function(){
return this.memotion
}
function Student(studentID) {
this.studentID = studentID;
}
Student.prototype = new Person();
构造函数的缺点:无法继承父类的公共方法和函数,优点:解决了原型链继承产生的实例修改共同属性的问题。
原型链继承的缺点:子类不能向父类传参数,子类的实例修改父类的实例时,其他实例都会引用相同的地方,值都会改变。
组合式继承
function Person(name){
this.name = name;
this.emotion = ['a','d','s'];
}
Person.prototype.eat = function(){
console.log('eat');
}
function Student(name){
Person.call(this,name);
}
Student.prototype = new Person();
Student.prototype.constructor = Student
15.new的执行过程
1.创造一个新的对象
2.将构造函数的this指向这个对象
3.执行构造函数
4.返回新的对象
工厂模式:
function Person(hair,face,eye){
var o = new Object;
o.hairs = hair;
o.eye = eye;
o.say = function(){
console.log('say something to me !')
}
}
构造函数模式:
function Person(hairs,face,eye){
this.hair = hair;
this.face = face;
this.eye = eye;
}
16.模块化
node(commonJS):require/module.exports/exports
exports是对module.exports的引用:exports = module.exports
require相当于把引用的module拷贝一份到当前的module当中去。
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
function someFunc() {}
module.exports = someFunc;
})(module, module.exports);
return module.exports;
}
require的简单实现,直接将需要载入的文件赋值到module.exports,并return出。其实就是复制。
requireJS(amd):require / defined;出现背景:require的本质是复制,如果浏览器端的时候,数据还没获取到,不能复制,导致阻塞。所以需要异步获取。同样存在问题,如果里边内容是通过条件判断获取的,则AMD有些浪费
requirejs(['jquery', 'canvas', 'app/sub'],function($,canvas,sub) {
//jQuery, canvas 和 app/sub 模块已经被加载并且可以在这里使用了。
});
define(["./cart", "./inventory"], function(cart, inventory) {
return {
addToCart: function() {
inventory.decrement(this);
cart.add(this);
}
}
}
);
seaJS(cmd):require / defined:
define(function(require, exports, module) {
if (xxx) {
var header = require('./header')
header.setHeader('new-title')
}
if (xxx) {
var main = require('./main')
main.setMain('new-content')
}
if (xxx) {
var footer = require('./footer')
footer.setFooter('new-footer')
}
});
AMD与CMD都是浏览器的规范,CommonJS是服务器端的规范
ES6:import ;export;
可以作为浏览器和服务器端的通用解决方案
require与import的区别:
require是同步加载,import是异步加载;require是值的拷贝,import是指针
17.函数改变this
this只有在函数执行的时候,才知道最终指向谁,谁调用它,它就指向谁
如何改变this:
this在函数定义的时候是无法确定的,只有在最后使用的时候,谁用它,那它就指向谁。
在严格模式下,所有指向windows的this都是undefined。当遇到new时,最后产生一个对象,那它最后指向的是这个对象
改变this只有四种方法,new,apply,call,bind(返回一个方法,需要再次执行)
1.new的关键字改变。
2.apply()有两个参数,第二个参数必须为数组。
3.call()有多个参数。
4.bind()可以传多参,返回一个修改过this后的函数。
普通函数的this永远指向执行它的对象,箭头函数 它会捕获其所在(即定义的位置)上下文的this值,无法改变指向。
箭头函数是匿名函数,不能使用new,且没有arguments,使用rest参数代替
var o = {
say1:function(){
setTimeOut(function(){
console.log(this)
},0)
},
say2:function(){
setTimeOut(() => {
console.log(this)
},0)
}
}
o.say1()// windows
o.say2() // o
18.解构赋值的方法
const s = new Set();
const test = [1,2,3,3,3,3,3];
[1,2,3,3,3,3,3,4].forEach(x => s.add(x)) || [...new Set(test)]
或者 result = Arrary.from(s);
// 并集
var a1 = [1,2,3]
var a2 = [3,4,5]
var result1 = new Set([...a1,...a2])
// 交集
var s = [1,2,3]
var e = new Set([3,4,5])
var t = new Set(s.filter((x) => {return e.has(x)}))
// 差集
var a1 = [1,2,3]
var a2 = [2,3,4]
var a1Set = new Set(a1);
var result = new Set(a2.filter((x) => !a1Set.has(x)))
19.数组Arrary
splice(startNum, number, a1,a2): 返回一个截取的数组,旧的数组被修改了,且把a1,a2加进去了。
slice(stratNum,endNum):返回截取的数组(或字符串),旧的数组或字符串不会发生改变
字符串:
slice(startNum, endNum):返回截取的字符串,旧字符串不会改变slice(-1)为倒数第一到最后的字符串
substr(startNum,length):返回截取的字符串,旧字符串不会改变substr(-1)为倒数第一到最后的字符串
substring(startNum,endNum):如果前边比后边大,那么这两个交换位置,如果是-1默认为从0开始,返回新的字符串
concat:连接数组,浅拷贝
indexOf,lastIndexOf(value,fromIndex):第一次出现某个值的index
sort:用来排序 = > [5,6,1,2,7,4].sort((a,b) => {return a - b}) // [1, 2, 4, 5, 6, 7]
20.webpack
webpack是一个模块化打包工具,里边有几大块:entry入口,output输出地址。
bundle:最终输出的文件。
chunk: 打包后的文件
module:开发的模块
plugins:全局使用的一些插件,通过new HtmlWebpackPlugin用来生成带有入口文件的index.html,可以使用happyPack,利用cpu来实现一些异步执行的loader,
loader:是执行的,一般用来处理less-loader/postcss-loader/css-loader/style-loader。React的babel也在loader中做处理。
optimization: 是做优化的,里边可以设置打包优化,例如不直接使用import React from react,如果多个文件有引用,则可以把该文件抽出来,避免多次打包。
- 关于webpack的使用:我们项目一共是有三个文件,分别是dev.config\prod.config\basis.config,每个都返回一个对象形式,最后使用webpackMerge将他们连接起来。这三个文件不同的设置,是因为一些在生产上跑的东西不需要在开发的时候跑一遍,例如我们在生产的plugins上有代码压缩,在开发的时候,不需要压缩,加了热更新。然后在loader上,我们通过happyPack对css文件和react进行一个模拟的异步执行。css上使用了less-loader,postcss-loader(向css前自动加前缀,做浏览器兼容),css-loader,style-loader。
21.ES6与ES7
通过babel解析成ES5可以让浏览器解析
let/const为块级作用域,不允许重复声明
class为类的声明,extends为类的继承
[...c]为解构赋值
Set,Map为伪数组,需要用[...]/Arrary.from/Arrary.Prototype.slice.bind方法转换为数组
Set
var a = [1,2,3]
var b = [2,3,4]
var aSet = new Set(a);
var bSet = new Set(b);
var result = new Set([...a, ...b]) // [1,2,3,4]
var result = a.filter((data) => bSet.has(data)) // [2,3]
var result = a.filter((data) => !bSet.has(data)) // [1]
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
Map
var aMap = new Map();
var o = {s:'s'}
aMap.set('oName',o);
aMap.get('oName');
aMap.has('oName');
aMap.delete('oName');
22.express/egg
app.use使用中间键,app.get通过获取是get请求的方法,然后根据url来判断执行操作。
egg主要做node层接口聚合的,分为router/controller/services/在router层确定请求的接口,controller层做请求,包括请求前参数的聚合,services层拿到返回的结果后,对返回的数据做处理。
path.resolve([from],to):path.resolve(__dirname, 'res')
23.浏览器的数据存储
1.localStorage:本地存储,数据长期存在,大小约为5M
2.sessionStorage:会话存储,数据在关闭网页后消失,大小5M
3.cookie:保存在浏览器端的数据,服务器端通过response的set-cookie来设置cookie的值,js通过document.cookie来读取和创建cookie,紧跟域名,同一个domain下的所有请求都会携带cookie。通过后台设置httpOnly,可以设置不让document.cookie获取到cookie。如果给cookie设置过期时间,则会存储在硬盘当中,下次还在,如果不设置过期时间,存储在浏览器的内存当中,浏览器关闭则关闭。
4.session:保存在服务器端的数据,用来保存用户信息,通过SessionID来分辨不同的用户,一般SessionID存储在cookie当中
24.浏览器的缓存
浏览器的缓存一般分为强缓存与协商缓存
强缓存:
不会向服务器发送请求,直接从缓存中获取数据,一般返回的Network可以看到返回的code为200,size是from Disk Cache(从硬盘缓存中获取)或者from memory Cache(从浏览器的缓存中获取)。强缓存可以通过两种HttpHeader:Expires和Cache-control
Cache-control:有一些选项,如max-age,比Expires优先级高。
协商缓存:
一般返回304,通过Last-modified设置
- 一般浏览器先判断强缓存,若强缓存生效,则直接从缓存中获取,如果不生效则从协商缓存,获取,如果获取成功,则返回304,从浏览器或者硬盘中获取数据,如果不成功,返回200重新拉取数据。。
缓存过程:
浏览器第二次请求资源 => 获取资源缓存的header信息判断cache-control和expries信息,如果命中直接取资源信息,包括缓存的header信息。如果没命中强缓存,浏览器会发送请求到服务器,请求会携带第一次请求返回的相关header信息字段,如(Last-modified、Etag)等,由服务器端根据请求中的相关header信息来比对是否协商缓存命中结果;若命中结果,则服务器返回新的响应header信息更新缓存中对应的header信息,但不返回资源。他会告知浏览器可以直接从缓存中获取;否则返回新的资源。
强:200,fromcache,不请求到服务器expries/cache-control
协商:304,notmodify,请求到服务器,由服务器告诉是否用缓存;
第一次请求用:last-modify/etag,之后的请求都是:if-modify-since;if-no-match
25.TCP/IP
tcp/ip协议是一个协议族,对应的OSI七层模型和TCP/IP四层模型
OSI:物理层、数据链路层、网络层、传输层、会话层、表达层、应用层
TCP/IP:数据链路层、网络层(IP头部,APR、RAPR)、传输层(TCP/UDP)、应用层(HTTP)
26.HTTP/HTTPS
HTTP协议是基于TCP/IP协议的应用层协议,叫做超文本传输协议。
每次请求,TCP建立一个与服务器连接的通道,当请求结束后,通道断开,所以HTTP是一个无状态的短链接协议。它定义了数据请求的头部和接收的格式。
HTTPS是一个以安全为目的的HTTP通道,在HTTP头部加一个SSL层,通过非对称加密算法加密RSA进行加密。
准确过程:非对称加密和解密是非常耗时间的,所以,需要使用对称加密。浏览器将自己的对称加密的密钥,通过服务器的公钥进行加密,将其发送给服务器,服务器使用自己的私钥解密,得到对称加密的密钥。
看似很完美,可能会有第三方劫持,当浏览器请求服务器的公钥时,第三方劫持服务器的公钥,将自己的公钥返回给浏览器,浏览器使用这个公钥加密,并发送,第三方接收到后,解密,并添加真正的服务器的公钥,发送给服务器,服务器返回结果,第三方用自己的私钥加密信息,返回给浏览器。这样,浏览器和服务器并不会感知。
所以需要CA,CA值得所有浏览器信赖,每个浏览器都已知CA的公钥。当浏览器向服务器请求加密通信时,服务器先向CA请求,获取证书,CA对证书进行hash加密,得到数字签名。明文和数字签名共同组成数字证书。浏览器拿到证书,使用公钥解密,并比对数字签名,如果相同,则可靠。
HTTP:8080,明文传输,HTTPS:443,密文传输,需要CA认证
27.TCP/UDP(传输层协议)
TCP:连接的、面向字节流的可靠的传输协议,通过三次握手建立连接,慢、耗时长,全双通工
UDP:无连接的、面向报文的不可靠传输协议,会发生丢包。主要在视频、语音、直播使用,不考虑报文是否到达。
28.三次握手/四次挥手
第一次握手:客户端向服务器端发送请求:seq=X,SYN=1
第二次握手:服务器端接收到请求后,返回seq=Y,ACK=X+1,SYN=1,确认了浏览器的发送端口和自己的接收端口正常。
第三次握手:浏览器端接收到返回请求后,发送ACK=Y+1,确认了自己的发送和接收端口,服务器的接收和发送端口正常。服务器端接收到请求后,确认了自己的发送,浏览器的接收端口正常。
为什么不多次握手:没有必要多次握手,三次握手已经足够确认通讯正常,两次握手不足以确定端口的正常
1.浏览器向服务器端发送请求FIN=1,seq=X,
2.服务器端返回,ACK=X+1,Seq=Y,
3.服务器发送,FIN=1,seq=Z
4.浏览器端返回:ACK=Z+1,等待2MSL后关闭端口
为什么要等待2MSL:1MSL是报文在网络中生存的最大时间,超时后报文将被丢弃,所以2MSL是报文在连接中存在的最长时间,如果没收到。服务端将会再发送一个FIN包,客户端可以再次发送确认包。
为什么握手是3次,挥手是4次:区别在于第一次握手的时候,服务端可以返回SYN+ACK,因为第一次建立连接,服务器端不会有向客户端要发送的东西。而在挥手的时候,双方已经建立了连接,服务器端可能还有东西需要向客户端发送,所以要先发ACK来确认收到,再发FIN释放通道,这个过程中,服务器端还是可以继续发送
29.GET/POST/状态码
GET:参数在URL中,可以被浏览器收藏标签,参数大小限制在2K,浏览器会主动缓存回退无害
POST:参数在Request的body中,不受限,浏览器不会主动缓存,回退会再次请求
他两都是HTTP协议定义的两种方式,而HTTP协议都是基于TCP/IP协议族来定义的,所以GET和POST的底层都是TCP协议,只不过定义的时候是这么定义的,可以在post上使用url来请求,也可以在get里传requestbody来使用。有一些说法是post会把head和body分两次发送,而get只发送一次。但这个应该是不同浏览器所造成的。
100:服务器接收请求,客户必须继续发出请求
200:成功
204:请求收到,但是返回结果为空
301:永久性重定向
302:临时性重定向
304:所请求资源未修改,协商缓存
400:请求的参数有误
403:请求成功,但是服务器端拒绝响应
404:请求的url有误
500:服务器内部错误
504:nginx未能从服务器端拉取到请求
29.一个页面从输入url开始经历了什么
1.进行DNS解析,先从浏览器缓存中查找对应的IP => host => 路由器的缓存 => 本地DNS => 根
2.拿到对应的IP后进行TCP三次握手
3.三次握手成功后,发起HTTP请求,一般当connection为keep-alive,表示持久链接,浏览器每隔45s会发送一个TCP的keep-alive包来确定连接是否存在,nginx默认75s没有新的请求,则断开连接
4.浏览器解析页面,首先开始解析HTML代码,遇到js/css/image等浏览器会再次发送请求获取数据。同步进行代码解析,在解析的过程中处理
1.HTML生成dom树,2.处理CSS生成CSS的规则树,3.将CSS的规则树与DOM树结合生成渲染树4.根据渲染树的数据计算节点,布局5.根据计算好的节点绘制页面。
布局:根据渲染树的信息,计算渲染树的位置及尺寸
重绘:某个节点颜色的变化等,不影响布局,只会引起重绘
回流:某个尺寸发生变化,需要重新计算渲染树
5.TCP四次挥手,断开连接
30.跨域问题
1.通过设置document.domain来设置同源
2.我们项目中使用nginx来做跨域的处理,nginx中设置location来判断url,然后设置add_header设置Access-Control-Allow-Orign为来标识允许所有域来请求
3.传递参数,用过postMessage(data,orignUrl)来传递父页面的参数,子页面通过window.onMessage来接收父页面传递的参数
4.在实际工作中遇到的问题,在做上传组件的时候,实现的方案是先向一个A域名发送请求,由A向服务器端做上传请求,因为请求为一个文件流的形式,是MIME类型的post请求,有可能对服务器产生副作用,所以浏览器先使用一个OPTIONS方法发起一个预检请求,从而获知服务器是否允许跨域请求。但是服务器返回404,所以在nginx的A域名下,设置了Access-Control-Allow-Orign:''。Access-Control-Allow-Methods: POST, GET, OPTIONS。
31. React
为什么setState后无法立刻拿到state?
setState通过一个队列的机制来更新state,当执行setState的时候,会将需要更新的state合并后放入队列,不会立刻更新this.state,所以无法设置this.state它只会在下次setState的时候被冲掉,所以只能在setState后调用回掉函数立即使用。实际会触发re-render方法,将新的节点放在旧的节点之前,然后删除掉旧的元素。
什么是JSX?
jsx是javascript的语法糖,它将js当中的html标签转换为js对象的形式描述。
React具体过程
jsx是一个javascript的语法糖,他能将js中的html标签转换为对象的形式(AST抽象语法树),其实就是虚拟dom树。当有页面数据的改动时,可以通过createElement方法,new一个Element对象接收三个参数(type、props、children)生成新的虚拟dom树。
更新页面的数据时,通过diff深度优先遍历比较。diff算法:接收旧的虚拟dom和新的虚拟dom,里边一共有patches(补丁)的对象。当新的DOM节点不存在时,生成一个对象{type:'Remove',index},当节点类型变化时,会有{'type':'REPLACE':newNode},当属性变化时会有{type:'ATTR',attr:'要更改的数据'}
通过diffAttr和diffChildren比较新老类型和节点。
最后一个步骤是打补丁,遍历patches旧的节点根据序号确认当前是哪一个节点的问题,打补丁,判断patches里的各种类型,然后根据不同的类型,做各种不同的操作,通过renderDom的方法,将相应的改变应用到旧的dom树上去。
用JS对象模拟DOM(虚拟DOM)
把此虚拟DOM转成真实DOM并插入页面中(render)
如果有事件发生修改了虚拟DOM,比较两棵虚拟DOM树的差异,得到差异对象(diff)
把差异对象应用到真正的DOM树上(patch)
React中的style属性的不同
<div style="font-size=16px"></div>
react中需要把它传成对象,然后使用驼峰命名
<div style = {{fontSize:16px}}></div>
React dangerouslySetInnerHtml
类似与innerHtml,可以向其中传入一个对象,使用{__html=html字符串}
React的生命周期
constructor
componentWillMount
render
componentDidMout
componentWillUnmount
===============================================
componentWillReceiveProps
shouldComponentMount
componentWillUpdate
render
componentDidUpdate
componentWillUnmount
ReactSSR
在页面初次加载的时候,把所有的地方都渲染好,一次性传给客户端,有SEO和首屏加载快的特点。
如ajax无法在服务器端使用,只能去寻找http.request的方法,找到了node-fetch,客户端使用whatwg-fetch,
浏览器端添加样式比较容易,写一个css文件,在webpack中配置style-loader和css-loader解析,而在服务端跑会报window is undefined的错,所以在浏览器端应该使用style-loader和css-loader,在服务器端使用isomorphic-style-loader。
isomorphic-style-loader,只在对应的dom元素上生成对应的class类名,然后返回生成的css代码。
在客户端中,我们使用css-loader和style-loader,不但在dom上添加class,还会把代码挂载到页面上,如果这么做,页面的样式最终是由客户端渲染的,页面会在一开始的时候展示的是没有样式的页面,所以,我们可以在isomorphic-style-loader拿到样式后,以字符串的形式将其添加到服务器端渲染的HTML标签里。
在渲染方面,react提供了两种方法,React.renderToString和React.renderToStaticMarkup,
renderToString方法渲染的节点带有data-reactId属性,可以精准的定位发生变化的节点,按需更新。
renderToStaticMarkup渲染出不带data-reactId的纯HTML文件,如果发生更新,则整体更新。
项目中的script和meta,title等属性都是通过其渲染的。页面reactRouter通过renderToString渲染的。
关于store,我们在浏览器端的时候,可以直接写store = createStore(),return store;
但是在服务器端是不可以的,因为用户浏览器端只有一个store,而客户端要为用户创建多个store,所以,要返回一个方法
var getStore = function(){
return createStore();
}
//www.greatytc.com/p/e1acf613adf6
这样在每次执行的时候,服务端都会重新为各个用户建立独立的store。
通过服务器端获取的数据,最终要展现在客户端,首先在服务器端获取到数据后,通过renderToStaticMarkup将其发送到页面中,在页面中嵌入一个script标签,该标签的作用是拿到数据,把它传递给window的方法,然后在client的文件中,通过取window方法,拿到数据,注入到组件当中。
使用universal-router路由,每个页面的title和description都是通过universal-router提供的中间键来为页面添加信息,通过调用context.next()来执行下一步
32. Redux
react-redux的connect是一个高阶组件,它接收一个组件和一个对象(stateToprops)返回一个新的组件。
在Provider中,我们将store放入context中,在connect里边将store从context中取出来。在connect里边有一个叫做allProps的state,在其componentWillMount中使用store.subscribe方法注入updateProps的一个方法,所以在每次使用store.dispatch的时候,必须执updateProps的方法,而这个方法内使用了store.getState和setState的方法,通过store.getState拿到新的store,赋值给mapStateToProps,然后通过setState方法使页面重新渲染,这样新的数据就可以实时更新。
createStore接收两个参数,一个是reducer,一个是enhancer一些中间键。reducer接收state和action,state为初始值,action为通过action.type判断更新store的值。
createReducer(reducer){
let state = null;
let listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(action);
}
const subscribe = (listener) => {
listeners.push(listener);
listeners.forEach((listener) => listener())
}
return {getState, dispatch,subscribe}
}
Redux与mobx区别,Redux比较重,它是把所有的数据全部放在顶层,然后向下传递,而mobx比较轻,可以分散在页面里使用,用起来比较方便。
33.获取层级的方法
getCeng = (data) => {
let index = 0;
const toString = Object.prototype.toString;
const calculCeng = (subData, leave) => {
const leaves = leave || 1;
if (typeof subData === 'object') {
Object.keys(subData).forEach((key) => {
const type = toString.apply(subData[key]);
if (type + '' === '[object Object]') {
calculCeng(subData[key], leaves + 1);
} else {
index = leaves + 1 > index ? leaves + 1 : index;
}
});
} else {
index = leaves > index ? leaves : index;
}
};
calculCeng(data);
return index;
};
34.斐波那契数列
function Fibonacci (n, ac1 = 1, ac2 = 1){
if (n < 2) {
return ac2
}
return Fibonacci( n-1 , ac2 , ac1+ac2)
}
35.关于宏任务、微任务和Promise
因为JS需要处理DOM操作等,所以它必定是一个单线程的语言,但是单线程容易造成页面的阻塞。而浏览器的渲染进程是多线程的,它有:JS引擎线程、事件触发线程、定时触发线程、渲染线程等。所以,在浏览器遇到定时器时,JS引擎线程会将他们交给其他线程处理,这样便实现了 异步非阻塞。
但定时触发线程也只是会处理setTimeout,时间到后,会把它交给消息队列去维护。而去执行消息队列,需要一套规则,就是 事件循环机制。
JS有一个执行栈,同步代码会依次加入执行栈中执行,结束后会退出执行栈,如果执行栈里边的任务执行完成,即JS线程引擎空闲的时候,事件触发线程才会从消息队列中取出一个任务,放入执行栈中执行。执行完后,执行栈为空时,会再次执行以上步骤,称为事件循环机制。
marcrotask:宏任务,主代码块、setTimeout、setInterval等
microtask:微任务,Promise等。
JS会先执行宏任务,执行宏任务的时候,遇到微任务,将其加入到微任务的队尾。所以执行
36.事件委托代理
当页面存在大量的li时,会有很多的监听函数,影响性能,所以我们可以只监听这些li的父级,通过事件委托代理的机制,监听是否点击到了希望点击的对象,然后执行对应的方法。
从子元素冒泡上来的事件,找到属于哪个子元素的事件。
优点:可以大量节省内存占用,减少事件注册。缺点:focus、blue之类不支持冒泡.不适应所有的事件,只适用于支持事件冒泡的事件