两栏布局的实现

个别两栏布局指的是右边一栏宽度固定,左边一栏宽度自适应,两栏布局的具体实现:

  • 利用浮动,将右边元素宽度设置为200px,并且设置向左浮动。将左边元素的margin-left设置为200px,宽度设置为auto(默认为auto,撑满整个父元素)。
.outer {  height: 100px;}.left {  float: left;  width: 200px;  background: tomato;}.right {  margin-left: 200px;  width: auto;  background: gold;}
  • 利用浮动,左侧元素设置固定大小,并左浮动,右侧元素设置overflow: hidden; 这样左边就触发了BFC,BFC的区域不会与浮动元素产生重叠,所以两侧就不会产生重叠。
.left{     width: 100px;     height: 200px;     background: red;     float: left; } .right{     height: 300px;     background: blue;     overflow: hidden; }
  • 利用flex布局,将右边元素设置为固定宽度200px,将左边的元素设置为flex:1。
.outer {  display: flex;  height: 100px;}.left {  width: 200px;  background: tomato;}.right {  flex: 1;  background: gold;}
  • 利用相对定位,将父级元素设置为绝对定位。右边元素设置为absolute定位,并且宽度设置为200px。将左边元素的margin-left的值设置为200px。
.outer {  position: relative;  height: 100px;}.left {  position: absolute;  width: 200px;  height: 100px;  background: tomato;}.right {  margin-left: 200px;  background: gold;}
  • 利用相对定位,将父级元素设置为绝对定位。右边元素宽度设置为200px,左边元素设置为相对定位,右边定位为200px,其余方向定位为0。
.outer {  position: relative;  height: 100px;}.left {  width: 200px;  background: tomato;}.right {  position: absolute;  top: 0;  right: 0;  bottom: 0;  left: 200px;  background: gold;}

React 17 带来了哪些扭转

最重要的是以下三点:
  • 新的 JSX 转换逻辑
  • 事件零碎重构
  • Lane 模型的引入

1. 重构 JSX 转换逻辑

在过来,如果咱们在 React 我的项目中写入上面这样的代码:

function MyComponent() {  return <p>这是我的组件</p>}

React 是会报错的,起因是 React 中对 JSX 代码的转换依赖的是 React.createElement 这个函数。因而凡是咱们在代码中蕴含了 JSX,那么就必须在文件中引入 React,像上面这样:

import React from 'react';function MyComponent() {  return <p>这是我的组件</p>}

React 17 则容许咱们在不引入 React 的状况下间接应用 JSX。这是因为在 React 17 中,编译器会主动帮咱们引入 JSX 的解析器,也就是说像上面这样一段逻辑:

function MyComponent() {  return <p>这是我的组件</p>}

会被编译器转换成这个样子:

import {jsx as _jsx} from 'react/jsx-runtime';function MyComponent() {  return _jsx('p', { children: '这是我的组件' });}

react/jsx-runtime 中的 JSX 解析器将取代 React.createElement 实现 JSX 的编译工作,这个过程对开发者而言是自动化、无感知的。因而,新的 JSX 转换逻辑带来的最显著的扭转就是升高了开发者的学习老本。

react/jsx-runtime 中的 JSX 解析器看上去仿佛在调用姿态上和 React.createElement 区别不大,那么它是否只是 React.createElement 换了个马甲呢?当然不是,它在外部实现了 React.createElement 无奈做到的性能优化和简化。在肯定状况下,它可能会稍微改善编译输入内容的大小

2. 事件零碎重构

事件零碎在 React 17 中的重构要从以下两个方面来看:

  • 卸掉历史包袱
  • 拥抱新的潮流

2.1 卸掉历史包袱:放弃利用 document 来做事件的中心化管控

React 16.13.x 版本中的事件零碎会通过将所有事件冒泡到 document 来实现对事件的中心化管控

这样的做法尽管看上去曾经足够奇妙,但依然有它不聪慧的中央——document 是整个文档树的根节点,操作 document 带来的影响范畴切实是太大了,这将会使事件变得更加不可控

在 React 17 中,React 团队终于侧面解决了这个问题:事件的中心化管控不会再全副依赖 document,管控相干的逻辑被转移到了每个 React 组件本人的容器 DOM 节点中。比如说咱们在 ID 为 root 的 DOM 节点下挂载了一个 React 组件,像上面代码这样:
const rootElement = document.getElementById("root");ReactDOM.render(<App />, rootElement);

那么事件管控相干的逻辑就会被装置到 root 节点下来。这样一来, React 组件就可能本人玩本人的,再也无奈对全局的事件流构成威胁了

2.2 拥抱新的潮流:放弃事件池

在 React 17 之前,合成事件对象会被放进一个叫作“事件池”的中央对立治理。这样做的目标是可能实现事件对象的复用,进而进步性能:每当事件处理函数执行结束后,其对应的合成事件对象外部的所有属性都会被置空,意在为下一次被复用做筹备。这也就意味着事件逻辑一旦执行结束,咱们就拿不到事件对象了,React 官网给出的这个例子就很能阐明问题,请看上面这个代码

