面试准备 --- 持续更新中

React

1.key的做用

key帮助React识别哪些项目已更改,已添加或已删除。也所以能够高效的更新虚拟DOM列表,key值是用来判断 VDOM 元素项的惟一依据css

  • key相同,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新。
  • key值不一样,则react先销毁该组件(有状态组件的componentWillUnmount会执行),而后从新建立该组件(有状态组件的constructor和componentWillUnmount都会执行)

2.vdom的更新策略diff

3.setState同步异步?

在React中,若是是由React引起的事件处理(好比经过onClick引起的事件处理)或者钩子函数中,调用setState不会同步更新this.state,除此以外的setState调用会同步执行this.state。所谓“除此以外”,指的是绕过React经过addEventListener直接添加的事件处理函数,还有经过setTimeout/setInterval产生的异步调用。html

缘由:在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state仍是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,可是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改成true,而当React在调用事件处理函数以前就会调用这个batchedUpdates,形成的后果,就是由React控制的事件处理过程setState不会同步更新this.state。前端

class App extends React.Component {
  state = { val: 0 }

  componentDidMount() {
    this.setState({ val: this.state.val + 1 })
    console.log(this.state.val) // 0 在钩子函数中不会当即更新

    this.setState({ val: this.state.val + 1 })
    console.log(this.state.val) // 0 在钩子函数中不会当即更新

    setTimeout(_ => {
      this.setState({ val: this.state.val + 1 })
      console.log(this.state.val); // 2 这里会当即更新,而且因为setstate批量更新测量,componentDidMount的两次setstate只会取最后一次,即1

      this.setState({ val: this.state.val + 1 })
      console.log(this.state.val) // 3 同上
    }, 0)
  }

  render() {
    return <div>{this.state.val}</div>
  }
}

4.什么是错误边界

在 React 中,咱们一般有一个组件树。若是任何一个组件发生错误,它将破坏整个组件树。没有办法捕捉这些错误,咱们能够用错误边界优雅地处理这些错误。node

错误边界有两个做用react

  • 若是发生错误,显示回退UI
  • 记录错误

下面是ErrorBoundary类的一个例子。若是类实现了 getDerivedStateFromErrorcomponentDidCatch 这两个生命周期方法的任何一下,,那么这个类就会成为ErrorBoundary。前者返回{hasError: true}来呈现回退UI,后者用于记录错误。jquery

export class ErrorBoundary extends React.Component {
    constructor(props) { 
        super(props); 
        this.state = { hasError: false }; 
     } 
     static getDerivedStateFromError(error) { 
         // Update state so the next render will show the fallback UI. 
         return { hasError: true }; 
     } 
     componentDidCatch(error, info) { 
        // You can also log the error to an error reporting service 
        console.log('Error::::', error); 
     } 
     render() { 
        if (this.state.hasError) { 
            // You can render any custom fallback UI               return <h1\>OOPS!. WE ARE LOOKING INTO IT.</h1\>; 
        } 
        return this.props.children; 
    } 
}

5.在构造函数调用super并将props做为参数传入的做用是啥?

在调用super()方法以前,子类构造函数没法使用this引用,ES6 子类也是如此。将props参数传递给super()调用的主要缘由是在子构造函数中可以经过this.props来获取传入的propswebpack

传递 propsgit

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    console.log(this.props);  // { name: 'sudheer',age: 30 }
  }
}

没有传递 propsweb

class MyComponent extends React.Component {
  constructor(props) {
    super();
    console.log(this.props); // undefined
    // 可是 Props 参数仍然可用
    console.log(props); // Prints { name: 'sudheer',age: 30 }
  }

  render() {
    // 构造函数外部不受影响
    console.log(this.props) // { name: 'sudheer',age: 30 }
  }
}

6.Fiber

查看正则表达式

Redux

Mobx

Webpack

1.webpack和gulp区别

gulp是基于任务和流的处理。放在之前好比我想用sass写css, coffee写js, 我必须手动的用相应的compiler去编译各自的文件,而后各自minify。这时候designer给你了两张新图片,好嘞,接着用本身的小工具手动去压缩图片。
后来前端人不能忍了,搞出个自动化这个流程的 Grunt/Gulp, 好比你写完代码后要想发布production版本,用一句gulp build就能够

rm掉 dist文件夹中之前的旧文件
自动把sass编译成css, coffee编译成js
压缩各自的文件,压缩图片,生成图片sprite 
拷贝minified/uglified 文件到 dist 文件夹

webpack是基于入口的。webpack会自动地递归解析入口所须要加载的全部资源文件,而后用不一样的Loader来处理不一样的文件,用Plugin来扩展webpack功能。

