一、js基礎(chǔ)
1.cdn原理
CDN 的工作原理就是將源站資源緩存到位于全球各地的 CDN 節(jié)點(diǎn)上,用戶請求資源時(shí)忽你,就近返回,不需要每個(gè)請求從源站獲取臂容,避免網(wǎng)絡(luò)擁塞科雳、緩解源站壓力,保證用戶訪問資源的速度
簡單來說脓杉,CDN就是根據(jù)用戶位置分配最近的資源
原理:
1.負(fù)載均衡系統(tǒng)
應(yīng)用CDN后糟秘,DNS 返回的不再是 IP 地址,而是指向CDN的全局負(fù)載均衡球散,找就近尿赚、相同網(wǎng)絡(luò)、負(fù)載較輕的節(jié)點(diǎn)蕉堰,然后把這個(gè)節(jié)點(diǎn)返回給用戶凌净,用戶就能夠就近訪問CDN的緩存代理
2.緩存代理
分成一級(jí)緩存節(jié)點(diǎn)和二級(jí)緩存節(jié)點(diǎn)。
一級(jí)緩存配置高一些屋讶,直連源站冰寻,二級(jí)緩存配置低一些,直連用戶
回源的時(shí)候二級(jí)緩存只找一級(jí)緩存丑婿,一級(jí)緩存沒有才回源站性雄,可以有效地減少真正的回源
于是,用戶在上網(wǎng)的時(shí)候不用直接訪問源站羹奉,而是訪問離他“最近的”一個(gè) CDN 節(jié)點(diǎn)秒旋,術(shù)語叫「邊緣節(jié)點(diǎn)」,其實(shí)就是緩存了源站內(nèi)容的代理服務(wù)器诀拭。
2.es5和es6繼承的區(qū)別迁筛、
https://www.mdnice.com/writing/d4a6f872b4484750916e72943baccf89
ES5 prototype 繼承:
實(shí)質(zhì)是先創(chuàng)造子類的實(shí)例對象this上迎膜,然后再將父類的方法添加到這個(gè)this上螟碎。類似使用:Father.apply(this)
S6 class 繼承
通過class的extends + super實(shí)現(xiàn)繼承迂烁。
子類沒有自己的this對象隧魄,因此必須在 constructor 中通過 super 繼承父類的 this 對象紊选,而后對此this對象進(jìn)行添加方法和屬性稚瘾。
super關(guān)鍵字在構(gòu)造函數(shù)中表示父類的構(gòu)造函數(shù)糯耍,用來新建父類的 this 對象团赁。
內(nèi)部實(shí)現(xiàn)機(jī)制上翰苫,ES6 的繼承機(jī)制完全不同止邮,實(shí)質(zhì)是先創(chuàng)造父類的實(shí)例對象this---需要提前調(diào)用super方法这橙,然后再用子類的構(gòu)造函數(shù)修改this指針
二者區(qū)別:
答:不是完全一樣的,主要有以下幾個(gè)差異點(diǎn):
1.寫法不一樣导披。class的繼承通過extends關(guān)鍵字和super函數(shù)屈扎、super方法繼承。(關(guān)于super實(shí)現(xiàn)繼承的使用方式撩匕,具體我就不展開了)
2.類內(nèi)部定義的方法都是不可枚舉的鹰晨,這個(gè) ES5 不一樣
3.類不存在變量提升,這一點(diǎn)與 ES5 完全不同
4.類相當(dāng)于實(shí)例的原型止毕,所有在類中定義的方法都會(huì)被實(shí)例繼承模蜡。如果在一個(gè)方法前,加上 static 關(guān)鍵字滓技,就表示該方法不會(huì)被實(shí)例繼承哩牍,而是直接通過類來調(diào)用,這就成為靜態(tài)方法
內(nèi)部實(shí)現(xiàn)機(jī)制不一樣
因?yàn)閷?shí)現(xiàn)機(jī)制不同令漂,導(dǎo)致這兩種繼承在繼承原生構(gòu)造函數(shù)時(shí)有些差異:
es5的寫法不能繼承原生構(gòu)造函數(shù)(比如Array膝昆、Number等)
因?yàn)閑s5的繼承是先創(chuàng)造子類的實(shí)例對象this,再將父類原型的屬性和方法重寫到子類上叠必,因?yàn)闆]法訪問父類的內(nèi)部屬性荚孵,導(dǎo)致es5的繼承方式無法繼原生的構(gòu)造函數(shù)。
es6允許繼承構(gòu)造函數(shù)生成子類纬朝。因?yàn)閑s6是先創(chuàng)建父類的實(shí)例對象this收叶,然后再用子類的構(gòu)造函數(shù)修飾,所以子類就可以繼承父類的所有屬性和方法共苛。因此class可以繼承并自定義原生構(gòu)造函數(shù)的子類判没。extends不僅可以用來繼承類,還能用來繼承原生構(gòu)造函數(shù)隅茎,因此也就可以在原生數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上澄峰,構(gòu)造自定義的數(shù)據(jù)結(jié)構(gòu)
3.閉包(高頻)
閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)
閉包用途:
1.能夠訪問函數(shù)定義時(shí)所在的詞法作用域(阻止其被回收)
2.私有化變量
3.模擬塊級(jí)作用域
4.創(chuàng)建模塊
閉包缺點(diǎn):會(huì)導(dǎo)致函數(shù)的變量一直保存在內(nèi)存中,過多的閉包可能會(huì)導(dǎo)致內(nèi)存泄漏
4.this指向辟犀、new關(guān)鍵字
this對象是是執(zhí)行上下文中的一個(gè)屬性俏竞,它指向最后一次調(diào)用這個(gè)方法的對象,在全局函數(shù)中堂竟,this等于window魂毁,而當(dāng)函數(shù)被作為某個(gè)對象調(diào)用時(shí),this等于那個(gè)對象出嘹。
在實(shí)際開發(fā)中席楚,this 的指向可以通過四種調(diào)用模式來判斷。
1.函數(shù)調(diào)用税稼,當(dāng)一個(gè)函數(shù)不是一個(gè)對象的屬性時(shí)烦秩,直接作為函數(shù)來調(diào)用時(shí)刁赦,this指向全局對象。
2.方法調(diào)用闻镶,如果一個(gè)函數(shù)作為一個(gè)對象的方法來調(diào)用時(shí),this指向這個(gè)對象丸升。
3.構(gòu)造函數(shù)調(diào)用铆农,this指向這個(gè)用new新創(chuàng)建的對象。
- apply 狡耻、 call 和 bind 調(diào)用模式墩剖,這三個(gè)方法都可以顯示的指定調(diào)用函數(shù)的 this 指向。apply接收參數(shù)的是數(shù)組夷狰,call接受參數(shù)列表岭皂,`` bind方法通過傳入一個(gè)對象,返回一個(gè) this 綁定了傳入對象的新函數(shù)沼头。這個(gè)函數(shù)的 this指向除了使用new `時(shí)會(huì)被改變爷绘,其他情況下都不會(huì)改變。
new的創(chuàng)建過程:
首先創(chuàng)建了一個(gè)新的空對象
設(shè)置原型进倍,將對象的原型設(shè)置為函數(shù)的prototype對象土至。
讓函數(shù)的this指向這個(gè)對象,執(zhí)行構(gòu)造函數(shù)的代碼(為這個(gè)新對象添加屬性)
判斷函數(shù)的返回值類型猾昆,如果是值類型陶因,返回創(chuàng)建的對象。如果是引用類型垂蜗,就返回這個(gè)引用類型的對象
5.Event Loop
執(zhí)行順序如下所示:
1)首先執(zhí)行同步代碼楷扬,這屬于宏任務(wù)
2)當(dāng)執(zhí)行完所有同步代碼后,執(zhí)行棧為空贴见,查詢是否有異步代碼需要執(zhí)行
執(zhí)行所有微任務(wù)
3)當(dāng)執(zhí)行完所有微任務(wù)后烘苹,如有必要會(huì)渲染頁面
4)然后開始下一輪 Event Loop,執(zhí)行宏任務(wù)中的異步代碼蝇刀,也就是 setTimeout 中的回調(diào)函數(shù)
微任務(wù)包括 process.nextTick 螟加,promise ,MutationObserver吞琐。
宏任務(wù)包括 script 捆探, setTimeout ,setInterval 站粟,setImmediate 黍图,I/O ,UI rendering奴烙。
6.http如何實(shí)現(xiàn)緩存
二.react
1.setState
只在合成事件和鉤子函數(shù)中是“異步”的助被,在原生事件和 setTimeout 中都是同步的剖张。
setState的“異步”并不是說內(nèi)部由異步代碼實(shí)現(xiàn),其實(shí)本身執(zhí)行的過程和代碼都是同步的揩环,只是合成事件和鉤子函數(shù)的調(diào)用順序在更新之前搔弄,導(dǎo)致在合成事件和鉤子函數(shù)中沒法立馬拿到更新后的值,形式了所謂的“異步”丰滑,當(dāng)然可以通過第二個(gè)參數(shù) setState(partialState, callback) 中的callback拿到更新后的結(jié)果顾犹。
setState 的批量更新優(yōu)化也是建立在“異步”(合成事件、鉤子函數(shù))之上的褒墨,在原生事件和setTimeout 中不會(huì)批量更新炫刷,在“異步”中如果對同一個(gè)值進(jìn)行多次 setState , setState 的批量更新策略會(huì)對其進(jìn)行覆蓋郁妈,取最后一次的執(zhí)行浑玛,如果是同時(shí) setState 多個(gè)不同的值,在更新時(shí)會(huì)對其進(jìn)行合并批量更新噩咪。
6.高階組件
高階組件是參數(shù)為組件顾彰,返回值為新組件的函數(shù)。HOC是純函數(shù)剧腻,沒有副作用拘央。HOC在React的第三方庫中很常見,例如Redux的connect組件书在。
高階組件的作用:
1.代碼復(fù)用灰伟,邏輯抽象,抽離底層準(zhǔn)備(bootstrap)代碼
2.渲染劫持
3.State 抽象和更改
4.Props 更改
三儒旬、計(jì)算機(jī)網(wǎng)絡(luò)
1.http與https區(qū)別
1.HTTP
的URL 以http:// 開頭栏账,而HTTPS 的URL 以https:// 開頭
2.HTTP
是不安全的,而 HTTPS 是安全的
3.HTTP
標(biāo)準(zhǔn)端口是80 栈源,而 HTTPS 的標(biāo)準(zhǔn)端口是443
4.在OSI
網(wǎng)絡(luò)模型中挡爵,HTTP工作于應(yīng)用層,而HTTPS 的安全傳輸機(jī)制工作在傳輸層
5.HTTP
無法加密甚垦,而HTTPS 對傳輸?shù)臄?shù)據(jù)進(jìn)行加密
6.HTTP
無需證書茶鹃,而HTTPS 需要CA機(jī)構(gòu)wosign的頒發(fā)的SSL證書
2.安全防御
XSS避免方式:
1.url參數(shù)使用encodeURIComponent方法轉(zhuǎn)義
2.盡量不是有InnerHtml插入HTML內(nèi)容
3.使用特殊符號(hào)、標(biāo)簽轉(zhuǎn)義符艰亮。
CSRF避免方式:
1.添加驗(yàn)證碼
2.使用token:
1)服務(wù)端給用戶生成一個(gè)token闭翩,加密后傳遞給用戶
2)用戶在提交請求時(shí),需要攜帶這個(gè)token
3)服務(wù)端驗(yàn)證token是否正確
3.狀態(tài)碼
200響應(yīng)成功
301永久重定向
302臨時(shí)重定向
304資源緩存
403服務(wù)器禁止訪問
404服務(wù)器資源未找到
500 502服務(wù)器內(nèi)部錯(cuò)誤
504 服務(wù)器繁忙
四迄埃、微前端
目前主流微前端方案:
1.iframe:基于iframe標(biāo)簽實(shí)現(xiàn)疗韵,技術(shù)難度低,隔離性和兼容性
2.基座模式:主要基于路由分發(fā)侄非,qiankun和single-spa就是基于這種模式蕉汪,監(jiān)聽路由來還在不同的應(yīng)用流译,以實(shí)現(xiàn)應(yīng)用間解耦
3.組合式集成,即組件單獨(dú)打包和發(fā)布者疤,然后在構(gòu)建或運(yùn)行時(shí)組合福澡,類似npm包的形式
4.EMP,一種去中心化的微前端實(shí)現(xiàn)方案,它不僅能很好地隔離應(yīng)用驹马,還可以輕松實(shí)現(xiàn)應(yīng)用間的資源共享和通信竞漾;
5.web components,它通過對組件進(jìn)行更高程度的封裝窥翩,來實(shí)現(xiàn)微前端,但是目前兼容性不夠好鳞仙,尚未普及寇蚊。
qiankun基本原理:
將一個(gè)大型應(yīng)用拆分成若干個(gè)更小、更簡單棍好,可獨(dú)立開發(fā)仗岸、測試和部署的子應(yīng)用,然后由一個(gè)基座應(yīng)用根據(jù)路由進(jìn)行應(yīng)用切換借笙。
基于sing-spa框架搭建的扒怖,在其基礎(chǔ)上進(jìn)行封裝和增強(qiáng),使其更加易用业稼。
微前端的主要優(yōu)勢
1.技術(shù)兼容性好盗痒,各個(gè)子應(yīng)用可基于不同技術(shù)架構(gòu)
2.代碼庫更小、內(nèi)聚性更高
3.便于獨(dú)立編譯低散、測試和部署俯邓,可靠性更高
4.耦合性更低,各個(gè)團(tuán)隊(duì)獨(dú)立開發(fā)互不干擾
5.可維護(hù)性和擴(kuò)展性更好熔号,便于局部升級(jí)和增量升級(jí)
關(guān)于技術(shù)兼容性稽鞭,由于在被基座應(yīng)用加載前, 所有子應(yīng)用已經(jīng)編譯成原生代碼輸出引镊,所以基座應(yīng)用可以加載各類技術(shù)棧編寫的應(yīng)用朦蕴;
由于拆分后應(yīng)用體積明顯變小,并且每個(gè)應(yīng)用只實(shí)現(xiàn)一個(gè)業(yè)務(wù)模塊弟头,因此其內(nèi)聚性更強(qiáng)吩抓;
另外子應(yīng)用本身也是完整的應(yīng)用,所以它可以獨(dú)立編譯亮瓷、測試和部署琴拧;
關(guān)于耦合性,由于各個(gè)子應(yīng)用只負(fù)責(zé)各自的業(yè)務(wù)模塊嘱支,所以耦合性很低蚓胸,非常便于獨(dú)立開發(fā)挣饥;
關(guān)于可維護(hù)性和擴(kuò)展性,由于拆分出的應(yīng)用都是完整的應(yīng)用沛膳,因此專門升級(jí)某個(gè)功能模塊就成為了可能扔枫,并且當(dāng)需要增加模塊時(shí),只需要?jiǎng)?chuàng)建一個(gè)新應(yīng)用锹安,并修改基座應(yīng)用的路由規(guī)則即可短荐。
微前端缺點(diǎn)
1.子應(yīng)用間的資源共享能力較差,使得項(xiàng)目總體積變大
2.需要對現(xiàn)有代碼進(jìn)行改造(指的是未按照微前端形式編寫的舊工程)
qiankun與single-spa實(shí)現(xiàn)原理
微前端問題分為:
1.應(yīng)用的加載與切換 :路由問題叹哭、應(yīng)用入口忍宋、應(yīng)用加載
2.應(yīng)用的隔離與通信:js隔離、css樣式隔離风罩、應(yīng)用間通信
single-spa很好地解決了路由和應(yīng)用入口兩個(gè)問題糠排,但并沒有解決應(yīng)用加載問題,而是將該問題暴露出來由使用者實(shí)現(xiàn)(一般可以用system.js或原生script標(biāo)簽來實(shí)現(xiàn))超升;qiankun在此基礎(chǔ)上封裝了一個(gè)應(yīng)用加載方案(即import-html-entry)入宦,并給出了js隔離、css樣式隔離和應(yīng)用間通信三個(gè)問題的解決方案室琢,同時(shí)提供了預(yù)加載功能乾闰。
single-spa實(shí)現(xiàn)原理
1)路由問題
single-spa通過監(jiān)聽hashChange
和popState
這兩個(gè)原生事件來檢測路由變化的。
總的來看盈滴,當(dāng)路由發(fā)生變化時(shí)涯肩,hashChange
或popState
會(huì)觸發(fā),這時(shí)single-spa會(huì)監(jiān)聽到巢钓,并觸發(fā)urlReroute
宽菜;接著它會(huì)調(diào)用reroute
,該函數(shù)正確設(shè)置各個(gè)應(yīng)用的狀態(tài)后竿报,直接通過調(diào)用應(yīng)用所暴露出的生命周期鉤子函數(shù)即可铅乡。
當(dāng)某個(gè)應(yīng)用被推送到appsToMount
后,它的mount函數(shù)會(huì)被調(diào)用烈菌,該應(yīng)用就會(huì)被掛載阵幸;而推送到appsToUnmount
中的應(yīng)用則會(huì)調(diào)用其unmount鉤子進(jìn)行卸載。
2)應(yīng)用入口
single-spa采用的是協(xié)議入口芽世,即只要實(shí)現(xiàn)了single-spa的入口協(xié)議規(guī)范挚赊,它就是可加載的應(yīng)用。single-spa的規(guī)范要求應(yīng)用入口必須暴露出以下三個(gè)生命周期鉤子函數(shù)济瓢,且必須返回Promise荠割,以保證single-spa可以注冊回調(diào)函數(shù):
1.bootstrap:用于應(yīng)用引導(dǎo),基座應(yīng)用會(huì)在子應(yīng)用掛載前調(diào)用它
2.mount: 用于應(yīng)用掛載,就是一般應(yīng)用中用于渲染的邏輯
3.unmount:用于應(yīng)用卸載
3)應(yīng)用加載
實(shí)際上single-spa并沒有提供自己的解決方案蔑鹦,而是將它開放出來夺克,由開發(fā)者提供。
qiankun原理
1)應(yīng)用加載
針對上面我們談到的幾個(gè)弊端嚎朽,qiankun進(jìn)行了一次封裝铺纽,給出了一個(gè)更完整的應(yīng)用加載方案,qiankun的作者將其封裝成了npm插件import-html-entry
該方案的主要思路是允許以html文件為應(yīng)用入口哟忍,然后通過一個(gè)html解析器從文件中提取js和css依賴狡门,并通過fetch下載依賴
2)js隔離
qiankun通過import-html-entry,可以對html入口進(jìn)行解析锅很,并獲得一個(gè)可以執(zhí)行腳本的方法execScripts其馏。qiankun引入該接口后,首先為該應(yīng)用生成一個(gè)window的代理對象爆安,然后將代理對象作為參數(shù)傳入接口尝偎,以保證應(yīng)用內(nèi)的js不會(huì)對全局window造成影響。由于IE11不支持proxy鹏控,所以qiankun通過快照策略來隔離js,缺點(diǎn)是無法支持多實(shí)例場景
3)css隔離
兩種:一種是基于shadowDom的肤寝;另一種則是實(shí)驗(yàn)性的当辐,思路類似于Vue中的scoped屬性,給每個(gè)子應(yīng)用的根節(jié)點(diǎn)添加一個(gè)特殊屬性鲤看,用作對所有css選擇器的約束
4)應(yīng)用通信
qiankun思路是基于一個(gè)全局的globalState對象缘揪。這個(gè)對象由基座應(yīng)用負(fù)責(zé)創(chuàng)建,內(nèi)部包含一組用于通信的變量义桂,以及兩個(gè)分別用于修改變量值和監(jiān)聽變量變化的方法:setGlobalState和onGlobalStateChange找筝。
1.解決的問題:
1)不同團(tuán)隊(duì)同時(shí)開發(fā)
2)每個(gè)團(tuán)隊(duì)開發(fā)的模塊都可以獨(dú)立開發(fā),獨(dú)立部署
3)實(shí)現(xiàn)增量遷移
基礎(chǔ):
1.flex布局
2.絕對布局和相對布局
3.作用域:全句作用域慷吊、函數(shù)作用域袖裕、塊級(jí)作用域,自由變量的查找:函數(shù)定義的地方向上級(jí)查找
4.let const var溉瓶?急鳄??堰酿?
var是es5語法疾宏,let const是es6語法,var有變量提升触创;
var和let是變量坎藐,可修改;const是常量哼绑,不可修改岩馍;(const指向的地址不可以更改碉咆,但內(nèi)容可以改,所以const a={},a=1會(huì)報(bào)錯(cuò)兼雄,但a.b=1就不會(huì)報(bào)錯(cuò))
let const有塊級(jí)作用域(外面訪問不到)吟逝,var沒有
5.this指向有哪幾種?
1)普通函數(shù):
1.new 方式:指向?qū)嵗?br>
2.非new方式:
1)fn() :指向window
2)obj.fn():指向obj
2)箭頭函數(shù):指向包裹箭頭函數(shù)的第一個(gè)普通函數(shù)的this
3)call赦肋、bind块攒、apply:this是第一個(gè)參數(shù)
6.深淺拷貝
淺拷貝拷貝的是地址,只能解決第一層的問題佃乘,如果下面的值有對象囱井,想要用到深拷貝(拷貝的是值)
淺拷貝:object.assign()、...
深拷貝:JSON.parse(JSON.stringify(object))
//實(shí)現(xiàn)思路:先排除null和非object的情況趣避,是數(shù)組返回[]庞呕,是對象返回{},然后判斷hasOwnproperty是不是自身屬性程帕,然后遞歸
7.原型:每個(gè)class都有自己的顯式原型住练,每個(gè)實(shí)例都有自己的隱式原型,xiaoluo._proto = Student. prototype
8.繼承:原型繼承和class繼承
1)原型繼承:
a.組合繼承:在子類的構(gòu)造函數(shù)中通過Parent.call(this, value)繼承父類的屬性愁拭,然后改變子類的原型為 new Parent() 來繼承父類的函數(shù)
function Child(value) {
Parent.call(this, value)
}
Child.prototype = new Parent()
缺點(diǎn):子類的原型上會(huì)多出不需要的父類屬性讲逛,內(nèi)存上有點(diǎn)浪費(fèi)
b.寄生組合繼承:去掉Child.prototype = new Parent(),換成Object.create()即可
function Child(value) {
Parent.call(this, value)
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
}
})
2)class繼承:expends+super(value)
注:super(value)就相當(dāng)于Parent.call(this, value)岭埠,所以必須調(diào)用
9模塊化
1)立即執(zhí)行函數(shù)()()
2)AMD盏混、AMD
// AMD
define(['./a', './b'], function(a, b) {
// 加載模塊完畢可以使用
a.do()
b.do()
})
// CMD
define(function(require, exports, module) {
// 加載模塊
// 可以把 require 寫在函數(shù)體的任意地方實(shí)現(xiàn)延遲加載
var a = require('./a')
a.doSomething()
})
3)CommonJS:常應(yīng)用于服務(wù)器端、支持同步加載
require()方式引入惜论、exports或者module.exports方式導(dǎo)出
4)es module:常用于瀏覽器端许赃,支持異步加載
問題1:有commonjs,為什么還需要es
1)commonjs應(yīng)用在服務(wù)器端馆类,es一般用于瀏覽器混聊,因?yàn)閏ommonjs支持同步加載,而在瀏覽器端乾巧,由于網(wǎng)絡(luò)延遲和文件大小限制技羔,使用同步加載會(huì)導(dǎo)致瀏覽器假死或者加載時(shí)間過長;
2)同時(shí)coomonjs和es的語法和運(yùn)行機(jī)制不同卧抗,可能會(huì)出現(xiàn)語法和運(yùn)行錯(cuò)誤
問題2:commonjs和es的區(qū)別
1)CommonJS 支持動(dòng)態(tài)導(dǎo)入藤滥,es暫不支持
2)CommonJS是同步加載,因?yàn)橛糜诜?wù)器,文件都在本地,卡住主線程影響也不大;但es是異步導(dǎo)入桌吃,用于瀏覽器标沪,需要下載文件榄攀,如果采用同步導(dǎo)入會(huì)對渲染有影響
3)CommonJS導(dǎo)出時(shí)時(shí)值拷貝,如果值更新需要重新導(dǎo)入金句,而es module實(shí)時(shí)綁定檩赢,會(huì)更新、
4)es module會(huì)編譯成require/exports來執(zhí)行
10.proxy违寞?贞瞒?
11.手寫promise
12.event loop
順序:
1)先執(zhí)行同步代碼,這屬于宏任務(wù)
2)執(zhí)行完所有同步代碼后趁曼,執(zhí)行棧為空军浆,看是否有異步任務(wù)要執(zhí)行
3)執(zhí)行所有的微任務(wù),
4)當(dāng)執(zhí)行完所有的微任務(wù)后挡闰,看下是否需要渲染
5)下一輪event loop乒融,執(zhí)行宏任務(wù)里面的異步代碼,如setTimeout
微任務(wù):promise摄悯、
宏任務(wù):script赞季、setTimeout、setInterval 奢驯、setImmediate 申钩、rendening、I/O
13.call\apply\bind叨橱、instanceof
call所有參數(shù),
apply傳數(shù)組
bind返回一個(gè)新函數(shù)
14.為什么0.1+0.2 != 0.3
因?yàn)?JS 采用的浮點(diǎn)數(shù)標(biāo)準(zhǔn)卻會(huì)裁剪掉我們的數(shù)字断盛,導(dǎo)致精度丟失罗洗,0.100000000000000002===0.1//true
辦法:parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true
15.垃圾回收機(jī)制
標(biāo)記清除
16事件機(jī)制:捕獲、觸發(fā)钢猛、冒泡
阻止冒泡:
event.stopImmediatePropagation()伙菜、
event.stopPropagation()
17跨域
1)jsonp:利用script標(biāo)簽沒有跨域限制的漏洞
通過 <script> 標(biāo)簽指向一個(gè)需要訪問的地址并提供一個(gè)回調(diào)函數(shù)來接收數(shù)據(jù)當(dāng)需要通訊時(shí)
<script src="http://domain/api?param1=a¶m2=b&callback=jsonp"></script>
2)cors:瀏覽器和后端同時(shí)支持,服務(wù)端設(shè)置服務(wù)端設(shè)置 Access-Control-Allow-Origin 就可以開啟 CORS
3)document.domain
該方式只能用于二級(jí)域名相同的情況下命迈,比如 a.test.com 和 b.test.com 適用于該方式贩绕。
只需要給頁面添加 document.domain = 'test.com' 表示二級(jí)域名都相同就可以實(shí)現(xiàn)跨域
4)postmessage
適用于獲取嵌入頁面中的第三方數(shù)據(jù)。一個(gè)頁面發(fā)送數(shù)據(jù)壶愤,另一個(gè)頁面判斷來源并接收
18.存儲(chǔ)
cookie淑倾,localStorage,sessionStorage征椒,indexDB
區(qū)別:
1)生命周期:
cookie一般由服務(wù)器生成娇哆,可以設(shè)置過期時(shí)間、localStorage和indexDB除非被清理,否則一直存在碍讨、sessionStorage頁面關(guān)閉就清理
2)數(shù)據(jù)存儲(chǔ)大兄瘟Α:
cookie:4k
localStorage,sessionStorage:5m
indexDB:無限
3)是否與服務(wù)器通訊勃黍?
cookie:會(huì)宵统,每次請求會(huì)攜帶
其他都不
cookie屬性及作用:
1)value:用于保護(hù)用戶登陸態(tài),應(yīng)加密
2)hoot-only:不能通過js訪問cookie覆获,減少xss攻擊
3)secure:只能在協(xié)議為https的請求中攜帶
4)same-site:規(guī)定瀏覽器不能在跨域請求中攜帶cookie马澈,減少ccsrf攻擊
19.緩存
react
1.react的setstate怎么看是異步還是同步
2.合成事件
react中所有的事件不是綁定到dom上,而是在document上監(jiān)聽所有事件锻梳,當(dāng)事件觸發(fā)冒泡到document上箭券,React將事件內(nèi)容封裝交給中間層 SyntheticEvent React將事件內(nèi)容封裝交給中間層 SyntheticEvent ,SyntheticEvent再對使用統(tǒng)一的分發(fā)函數(shù) dispatchEvent 將指定函數(shù)執(zhí)行
阻止冒泡事件:event.preventDefault
實(shí)現(xiàn)合成事件的目的是什么疑枯?有什么好處辩块?
1)抹平瀏覽器之間的兼容問題
2)性能:對于原生瀏覽器事件而言,瀏覽器會(huì)給監(jiān)聽器創(chuàng)建一個(gè)事件對象荆永。如果有多個(gè)废亭,會(huì)造成高額的內(nèi)存分配問題。
但對于合成事件來說具钥,有一個(gè)事件池子來管理創(chuàng)建和銷毀
3.前端路由原理豆村?本質(zhì)就是監(jiān)聽url的變化,然后匹配路由規(guī)則骂删,顯示相應(yīng)的頁面掌动,并且無需刷新。目前有兩種方式:hash模式宁玫、history模式
1)hash模式
當(dāng)#后的哈希值變化粗恢,可以通過hashchange事件來監(jiān)聽,從而跳轉(zhuǎn)頁面
2)history模式
改變url:history.pushState 和 history.replaceState
后退:popState欧瘪。如window.addEventListener('popstate', e => {})
兩種模式對比:
1)hash模式只可以更改#后面的內(nèi)容眷射,history模式可以通過api設(shè)置任意的同源url
2)history模式可以通過api添加任意類型的數(shù)據(jù)到歷史記錄,hash模式只能更改哈希值
3)hash模式無需后端配置佛掖,兼容性好妖碉。history模式需要發(fā)起url請求,后端需要配置index.html頁面用于匹配不到靜態(tài)資源的時(shí)候
4.react-route實(shí)現(xiàn)原理
1)基于hash路由:監(jiān)聽hashchange事件芥被,感知hash的變化(loaction.hash=xxx)
2)基于history路由:
改變 url 可以通過 history.pushState 和 resplaceState 等欧宜,會(huì)將URL壓入堆棧,同時(shí)能夠應(yīng)用 history.go() 等 API拴魄;
監(jiān)聽 url 的變化可以通過自定義事件觸發(fā)實(shí)現(xiàn)
5.react-route實(shí)現(xiàn)思想:
1)基于history實(shí)現(xiàn)上述不同客戶端路由實(shí)現(xiàn)思想鱼鸠,保存歷史記錄等猛拴,磨平瀏覽器差異,上層無感知
2)通過維護(hù)的列表蚀狰,在每次url發(fā)生變化的回收愉昆,通過配置的路由路徑,匹配到對應(yīng)component麻蹋,并且redner
6.如何配置 React-Router 實(shí)現(xiàn)路由切換
1)使用<Route> 組件:
<Route path='/about' component={About}/>
2)結(jié)合使用 <Switch> 組件和 <Route> 組件
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
3)使用 <Link>跛溉、 <NavLink>、<Redirect> 組件
7.React-Router怎么設(shè)置重定向扮授?
使用<Redirect>組件實(shí)現(xiàn)路由的重定向:
<Switch>
<Redirect from='/users/:id' to='/users/profile/:id'/>
<Route path='/users/profile/:id' component={Profile}/>
</Switch>
8.react生命周期芳室?React 的異步請求應(yīng)該放在哪里,為什么刹勃?
componentDidMount堪侯。從事件順序上來看,也可以:
1)constructor:可以放荔仁,但不推薦伍宦,因?yàn)閏onstructor主要用于初始化state與函數(shù)綁定,并不承載業(yè)務(wù)邏輯
2)componentWillMount:已廢除
所以componentDidMount是最好的選擇
9.React Fiber架構(gòu)
10.調(diào)和過程中setState內(nèi)部做了什么乏梁?
1)將傳遞給setState對象合并到組件的當(dāng)前狀態(tài)
2)啟動(dòng)和解(reconciliation):react構(gòu)建一個(gè)新的react元素樹次洼,根據(jù)新的狀態(tài)來更新UI
3)將新樹與上一個(gè)元素樹diff
11.setState異步還是同步?
異步:合成事件和鉤子函數(shù)中
同步:原生和setTimeout中
主要通過isBatchingUpdates是否為true來決定是先存進(jìn)隊(duì)列還是直接更新
為true表示是在可以控制的地方遇骑,如生命周期和合成事件中卖毁;false則表示在react無法控制的地方,比如原生事件(如addEventListener 落萎、setTimeout亥啦、setInterval)等事件
12.setState 的批量更新
在異步中如果對同一值進(jìn)行多次setState,內(nèi)部會(huì)對其覆蓋练链,取最后一次執(zhí)行
在react生命周期和合成事件執(zhí)行前后都有相應(yīng)的鉤子翔脱,分別是pre鉤子和post鉤子,pre鉤子會(huì)調(diào)用batchedUpdate方法將isBatchingUpdates變量置為true兑宇,開啟批量更新碍侦,而post鉤子會(huì)將isBatchingUpdates置為false
isBatchingUpdates變量置為true粱坤,則會(huì)走批量更新分支隶糕,setState的更新會(huì)被存入隊(duì)列中,待同步代碼執(zhí)行完后站玄,再執(zhí)行隊(duì)列中的state更新枚驻。 isBatchingUpdates為 true,則把當(dāng)前組件(即調(diào)用了 setState的組件)放入 dirtyComponents 數(shù)組中株旷;否則 batchUpdate 所有隊(duì)列中的更新
而在原生事件和異步操作中再登,不會(huì)執(zhí)行pre鉤子尔邓,或者生命周期的中的異步操作之前執(zhí)行了pre鉤子,但是pos鉤子也在異步操作之前執(zhí)行完了锉矢,isBatchingUpdates必定為false梯嗽,也就不會(huì)進(jìn)行批量更新
13.事務(wù)機(jī)制
4.hooks是什么?
5.為什么不能在條件語句中寫 hook
1.緩存:強(qiáng)緩存和協(xié)商緩存
1)強(qiáng)緩存:
表示緩存期間不需要請求沽损,可以通過兩種響應(yīng)頭實(shí)現(xiàn):
Expires / Cache-Control(優(yōu)先級(jí)高)
Expires: Wed, 22 Oct 2018 08:41:00 GMT
//表示資源會(huì)在 Wed, 22 Oct 2018 08:41:00 GMT 后過期灯节,需要再次請求。并且 Expires 受限于本地時(shí)間绵估,如果修改了本地時(shí)間炎疆,可能會(huì)造成緩存失效
Cache-control: max-age=30
//表示資源會(huì)在 30 秒后過期,需要再次請求
2)協(xié)商緩存
如果緩存過期国裳,需要通過協(xié)商緩存解決形入。協(xié)商緩存需要請求,如果緩存有效會(huì)返回304缝左,需要客戶端和服務(wù)端共同實(shí)現(xiàn)亿遂,有兩種方式:Last-Modified 和 ETag(優(yōu)先級(jí)高)
a.Last-Modified 和 If-Modified-Since
Last-Modified表示本地文件最后修改日期,if-modified-since會(huì)將Last-Modified的值發(fā)送給服務(wù)器盒使,查詢在該日期后資源是否有更新崩掘,如果有就會(huì)將新資源更新
缺點(diǎn):
1)如果在本地打開緩存文件,也會(huì)造成Last-Modified被修改
2)因?yàn)?Last-Modified 只能以秒計(jì)時(shí)少办,如果在不可感知的時(shí)間內(nèi)修改完成文件苞慢,那么服務(wù)端會(huì)認(rèn)為資源還是命中了,不會(huì)返回正確的資源
所以HTTP / 1.1 出現(xiàn)了 ETag
b.ETag 和 If-None-Match
ETag類似于文件指紋英妓,If-None-Match會(huì)將ETag值發(fā)送給服務(wù)器挽放,查詢是否有改動(dòng),有的話就將新資源發(fā)送回來
怎么選擇蔓纠?
1)不需要緩存的資源:使用Cache-control: no-store 辑畦,表示該資源不需要緩存
2)頻繁變動(dòng)的資源:可以使用 Cache-Control: no-cache 并配合 ETag 使用,表示該資源已被緩存腿倚,但是每次都會(huì)發(fā)送請求詢問資源是否更新
3)對于代碼文件來說纯出,通常使用 Cache-Control: max-age=31536000 并配合策略緩存使用,然后對文件進(jìn)行指紋處理敷燎,一旦文件名變動(dòng)就會(huì)立刻下載新的文件
2.圖片優(yōu)化
1)不用圖片暂筝。用css代替
2)用cdn加載,計(jì)算適配屏幕的寬度硬贯,然后請求響應(yīng)建材好的圖片
3)小圖用base64格式
4)雪碧圖(多個(gè)圖標(biāo)整合到一張圖片中)
5)選擇正確的圖片格式
a.能夠顯示 WebP 格式的瀏覽器盡量使用 WebP 格式
(較好的圖像數(shù)據(jù)壓縮算法焕襟,優(yōu)化體積,圖片質(zhì)量幾乎無差別饭豹,缺點(diǎn)就是兼容性不好)
b.小圖用png鸵赖,大部分圖標(biāo)可以用svg代替
c.照片使用jpeg格式
display:flex
1.方向?qū)傩?flex-direction
2.換行flex-nowrap
3.主軸方向排列justify-content
2)flex-end
4.space-around
5.space-between
4.豎軸方向align-items
5.行與行之間的排列align-content
6.單獨(dú)排序order(數(shù)值小的靠前排)
7.剩余空間分配比例flex-grow
1:1
2:1
如何收縮flex-shrik:1
單獨(dú)設(shè)置某個(gè)元素的位置align-self
-
auto
-
flex-start
-
flex-end
-
center
-
baseline
-
stretch
事件執(zhí)行機(jī)制
回流:
怎么避免回流:
垃圾回收機(jī)制:
1.引用計(jì)數(shù):
問題:位置不連貫饵骨,會(huì)有找位置問題
v8優(yōu)化-標(biāo)記算法:
根據(jù)是否頻繁回收,小的需要頻繁回收茫打,大的不需要
老生代:
箭頭函數(shù)
xxs攻擊
enhanceHybird性能優(yōu)化
css內(nèi)容:
1.盒模型:標(biāo)準(zhǔn)盒子模型宏悦、IE盒子模型
區(qū)別:
1)標(biāo)準(zhǔn)盒子模型:margin、border包吝、padding饼煞、content
2)IE盒子模型:margin、content(content+padding+border)
怎么轉(zhuǎn)換
box-sizing:content-box //標(biāo)準(zhǔn)盒子模型
box-sizing:border-box //IE盒子模型
2.line-height和height區(qū)別诗越?
line-height是每一行文字的高砖瞧,如果文字換行整個(gè)div盒子高度會(huì)撐大(行數(shù)*行高)
height:固定值,即該盒子的高度
3.css選擇符號(hào)有哪些嚷狞?哪些可以繼承
css選擇符:通配(*)块促、
id選擇器(#)、
類選擇器(.)床未、
標(biāo)簽選擇器(div竭翠、p、h1)薇搁、
相鄰選擇器(+)斋扰、
后代選擇器(ul li)、
子元素選擇器(>)啃洋、
屬性選擇器(a[href])
哪些可以繼承:font-size传货、color、line-height宏娄、text-aligin
哪些不可以繼承:border问裕、padding、margin
4.css優(yōu)先級(jí)
!important >內(nèi)聯(lián)(1000)>id(100)>class(10)>標(biāo)簽/偽元素選擇器(1)>通配(0)
5.css畫一個(gè)三角形(用邊框畫:在哪個(gè)位置就把哪個(gè)邊框顏色保留孵坚,其他全置為透明)
eg:畫一個(gè)“上”三角形
.div{
width:0;
height:0;
boder-top:100px solid #ccc;
boder-left:100px solid transparent;
boder-right:100px solid transparent;
boder-bottom:100px solid transparent;
}
6.水平居中粮宛,不給寬高
<div class='parent'>
<div class='children'>子</div>
</div>
方式一:flex,給父元素設(shè)置
.parent{
display:flex;
justify-content:center卖宠;
align-items:center巍杈;
}
方式二:絕對定位(absolute +left50%+right50%+translate-50%)
.parent{
position: relative;
}
.children{
position: absolute;
left:50%;
right:50%;
transform:translate(-50%, -50%);
}
7:display有哪些值?說明作用
none: 隱藏元素
block:把某元素轉(zhuǎn)換成塊元素
inline:把某元素轉(zhuǎn)換成行內(nèi)元素逗堵,讓元素?fù)碛行袃?nèi)元素的特性
inline-block:把某元素轉(zhuǎn)換成行內(nèi)塊元素
flex:設(shè)置為彈性伸縮盒
8.BFC理解(塊級(jí)格式化上下文)
1)bfc理解:頁面上獨(dú)立容器秉氧,容器的子元素不影響外面的元素
2)bfc原則:如果一個(gè)元素具有bfc眷昆,那么內(nèi)部元素再怎么弄蜒秤,都不會(huì)影響到外面的元素
3)如何觸發(fā):
float的值非none汁咏、
overflow的值非visible、
display的值為:inline-block作媚、table-cell
position的值為:absolute攘滩、fixed
9.清除浮動(dòng)方式?第三種常用
1)觸發(fā)bfc :overflow:hidden
2)子元素新增一個(gè)div標(biāo)簽纸泡,clear:both
3):after增加內(nèi)容:
父元素:after{
content:'';
display:none;
clear:both
}
10.position有幾種定位漂问?分別根據(jù)什么定位
static默認(rèn)值,沒有定位
fixed:根據(jù)瀏覽器窗口定位
relative:相對于自身定位女揭,不脫離文檔流
absolute:相對于第一個(gè)有relative的父元素蚤假,脫離文檔流
ps:relative如果設(shè)置了left、right吧兔、top磷仰、bottom,生效的只有l(wèi)eft境蔼、top灶平;而absolute不影響
11.css reset用來重置默認(rèn)樣式
12.css雪碧圖
1)是什么:把多個(gè)小圖標(biāo)合并成一張大圖片
2)優(yōu)缺點(diǎn):
優(yōu)點(diǎn):減少http請求,提升性能
缺點(diǎn):難維護(hù)
13.哪些方式可以隱藏元素箍土?
opacity:0設(shè)置透明度
display:none徹底隱藏了元素逢享,元素從文檔流中消失,既不占據(jù)空間也不交互吴藻,也不影響布局
visibility:hidden隱藏了元素瞒爬,占據(jù)空間,但是不可以交互了
overflow:hidden只隱藏元素溢出的部分沟堡,但是占據(jù)空間且不可交互
z-index:-9999: 原理是將層級(jí)放到底部疮鲫,這樣就被覆蓋了,看起來隱藏了
transform:scale(0,0): 平面變換弦叶,將元素縮放為0俊犯,但是依然占據(jù)空間,但不可交互
14.display: none 與 visibility: hidden 的區(qū)別
1)重排重繪:display: none會(huì)造成文檔重排伤哺,visibility只會(huì)重繪
2)是否讀內(nèi)容燕侠?讀屏器只會(huì)讀visibility:hidden內(nèi)容
3)是否從渲染樹消失?
display:none會(huì)消失立莉,并且不占空間
visibility:hidden不會(huì)消失绢彤,會(huì)占空間,但內(nèi)容不可見
4)是否為繼承屬性蜓耻?
visibility:hidden是茫舶,子孫節(jié)點(diǎn)可通過visibility:visible使節(jié)點(diǎn)顯式
display:none不是
待辦:兩欄布局、三欄布局
》〉》〉
css篇
1.塊元素和行內(nèi)元素
2.想讓offsetwidth為100刹淌,需要設(shè)置border-box
3.bfc理解和應(yīng)用饶氏?
概念:塊級(jí)格式化上下文讥耗,一塊獨(dú)立渲染區(qū)域,內(nèi)部元素的渲染不會(huì)影響邊界以外的元素
觸發(fā)條件:
1)float不是none
2)position是absolute或者fixed
3)overflow不是visible
4)display是flex疹启、inline-block
應(yīng)用:清除浮動(dòng)
設(shè)置overflow:hidden
4.清除浮動(dòng)
1)額外標(biāo)簽
父
<子:style={clear:both}></>
2)bfc
<父 style ={overflow:hidden}></>
3)偽類選擇器+clearfloat
parent:after{
content:'',
display:block,
clear:both,
hight:0,
visibility:hidden
}
5.relative與absolute
relative:依據(jù)自身定位
absolute依據(jù)最近上一層定位
6.line-height如何繼承
所以p標(biāo)簽的line-height的值為20*200%=40px
響應(yīng)式
7.px古程、em、rem喊崖、%挣磨、vh/vw是什么?
px荤懂,是絕對長度單位茁裙,最常用
em,相對長度單位节仿,相對于父元素呜达,不常用
rem,相對長度單位粟耻,相對于根元素查近,常用于響應(yīng)式
%,相對長度單位挤忙,相對于父元素
vh/vw霜威,相對長度單位,相對于屏幕高度/屏幕寬度
8.響應(yīng)式布局的常用方案
先設(shè)置蘋果6/7/8的html字體大小為100px作為基準(zhǔn)册烈,然后換算蘋果5小尺寸的戈泼,86/100 = 320/375,大尺寸的同理
eg:body可基于html父元素設(shè)置rem
[圖片上傳中...(image.png-27e93d-1708348213008-0)]
網(wǎng)頁視口尺寸:
屏幕高度:window.screen.height
網(wǎng)頁視口高度:window.innerHeight
body高度:window.body.clinetHeight
vh/vw赏僧,相對長度單位大猛,相對于屏幕高度/屏幕寬度
vmax/vmin 兩者最大值/最小值
js進(jìn)階
1.event loop
異步回調(diào)的實(shí)現(xiàn)原理
執(zhí)行過程:
1)先執(zhí)行同步代碼,這屬于紅任務(wù)
2)執(zhí)行完同步代碼淀零,看下執(zhí)行棧是否為空挽绩,再執(zhí)行異步代碼
3)執(zhí)行微任務(wù)
4)執(zhí)行完所有的微任務(wù),看是否需要渲染
5)進(jìn)入下一輪event loop驾中,執(zhí)行宏任務(wù)中的異步代碼唉堪,如setTimeout中的回調(diào)函數(shù)
//Hi Bye cb1
ps:從上到下執(zhí)行,遇到同步代碼console.log('Hi'),進(jìn)入call back肩民,所以打印"Hi"唠亚,執(zhí)行完就清空了,然后繼續(xù)走遇到setTimeout定時(shí)器持痰,這個(gè)屬于web apis灶搜,所以進(jìn)入web apis此時(shí)有一個(gè)定時(shí)器任務(wù)(等待5s后執(zhí)行),時(shí)間沒到之前繼續(xù)往下走,遇到同步代碼console.log('Bye')割卖,然后就進(jìn)入call back棧里執(zhí)行console.log('Bye')前酿,所以打印'Bye',執(zhí)行完清空究珊。
再繼續(xù)往下走,發(fā)現(xiàn)沒有可執(zhí)行代碼了纵苛,執(zhí)行棧call stack為空剿涮,啟動(dòng)evevt loop,此時(shí)5s時(shí)間還沒到攻人,此時(shí)callback queue依舊為空取试,等到5s后,定時(shí)器里面的函數(shù)console.log('cb1')被推到callback queue里執(zhí)行怀吻,執(zhí)行后callback queue會(huì)推console.log('cb1')到執(zhí)行棧call stack里執(zhí)行瞬浓,執(zhí)行完清空
2.DOM和event loop
//Hi Bye button clicked
ps:從上到下執(zhí)行,先打印"Hi"蓬坡,再執(zhí)行click事件代碼猿棉,但只能用戶點(diǎn)擊了才能出發(fā),所以會(huì)放到web apis里等待觸發(fā)屑咳,再繼續(xù)執(zhí)行'Bye'代碼萨赁,此時(shí)執(zhí)行棧中為空,啟動(dòng)event loop兆龙,發(fā)現(xiàn)還是沒有什么可執(zhí)行(這里如果用戶還是沒觸發(fā)杖爽,就不會(huì)打印點(diǎn)擊事件里面的代碼),等到用戶點(diǎn)擊觸發(fā)click事件紫皇,這個(gè)事件被推到callback queue里慰安,從而打印'button clicked'
3.Promise
3.1 then和catch改變狀態(tài)
then正常返回resolved,里面有報(bào)錯(cuò)則返回rejected聪铺、
catch正常返回resolved化焕,里面有報(bào)錯(cuò)則返回rejected
.catch返回的是
3.async/await
用來解決回調(diào)地獄,Promise then catch鏈?zhǔn)秸{(diào)用铃剔,雖然也可以解決回調(diào)地獄锣杂,但也是基于回調(diào)函數(shù)實(shí)現(xiàn)的,而async/await是同步語法番宁,徹底消滅回調(diào)函數(shù)
與Promise的關(guān)系
1)執(zhí)行async函數(shù)元莫,返回的是Promise對象
2)await相當(dāng)于Promise的then
3)try...catch可捕獲異常,代替了Promise的catch
4.宏任務(wù)與微任務(wù)
宏任務(wù):setTimeout蝶押、setInterval踱蠢、ajax、dom事件
微任務(wù):Promise、async/await
微任務(wù)執(zhí)行時(shí)機(jī)比宏任務(wù)早(微任務(wù)在dom渲染前觸發(fā)茎截,是es6語法規(guī)定的苇侵,宏任務(wù)在dom渲染后觸發(fā),是瀏覽器規(guī)定的)
場景題
async相當(dāng)于返回一個(gè)Promise企锌,await相當(dāng)于.then()
所以榆浓,
左邊打印: Promise resolve ,100
右邊打铀涸堋:start 100 200 報(bào)錯(cuò)后面都不執(zhí)行(await相當(dāng)于then(),但里面是reject,then接收不了)
hooks的坑:
1.useState初始化值脏榆,只有第一次有效
描述:父通過props傳給子組件propssum城舞,子組件用新變量sum保存生宛,初始化的時(shí)候子組件會(huì)是傳過來的值味咳,但父組件更新了,子組件不會(huì)更新
解決:
hooks:通過useEffect的第二個(gè)參數(shù)監(jiān)聽某個(gè)poros值擦俐,然后set
import React, { useEffect, useState } from "react";
const ChildModal = (props) => {
console.log('props', props);
const [sum, setSum] = useState(-1);
//通過第二個(gè)參數(shù)更新
// useEffect(() => {
// setSum(props.sum);
// }, [props.sum])
return (
<div>子組件的數(shù)量:{sum}</div>
)
}
export default ChildModal
class組件:通過生命周期 componentWillReceiveProps(props) {
console.log(props)
this.setState({show: props.checked})
}
ps:
或者直接用props.sum渲染
const ChildModal = (props) => {
console.log('props', props);
return (
<div>子組件的數(shù)量:{props.sum}</div>
)
2.useEffect內(nèi)部不能修改state脊阴,第二個(gè)參數(shù)需要是空的依賴[]
會(huì)出現(xiàn)閉包陷阱
function UseEffectChangeState() {
const [count, setCount] = useState(0)
// 模擬 DidMount
const countRef = useRef(0)
useEffect(() => {
console.log('useEffect...', count)
// 定時(shí)任務(wù)
const timer = setInterval(() => {
console.log('setInterval...', countRef.current) // 一直是0 閉包陷阱
// setCount(count + 1)
setCount(++countRef.current) // 解決方案使用useRef
}, 1000)
// 清除定時(shí)任務(wù)
return () => clearTimeout(timer)
}, []) // 依賴為 []
// 依賴為 [] 時(shí): re-render 不會(huì)重新執(zhí)行 effect 函數(shù)
// 沒有依賴:re-render 會(huì)重新執(zhí)行 effect 函數(shù)
return <div>count: {count}</div>
}
export default UseEffectChangeState
useEffect中第二個(gè)參數(shù)[]+返回函數(shù),模擬的是conponmentDidMount這個(gè)生命周期蚯瞧,所以re-redenr的時(shí)候不會(huì)走進(jìn)來嘿期,所以不會(huì)拿到最新的值,所以需要借助useRef來獲取
3.useEffect可能出現(xiàn)死循環(huán)埋合,依賴[]里面有對象秽五、數(shù)組等引用類型,把引用類型拆解為值類型
useEffect(() => {
setSum(props.sum);
}, [props.sum,props ])
如果第二個(gè)參數(shù)有對象porps饥悴,會(huì)陷入死循環(huán)坦喘,因?yàn)闀?huì)進(jìn)行淺比較,Object.is({},{})//false
React揭秘:
前言:主瀏覽器更新pi
相較于 React15西设,React16 中新增了Scheduler(調(diào)度器)
React16 架構(gòu)可以分為三層:
Scheduler(調(diào)度器)—— 調(diào)度任務(wù)的優(yōu)先級(jí)瓣铣,高優(yōu)任務(wù)優(yōu)先進(jìn)入Reconciler
Reconciler(協(xié)調(diào)器)—— 負(fù)責(zé)找出變化的組件
Renderer(渲染器)—— 負(fù)責(zé)將變化的組件渲染到頁面上
1.Scheduler(調(diào)度器)
實(shí)現(xiàn)了功能更完備的requestIdleCallback polyfill,需要當(dāng)瀏覽器有剩余時(shí)間的時(shí)候通知我們贷揽,并提供優(yōu)先級(jí)任務(wù)
2.Reconciler(協(xié)調(diào)器)
React15 中Reconciler是遞歸處理虛擬 DOM 的棠笑;
React16 中Reconciler從遞歸變成了可以中斷的循環(huán)過程。
那如何解決中斷更新時(shí) DOM 渲染不完全的問題禽绪?
16中蓖救,Reconciler與Renderer不再是交替工作的,而是當(dāng)Scheduler將任務(wù)交給Reconciler后印屁,Reconciler會(huì)為變化的虛擬 DOM 打上代表增/刪/更新的標(biāo)記循捺。只有當(dāng)所有組件都完成Reconciler的工作,才會(huì)統(tǒng)一交給Renderer
3.Renderer(渲染器)
其中紅框中的步驟隨時(shí)可能由于以下原因被中斷:
1)有其他更高優(yōu)任務(wù)需要先更新
2)當(dāng)前幀沒有剩余時(shí)間
從React15到React16雄人,協(xié)調(diào)器(Reconciler)重構(gòu)的一大目的是:將老的同步更新的架構(gòu)變?yōu)楫惒娇芍袛喔?/p>
為什么沒有用Generator來實(shí)現(xiàn)協(xié)調(diào)器从橘?
因?yàn)镚enerator只能解決“單一優(yōu)先級(jí)任務(wù)的中斷與繼續(xù)”情況下實(shí)現(xiàn)異步可中斷更新,一旦有‘高優(yōu)先級(jí)任務(wù)插隊(duì)’就不能實(shí)現(xiàn)了(因?yàn)槿绻衋任務(wù)在執(zhí)行,此時(shí)已經(jīng)完成a任務(wù)的計(jì)算恰力,高優(yōu)先級(jí)任務(wù)b插隊(duì)叉谜,此時(shí)參數(shù)變了,需要重新計(jì)算踩萎,如果通過全局變量保存之前執(zhí)行的中間狀態(tài)停局,又會(huì)引入新問題
)。因此React沒有采用Generator實(shí)現(xiàn)協(xié)調(diào)器
二香府、Fiber
1.Fiber的含義:
1)之前React15的Reconciler采用遞歸的方式執(zhí)行董栽,數(shù)據(jù)保存在遞歸調(diào)用棧中,所以被稱為stack Reconcile
2)作為靜態(tài)的數(shù)據(jù)結(jié)構(gòu)來說回还,每個(gè)Fiber節(jié)點(diǎn)對應(yīng)一個(gè)React element裆泳,保存了該組件的類型(函數(shù)組件/類組件/原生組件...)叹洲、對應(yīng)的DOM節(jié)點(diǎn)等信息
3)作為動(dòng)態(tài)的工作單元來說柠硕,每個(gè)Fiber節(jié)點(diǎn)保存了本次更新中該組件改變的狀態(tài)、要執(zhí)行的工作(需要被刪除/被插入頁面中/被更新...)
2.雙緩存:在內(nèi)存中構(gòu)建并直接替換的技術(shù)
為什么要雙緩存运提?
繪制頁面時(shí)蝗柔,每一幀繪制前都會(huì)先將上一幀的畫面清除,當(dāng)計(jì)算量比較大時(shí)民泵,就會(huì)在兩幀之間出現(xiàn)較長空隙癣丧,白屏。
所以為了解決該問題栈妆,可以現(xiàn)在內(nèi)存中繪制當(dāng)前幀胁编,繪制完畢后直接替換,節(jié)省白屏?xí)r間
3.雙緩存fiber樹
1)current Fiber樹:當(dāng)前屏幕上顯示內(nèi)容對應(yīng)的Fiber樹
2)workInProgress Fiber樹:正在內(nèi)存中構(gòu)建的Fiber樹
當(dāng)workInProgress Fiber樹構(gòu)建完成交給Renderer渲染在頁面上后鳞尔,應(yīng)用根節(jié)點(diǎn)的current指針指向workInProgress Fiber樹嬉橙,此時(shí)workInProgress Fiber樹就變?yōu)閏urrent Fiber樹
每次狀態(tài)更新都會(huì)產(chǎn)生新的workInProgress Fiber樹,通過current與workInProgress的替換寥假,完成DOM更新
4.mount時(shí)市框、update時(shí)的構(gòu)建/替換流程
mount階段:
1)首次執(zhí)行ReactDOM.render會(huì)創(chuàng)建fiberRootNode(源碼中叫fiberRoot)和rootFiber。
其中:
fiberRootNode是整個(gè)應(yīng)用的根節(jié)點(diǎn)糕韧,
rootFiber是<App/>所在組件樹的根節(jié)點(diǎn)枫振。
fiberRootNode的current會(huì)指向當(dāng)前頁面上已渲染內(nèi)容對應(yīng)Fiber樹,即current Fiber樹
2)接下來render階段萤彩,根據(jù)組件返回的JSX在內(nèi)存中依次創(chuàng)建Fiber節(jié)點(diǎn)并連接在一起構(gòu)建Fiber樹粪滤,被稱為workInProgress Fiber樹。(下圖中右側(cè)為內(nèi)存中構(gòu)建的樹雀扶,左側(cè)為頁面顯示的樹)
構(gòu)建內(nèi)存樹時(shí)會(huì)復(fù)用current Fiber樹中已有的Fiber節(jié)點(diǎn)內(nèi)的屬性额衙,在首屏渲染時(shí)只有rootFiber存在對應(yīng)的current fiber(即rootFiber.alternate)。
3)內(nèi)存樹替換更新到頁面
fiberRootNode的current指針指向workInProgress Fiber樹使其變?yōu)閏urrent Fiber 樹
update時(shí):
1)當(dāng)觸發(fā)狀態(tài)改變時(shí),開啟新的render階段窍侧,構(gòu)建新內(nèi)存樹
和mount時(shí)一樣县踢,workInProgress fiber的創(chuàng)建可以復(fù)用(是否復(fù)用就是diff算法的過程)current Fiber樹對應(yīng)的節(jié)點(diǎn)數(shù)據(jù)
2)內(nèi)存樹在render階段完成構(gòu)建后進(jìn)入commit階段渲染到頁面上。渲染完畢后伟件,內(nèi)存樹(workInProgress Fiber 樹)變成current Fiber 樹
小結(jié):上面介紹了Fiber樹的構(gòu)建與替換過程
問題:構(gòu)建過程中每個(gè)fiber節(jié)點(diǎn)怎么創(chuàng)建硼啤?