function handleChange(e) {  // This won't work because the event object gets reused.  setTimeout(() => {    console.log(e.target.value); // Too late!  }, 100);}
异步执行的 setTimeout 回调会在 handleChange 这个事件处理函数执行结束后执行,因而它拿不到想要的那个事件对象 e

要想拿到指标事件对象,必须显式地通知 React——我永远须要它,也就是调用 e.persist() 函数,像上面这样:

function handleChange(e) {  // Prevents React from resetting its properties:  e.persist();  setTimeout(() => {    console.log(e.target.value); // Works  }, 100);}

在 React 17 中,咱们不须要 e.persist(),也能够随时随地拜访咱们想要的事件对象。

3. Lane 模型的引入

初学 React 源码的同学由此可能会很天然地认为:优先级就应该是用 Lane 来解决的。但事实上,React 16 中解决优先级采纳的是 expirationTime 模型

expirationTime 模型应用 expirationTime(一个工夫长度) 来形容工作的优先级;而 Lane 模型则应用二进制数来示意工作的优先级

lane 模型通过将不同优先级赋值给一个位,通过 31 位的位运算来操作优先级。

Lane 模型提供了一个新的优先级排序的思路,绝对于 expirationTime 来说,它对优先级的解决会更细腻,可能笼罩更多的边界条件。

变量晋升

当执行 JS 代码时,会生成执行环境,只有代码不是写在函数中的,就是在全局执行环境中,函数中的代码会产生函数执行环境,只此两种执行环境。
b() // call bconsole.log(a) // undefinedvar a = 'Hello world'function b() {    console.log('call b')}
想必以上的输入大家必定都曾经明确了,这是因为函数和变量晋升的起因。通常晋升的解释是说将申明的代码移动到了顶部,这其实没有什么谬误,便于大家了解。然而更精确的解释应该是:在生成执行环境时,会有两个阶段。第一个阶段是创立的阶段,JS 解释器会找出须要晋升的变量和函数,并且给他们提前在内存中开拓好空间,函数的话会将整个函数存入内存中,变量只申明并且赋值为 undefined,所以在第二个阶段,也就是代码执行阶段,咱们能够间接提前应用
  • 在晋升的过程中,雷同的函数会笼罩上一个函数,并且函数优先于变量晋升
b() // call b secondfunction b() {    console.log('call b fist')}function b() {    console.log('call b second')}var b = 'Hello world'
var 会产生很多谬误,所以在 ES6中引入了 letlet不能在申明前应用,然而这并不是常说的 let 不会晋升,let晋升了,在第一阶段内存也曾经为他开拓好了空间,然而因为这个申明的个性导致了并不能在申明前应用

OSI七层模型

ISO为了更好的使网络应用更为遍及,推出了OSI参考模型。

(1)应用层

OSI参考模型中最靠近用户的一层,是为计算机用户提供利用接口,也为用户间接提供各种网络服务。咱们常见应用层的网络服务协定有:HTTPHTTPSFTPPOP3SMTP等。

  • 在客户端与服务器中常常会有数据的申请,这个时候就是会用到http(hyper text transfer protocol)(超文本传输协定)或者https.在后端设计数据接口时,咱们经常应用到这个协定。
  • FTP是文件传输协定,在开发过程中,集体并没有波及到,然而我想,在一些资源网站,比方百度网盘`迅雷`应该是基于此协定的。
  • SMTPsimple mail transfer protocol(简略邮件传输协定)。在一个我的项目中,在用户邮箱验证码登录的性能时,应用到了这个协定。

(2)表示层

表示层提供各种用于应用层数据的编码和转换性能,确保一个零碎的应用层发送的数据能被另一个零碎的应用层辨认。如果必要,该层可提供一种规范示意模式,用于将计算机外部的多种数据格式转换成通信中采纳的规范示意模式。数据压缩和加密也是表示层可提供的转换性能之一。

在我的项目开发中,为了不便数据传输,能够应用base64对数据进行编解码。如果按性能来划分,base64应该是工作在表示层。

(3)会话层

会话层就是负责建设、治理和终止表示层实体之间的通信会话。该层的通信由不同设施中的应用程序之间的服务申请和响应组成。

(4)传输层

传输层建设了主机端到端的链接,传输层的作用是为下层协定提供端到端的牢靠和通明的数据传输服务,包含解决差错控制和流量管制等问题。该层向高层屏蔽了上层数据通信的细节,使高层用户看到的只是在两个传输实体间的一条主机到主机的、可由用户管制和设定的、牢靠的数据通路。咱们通常说的,TCP UDP就是在这一层。端口号既是这里的“端”。

(5)网络层

本层通过IP寻址来建设两个节点之间的连贯,为源端的运输层送来的分组,抉择适合的路由和替换节点,正确无误地依照地址传送给目标端的运输层。就是通常说的IP层。这一层就是咱们常常说的IP协定层。IP协定是Internet的根底。咱们能够这样了解,网络层规定了数据包的传输路线,而传输层则规定了数据包的传输方式。

(6)数据链路层

将比特组合成字节,再将字节组合成帧,应用链路层地址 (以太网应用MAC地址)来拜访介质,并进行过错检测。
网络层与数据链路层的比照,通过下面的形容,咱们或者能够这样了解,网络层是布局了数据包的传输路线,而数据链路层就是传输路线。不过,在数据链路层上还减少了差错控制的性能。

(7)物理层

理论最终信号的传输是通过物理层实现的。通过物理介质传输比特流。规定了电平、速度和电缆针脚。罕用设施有(各种物理设施)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。

OSI七层模型通信特点:对等通信 对等通信,为了使数据分组从源传送到目的地,源端OSI模型的每一层都必须与目标端的对等层进行通信,这种通信形式称为对等层通信。在每一层通信过程中,应用本层本人协定进行通信。

参考:前端进阶面试题具体解答

== 操作符的强制类型转换规定?

对于 == 来说,如果比照单方的类型不一样,就会进行类型转换。如果比照 xy 是否雷同,就会进行如下判断流程:

  1. 首先会判断两者类型是否雷同,雷同的话就比拟两者的大小;
  2. 类型不雷同的话,就会进行类型转换;
  3. 会先判断是否在比照 nullundefined,是的话就会返回 true
  4. 判断两者类型是否为 stringnumber,是的话就会将字符串转换为 number
1 == '1'      ↓1 ==  1
  1. 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
'1' == true        ↓'1' ==  1        ↓ 1  ==  1
  1. 判断其中一方是否为 object 且另一方为 stringnumber 或者 symbol,是的话就会把 object 转为原始类型再进行判断
'1' == { name: 'js' }        ↓'1' == '[object Object]'

Ajax

它是一种异步通信的办法,通过间接由 js 脚本向服务器发动 http 通信,而后依据服务器返回的数据,更新网页的相应局部,而不必刷新整个页面的一种办法。

面试手写(原生):

//1:创立Ajax对象var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本//2:配置 Ajax申请地址xhr.open('get','index.xml',true);//3:发送申请xhr.send(null); // 谨严写法//4:监听申请,承受响应xhr.onreadysatechange=function(){     if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )          console.log(xhr.responsetXML)}

jQuery写法

$.ajax({  type:'post',  url:'',  async:ture,//async 异步  sync  同步  data:data,//针对post申请  dataType:'jsonp',  success:function (msg) {  },  error:function (error) {  }})

promise 封装实现:

// promise 封装实现:function getJSON(url) {  // 创立一个 promise 对象  let promise = new Promise(function(resolve, reject) {    let xhr = new XMLHttpRequest();    // 新建一个 http 申请    xhr.open("GET", url, true);    // 设置状态的监听函数    xhr.onreadystatechange = function() {      if (this.readyState !== 4) return;      // 当申请胜利或失败时,扭转 promise 的状态      if (this.status === 200) {        resolve(this.response);      } else {        reject(new Error(this.statusText));      }    };    // 设置谬误监听函数    xhr.onerror = function() {      reject(new Error(this.statusText));    };    // 设置响应的数据类型    xhr.responseType = "json";    // 设置申请头信息    xhr.setRequestHeader("Accept", "application/json");    // 发送 http 申请    xhr.send(null);  });  return promise;}

数字证书是什么?

当初的办法也不肯定是平安的,因为没有方法确定失去的公钥就肯定是平安的公钥。可能存在一个中间人,截取了对方发给咱们的公钥,而后将他本人的公钥发送给咱们,当咱们应用他的公钥加密后发送的信息,就能够被他用本人的私钥解密。而后他伪装成咱们以同样的办法向对方发送信息,这样咱们的信息就被窃取了,然而本人还不晓得。为了解决这样的问题,能够应用数字证书。

首先应用一种 Hash 算法来对公钥和其余信息进行加密,生成一个信息摘要,而后让有公信力的认证核心(简称 CA )用它的私钥对音讯摘要加密,造成签名。最初将原始的信息和签名合在一起,称为数字证书。当接管方收到数字证书的时候,先依据原始信息应用同样的 Hash 算法生成一个摘要,而后应用公证处的公钥来对数字证书中的摘要进行解密,最初将解密的摘要和生成的摘要进行比照,就能发现失去的信息是否被更改了。

这个办法最要的是认证核心的可靠性,个别浏览器里会内置一些顶层的认证核心的证书,相当于咱们主动信赖了他们,只有这样能力保证数据的平安。

介绍一下 Tree Shaking

对tree-shaking的理解

作用:

它示意在打包的时候会去除一些无用的代码

原理

  • ES6的模块引入是动态剖析的,所以在编译时能正确判断到底加载了哪些模块
  • 分析程序流,判断哪些变量未被应用、援用,进而删除此代码

特点:

  • 在生产模式下它是默认开启的,然而因为通过babel编译全副模块被封装成IIFE,它存在副作用无奈被tree-shaking
  • 能够在package.json中配置sideEffects来指定哪些文件是有副作用的。它有两种值,一个是布尔类型,如果是false则示意所有文件都没有副作用;如果是一个数组的话,数组里的文件门路示意改文件有副作用
  • rollupwebpack中对tree-shaking的层度不同,例如对babel转译后的class,如果babel的转译是宽松模式下的话(也就是loosetrue),webpack依旧会认为它有副作用不会tree-shaking掉,而rollup会。这是因为rollup有程序流剖析的性能,能够更好的判断代码是否真正会产生副作用。

原理

  • ES6 Module 引入进行动态剖析,故而编译的时候正确判断到底加载了那些模块
  • 动态分析程序流,判断那些模块和变量未被应用或者援用,进而删除对应代码
依赖于import/export

通过导入所有的包后再进行条件获取。如下:

import foo from "foo";import bar from "bar";if(condition) {    // foo.xxxx} else {    // bar.xxx}
ES6的import语法完满能够应用tree shaking,因为能够在代码不运行的状况下就能剖析出不须要的代码

CommonJS的动静个性模块意味着tree shaking不实用 。因为它是不可能确定哪些模块理论运行之前是须要的或者是不须要的。在ES6中,进入了齐全动态的导入语法:import。这也意味着上面的导入是不可行的:

// 不可行,ES6 的import是齐全动态的if(condition) {    myDynamicModule = require("foo");} else {    myDynamicModule = require("bar");}

setState原理剖析

1. setState异步更新

  • 咱们都晓得,React通过this.state来拜访state,通过this.setState()办法来更新state。当this.setState()办法被调用的时候,React会从新调用render办法来从新渲染UI
  • 首先如果间接在setState前面获取state的值是获取不到的。在React外部机制能检测到的中央, setState就是异步的;在React检测不到的中央,例如setInterval,setTimeoutsetState就是同步更新的

因为setState是能够承受两个参数的,一个state,一个回调函数。因而咱们能够在回调函数外面获取值

  • setState办法通过一个队列机制实现state更新,当执行setState的时候,会将须要更新的state合并之后放入状态队列,而不会立刻更新this.state
  • 如果咱们不应用setState而是应用this.state.key来批改,将不会触发组件的re-render
  • 如果将this.state赋值给一个新的对象援用,那么其余不在对象上的state将不会被放入状态队列中,当下次调用setState并对状态队列进行合并时,间接造成了state失落

1.1 setState批量更新的过程

react生命周期和合成事件执行前后都有相应的钩子,别离是pre钩子和post钩子,pre钩子会调用batchedUpdate办法将isBatchingUpdates变量置为true,开启批量更新,而post钩子会将isBatchingUpdates置为false
  • isBatchingUpdates变量置为true,则会走批量更新分支,setState的更新会被存入队列中,待同步代码执行完后,再执行队列中的state更新。 isBatchingUpdatestrue,则把以后组件(即调用了 setState的组件)放入 dirtyComponents 数组中;否则 batchUpdate 所有队列中的更新
  • 而在原生事件和异步操作中,不会执行pre钩子,或者生命周期的中的异步操作之前执行了pre钩子,然而pos钩子也在异步操作之前执行完了,isBatchingUpdates必然为false,也就不会进行批量更新

enqueueUpdate蕴含了React防止反复render的逻辑。mountComponentupdateComponent办法在执行的最开始,会调用到batchedUpdates进行批处理更新,此时会将isBatchingUpdates设置为true,也就是将状态标记为当初正处于更新阶段了。 isBatchingUpdatestrue,则把以后组件(即调用了 setState 的组件)放入dirtyComponents 数组中;否则 batchUpdate 所有队列中的更新

1.2 为什么间接批改this.state有效

  • 要晓得setState实质是通过一个队列机制实现state更新的。 执行setState时,会将须要更新的state合并后放入状态队列,而不会立即更新state,队列机制能够批量更新state
  • 如果不通过setState而间接批改this.state,那么这个state不会放入状态队列中,下次调用setState时对状态队列进行合并时,会疏忽之前间接被批改的state,这样咱们就无奈合并了,而且理论也没有把你想要的state更新下来

1.3 什么是批量更新 Batch Update

在一些mv*框架中,,就是将一段时间内对model的批改批量更新到view的机制。比方那前端比拟火的ReactvuenextTick机制,视图的更新以及实现)

1.4 setState之后产生的事件

  • setState操作并不保障是同步的,也能够认为是异步的
  • ReactsetState之后,会经对state进行diff,判断是否有扭转,而后去diff dom决定是否要更新UI。如果这一系列过程立即产生在每一个setState之后,就可能会有性能问题
  • 在短时间内频繁setStateReact会将state的扭转压入栈中,在适合的机会,批量更新state和视图,达到进步性能的成果

1.5 如何晓得state曾经被更新

传入回调函数
setState({    index: 1}}, function(){    console.log(this.state.index);})
在钩子函数中体现
componentDidUpdate(){    console.log(this.state.index);}

2. setState循环调用危险

  • 当调用setState时,实际上会执行enqueueSetState办法,并对partialState以及_pending-StateQueue更新队列进行合并操作,最终通过enqueueUpdate执行state更新
  • performUpdateIfNecessary办法会获取_pendingElement,_pendingStateQueue_pending-ForceUpdate,并调用receiveComponentupdateComponent办法进行组件更新
  • 如果在shouldComponentUpdate或者componentWillUpdate办法中调用setState,此时this._pending-StateQueue != null,就会造成循环调用,使得浏览器内存占满后解体

3 事务

  • 事务就是将须要执行的办法应用wrapper封装起来,再通过事务提供的perform办法执行,先执行wrapper中的initialize办法,执行完perform之后,在执行所有的close办法,一组initializeclose办法称为一个wrapper
  • 那么事务和setState办法的不同体现有什么关系,首先咱们把4setState简略归类,前两次属于一类,因为它们在同一调用栈中执行,setTimeout中的两次setState属于另一类
  • setState调用之前,曾经处在batchedUpdates执行的事务中了。那么这次batchedUpdates办法是谁调用的呢,原来是ReactMount.js中的_renderNewRootComponent办法。也就是说,整个将React组件渲染到DOM中的过程就是处于一个大的事务中。而在componentDidMount中调用setState时,batchingStrategyisBatchingUpdates曾经被设为了true,所以两次setState的后果没有立刻失效
  • 再反观setTimeout中的两次setState,因为没有前置的batchedUpdates调用,所以导致了新的state马上失效

4. 总结

  • 通过setState去更新this.state,不要间接操作this.state,请把它当成不可变的
  • 调用setState更新this.state不是马上失效的,它是异步的,所以不要天真认为执行完setStatethis.state就是最新的值了
  • 多个程序执行的setState不是同步地一个一个执行滴,会一个一个退出队列,而后最初一起执行,即批处理

absolute与fixed共同点与不同点

共同点:

  • 扭转行内元素的出现形式,将display置为inline-block
  • 使元素脱离一般文档流,不再占据文档物理空间
  • 笼罩非定位文档元素

不同点:

  • abuselute与fixed的根元素不同,abuselute的根元素能够设置,fixed根元素是浏览器。
  • 在有滚动条的页面中,absolute会跟着父元素进行挪动,fixed固定在页面的具体位置。

async/await比照Promise的劣势

  • 代码读起来更加同步,Promise尽管解脱了回调天堂,然而then的链式调⽤也会带来额定的浏览累赘
  • Promise传递两头值⾮常麻烦,⽽async/await⼏乎是同步的写法,⾮常优雅
  • 错误处理敌对,async/await能够⽤成熟的try/catch,Promise的谬误捕捉⾮常冗余
  • 调试敌对,Promise的调试很差,因为没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个.then代码块中使⽤调试器的步进(step-over)性能,调试器并不会进⼊后续的.then代码块,因为调试器只能跟踪同步代码的每⼀步。

性能优化

DNS 预解析

  • DNS 解析也是须要工夫的,能够通过预解析的形式来事后取得域名所对应的 IP
<link rel="dns-prefetch" href="//blog.poetries.top">

缓存

  • 缓存对于前端性能优化来说是个很重要的点,良好的缓存策略能够升高资源的反复加载进步网页的整体加载速度
  • 通常浏览器缓存策略分为两种:强缓存和协商缓存

强缓存

实现强缓存能够通过两种响应头实现:ExpiresCache-Control 。强缓存示意在缓存期间不须要申请,state code200
Expires: Wed, 22 Oct 2018 08:41:00 GMT
ExpiresHTTP / 1.0 的产物,示意资源会在 Wed, 22 Oct 2018 08:41:00 GMT 后过期,须要再次申请。并且 Expires 受限于本地工夫,如果批改了本地工夫,可能会造成缓存生效
Cache-control: max-age=30
Cache-Control 呈现于 HTTP / 1.1,优先级高于 Expires 。该属性示意资源会在 30 秒后过期,须要再次申请

协商缓存

  • 如果缓存过期了,咱们就能够应用协商缓存来解决问题。协商缓存须要申请,如果缓存无效会返回 304
  • 协商缓存须要客户端和服务端独特实现,和强缓存一样,也有两种实现形式

Last-ModifiedIf-Modified-Since

  • Last-Modified 示意本地文件最初批改日期,If-Modified-Since 会将 Last-Modified的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来
  • 然而如果在本地关上缓存文件,就会造成 Last-Modified 被批改,所以在 HTTP / 1.1 呈现了 ETag

ETagIf-None-Match

  • ETag 相似于文件指纹,If-None-Match 会将以后 ETag 发送给服务器,询问该资源 ETag 是否变动,有变动的话就将新的资源发送回来。并且 ETag 优先级比 Last-Modified

抉择适合的缓存策略

对于大部分的场景都能够应用强缓存配合协商缓存解决,然而在一些非凡的中央可能须要抉择非凡的缓存策略
  • 对于某些不须要缓存的资源,能够应用 Cache-control: no-store ,示意该资源不须要缓存
  • 对于频繁变动的资源,能够应用 Cache-Control: no-cache 并配合 ETag 应用,示意该资源已被缓存,然而每次都会发送申请询问资源是否更新。
  • 对于代码文件来说,通常应用 Cache-Control: max-age=31536000 并配合策略缓存应用,而后对文件进行指纹解决,一旦文件名变动就会立即下载新的文件

应用 HTTP / 2.0

  • 因为浏览器会有并发申请限度,在 HTTP / 1.1 时代,每个申请都须要建设和断开,耗费了好几个 RTT 工夫,并且因为 TCP 慢启动的起因,加载体积大的文件会须要更多的工夫
  • HTTP / 2.0 中引入了多路复用,可能让多个申请应用同一个 TCP 链接,极大的放慢了网页的加载速度。并且还反对 Header 压缩,进一步的缩小了申请的数据大小

预加载

  • 在开发中,可能会遇到这样的状况。有些资源不须要马上用到,然而心愿尽早获取,这时候就能够应用预加载
  • 预加载其实是申明式的 fetch ,强制浏览器申请资源,并且不会阻塞 onload 事件,能够应用以下代码开启预加载
<link rel="preload" href="http://example.com">
预加载能够肯定水平上升高首屏的加载工夫,因为能够将一些不影响首屏但重要的文件延后加载,惟一毛病就是兼容性不好

预渲染

能够通过预渲染将下载的文件事后在后盾渲染,能够应用以下代码开启预渲染
<link rel="prerender" href="http://poetries.com">
  • 预渲染尽管能够进步页面的加载速度,然而要确保该页面百分百会被用户在之后关上,否则就白白浪费资源去渲染

总结

  • deferasync在网络读取的过程中都是异步解析
  • defer是有程序依赖的,async只有脚本加载完后就会执行
  • preload 能够对以后页面所需的脚本、款式等资源进行预加载
  • prefetch 加载的资源个别不是用于以后页面的,是将来很可能用到的这样一些资源

懒执行与懒加载

懒执行

  • 懒执行就是将某些逻辑提早到应用时再计算。该技术能够用于首屏优化,对于某些耗时逻辑并不需要在首屏就应用的,就能够应用懒执行。懒执行须要唤醒,个别能够通过定时器或者事件的调用来唤醒

懒加载

  • 懒加载就是将不要害的资源延后加载
懒加载的原理就是只加载自定义区域(通常是可视区域,但也能够是行将进入可视区域)内须要加载的货色。对于图片来说,先设置图片标签的 src 属性为一张占位图,将实在的图片资源放入一个自定义属性中,当进入自定义区域时,就将自定义属性替换为 src 属性,这样图片就会去下载资源,实现了图片懒加载
  • 懒加载不仅能够用于图片,也能够应用在别的资源上。比方进入可视区域才开始播放视频等

文件优化

图片优化

对于如何优化图片,有 2 个思路
  • 缩小像素点
  • 缩小每个像素点可能显示的色彩

图片加载优化

  • 不必图片。很多时候会应用到很多润饰类图片,其实这类润饰图片齐全能够用 CSS 去代替。
  • 对于挪动端来说,屏幕宽度就那么点,齐全没有必要去加载原图节约带宽。个别图片都用 CDN 加载,能够计算出适配屏幕的宽度,而后去申请相应裁剪好的图片
  • 小图应用 base64格局
  • 将多个图标文件整合到一张图片中(雪碧图)
  • 抉择正确的图片格式:

    • 对于可能显示 WebP 格局的浏览器尽量应用 WebP 格局。因为 WebP 格局具备更好的图像数据压缩算法,能带来更小的图片体积,而且领有肉眼辨认无差别的图像品质,毛病就是兼容性并不好
    • 小图应用 PNG,其实对于大部分图标这类图片,齐全能够应用 SVG 代替
    • 照片应用 JPEG

其余文件优化

  • CSS文件放在 head
  • 服务端开启文件压缩性能
  • script 标签放在 body 底部,因为 JS 文件执行会阻塞渲染。当然也能够把 script 标签放在任意地位而后加上 defer ,示意该文件会并行下载,然而会放到 HTML 解析实现后程序执行。对于没有任何依赖的 JS文件能够加上 async ,示意加载和渲染后续文档元素的过程将和 JS 文件的加载与执行并行无序进行。 执行 JS代码过长会卡住渲染,对于须要很多工夫计算的代码
  • 能够思考应用 WebworkerWebworker能够让咱们另开一个线程执行脚本而不影响渲染。

CDN

动态资源尽量应用 CDN 加载,因为浏览器对于单个域名有并发申请下限,能够思考应用多个 CDN 域名。对于 CDN 加载动态资源须要留神 CDN 域名要与主站不同,否则每次申请都会带上主站的 Cookie

其余

应用 Webpack 优化我的项目

  • 对于 Webpack4,打包我的项目应用 production 模式,这样会主动开启代码压缩
  • 应用 ES6 模块来开启 tree shaking,这个技术能够移除没有应用的代码
  • 优化图片,对于小图能够应用 base64 的形式写入文件中
  • 依照路由拆分代码,实现按需加载
  • 给打包进去的文件名增加哈希,实现浏览器缓存文件

监控

对于代码运行谬误,通常的方法是应用 window.onerror 拦挡报错。该办法能拦挡到大部分的具体报错信息,然而也有例外
  • 对于跨域的代码运行谬误会显示 Script error. 对于这种状况咱们须要给 script 标签增加 crossorigin 属性
  • 对于某些浏览器可能不会显示调用栈信息,这种状况能够通过 arguments.callee.caller 来做栈递归
  • 对于异步代码来说,能够应用 catch 的形式捕捉谬误。比方 Promise 能够间接应用 catch 函数,async await 能够应用 try catch
  • 然而要留神线上运行的代码都是压缩过的,须要在打包时生成 sourceMap 文件便于 debug
  • 对于捕捉的谬误须要上传给服务器,通常能够通过 img 标签的 src发动一个申请

如何依据chrome的timing优化

性能优化API

  • Performanceperformance.now()new Date()区别,它是高精度的,且是绝对工夫,绝对于页面加载的那一刻。然而不肯定适宜单页面场景
  • window.addEventListener("load", ""); window.addEventListener("domContentLoaded", "");
  • Imgonload事件,监听首屏内的图片是否加载实现,判断首屏事件
  • RequestFrameAnmationRequestIdleCallback
  • IntersectionObserverMutationObserverPostMessage
  • Web Worker,耗时工作放在外面执行

检测工具

  • Chrome Dev Tools
  • Page Speed
  • Jspref

前端指标

window.onload = function(){    setTimeout(function(){        let t = performance.timing        console.log('DNS查问耗时 :' + (t.domainLookupEnd - t.domainLookupStart).toFixed(0))        console.log('TCP链接耗时 :' + (t.connectEnd - t.connectStart).toFixed(0))        console.log('request申请耗时 :' + (t.responseEnd - t.responseStart).toFixed(0))        console.log('解析dom树耗时 :' + (t.domComplete - t.domInteractive).toFixed(0))        console.log('白屏工夫 :' + (t.responseStart - t.navigationStart).toFixed(0))        console.log('domready工夫 :' + (t.domContentLoadedEventEnd - t.navigationStart).toFixed(0))        console.log('onload工夫 :' + (t.loadEventEnd - t.navigationStart).toFixed(0))        if(t = performance.memory){            console.log('js内存应用占比 :' + (t.usedJSHeapSize / t.totalJSHeapSize * 100).toFixed(2) + '%')        }    })}

DNS预解析优化

dns解析是很耗时的,因而如果解析域名过多,会让首屏加载变得过慢,能够思考dns-prefetch优化

DNS Prefetch 应该尽量的放在网页的后面,举荐放在 前面。具体应用办法如下:

<meta http-equiv="x-dns-prefetch-control" content="on"><link rel="dns-prefetch" href="//www.zhix.net"><link rel="dns-prefetch" href="//api.share.zhix.net"><link rel="dns-prefetch" href="//bdimg.share.zhix.net">

request申请耗时

  • 不申请,用cache(最好的形式就是尽量援用公共资源,同时设置缓存,不去从新申请资源,也能够使用PWA的离线缓存技术,能够帮忙wep实现离线应用)
  • 前端打包时压缩
  • 服务器上的zip压缩
  • 图片压缩(比方tiny),应用webp等高压缩比格局
  • 把过大的包,拆分成多个较少的包,避免单个资源耗时过大
  • 同一时间针对同一域名下的申请有肯定数量限度,超过限度数目的申请会被阻塞。如果资源来自于多个域下,能够增大并行申请和下载速度
  • 提早、异步、预加载、懒加载
  • 对于非首屏的资源,能够应用 defer 或 async 的形式引入
  • 也能够按需加载,在逻辑中,只有执行到时才做申请
  • 对于多屏页面,滚动时才动静载入图片

挪动端优化

1. 概述

  • PC优化伎俩在Mobile侧同样实用
  • Mobile侧咱们提出三秒种渲染实现首屏指标
  • 基于第二点,首屏加载3秒实现或应用Loading
  • 基于联通3G网络均匀338KB/s(2.71Mb/s),所以首屏资源不应超过1014KB
  • Mobile侧因手机配置起因,除加载外渲染速度也是优化重点
  • 基于第五点,要正当解决代码缩小渲染损耗
  • 基于第二、第五点,所有影响首屏加载和渲染的代码应在解决逻辑中后置
  • 加载实现后用户交互应用时也需注意性能

2. 加载优化

加载过程是最为耗时的过程,可能会占到总耗时的80%工夫,因而是优化的重点

2.1 缓存

应用缓存能够缩小向服务器的申请数,节俭加载工夫,所以所有动态资源都要在服务器端设置缓存,并且尽量应用长Cache(长Cache资源的更新可应用工夫戳)

2.2 压缩HTML、CSS、JavaScript

缩小资源大小能够放慢网页显示速度,所以要对HTMLCSSJavaScript等进行代码压缩,并在服务器端设置GZip
  • a) 压缩(例如,多余的空格、换行符和缩进)
  • b) 启用GZip

2.3 无阻塞

写在HTML头部的JavaScript(无异步),和写在HTML标签中的Style会阻塞页面的渲染,因而CSS放在页面头部并应用Link形式引入,防止在HTML标签中写StyleJavaScript放在页面尾部或应用异步形式加载

2.4 应用首屏加载

首屏的疾速显示,能够大大晋升用户对页面速度的感知,因而应尽量针对首屏的疾速显示做优化。

2.5 按需加载

将不影响首屏的资源和以后屏幕资源不必的资源放到用户须要时才加载,能够大大晋升重要资源的显示速度和升高总体流量。

PS:按需加载会导致大量重绘,影响渲染性能

  • a) LazyLoad
  • b) 滚屏加载
  • c) 通过Media Query加载

