react 16 做了哪些更新
- react 作为一个 ui 库,将前端编程由传统的
命令式
编程转变为申明式
编程,即所谓的数据驱动视图,但如果简略粗犷的操作,比方讲生成的 html 间接采纳 innerHtml 替换,会带来重绘重排
之类的性能问题。为了尽量进步性能,React 团队引入了虚构 dom,即采纳 js 对象来形容 dom 树,通过比照前后两次的虚构对象,来找到最小的 dom 操作(vdom diff),以此进步性能。 - 下面提到的 reactDom diff,在 react 16 之前,这个过程咱们称之为
stack reconciler
,它是一个递归的过程,在树很深的时候,单次 diff 工夫过长会造成 JS 线程继续被占用,用户交互响应通畅,页面渲染会呈现显著的卡顿,这在古代前端是一个致命的问题。所以为了解决这种问题,react 团队对整个架构进行了调整,引入了 fiber 架构,将以前的 stack reconciler 替换为fiber reconciler
。采纳增量式渲染
。引入了工作优先级 (expiration)
和requestIdleCallback
的循环调度算法,简略来说就是将以前的一根筋 diff 更新,首先拆分成两个阶段:reconciliation
与commit
; 第一个reconciliation
阶段是可打断的,被拆分成一个个的小工作(fiber),在每一侦的渲染闲暇期做小工作 diff。而后是 commit 阶段,这个阶段是不拆分且不能打断的,将 diff 节点的 effectTag 一口气更新到页面上。 - 因为 reconciliation 是能够被打断的,且存在工作优先级的问题,所以会导致 commit 前的一些生命周期函数屡次被执行,如 componentWillMount、componentWillReceiveProps 和 componetWillUpdate,但 react 官网已申明这些问题,并将其标记为 unsafe,在 React17 中将会移除
- 因为每次唤起更新是从根节点 (RootFiber) 开始,为了更好的节点复用与性能优化。在 react 中始终存
workInprogressTree
(future vdom) 与oldTree
(current vdom)两个链表,两个链表互相援用。这无形中又解决了另一个问题,当 workInprogressTree 生成报错时,这时也不会导致页面渲染解体,而只是更新失败, 页面依然还在
React hooks 原理
在 React 16 前,函数式组件不能领有状态治理?因为 16 以前只有类组件有对应的实例,而 16 当前 Fiber 架构的呈现,让每一个节点都领有对应的实例,也就领有了保留状态的能力。
Hooks 的实质就是 闭包
和两级链表
。
闭包是指有权拜访另一个
函数作用域中变量或办法
的函数,创立闭包的形式就是在一个函数内创立闭包函数,通过闭包函数拜访这个函数的局部变量, 利用闭包能够冲破作用链域的个性,将函数外部的变量和办法
传递到内部。
hooks 链表
一个组件蕴含的 hooks 以链表的模式存储在 fiber 节点的 memoizedState 属性上,currentHook 链表就是以后正在遍历的 fiber 节点的。nextCurrentHook 就是行将被增加到正在遍历 fiber 节点的 hooks 的新链表
let currentHook: Hook | null = null;
let nextCurrentHook: Hook | null = null;
type Hooks = {
memoizedState: any, // 指向以后渲染节点 Fiber
baseState: any, // 初始化 initialState,最新的 state
baseUpdate: Update<any> | null,
// 以后须要更新的 Update,每次更新完之后,会赋值上一个 update,不便 react 在渲染谬误的边缘,数据回溯
queue: UpdateQueue<any> | null,// 能够让 state 变动的,即 update 或 dispach 产生的 update
next: Hook | null, // link 到下一个 hooks
}
state 链表
其实 state 链表不是 hooks 独有的,类操作的 setState 也存在。
- memoizedState,cursor 是存在哪里的?如何和每个函数组件一一对应的?
react 会生成一棵组件树(或 Fiber 单链表),树中每个节点对应了一个组件,hooks 的数据就作为组件的一个信息,存储在这些节点上,随同组件一起出世,一起死亡。 - 为什么只能在函数最外层调用 Hook?
memoizedState 是按 hook 定义的程序来搁置数据的,如果 hook 程序变动,memoizedState 并不会感知到。
- 自定义的 Hook 是如何影响应用它的函数组件的?
共享同一个 memoizedState,共享同一个程序。 - “Capture Value”个性是如何产生的?
每一次 ReRender 的时候,都是从新去执行函数组件了,对于之前曾经执行过的函数组件,并不会做任何操作。
react setState 异步更新
setState 实现原理
setState 通过一个 队列机制 来实现 state 更新,当执行 setState() 时,会将须要更新的 state 浅合并 后放入 状态队列,而不会立刻更新 state,队列机制能够高效的 批量更新 state。如果不通过 setState,间接批改 this.state 的值,则不会放入状态队列,当下一次调用 setState 对状态队列进行合并时,之前对 this.state 的批改将会被疏忽,造成无奈预知的谬误。
setState()有的同步有的异步?
在 React 中,如果是由 React 引发的事件处理(比方通过 onClick 引发的事件处理),调用 setState 不会同步更新 this.state,除此之外的 setState 调用会同步执行 this.state。所谓“除此之外”,指的是绕过 React 通过 addEventListener 间接增加的事件处理函数,还有通过 setTimeout/setInterval 产生的异步调用。
起因: 在 React 的 setState 函数实现中,会依据一个变量 isBatchingUpdates 判断是间接更新 this.state 还是放到队列中回头再说,而 isBatchingUpdates 默认是 false,也就示意 setState 会同步更新 this.state,然而,有一个函数 batchedUpdates,这个函数会把 isBatchingUpdates 批改为 true,而当 React 在调用事件处理函数之前就会调用这个 batchedUpdates,造成的结果,就是由 React 管制的事件处理过程 setState 不会同步更新 this.state。
setState 的“异步”并不是说外部由异步代码实现,其实自身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用程序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,模式了所谓的“异步”,能够通过第二个参数 setState(partialState, callback) 中的 callback 拿到更新后的后果。
调用危险
当调用 setState 时,实际上是会执行 enqueueSetState
办法,并会对 partialState
及 _pendingStateQueue
队列进行合并操作,最终通过 enqueueUpdate
执行 state 更新。
而 performUpdateIfNecessary
获取 _pendingElement
、 _pendingStateQueue
、_pendingForceUpdate
,并调用 reaciveComponent
和 updateComponent
来进行组件更新。
但,如果在 shouldComponentUpdate
或 componentWillUpdate
办法里调用 this.setState 办法,就会造成解体。这是因为在 shouldComponentUpdate
或 componentWillUpdate
办法里调用 this.setState
时,this._pendingStateQueue!=null
,则 performUpdateIfNecessary
办法就会调用 updateComponent
办法进行组件更新,而 updateComponent
办法又会调用 shouldComponentUpdate
和componentWillUpdate
办法,因而造成循环调用,使得浏览器内存占满后解体。
React Fiber
掉帧 :在页面元素很多,且须要频繁刷新的场景下,React 15 会呈现掉帧的景象,其根本原因,是大量的同步计算工作阻塞了浏览器的 UI 渲染。默认状况下,JS 运算、页面布局和页面绘制都是运行在浏览器的主线程当中,他们之间是互斥的关系。如果 JS 运算继续占用主线程,页面就没法失去及时的更新。当咱们调用setState
更新页面的时候,React 会遍历利用的所有节点,计算出差别,而后再更新 UI,整个过程不能被打断。如果页面元素很多,整个过程占用的机会就可能超过 16 毫秒,就容易呈现掉帧的景象。
如何解决主线程长时间被 JS 运算?将 JS 运算切割为多个步骤,分批实现。在实现一部分工作之后,将控制权交回给浏览器,让浏览器有工夫进行页面的渲染。等浏览器忙完之后,再持续之前未实现的工作。
React 15 及以下版本通过递归的形式进行渲染,应用的是 JS 引擎本身的函数调用栈,它会始终执行到栈空为止。而 Fiber
实现了本人的组件调用栈,它以链表的模式遍历组件树,能够灵便的暂停、持续和抛弃执行的工作。实现形式是应用了浏览器的requestIdleCallback
window.requestIdleCallback()会在浏览器闲暇期间顺次调用函数,这就能够让开发者在主事件循环中执行后盾或低优先级的工作,而且不会对像动画和用户交互这些提早触发但要害的事件产生影响。函数个别会按先进先调用的程序执行,除非函数在浏览器调用它之前就到了它的超时工夫。
React 框架外部的运作能够分为 3 层:
- Virtual DOM 层,形容页面长什么样。
- Reconciler 层,负责调用组件生命周期办法,进行 Diff 运算等。
- Renderer 层,依据不同的平台,渲染出相应的页面,比拟常见的是 ReactDOM 和 ReactNative。
Fiber 表征 reconciliation 阶段所能拆分的最小工作单元,其实指的是一种链表树,它能够用一个纯 JS 对象来示意:
const fiber = {stateNode: {}, // 节点实例
child: {}, // 子节点
sibling: {}, // 兄弟节点
return: {}, // 示意解决实现后返回后果所要合并的指标,通常指向父节点};
Reconciler 区别
- 以前的 Reconciler 被命名为
Stack Reconciler
。Stack Reconciler 运作的过程是不能被打断的,必须一条道走到黑; - Fiber Reconciler 每执行一段时间,都会将控制权交回给浏览器,能够分段执行;
从 Stack Reconciler
到Fiber Reconciler
,源码层面其实就是干了一件递归改循环的事件。
scheduling(调度)
scheduling(调度)是 fiber reconciliation 的一个过程,次要是进行任务分配,达到分段执行。工作的优先级有六种:
- synchronous,与之前的 Stack Reconciler 操作一样,同步执行
- task,在 next tick 之前执行
- animation,下一帧之前执行
- high,在不久的未来立刻执行
- low,略微提早执行也没关系
- offscreen,下一次 render 时或 scroll 时才执行
优先级高的工作(如键盘输入)能够打断优先级低的工作(如 Diff)的执行,从而更快的失效。
Fiber Reconciler 在执行过程中,会分为 2 个阶段:
- 阶段一,生成 Fiber 树,得出须要更新的节点信息。这一步是一个渐进的过程,能够被打断。
- 阶段二,将须要更新的节点一次过批量更新,这个过程不能被打断。
阶段一可被打断的个性,让优先级更高的工作先执行,从框架层面大大降低了页面掉帧的概率。
参考:
React Fiber 原理介绍
React Fiber
HOC 与 render props 区别
Render Props: 把将要包裹的组件作为 props 属性传入,而后容器组件调用这个属性,并向其传参
实现形式:
- 通过
props.children(props)
,props.children 返回的是 UI 元素。<RenderProps>
JSX 标签中的所有内容都会作为一个children
prop 传递给RenderProps
组件。因为RenderProps
将{props.children}
渲染在一个<div>
中,被传递的这些子组件最终都会呈现在输入后果中。
// 定义
const RenderProps = props => <div>
{props.children(props)}
</div>
// 调用
<RenderProps>
{() => <>Hello RenderProps</>}
</RenderProps>
- 通过 props 中的任何函数, 自行定义传入内容
// 定义
const LoginForm = props => {
const flag = false;
const allProps = {flag, ...props};
if (flag) {return <>{props.login(allProps)}</>
} else {return <>{props.notLogin(allProps)}</>
}
}
// 调用
<LoginForm
login={() => <h1>LOGIN</h1>}
noLogin={() => <h1>NOT LOGIN</h1>}
/>
长处:
1、反对 ES6
2、不必放心 props 命名问题,在 render 函数中只取须要的 state
3、不会产生无用的组件加深层级
4、render props 模式的构建都是动静的,所有的扭转都在 render 中触发,能够更好的利用组件内的生命周期。
HOC: 承受一个组件作为参数,返回一个新的组件的函数。
class Home extends React.Component {// UI}
export default Connect()(Home);
高阶组件因为每次都会返回一个新的组件,对于 react 来说,这是不利于 diff 和状态复用的,所以高阶组件的包装不能在 render 办法中进行,而只能像下面那样在组件申明时包裹,这样也就不利于动静传参。
长处:
1、反对 ES6
2、复用性强,HOC 为纯函数且返回值为组件,能够多层嵌套
3、反对传入多个参数,加强了适用范围
毛病:
1、当多个 HOC 一起应用时,无奈直接判断子组件的 props 是哪个 HOC 负责传递的
2、多个组件嵌套,容易产生同样名称的 props
3、HOC 可能会产生许多无用的组件,加深了组件的层级
总的来说,render props 其实和高阶组件相似,就是在 puru component 上减少 state,响应 react 的生命周期。
React 通信
react 的数据流是单向的,最常见的就是通过 props 由父组件向子组件传值。
- 父向子通信:传入 props
- 子向父通信:父组件向子组件传一个函数,而后通过这个函数的回调,拿到子组件传过来的值
- 父向孙通信:利用 context 传值。
React.createContext()
- 兄弟间通信:
1、找一个雷同的父组件,既能够用 props 传递数据,也能够用 context 的形式来传递数据。
2、用一些全局机制去实现通信,比方 redux 等
3、公布订阅模式
react 合成事件
React 合成事件(SyntheticEvent)是 React 模仿原生 DOM 事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器。
为什么要应用合成事件?
- 进行浏览器兼容,实现更好的跨平台
React 采纳的是顶层事件代理机制,可能保障冒泡一致性,能够跨浏览器执行。React 提供的合成事件用来抹平不同浏览器事件对象之间的差别,将不同平台事件模仿合成事件。 - 防止垃圾回收
事件对象可能会被频繁创立和回收,因而 React 引入 事件池 ,在事件池中获取或开释事件对象。 即 React 事件对象不会被开释掉,而是寄存进一个数组中,当事件触发,就从这个数组中弹出,防止频繁地去创立和销毁(垃圾回收)。 - 不便事件对立治理和事务机制
实现原理
在 React 中,“合成事件”会以事件委托形式绑定在 document
对象上,并在组件卸载(unmount)阶段主动销毁绑定的事件。
合成事件和原生事件
当实在 DOM 元素触发事件,会冒泡到 document
对象后,再解决 React 事件;所以会先执行原生事件,而后解决 React 事件;最初真正执行 document
上挂载的事件。
合成事件和原生事件最好不要混用。原生事件中如果执行了 stopPropagation
办法,则会导致其余 React
事件生效。因为所有元素的事件将无奈冒泡到 document
上,所有的 React 事件都将无奈被注册。
合成事件的事件池
合成事件对象池,是 React 事件零碎提供的一种 性能优化形式 。 合成事件对象在事件池对立治理 , 不同类型的合成事件具备不同的事件池。
react 与 vue 区别
1. 监听数据变动的实现原理不同
Vue 通过 getter/setter 以及一些函数的劫持,能准确晓得数据变动。
React 默认是通过比拟援用的形式(diff)进行的,如果不优化可能导致大量不必要的 VDOM 的从新渲染。
2. 数据流不同
Vue1.0 中能够实现两种双向绑定:父子组件之间 props 能够双向绑定;组件与 DOM 之间能够通过 v -model 双向绑定。
Vue2.x 中父子组件之间不能双向绑定了(然而提供了一个语法糖主动帮你通过事件的形式批改)。
React 始终不反对双向绑定,提倡的是单向数据流,称之为 onChange/setState()模式。
3. HoC 和 mixins
Vue 组合不同性能的形式是通过 mixin,Vue 中组件是一个被包装的函数,并不简略的就是咱们定义组件的时候传入的对象或者函数。
React 组合不同性能的形式是通过 HoC(高阶组件)。
4. 模板渲染形式的不同
模板的语法不同,React 是通过 JSX 渲染模板,Vue 是通过一种拓展的 HTML 语法进行渲染。
模板的原理不同,React 通过原生 JS 实现模板中的常见语法,比方插值,条件,循环等。而 Vue 是在和组件 JS 代码拆散的独自的模板中,通过指令来实现的,比方 v-if。
举个例子,阐明 React 的益处:react 中 render 函数是反对闭包个性的,所以咱们 import 的组件在 render 中能够间接调用。然而在 Vue 中,因为模板中应用的数据都必须挂在 this 上进行一次直达,所以咱们 import 一个组件完了之后,还须要在 components 中再申明下。
5. 渲染过程不同
Vue 能够更快地计算出 Virtual DOM 的差别,这是因为它会跟踪每一个组件的依赖关系,不须要从新渲染整个组件树。
React 当状态被扭转时,全副子组件都会从新渲染。通过 shouldComponentUpdate 这个生命周期办法能够进行管制,但 Vue 将此视为默认的优化。
6. 框架实质不同
Vue 实质是 MVVM 框架,由 MVC 倒退而来;
React 是前端组件化框架,由后端组件化发展而来。
性能优化
1. 动态资源应用 CDN
CDN 是一组散布在多个不同地理位置的 Web 服务器。当服务器离用户越远时,提早越高。
2. 无阻塞
头部内联的款式和脚本会阻塞页面的渲染,款式放在头部并应用 link 形式引入,脚本放在尾部并应用异步形式加载
3. 压缩文件
压缩文件能够缩小文件下载工夫。
- 在 webpack 能够应用如下插件进行压缩:
- JavaScript:UglifyPlugin
- CSS:MiniCssExtractPlugin
- HTML:HtmlWebpackPlugin
- 应用 gzip 压缩。通过向 HTTP 申请头中的 Accept-Encoding 头增加 gzip 标识来开启这一性能。
4. 图片优化
- 图片懒加载
- 响应式图片:浏览器依据屏幕大小主动加载适合的图片。
- 升高图片品质:办法有两种,一是通过 webpack 插件
image-webpack-loader
,二是通过在线网站进行压缩。
5. 缩小重绘重排
- 升高 CSS 选择器的复杂性
- 应用 transform 和 opacity 属性更改来实现动画
- 用 JavaScript 批改款式时,最好不要间接写款式,而是替换 class 来扭转款式。
- 如果要对 DOM 元素执行一系列操作,能够将 DOM 元素脱离文档流,批改实现后,再将它带回文档。举荐应用暗藏元素(display:none)或文档碎片(DocumentFragement),都能很好的实现这个计划。
6. 应用 requestAnimationFrame 来实现视觉变动
7. webpack 打包,增加文件缓存
index.html
设置成 no-cache
,这样每次申请的时候都会比对一下 index.html
文件有没变动,如果没变动就应用缓存,有变动就应用新的 index.html
文件。
其余所有文件一律应用长缓存,例如设置成缓存一年 maxAge: 1000 * 60 * 60 * 24 * 365
。
前端代码应用 webpack 打包,依据文件内容生成对应的文件名,每次从新打包时只有内容产生了变动,文件名才会发生变化。
- max-age: 设置缓存存储的最大周期,超过这个工夫缓存被认为过期(单位秒)。在这个工夫前,浏览器读取文件不会收回新申请,而是间接应用缓存。
- 指定 no-cache 示意客户端能够缓存资源,每次应用缓存资源前都必须从新验证其有效性
输出 url 后产生了什么
- DNS 域名解析;
- 建设 TCP 连贯(三次握手);
- 发送 HTTP 申请;
- 服务器解决申请;
- 返回响应后果;
- 敞开 TCP 连贯(四次握手);
- 浏览器解析 HTML;
- 浏览器布局渲染;
1. DNS 域名解析:拿到服务器 ip
客户端收到你输出的域名地址后,它首先去找本地的 hosts 文件,查看在该文件中是否有相应的域名、IP 对应关系,如果有,则向其 IP 地址发送申请,如果没有,再去找 DNS 服务器。
2. 建设 TCP 链接:客户端链接服务器
TCP 提供了一种牢靠、面向连贯、字节流、传输层的服务。对于客户端与服务器的 TCP 链接,必然要说的就是『三次握手』。“3 次握手”的作用就是 单方都能明确本人和对方的收、发能力是失常的
。
客户端发送一个带有 SYN 标记的数据包给服务端,服务端收到后,回传一个带有 SYN/ACK 标记的数据包以示传播确认信息,最初客户端再回传一个带 ACK 标记的数据包,代表握手完结,连贯胜利。
SYN —— 用于初如化一个连贯的序列号。
ACK —— 确认,使得确认号无效。
RST —— 重置连贯。
FIN —— 该报文段的发送方曾经完结向对方发送数据。
客户端:“你好,在家不。”— SYN
服务端:“在的,你来吧。”— SYN + ACK
客户端:“好嘞。”— ACK
3. 发送 HTTP 申请
4. 服务器解决申请
5. 返回响应后果
6. 敞开 TCP 连贯(须要 4 次握手)
为了防止服务器与客户端单方的资源占用和损耗,当单方没有申请或响应传递时,任意一方都能够发动敞开申请。
敞开连贯时,服务器收到对方的 FIN 报文时,仅仅示意客户端不再发送数据了然而还能接收数据,而服务器也未必全副数据都发送给客户端,所以服务器能够立刻敞开,也能够发送一些数据给对方后,再发送 FIN 报文给对方来表示同意当初敞开连贯,因而,己方 ACK 和 FIN 个别都会离开发送,从而导致多了一次。
客户端:“兄弟,我这边没数据要传了,咱敞开连贯吧。”— FIN + seq
服务端:“收到,我看看我这边有木有数据了。”— ACK + seq + ack
服务端:“兄弟,我这边也没数据要传你了,咱能够敞开连贯了。”– FIN + ACK + seq + ack
客户端:“好嘞。”— ACK + seq + ack
7. 浏览器解析 HTML
浏览器须要加载解析的不仅仅是 HTML,还包含 CSS、JS,以及还要加载图片、视频等其余媒体资源。
浏览器通过解析 HTML,生成 DOM 树,解析 CSS,生成 CSSOM 树,而后通过 DOM 树和 CSSPOM 树生成渲染树。渲染树与 DOM 树不同,渲染树中并没有 head、display 为 none 等不用显示的节点。
浏览器的解析过程并非是串连进行的,比方在解析 CSS 的同时,能够持续加载解析 HTML,但在解析执行 JS 脚本时,会进行解析后续 HTML,会呈现阻塞问题。
8. 浏览器渲染页面
依据渲染树布局,计算 CSS 款式,即每个节点在页面中的大小和地位等几何信息。HTML 默认是流式布局的,CSS 和 js 会突破这种布局,扭转 DOM 的外观款式以及大小和地位。最初浏览器绘制各个节点,将页面展现给用户。
replaint:屏幕的一部分重画,不影响整体布局,比方某个 CSS 的背景色变了,但元素的几何尺寸和地位不变。
reflow:意味着元素的几何尺寸变了,须要从新计算渲染树。
参考:
细说浏览器输出 URL 后产生了什么
浏览器输出 URL 后产生了什么?
Babel Plugin 与 preset 区别
Babel 是代码转换器,比方将 ES6 转成 ES5,或者将 JSX 转成 JS 等。借助 Babel,开发者能够提前用上新的 JS 个性。
原始代码 --> [Babel Plugin] --> 转换后的代码
Plugin
实现 Babel 代码转换性能的外围,就是 Babel 插件(plugin)。Babel 插件个别尽可能拆成小的力度,开发者能够按需引进, 既进步了性能,也进步了扩展性。比方对 ES6 转 ES5 的性能,Babel 官网拆成了 20+ 个插件。开发者想要体验 ES6 的箭头函数个性,那只须要引入 transform-es2015-arrow-functions
插件就能够,而不是加载 ES6 全家桶。
Preset
能够简略的把 Babel Preset 视为 Babel Plugin 的汇合。想要将所有 ES6 的代码转成 ES5,一一插件引入的效率比拟低下, 就能够采纳 Babel Preset。比方 babel-preset-es2015
就蕴含了所有跟 ES6 转换无关的插件。
Plugin 与 Preset 执行程序
能够同时应用多个 Plugin 和 Preset,此时,它们的执行程序十分重要。
- 先执行完所有 Plugin,再执行 Preset。
- 多个 Plugin,依照申明秩序程序执行。
- 多个 Preset,依照申明秩序逆序执行。
比方 .babelrc
配置如下,那么执行的程序为:
- Plugin:transform-react-jsx、transform-async-to-generator
- Preset:es2016、es2015
{
"presets": [
"es2015",
"es2016"
],
"plugins": [
"transform-react-jsx",
"transform-async-to-generator"
]
}
webpack hash 区别
hash 个别是联合 CDN 缓存来应用,通过 webpack 构建之后,生成对应文件名主动带上对应的 MD5 值。如果文件内容扭转的话,那么对应文件哈希值也会扭转,对应的 HTML 援用的 URL 地址也会扭转,触发 CDN 服务器从源服务器上拉取对应数据,进而更新本地缓存。
- hash
hash 是跟整个我的项目的构建相干,只有我的项目里有文件更改,整个我的项目构建的 hash 值都会更改。同一次构建过程中生成的哈希都是一样的。
output:{path:path.join(__dirname, '/dist'),
filename: 'bundle.[name].[hash].js',
}
- chunkhash
依据不同的入口文件 (Entry) 进行依赖文件解析、构建对应的 chunk,生成对应的哈希值。把一些公共库和程序入口文件辨别开,独自打包构建,接着咱们采纳 chunkhash 的形式生成哈希值,那么只有咱们不改变公共库的代码,就能够保障其哈希值不会受影响。
output:{path:path.join(__dirname, '/dist/js'),
filename: 'bundle.[name].[chunkhash].js',
}
采纳 chunkhash,我的项目主入口文件 Index.js 及其对应的依赖文件 Index.css 因为被打包在同一个模块,共用雷同的 chunkhash。因为公共库是不同的模块,有独自的 chunkhash。所以 Index 文件的更改不会影响公共库。如果 index.js 更改了代码,css 未扭转,因为该模块产生了扭转,导致 css 文件会反复构建。
- contenthash
依据文件内容创立出惟一 hash。当文件内容发生变化时,[contenthash] 才会发生变化。
output: {filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js',
path: path.resolve(__dirname, '../dist'),
}
typeof 如何判断的
js 在底层存储变量的时候,会在变量的机器码的低位 1 - 3 位存储其类型信息:
对象:000
浮点数:010
字符串:100
布尔:110
整数:1
null:所有机器码均为 0
undefined:用 −2^30 整数来示意
// JavaScript 诞生以来便如此
typeof null === 'object';
在 JavaScript 最后的实现中,JavaScript 中的值是由一个示意类型的标签和理论数据值示意的。对象的类型标签是 0。因为 null
代表的是空指针(大多数平台下值为 0x00),因而,null 的类型标签是 0,typeof null
也因而返回 "object"
。
怎么开发和部署前端代码
为了进一步晋升网站性能,会把动态资源和动静网页分集群部署,动态资源会被部署到 CDN 节点上,网页中援用的资源也会变成对应的部署门路。当须要更新动态资源的时候,同时也会更新 html 中的援用。
如果同时改了页面构造和款式,也更新了动态资源对应的 url 地址,当初要公布代码上线,是先上线页面,还是先上线动态资源?
- 先部署页面,再部署资源:在二者部署的工夫距离内,如果有用户拜访页面,就会在新的页面构造中加载旧的资源,并且把这个旧版本的资源当做新版本缓存起来,其后果就是:用户拜访到了一个款式错乱的页面,除非手动刷新,否则在资源缓存过期之前,页面会始终执行谬误。
- 先部署资源,再部署页面:在部署工夫距离之内,有旧版本资源本地缓存的用户拜访网站,因为申请的页面是旧版本的,资源援用没有扭转,浏览器将间接应用本地缓存,这种状况下页面展示失常;但没有本地缓存或者缓存过期的用户拜访网站,就会呈现旧版本页面加载新版本资源的状况,导致页面执行谬误,但当页面实现部署,这部分用户再次拜访页面又会恢复正常了。
这个奇葩问题,起源于资源的 笼罩式公布 ,用 待发布资源 笼罩 已公布资源,就有这种问题。解决它也好办,就是实现 非笼罩式公布。用文件的摘要信息来对资源文件进行重命名,把摘要信息放到资源文件公布门路中,这样,内容有批改的资源就变成了一个新的文件公布到线上,不会笼罩已有的资源文件。上线过程中,先全量部署动态资源,再灰度部署页面,整个问题就比拟完满的解决了。
大公司的动态资源优化计划,基本上要实现这么几个货色:
- 配置超长工夫的本地缓存 —— 节俭带宽,进步性能
- 采纳内容摘要作为缓存更新根据 —— 准确的缓存管制
- 动态资源 CDN 部署 —— 优化网络申请
- 更改资源公布门路实现非笼罩式公布 —— 平滑降级
大数相加
function add(a, b){const maxLength = Math.max(a.length, b.length);
a = a.padStart(maxLength, 0);
b = b.padStart(maxLength, 0);
let t = 0;
let f = 0;
let sum = "";
for (let i = maxLength - 1; i >= 0; i--) {t = parseInt(a[i]) + parseInt(b[i]) + f;
f = Math.floor(t / 10);
sum = `${t % 10}${sum}`;
}
if (f === 1){sum = "1" + sum;}
return sum;
}
斐波那契数列求和
function fib(n) {if (n <= 0) {return 0;}
let n1 = 1;
let n2 = 1;
let sum = 1;
for(let i = 3; i <= n; i++) {[n1, n2] = [n2, sum];
sum = n1 + n2;
}
return sum;
};