2.loader 的执行顺序为何是后写的先执行

loader中采起compose的洋葱模型,内部采用reduceRight实现,所以是后写的先执行

function compose(...loaders){
    return function(arg){
        return loaders.reduceRight((res, cur) => {
            return cur(res)
        }, arg)
    }
}

3.经常使用的loader和plugins以及他们的区别

常见的loader
  • file-loader:把文件输出到一个文件夹中,在代码中经过相对 URL 去引用输出的文件
  • url-loader:和 file-loader 相似,可是能在文件很小的状况下以 base64 的方式把文件内容注入到代码中去
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试
  • image-loader:加载而且压缩图片文件
  • babel-loader:把 ES6 转换成 ES5
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
  • style-loader:把 CSS 代码注入到 JavaScript 中,经过 DOM 操做去加载 CSS。
  • eslint-loader:经过 ESLint 检查 JavaScript 代码
常见的plugins
  • define-plugin:定义环境变量
  • commons-chunk-plugin:提取公共代码
  • uglifyjs-webpack-plugin:经过UglifyES压缩ES6代码
不一样的做用
  • Loader直译为"加载器"。Webpack将一切文件视为模块,可是webpack原生是只能解析js文件,若是想将其余文件也打包的话,就会用到loader。 因此Loader的做用是让webpack拥有了加载和解析非js文件的能力。
  • Plugin直译为"插件"。Plugin能够扩展webpack的功能,让webpack具备更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 能够监听这些事件,在合适的时机经过 Webpack 提供的 API 改变输出结果。
不一样的用法
  • Loadermodule.rules中配置,也就是说他做为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options
  • Pluginplugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都经过构造函数传入。

3.webpack优化

优化Loader配置

因为loader对于文件的转换很耗时,因此须要让尽可能少的文件被处理,经过修改test,use,include或者exclude配置来实现

module.exports = { 
    module : { 
        rules : [{
            //若是项目源码中只有 文件,就不要写成/\jsx?$/,以提高正则表达式的性能
            test: /\.js$/, 
            //babel -loader 支持缓存转换出的结果,经过 cacheDirectory 选项开启
            use: ['babel-loader?cacheDirectory'] , 
            //只对项目根目录下 src 目录中的文件采用 babel-loader
            include: path.resolve(__dirname,'src'),
        }],
    }
}
优化resolve.modules

resolve.modules默认值是['node_modules'],webpack查找模块的机制和nodejs很相似,先从当前目录的node_modules下去找,找不到再去上一级../node_modules,以此类推。前端大部分项目node_modules都是在根目录,所以直接配置以下方式能够减小查找

module.exports = {
        resolve: {
            modules: [path.resolve( __dirname,'node modules')] 
        },
    }
module.noParse

经过以下配置使得没有模块化的第三方库无需被解析

module.exports = {
    module: {
        noParse: /jquery/,
    }
};
使用happyPack

因为webpack是单线程处理,因此只能一个个处理任务。happyPack能够将任务拆分红多个分配给子进程并发处理,子进程都处理完了再通知主线程

const HappyPack = require('happypack')
    const os = require('os')
    const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })

    {
        test: /\.js$/,
        // loader: 'babel-loader',
        loader: 'happypack/loader?id=happy-babel-js', // 增长新的HappyPack构建loader
        include: [resolve('src')],
        exclude: /node_modules/,
    }
    
    plugins: [
        new HappyPack({
            id: 'happy-babel-js',
            loaders: ['babel-loader?cacheDirectory=true'],
            threadPool: happyThreadPool
        })
    ]
使用 ParallelUglifyPlugin压缩

webpack提供的UglifyJS只能单线程的一个个压缩js文件(压缩JS代码须要先把代码解析成用Object抽象表示的AST语法树,再去应用各类规则分析和处理AST,致使这个过程耗时很是大),而经过使用ParallelUglifyPlugin能够开启多个子进程并行使用UglifyJS压缩每一个任务

使用 splitChunks 进行分包
tree-shaking

Tree Shaking 正常工做的前提是,提交给 Webpack 的 JavaScript 代码必须采用了 ES6 的模块化语法,由于 ES6 模块化语法是静态的,能够进行静态分析。
首先,为了将采用 ES6 模块化的代码提交给 Webpack ,须要配置 Babel 以让其保留 ES6 模块化语句。修改 .babelrc 文件以下:

{
        'presets':[
            [
                'env',{ 
                    'module':false
                }
            ]
        ]
    }

第二个要求,须要使用UglifyJsPlugin插件。若是在mode:"production"模式,这个插件已经默认添加了,若是在其它模式下,能够手工添加它。