2.6 预加载

大型重资源页面(如游戏)可应用减少Loading的办法,资源加载实现后再显示页面。但Loading工夫过长,会造成用户散失。

对用户行为剖析,能够在当前页加载下一页资源,晋升速度。

  • a)可感知Loading
  • b)不可感知的Loading(如提前加载下一页)

2.7 压缩图片

图片是最占流量的资源,因而尽量避免应用他,应用时抉择最合适的格局(实现需求的前提下,以大小判断),适合的大小,而后应用智图压缩,同时在代码中用Srcset来按需显示

PS:适度压缩图片大小影响图片显示成果

  • a)应用智图( http://zhitu.tencent.com/ )
  • b)应用其它形式代替图片(1. 应用CSS3 2. 应用SVG 3. 应用IconFont
  • c)应用Srcset
  • d)抉择适合的图片(1. webP优于JPG2. PNG8优于GIF
  • e)抉择适合的大小(1. 首次加载不大于1014KB 2. 不宽于640(基于手机屏幕个别宽度))

2.8 缩小Cookie

Cookie会影响加载速度,所以动态资源域名不应用Cookie

2.9 防止重定向

重定向会影响加载速度,所以在服务器正确设置防止重定向。

2.10 异步加载第三方资源

第三方资源不可控会影响页面的加载和显示,因而要异步加载第三方资源

