美文网首页
2019-07-26

2019-07-26

作者: ohmygod_eed6 | 来源:发表于2019-07-26 11:15 被阅读0次

    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

    image

    构造函数: 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();
    }

    https://www.jianshu.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之类不支持冒泡.不适应所有的事件,只适用于支持事件冒泡的事件

    相关文章

      网友评论

          本文标题:2019-07-26

          本文链接:https://www.haomeiwen.com/subject/krdrrctx.html