另外要记住的是打开optimization.usedExports。在mode: "production"模式下,它也是默认打开了的。它告诉webpack每一个模块明确使用exports。这样以后,webpack会在打包文件中添加诸如/* unused harmony export */这样的注释,其后UglifyJsPlugin插件会对这些注释做出理解。

module.exports = {
    mode: 'none',
    optimization: {
        minimize: true,
        minimizer: [
            new UglifyJsPlugin()
        ],
        usedExports: true,
        sideEffects: true
    }
}

4.webpack工做流程

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行如下流程:

  1. 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
  2. 开始编译:用上一步获得的参数初始化 Compiler 对象,加载全部配置的插件,执行对象的 run 方法开始执行编译;
  3. 肯定入口:根据配置中的 entry 找出全部的入口文件;
  4. 编译模块:从入口文件出发,调用全部配置的 Loader 对模块进行翻译。利用babel转为Ast->遍历Ast->调用ImportDeclaration。找出该模块依赖的模块,再递归本步骤直到全部入口依赖的文件都通过了本步骤的处理;
  5. 完成模块编译:在通过第4步使用 Loader 翻译完全部模块后,获得了每一个模块被翻译后的最终内容以及它们之间的依赖关系;
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每一个 Chunk 转换成一个单独的文件加入到输出列表,这步是能够修改输出内容的最后机会;
  7. 输出完成:在肯定好输出内容后,根据配置肯定输出的路径和文件名,把文件内容写入到文件系统。

在以上过程当中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,而且插件能够调用 Webpack 提供的 API 改变 Webpack 的运行结果。

5.webpack的热更新是如何作到的?说明其原理?

浏览器

1.浏览器打开一个tab须要多少个进程

浏览器从关闭状态进行启动,而后新开 1 个页面至少须要 1 个网络进程、1 个浏览器进程、1 个 GPU 进程以及 1 个渲染进程,共 4 个进程;后续再新开标签页,浏览器、网络进程、GPU进程是共享的,不会从新启动,若是2个页面属于同一站点的话,而且从a页面中打开的b页面,那么他们也会共用一个渲染进程,不然新开一个渲染进程。

最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。

  • 浏览器进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
  • 渲染进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户能够与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认状况下,Chrome 会为每一个 Tab 标签建立一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
  • GPU 进程:其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器广泛的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
  • 网络进程:主要负责页面的网络资源加载,以前是做为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
  • 插件进程:主要是负责插件的运行,因插件易崩溃,因此须要经过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面形成影响。

2.TCP三次握手四次挥手

创建TCP链接
  • 第一次握手:创建链接。客户端发送链接请求报文段,将SYN位置为1,Sequence Number为x;而后,客户端进入SYN_SEND状态,等待服务器的确认;
  • 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,须要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,本身本身还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述全部信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
  • 第三次握手:客户端收到服务器的SYN+ACK报文段。而后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕之后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