2.11 缩小HTTP申请

因为手机浏览器同时响应申请为4个申请(Android反对4个,iOS 5后可反对6个),所以要尽量减少页面的申请数,首次加载同时申请数不能超过4个
  • a)合并CSSJavaScript
  • b)合并小图片,应用雪碧图

3. 三、脚本执行优化

脚本处理不当会阻塞页面加载、渲染,因而在应用时需当留神
  • CSS写在头部,JavaScript写在尾部或异步
  • 防止图片和iFrame等的空Src,空Src会从新加载以后页面,影响速度和效率。
  • 尽量避免重设图片大小
  • 重设图片大小是指在页面、CSS、JavaScript等中屡次重置图片大小,屡次重设图片大小会引发图片的多次重绘,影响性能
  • 图片尽量避免应用DataURLDataURL图片没有应用图片的压缩算法文件会变大,并且要解码后再渲染,加载慢耗时长

4. CSS优化

尽量避免写在HTML标签中写Style属性

4.1 css3过渡动画开启硬件加速

.translate3d{   -webkit-transform: translate3d(0, 0, 0);   -moz-transform: translate3d(0, 0, 0);   -ms-transform: translate3d(0, 0, 0);   transform: translate3d(0, 0, 0); }

4.2 防止CSS表达式

CSS表达式的执行需跳出CSS树的渲染,因而请防止CSS表达式。

4.3 不滥用Float

Float在渲染时计算量比拟大,尽量减少应用

4.4 值为0时不须要任何单位

为了浏览器的兼容性和性能,值为0时不要带单位

5. JavaScript执行优化

5.1 缩小重绘和回流

  • 防止不必要的Dom操作
  • 尽量扭转Class而不是Style,应用classList代替className
  • 防止应用document.write
  • 缩小drawImage

5.2 TOUCH事件优化

应用touchstarttouchend代替click,因快影响速度快。但应留神Touch响应过快,易引发误操作

6. 渲染优化

6.1 HTML应用Viewport

Viewport能够减速页面的渲染,请应用以下代码

<meta name=”viewport” content=”width=device-width, initial-scale=1″>

6.2 动画优化

  • 尽量应用CSS3动画
  • 正当应用requestAnimationFrame动画代替setTimeout
  • 适当应用Canvas动画 5个元素以内应用css动画,5个以上应用Canvas动画(iOS8可应用webGL

6.3 高频事件优化

TouchmoveScroll 事件可导致屡次渲染
  • 应用requestAnimationFrame监听帧变动,使得在正确的工夫进行渲染
  • 减少响应变动的工夫距离,缩小重绘次数

6.4 GPU减速

CSS中以下属性(CSS3 transitionsCSS3 3D transformsOpacityCanvasWebGLVideo)来触发GPU渲染,请正当应用

HTTPS通信(握手)过程

HTTPS的通信过程如下:

  1. 客户端向服务器发动申请,申请中蕴含应用的协定版本号、生成的一个随机数、以及客户端反对的加密办法。
  2. 服务器端接管到申请后,确认单方应用的加密办法、并给出服务器的证书、以及一个服务器生成的随机数。
  3. 客户端确认服务器证书无效后,生成一个新的随机数,并应用数字证书中的公钥,加密这个随机数,而后发给服 务器。并且还会提供一个后面所有内容的 hash 的值,用来供服务器测验。
  4. 服务器应用本人的私钥,来解密客户端发送过去的随机数。并提供后面所有内容的 hash 值来供客户端测验。
  5. 客户端和服务器端依据约定的加密办法应用后面的三个随机数,生成对话秘钥,当前的对话过程都应用这个秘钥来加密信息。

JavaScript 中如何进行隐式类型转换?

首先要介绍ToPrimitive办法,这是 JavaScript 中每个值隐含的自带的办法,用来将值 (无论是根本类型值还是对象)转换为根本类型值。如果值为根本类型,则间接返回值自身;如果值为对象,其看起来大略是这样:

/*** @obj 须要转换的对象* @type 冀望的后果类型*/ToPrimitive(obj,type)

type的值为number或者string

(1)当typenumber时规定如下:

  • 调用objvalueOf办法,如果为原始值,则返回,否则下一步;
  • 调用objtoString办法,后续同上;
  • 抛出TypeError 异样。

(2)当typestring时规定如下:

  • 调用objtoString办法,如果为原始值,则返回,否则下一步;
  • 调用objvalueOf办法,后续同上;
  • 抛出TypeError 异样。

能够看出两者的次要区别在于调用toStringvalueOf的先后顺序。默认状况下:

  • 如果对象为 Date 对象,则type默认为string
  • 其余状况下,type默认为number

总结下面的规定,对于 Date 以外的对象,转换为根本类型的大略规定能够概括为一个函数:

var objToNumber = value => Number(value.valueOf().toString())objToNumber([]) === 0objToNumber({}) === NaN

而 JavaScript 中的隐式类型转换次要产生在+、-、*、/以及==、>、<这些运算符之间。而这些运算符只能操作根本类型值,所以在进行这些运算前的第一步就是将两边的值用ToPrimitive转换成根本类型,再进行操作。

以下是根本类型的值在不同操作符的状况下隐式转换的规定 (对于对象,其会被ToPrimitive转换成根本类型,所以最终还是要利用根本类型转换规定):

  1. +操作符 +操作符的两边有至多一个string类型变量时,两边的变量都会被隐式转换为字符串;其余状况下两边的变量都会被转换为数字。
1 + '23' // '123' 1 + false // 1  1 + Symbol() // Uncaught TypeError: Cannot convert a Symbol value to a number '1' + false // '1false' false + true // 1
  1. -*\操作符

NaN也是一个数字

1 * '23' // 23 1 * false // 0 1 / 'aa' // NaN
  1. 对于==操作符

操作符两边的值都尽量转成number

3 == true // false, 3 转为number为3,true转为number为1'0' == false //true, '0'转为number为0,false转为number为0'0' == 0 // '0'转为number为0
  1. 对于<>比拟符

如果两边都是字符串,则比拟字母表程序:

'ca' < 'bd' // false'a' < 'b' // true

其余状况下,转换为数字再比拟:

'12' < 13 // truefalse > -1 // true

以上说的是根本类型的隐式转换,而对象会被ToPrimitive转换为根本类型再进行转换:

var a = {}a > 2 // false

其比照过程如下:

a.valueOf() // {}, 下面提到过,ToPrimitive默认type为number,所以先valueOf,后果还是个对象,下一步a.toString() // "[object Object]",当初是一个字符串了Number(a.toString()) // NaN,依据下面 < 和 > 操作符的规定,要转换成数字NaN > 2 //false,得出比拟后果

又比方:

var a = {name:'Jack'}var b = {age: 18}a + b // "[object Object][object Object]"

运算过程如下:

a.valueOf() // {},下面提到过,ToPrimitive默认type为number,所以先valueOf,后果还是个对象,下一步a.toString() // "[object Object]"b.valueOf() // 同理b.toString() // "[object Object]"a + b // "[object Object][object Object]"

HTTPS的特点

HTTPS的长处如下:

  • 应用HTTPS协定能够认证用户和服务器,确保数据发送到正确的客户端和服务器;
  • 应用HTTPS协定能够进行加密传输、身份认证,通信更加平安,避免数据在传输过程中被窃取、批改,确保数据安全性;
  • HTTPS是现行架构下最平安的解决方案,尽管不是相对的平安,然而大幅减少了中间人攻打的老本;

HTTPS的毛病如下:

  • HTTPS须要做服务器和客户端单方的加密个解密解决,消耗更多服务器资源,过程简单;
  • HTTPS协定握手阶段比拟费时,减少页面的加载工夫;
  • SSL证书是免费的,性能越弱小的证书费用越高;
  • HTTPS连贯服务器端资源占用高很多,反对访客稍多的网站须要投入更大的老本;
  • SSL证书须要绑定IP,不能再同一个IP上绑定多个域名。

HTTP前生今世

  • HTTP 协定始于三十年前蒂姆·伯纳斯 - 李的一篇论文
  • HTTP/0.9 是个简略的文本协定,只能获取文本资源;
  • HTTP/1.0 确立了大部分当初应用的技术,但它不是正式规范;
  • HTTP/1.1 是目前互联网上应用最宽泛的协定,性能也十分欠缺;
  • HTTP/2 基于 Google 的 SPDY 协定,重视性能改善,但还未遍及;
  • HTTP/3 基于 Google 的 QUIC 协定,是未来的倒退方向

::before 和 :after 的双冒号和单冒号有什么区别?

(1)冒号(:)用于CSS3伪类,双冒号(::)用于CSS3伪元素。
(2)::before就是以一个子元素的存在,定义在元素主体内容之前的一个伪元素。并不存在于dom之中,只存在在页面之中。

留神: :before:after 这两个伪元素,是在CSS2.1里新呈现的。起初,伪元素的前缀应用的是单冒号语法,但随着Web的进化,在CSS3的标准里,伪元素的语法被批改成应用双冒号,成为::before::after

即时通讯的实现:短轮询、长轮询、SSE 和 WebSocket 间的区别?

短轮询和长轮询的目标都是用于实现客户端和服务器端的一个即时通讯。

短轮询的基本思路: 浏览器每隔一段时间向浏览器发送 http 申请,服务器端在收到申请后,不管是否有数据更新,都间接进行响应。这种形式实现的即时通信,实质上还是浏览器发送申请,服务器承受申请的一个过程,通过让客户端一直的进行申请,使得客户端可能模仿实时地收到服务器端的数据的变动。这种形式的长处是比较简单,易于了解。毛病是这种形式因为须要一直的建设 http 连贯,重大节约了服务器端和客户端的资源。当用户减少时,服务器端的压力就会变大,这是很不合理的。

长轮询的基本思路: 首先由客户端向服务器发动申请,当服务器收到客户端发来的申请后,服务器端不会间接进行响应,而是先将这个申请挂起,而后判断服务器端数据是否有更新。如果有更新,则进行响应,如果始终没有数据,则达到肯定的工夫限度才返回。客户端 JavaScript 响应处理函数会在解决完服务器返回的信息后,再次发出请求,从新建设连贯。长轮询和短轮询比起来,它的长处是显著缩小了很多不必要的 http 申请次数,相比之下节约了资源。长轮询的毛病在于,连贯挂起也会导致资源的节约。

SSE 的根本思维: 服务器应用流信息向服务器推送信息。严格地说,http 协定无奈做到服务器被动推送信息。然而,有一种变通方法,就是服务器向客户端申明,接下来要发送的是流信息。也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过去。这时,客户端不会敞开连贯,会始终等着服务器发过来的新的数据流,视频播放就是这样的例子。SSE 就是利用这种机制,应用流信息向浏览器推送信息。它基于 http 协定,目前除了 IE/Edge,其余浏览器都反对。它绝对于后面两种形式来说,不须要建设过多的 http 申请,相比之下节约了资源。

WebSocket 是 HTML5 定义的一个新协定议,与传统的 http 协定不同,该协定容许由服务器被动的向客户端推送信息。应用 WebSocket 协定的毛病是在服务器端的配置比较复杂。WebSocket 是一个全双工的协定,也就是通信单方是平等的,能够互相发送音讯,而 SSE 的形式是单向通信的,只能由服务器端向客户端推送信息,如果客户端须要发送信息就是属于下一个 http 申请了。

下面的四个通信协议,前三个都是基于HTTP协定的。

对于这四种即便通信协议,从性能的角度来看: WebSocket > 长连贯(SEE) > 长轮询 > 短轮询 然而,咱们如果思考浏览器的兼容性问题,程序就恰恰相反了: 短轮询 > 长轮询 > 长连贯(SEE) > WebSocket 所以,还是要依据具体的应用场景来判断应用哪种形式。

同样是重定向,307303302的区别?

302是http1.0的协定状态码,在http1.1版本的时候为了细化302状态码⼜进去了两个303和307。 303明确示意客户端该当采⽤get⽅法获取资源,他会把POST申请变为GET申请进⾏重定向。 307会遵循浏览器规范,不会从post变为get。

vue3带来的新个性/亮点

1. 压缩包体积更小

以后最小化并被压缩的 Vue 运行时大小约为 20kB(2.6.10 版为 22.8kB)。Vue 3.0捆绑包的大小大概会缩小一半,即只有10kB!

2. Object.defineProperty -> Proxy

  • Object.defineProperty是一个绝对比拟低廉的操作,因为它间接操作对象的属性,颗粒度比拟小。将它替换为es6的Proxy,在指标对象之上架了一层拦挡,代理的是对象而不是对象的属性。这样能够将本来对对象属性的操作变为对整个对象的操作,颗粒度变大。
  • javascript引擎在解析的时候心愿对象的构造越稳固越好,如果对象始终在变,可优化性升高,proxy不须要对原始对象做太多操作。

3. Virtual DOM 重构

vdom的实质是一个形象层,用javascript形容界面渲染成什么样子。react用jsx,没方法检测出能够优化的动静代码,所以做工夫分片,vue中足够快的话能够不必工夫分片
  • 传统vdom的性能瓶颈:

    • 尽管 Vue 可能保障触发更新的组件最小化,但在单个组件外部仍然须要遍历该组件的整个 vdom 树。
    • 传统 vdom 的性能跟模版大小正相干,跟动静节点的数量无关。在一些组件整个模版内只有大量动静节点的状况下,这些遍历都是性能的节约。
    • JSX 和手写的 render function 是齐全动静的,适度的灵活性导致运行时能够用于优化的信息有余
  • 那为什么不间接摈弃vdom呢?

    • 高级场景下手写 render function 取得更强的表达力
    • 生成的代码更简洁
    • 兼容2.x
vue的特点是底层为Virtual DOM,下层蕴含有大量动态信息的模版。为了兼容手写 render function,最大化利用模版动态信息,vue3.0采纳了动静联合的解决方案,将vdom的操作颗粒度变小,每次触发更新不再以组件为单位进行遍历,次要更改如下
  • 将模版基于动静节点指令切割为嵌套的区块
  • 每个区块外部的节点构造是固定的
  • 每个区块只须要以一个 Array 追踪本身蕴含的动静节点
vue3.0将 vdom 更新性能由与模版整体大小相干晋升为与动静内容的数量相干

Vue 3.0 动静联合的 Dom diff

  • Vue3.0 提出动静联合的 DOM diff 思维,动静联合的 DOM diff其实是在预编译阶段进行了优化。之所以可能做到预编译优化,是因为 Vue core 能够动态剖析 template,在解析模版时,整个 parse 的过程是利用正则表达式程序解析模板,当解析到开始标签、闭合标签和文本的时候都会别离执行对应的回调函数,来达到结构 AST 树的目标。
  • 借助预编译过程,Vue 能够做到的预编译优化就很弱小了。比方在预编译时标记出模版中可能变动的组件节点,再次进行渲染前 diff 时就能够跳过“永远不会变动的节点”,而只须要比照“可能会变动的动静节点”。这也就是动静联合的 DOM diff 将 diff 老本与模版大小正相干优化到与动静节点正相干的理论依据。

4. Performance

vue3在性能方面比vue2快了2倍。
  • 重写了虚构DOM的实现
  • 运行时编译
  • update性能进步
  • SSR速度进步

5. Tree-shaking support

vue3中的外围api都反对了tree-shaking,这些api都是通过包引入的形式而不是间接在实例化时就注入,只会对应用到的性能或个性进行打包(按需打包),这意味着更多的性能和更小的体积。

6. Composition API

vue2中,咱们个别会采纳mixin来复用逻辑代码,用倒是挺好用的,不过也存在一些问题:例如代码起源不清晰、办法属性等抵触。基于此在vue3中引入了Composition API(组合API),应用纯函数分隔复用代码。和React中的hooks的概念很类似
  • 更好的逻辑复用和代码组织
  • 更好的类型推导
<template>    <div>X: {{ x }}</div>    <div>Y: {{ y }}</div></template><script>import { defineComponent, onMounted, onUnmounted, ref } from "vue";const useMouseMove = () => {    const x = ref(0);    const y = ref(0);    function move(e) {        x.value = e.clientX;        y.value = e.clientY;    }    onMounted(() => {        window.addEventListener("mousemove", move);    });    onUnmounted(() => {        window.removeEventListener("mousemove", move);    });    return { x, y };};export default defineComponent({    setup() {        const { x, y } = useMouseMove();        return { x, y };    }});</script>

7. 新增的三个组件Fragment、Teleport、Suspense

Fragment

在书写vue2时,因为组件必须只有一个根节点,很多时候会增加一些没有意义的节点用于包裹。Fragment组件就是用于解决这个问题的(这和React中的Fragment组件是一样的)。

这意味着当初能够这样写组件了。

/* App.vue */<template>  <header>...</header>  <main v-bind="$attrs">...</main>  <footer>...</footer></template><script>export default {};</script>

或者这样

// app.jsimport { defineComponent, h, Fragment } from 'vue';export default defineComponent({    render() {        return h(Fragment, {}, [            h('header', {}, ['...']),            h('main', {}, ['...']),            h('footer', {}, ['...']),        ]);    }});

Teleport

Teleport其实就是React中的Portal。Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优良的计划。

一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 款式时,但你须要子组件可能在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。

/* App.vue */<template>    <div>123</div>    <Teleport to="#container">        Teleport    </Teleport></template><script>import { defineComponent } from "vue";export default defineComponent({    setup() {}});</script>/* index.html */<div id="app"></div><div id="container"></div>

Suspense

同样的,这和React中的Supense是一样的。

Suspense 让你的组件在渲染之前进行“期待”,并在期待时显示 fallback 的内容
// App.vue<template>    <Suspense>        <template #default>            <AsyncComponent />        </template>        <template #fallback>            Loading...        </template>    </Suspense></template><script lang="ts">import { defineComponent } from "vue";import AsyncComponent from './AsyncComponent.vue';export default defineComponent({    name: "App",    components: {        AsyncComponent    }});</script>// AsyncComponent.vue<template>    <div>Async Component</div></template><script lang="ts">import { defineComponent } from "vue";const sleep = () => {    return new Promise(resolve => setTimeout(resolve, 1000));};export default defineComponent({    async setup() {        await sleep();    }});</script>

8. Better TypeScript support

在vue2中应用过TypesScript的童鞋应该有过领会,写起来切实是有点好受。vue3则是应用ts进行了重写,开发者应用vue3时领有更好的类型反对和更好的编写体验。