简言之,客户端先向服务端发送消息,证实了客户端的发送能力,服务端接受消息后向客户端发送,证实了服务端的接收和发送能力,最后客户端接收到消息向服务端告知,证实了客户端的接受能力。
完成了三次握手,客户端和服务器端就能够开始传送数据。

ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0。
TCP协议规定,只有ACK=1时有效,也规定链接创建后全部发送的报文的ACK必须为1。
SYN(SYNchronization) : 在链接创建时用来同步序号。当SYN=1而ACK=0时,代表这是一个链接请求报文。对方若赞成创建链接,则应在响应报文中使SYN=1和ACK=1. 所以, SYN置1就表示这是一个链接请求或链接接受报文。
FIN (finis)即完,终结的意思, 用来释放一个链接。当 FIN = 1 时,代表此报文段的发送方的数据已经发送完毕,并要求释放链接。
关闭TCP链接
  • 第一次分手:主机1(可使客户端,也能够是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
  • 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“赞成”你的关闭请求;
  • 第三次分手:主机2向主机1发送FIN报文段,请求关闭链接,同时主机2进入LAST_ACK状态;
  • 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,而后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段之后,就关闭链接;此时,主机1等待2MSL后依然没有收到回复,则证实Server端已正常关闭,那好,主机1也能够关闭链接了。

3.HTTPS的TCP握手

https的握手阶段相比http多了一个ssl/tls协议。
该协议有这三个做用:
(1) 全部信息都是加密传播,第三方没法窃听。
(2) 具备校验机制,一旦被篡改,通讯双方会马上发现。
(3) 配备身份证书,防止身份被冒充。
ssl/tls协议的核心思想是采用公钥加密。客户端向服务器索要公钥,利用公钥对传输的信息加密,服务端收到密文后用私钥解密。
但是这个过程会产生两个问题
(1)如何保证公钥不被篡改?

解决方法:将公钥放在[数字证书](http://en.wikipedia.org/wiki/Digital_certificate)中。只要证书是可信的,公钥就是可信的。

(2)公钥加密计算量太大,如何减小耗用的时间?

解决方法:每一次对话(session),客户端和服务器端都生成一个"对话密钥"(session key),用它来加密信息。因为"对话密钥"是对称加密,因此运算速度很是快,而服务器公钥只用于加密"对话密钥"自己,这样就减小了加密运算的消耗时间。

所以ssl/tls协议的基本过程就是这样的
(1) 客户端向服务器端索要并验证公钥。
(2) 双方协商生成"对话密钥"。
(3) 双方采用"对话密钥"进行加密通讯。
而前两步就是握手阶段。这其中涉及四次握手,且都是明文

客户端发出请求(ClientHello)

首先,客户端(一般是浏览器)先向服务器发出加密通讯的请求,这被叫作ClientHello请求。

在这一步,客户端主要向服务器提供如下信息。

(1) 支持的协议版本,好比TLS 1.0版。

(2) 一个客户端生成的随机数,稍后用于生成"对话密钥"。

(3) 支持的加密方法,好比RSA公钥加密。

(4) 支持的压缩方法。

这里须要注意的是,客户端发送的信息之中不包括服务器的域名。也就是说,理论上服务器只能包含一个网站,不然会分不清应该向客户端提供哪个网站的数字证书。这就是为何一般一台服务器只能有一张数字证书的缘由。

对于虚拟主机的用户来讲,这固然很不方便。2006年,TLS协议加入了一个Server Name Indication扩展,容许客户端向服务器提供它所请求的域名。

服务器回应(SeverHello)

服务器收到客户端请求后,向客户端发出回应,这叫作SeverHello。服务器的回应包含如下内容。

(1) 确认使用的加密通讯协议版本,好比TLS 1.0版本。若是浏览器与服务器支持的版本不一致,服务器关闭加密通讯。

(2) 一个服务器生成的随机数,稍后用于生成"对话密钥"。

(3) 确认使用的加密方法,好比RSA公钥加密。

(4) 服务器证书。

除了上面这些信息,若是服务器须要确认客户端的身份,就会再包含一项请求,要求客户端提供"客户端证书"。好比,金融机构每每只容许认证客户连入本身的网络,就会向正式客户提供USB密钥,里面就包含了一张客户端证书。

客户端回应

客户端收到服务器回应之后,首先验证服务器证书。若是证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已通过期,就会向访问者显示一个警告,由其选择是否还要继续通讯。

若是证书没有问题,客户端就会从证书中取出服务器的公钥。而后,向服务器发送下面三项信息。

(1) 一个随机数。该随机数用服务器公钥加密,防止被窃听。

(2) 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。

(3) 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的全部内容的hash值,用来供服务器校验。

上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称"pre-master key"。有了它之后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把"会话密钥"。

至于为何必定要用三个随机数,来生成"会话密钥",dog250解释得很好:

"无论是客户端仍是服务器,都须要随机数,这样生成的密钥才不会每次都同样。因为SSL协议中证书是静态的,所以十分有必要引入一种随机因素来保证协商出来的密钥的随机性。

对于RSA密钥交换算法来讲,pre-master-key自己就是一个随机数,再加上hello消息中的随机,三个随机数经过一个密钥导出器最终导出一个对称密钥。

pre master的存在在于SSL协议不信任每一个主机都能产生彻底随机的随机数,若是随机数不随机,那么pre master secret就有可能被猜出来,那么仅适用pre master secret做为密钥就不合适了,所以必须引入新的随机因素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能彻底不随机,但是是三个伪随机就十分接近随机了,每增长一个自由度,随机性增长的可不是一。"

此外,若是前一步,服务器要求客户端证书,客户端会在这一步发送证书及相关信息。

服务器的最后回应

服务器收到客户端的第三个随机数pre-master key以后,计算生成本次会话所用的"会话密钥"。而后,向客户端最后发送下面信息。

(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。

(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的全部内容的hash值,用来供客户端校验。

至此,整个握手阶段所有结束。接下来,客户端与服务器进入加密通讯,就彻底是使用普通的HTTP协议,只不过用"会话密钥"加密内容。

4.如何检测页面是否有卡顿

每秒中计算一次网页的 FPS 值,得到一列数据,而后分析。通俗地解释就是,经过 requestAnimationFrame API 来定时执行一些 JS 代码,若是浏览器卡顿,没法很好地保证渲染的频率,1s 中 frame 没法达到 60 帧,便可间接地反映浏览器的渲染帧率。

相关文章
相关标签/搜索