<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          1.7w字總結(jié)前端常規(guī)知識點(diǎn)

          共 32158字,需瀏覽 65分鐘

           ·

          2021-09-30 14:28

          關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~

          一、JS相關(guān)

          JS原型及原型鏈

          function?Person()?{}
          Person.prototype.name?=?'Zaxlct';
          Person.prototype.sayName?=?function()?{
          ??alert(this.name);
          }
          var?person1?=?new?Person();
          //JS 在創(chuàng)建對象的時(shí)候,都有一個(gè)__proto__?的內(nèi)置屬性,用于指向創(chuàng)建它的構(gòu)造函數(shù)的原型對象。
          //每個(gè)對象都有?__proto__?屬性,但只有函數(shù)對象才有?prototype?屬性
          //?對象?person1?有一個(gè)?__proto__屬性,創(chuàng)建它的構(gòu)造函數(shù)是?Person,構(gòu)造函數(shù)的原型對象是?Person.prototype
          console.log(person1.__proto__?==?Person.prototype)?//true
          //所有函數(shù)對象的__proto__都指向Function.prototype
          String.__proto__?===?Function.prototype??//?true
          String.constructor?==?Function?//true
          cb5ca24e8083e3879091f058a4c36556.webpprototype.jpg

          JS繼承的幾種方式

          詳解

          1. 原型繼承
          function?Parent?()?{
          ??this.name?=?'Parent'
          ??this.sex?=?'boy'
          }
          function?Child?()?{
          ??this.name?=?'child'
          }
          //?將子類的原型對象指向父類的實(shí)例
          Child.prototype?=?new?Parent()
          //優(yōu):繼承了父類的模板,又繼承了父類的原型對象
          //缺:1.無法實(shí)現(xiàn)多繼承(因?yàn)橐呀?jīng)指定了原型對象了)
          //???2.創(chuàng)建子類時(shí),無法向父類構(gòu)造函數(shù)傳參數(shù)
          1. 構(gòu)造函數(shù)繼承

          在子類構(gòu)造函數(shù)內(nèi)部使用call或apply來調(diào)用父類構(gòu)造函數(shù),復(fù)制父類的實(shí)例屬性給子類。

          function?Parent?(name)?{
          ??this.name?=?name
          }
          function?Child?()?{
          ??//用.call?來改變?Parent?構(gòu)造函數(shù)內(nèi)的指向
          ??Parent.call(this,?'child')
          }
          //優(yōu):解決了原型鏈繼承中子類實(shí)例共享父類引用對象的問題,實(shí)現(xiàn)多繼承,創(chuàng)建子類實(shí)例時(shí),可以向父類傳遞參數(shù)
          //缺:構(gòu)造繼承只能繼承父類的實(shí)例屬性和方法,不能繼承父類原型的屬性和方法
          1. 組合繼承

            組合繼承就是將原型鏈繼承與構(gòu)造函數(shù)繼承組合在一起。

          • 使用原型鏈繼承來保證子類能繼承到父類原型中的屬性和方法
          • 使用構(gòu)造繼承來保證子類能繼承到父類的實(shí)例屬性和方法

          寄生組合繼承

          class繼承

          class?中繼承主要是依靠兩個(gè)東西:

          • extends

          • super

          class?Parent?{
          ??constructor?(name)?{
          ????this.name?=?name
          ??}
          ??getName?()?{
          ????console.log(this.name)
          ??}
          }
          class?Child?extends?Parent?{
          ??constructor?(name)?{
          ????super(name)
          ????this.sex?=?'boy'
          ??}
          }

          Event Loop 事件循環(huán)

          同步與異步、宏任務(wù)和微任務(wù)分別是函數(shù)兩個(gè)不同維度的描述。

          同步任務(wù)指的是,在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù);異步任務(wù)指的是,不進(jìn)入主線程、而進(jìn)入任務(wù)隊(duì)列task queue)的任務(wù),只有等主線程任務(wù)執(zhí)行完畢,任務(wù)隊(duì)列開始通知主線程,請求執(zhí)行任務(wù),該任務(wù)才會進(jìn)入主線程執(zhí)行。

          當(dāng)某個(gè)宏任務(wù)執(zhí)行完后,會查看是否有微任務(wù)隊(duì)列。如果有,先執(zhí)行微任務(wù)隊(duì)列中的所有任務(wù);如果沒有,在執(zhí)行環(huán)境棧中會讀取宏任務(wù)隊(duì)列中排在最前的任務(wù);執(zhí)行宏任務(wù)的過程中,遇到微任務(wù),依次加入微任務(wù)隊(duì)列。??蘸?,再次讀取微任務(wù)隊(duì)列里的任務(wù),依次類推。

          同步(Promise)>異步(微任務(wù)(process.nextTick ,Promises.then, Promise.catch ,resove,reject,MutationObserver)>宏任務(wù)(setTimeout,setInterval,setImmediate))

          await阻塞?后面的代碼執(zhí)行,因此跳出async函數(shù)執(zhí)行下一個(gè)微任務(wù)

          Promise 與?Async/Await??區(qū)別

          async/await是基于Promise實(shí)現(xiàn)的,看起來更像同步代碼,

          • 不需要寫匿名函數(shù)處理Promise的resolve值
          • 錯(cuò)誤處理: Async/Await 讓 try/catch 可以同時(shí)處理同步和異步錯(cuò)誤。
          • 條件語句也跟錯(cuò)誤處理一樣簡潔一點(diǎn)
          • 中間值處理(第一個(gè)方法返回值,用作第二個(gè)方法參數(shù)) 解決嵌套問題
          • 調(diào)試方便
          const?makeRequest?=?()?=>?{
          ????try?{
          ????????getJSON().then(result?=>?{
          ????????????//?JSON.parse可能會出錯(cuò)
          ????????????const?data?=?JSON.parse(result)
          ????????????console.log(data)
          ????????})
          ????????//?取消注釋,處理異步代碼的錯(cuò)誤
          ????????//?.catch((err)?=>?{
          ????????//???console.log(err)
          ????????//?})
          ????}?catch?(err)?{
          ????????console.log(err)
          ????}
          }

          使用aync/await的話,catch能處理JSON.parse錯(cuò)誤:

          const?makeRequest?=?async?()?=>?{
          ????try?{
          ????????//?this?parse?may?fail
          ????????const?data?=?JSON.parse(await?getJSON())
          ????????console.log(data)
          ????}?catch?(err)?{
          ????????console.log(err)
          ????}
          }

          promise怎么實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用跟返回不同的狀態(tài)

          實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用:使用.then()或者.catch()方法之后會返回一個(gè)promise對象,可以繼續(xù)用.then()方法調(diào)用,再次調(diào)用所獲取的參數(shù)是上個(gè)then方法return的內(nèi)容

          1. promise的三種狀態(tài)是?fulfilled(已成功)/pengding(進(jìn)行中)/rejected(已拒絕)

          2. 狀態(tài)只能由 Pending --> Fulfilled 或者 Pending --> Rejected,且一但發(fā)生改變便不可二次修改;

          3. Promise 中使用?resolve?和?reject?兩個(gè)函數(shù)來更改狀態(tài);

          4. then 方法內(nèi)部做的事情就是狀態(tài)判斷:

          • 如果狀態(tài)是成功,調(diào)用成功回調(diào)函數(shù)
          • 如果狀態(tài)是失敗,調(diào)用失敗回調(diào)函數(shù)

          函數(shù)柯里化

          柯里化(Currying)?是把接收多個(gè)參數(shù)的原函數(shù)變換成接受一個(gè)單一參數(shù)(原來函數(shù)的第一個(gè)參數(shù)的函數(shù))并返回一個(gè)新的函數(shù),新的函數(shù)能夠接受余下的參數(shù),并返回和原函數(shù)相同的結(jié)果。

          1. 參數(shù)對復(fù)用
          2. 提高實(shí)用性
          3. 延遲執(zhí)行 只傳遞給函數(shù)一部分參數(shù)來調(diào)用它,讓它返回一個(gè)函數(shù)去處理剩下的參數(shù)??吕锘暮瘮?shù)可以延遲接收參數(shù),就是比如一個(gè)函數(shù)需要接收的參數(shù)是兩個(gè),執(zhí)行的時(shí)候必須接收兩個(gè)參數(shù),否則沒法執(zhí)行。但是柯里化后的函數(shù),可以先接收一個(gè)參數(shù)
          //?普通的add函數(shù)
          function?add(x,?y)?{
          ????return?x?+?y
          }

          //?Currying后
          function?curryingAdd(x)?{
          ????return?function?(y)?{
          ????????return?x?+?y
          ????}
          }

          add(1,?2)???????????//?3
          curryingAdd(1)(2)???//?3

          JS對象深克隆

          遞歸遍歷對象,解決循環(huán)引用問題

          解決循環(huán)引用問題,我們需要一個(gè)存儲容器存放當(dāng)前對象和拷貝對象的對應(yīng)關(guān)系(適合用key-value的數(shù)據(jù)結(jié)構(gòu)進(jìn)行存儲,也就是map),當(dāng)進(jìn)行拷貝當(dāng)前對象的時(shí)候,我們先查找存儲容器是否已經(jīng)拷貝過當(dāng)前對象,如果已經(jīng)拷貝過,那么直接把返回,沒有的話則是繼續(xù)拷貝。

          function?deepClone(target)?{
          ????const?map?=?new?Map()
          ????function?clone?(target)?{
          ????????if?(isObject(target))?{
          ????????????let?cloneTarget?=?isArray(target)???[]?:?{};
          ????????????if?(map.get(target))?{
          ????????????????return?map.get(target)
          ????????????}
          ????????????map.set(target,cloneTarget)
          ????????????for?(const?key?in?target)?{
          ????????????????cloneTarget[key]?=?clone(target[key]);
          ????????????}
          ????????????return?cloneTarget;
          ????????}?else?{
          ????????????return?target;
          ????????}
          ????}
          ????return?clone(target)
          };

          JS模塊化

          nodeJS里面的模塊是基于commonJS規(guī)范實(shí)現(xiàn)的,原理是文件的讀寫,導(dǎo)出文件要使用exports、module.exports,引入文件用require。每個(gè)文件就是一個(gè)模塊;每個(gè)文件里面的代碼會用默認(rèn)寫在一個(gè)閉包函數(shù)里面AMD規(guī)范則是非同步加載模塊,允許指定回調(diào)函數(shù),AMD?是?RequireJS?在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。

          AMD推崇依賴前置,?CMD推崇依賴就近。對于依賴的模塊AMD是提前執(zhí)行,CMD是延遲執(zhí)行。

          ES6中,我們可以使用?import?關(guān)鍵字引入模塊,通過?exprot?關(guān)鍵字導(dǎo)出模塊,但是由于ES6目前無法在瀏覽器中執(zhí)行,所以,我們只能通過babel將不被支持的import編譯為當(dāng)前受到廣泛支持的?require。

          CommonJs 和 ES6 模塊化的區(qū)別:

          1. CommonJS 模塊輸出的是一個(gè)值的拷貝,ES6 模塊輸出的是值的引用。
          2. CommonJS 模塊是運(yùn)行時(shí)加載,ES6 模塊是編譯時(shí)輸出接口。

          前端模塊化:CommonJS,AMD,CMD,ES6

          import 和 require 導(dǎo)入的區(qū)別

          import 的ES6 標(biāo)準(zhǔn)模塊;require 是 AMD規(guī)范引入方式;

          import是編譯時(shí)調(diào)用,所以必須放在文件開頭;是解構(gòu)過程 require是運(yùn)行時(shí)調(diào)用,所以require理論上可以運(yùn)用在代碼的任何地方;是賦值過程。其實(shí)require的結(jié)果就是對象、數(shù)字、字符串、函數(shù)等,再把require的結(jié)果賦值給某個(gè)變量

          異步加載JS方式

          1. 匿名函數(shù)自調(diào)動(dòng)態(tài)創(chuàng)建script標(biāo)簽加載js
          (function(){
          ????var?scriptEle?=?document.createElement("script");
          ????scriptEle.type?=?"text/javasctipt";
          ????scriptEle.async?=?true;
          ????scriptEle.src?=?"http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js";
          ????var?x?=?document.getElementsByTagName("head")[0];
          ????x.insertBefore(scriptEle,?x.firstChild);??
          ?})();
          1. async屬性
          //?async屬性規(guī)定一旦加載腳本可用,則會異步執(zhí)行
          <script?type="text/javascript"?src="xxx.js"?async="async"></script>
          1. defer屬性
          //?defer屬性規(guī)定是否對腳本執(zhí)行進(jìn)行延遲,直到頁面加載為止
          <script?type="text/javascript"?src="xxx.js"?defer="defer"></script>

          Set、Map、WeakSet、WeakMap

          Set對象可以存儲任何類型的數(shù)據(jù)。值是唯一的,沒有重復(fù)的值。

          Map對象保存鍵值對,任意值都可以成為它的鍵或值。

          WeakSet?結(jié)構(gòu)與?Set?類似,也是不重復(fù)的值的集合 .?WeakMap?對象是一組鍵值對的集合

          不同:WeakSet?的成員只能是對象,而不能是其他類型的值。WeakSet 不可遍歷。

          WeakMap只接受對象作為鍵名null除外),不接受其他類型的值作為鍵名。

          WeakMap的鍵名所指向的對象,不計(jì)入垃圾回收機(jī)制。

          call、apply

          call( this,a,b,c )?在第一個(gè)參數(shù)之后的,后續(xù)所有參數(shù)就是傳入該函數(shù)的值。apply( this,[a,b,c] )?只有兩個(gè)參數(shù),第一個(gè)是對象,第二個(gè)是數(shù)組,這個(gè)數(shù)組就是該函數(shù)的參數(shù)。

          共同之處:都可以用來代替另一個(gè)對象調(diào)用一個(gè)方法,將一個(gè)函數(shù)的對象上下文從初始的上下文改變?yōu)橛蓆hisObj指定的新對象。

          所謂防抖,就是指觸發(fā)事件后在 n 秒內(nèi)函數(shù)只能執(zhí)行一次所謂節(jié)流,就是指連續(xù)觸發(fā)事件但是在 n 秒中只執(zhí)行一次函數(shù)。

          addEventListener的第三個(gè)參數(shù)干嘛的,為true時(shí)捕獲,false時(shí)冒泡

          Object.prototype.toString.call()?判斷對象類型

          //?new?Set是實(shí)現(xiàn)數(shù)組去重,
          //?Array.from()把去重之后轉(zhuǎn)換成數(shù)組
          let?arr2?=?Array.from(new?Set(arr));

          詞法作用域與作用域鏈

          作用域規(guī)定了如何查找變量,也就是確定當(dāng)前執(zhí)行代碼對變量的訪問權(quán)限。

          ES5只有全局作用域沒和函數(shù)作用域,ES6增加塊級作用域

          暫時(shí)性死區(qū):在代碼塊內(nèi),使用?let?和?const?命令聲明變量之前,該變量都是不可用的,語法上被稱為暫時(shí)性死區(qū)。

          JavaScript 采用詞法作用域(lexical scoping),也就是靜態(tài)作用域。

          函數(shù)的作用域在函數(shù)定義的時(shí)候就決定了。

          當(dāng)查找變量的時(shí)候,會先從當(dāng)前上下文的變量對象中查找,如果沒有找到,就會從父級(詞法層面上的父級執(zhí)行上下文的變量對象中查找,一直找到全局上下文的變量對象,也就是全局對象。這樣由多個(gè)執(zhí)行上下文的變量對象構(gòu)成的鏈表就叫做作用域鏈。

          new關(guān)鍵字做了4件事:

          function?_new(constructor,?...arg)?{
          //?創(chuàng)建一個(gè)空對象
          ??var?obj?=?{};
          ??//?空對象的`__proto__`指向構(gòu)造函數(shù)的`prototype`,?為這個(gè)新對象添加屬性?
          ??obj.__proto__?=?constructor.prototype;?
          ??//?構(gòu)造函數(shù)的作用域賦給新對象
          ??var?res?=?constructor.apply(obj,?arg);?
          ??//?返回新對象.如果沒有顯式return語句,則返回this
          ??return?Object.prototype.toString.call(res)?===?'[object?Object]'???res?:?obj;?
          }

          不應(yīng)該使用箭頭函數(shù)一些情況:

          • 當(dāng)想要函數(shù)被提升時(shí)(箭頭函數(shù)是匿名的)
          • 要在函數(shù)中使用this/arguments時(shí),由于箭頭函數(shù)本身不具有this/arguments,因此它們?nèi)Q于外部上下文
          • 使用命名函數(shù)(箭頭函數(shù)是匿名的)
          • 使用函數(shù)作為構(gòu)造函數(shù)時(shí)(箭頭函數(shù)沒有構(gòu)造函數(shù))
          • 當(dāng)想在對象字面是以將函數(shù)作為屬性添加并在其中使用對象時(shí),因?yàn)樵蹅儫o法訪問?this?即對象本身。

          判斷數(shù)組的四種方法

          1. Array.isArray()?判斷

          2. instanceof?判斷: 檢驗(yàn)構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在對象的原型鏈中,返回一個(gè)布爾值。let a = []; a instanceof Array; //true

          3. constructor判斷: 實(shí)例的構(gòu)造函數(shù)屬性constructor指向構(gòu)造函數(shù)let a = [1,3,4]; a.constructor === Array;//true

          4. Object.prototype.toString.call()?判斷let a = [1,2,3]; Object.prototype.toString.call(a) === '[object Array]';//true

          TS有什么優(yōu)勢

          1. 靜態(tài)輸入:靜態(tài)類型化是一種功能,可以在開發(fā)人員編寫腳本時(shí)檢測錯(cuò)誤。

          2. 大型的開發(fā)項(xiàng)目:使用TypeScript工具來進(jìn)行重構(gòu)更變的容易、快捷。

          3. 更好的協(xié)作:類型安全是在編碼期間檢測錯(cuò)誤,而不是在編譯項(xiàng)目時(shí)檢測錯(cuò)誤。

          4. 更強(qiáng)的生產(chǎn)力:干凈的 ECMAScript 6 代碼,自動(dòng)完成和動(dòng)態(tài)輸入等因素有助于提高開發(fā)人員的工作效率。

          interface 和 type的區(qū)別

          • interface 只能定義對象類型。type聲明可以聲明任何類型。

          • interface 能夠聲明合并,兩個(gè)相同接口會合并。Type聲明合并會報(bào)錯(cuò)

          • type可以類型推導(dǎo)

          二、框架 Vue | React

          Vue3.0 新特性

          雙向數(shù)據(jù)綁定 Proxy

          代理,可以理解為在對象之前設(shè)置一個(gè)“攔截”,當(dāng)該對象被訪問的時(shí)候,都必須經(jīng)過這層攔截。意味著你可以在這層攔截中進(jìn)行各種操作。比如你可以在這層攔截中對原對象進(jìn)行處理,返回你想返回的數(shù)據(jù)結(jié)構(gòu)。

          ES6 原生提供 Proxy 構(gòu)造函數(shù),MDN上的解釋為:Proxy 對象用于定義基本操作的自定義行為(如屬性查找,賦值,枚舉,函數(shù)調(diào)用等)。

          const?p?=?new?Proxy(target,?handler);
          //target:?所要攔截的目標(biāo)對象(可以是任何類型的對象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理)
          //handler:一個(gè)對象,定義要攔截的行為

          const?p?=?new?Proxy({},?{
          ????get(target,?propKey)?{
          ????????return?'哈哈,你被我攔截了';
          ????}
          });

          console.log(p.name);

          新增的屬性,并不需要重新添加響應(yīng)式處理,因?yàn)?Proxy 是對對象的操作,只要你訪問對象,就會走到 Proxy 的邏輯中。

          Vue3 Composition API

          Vue3.x?推出了Composition APIsetup?是組件內(nèi)使用 Composition API的入口。setup?執(zhí)行時(shí)機(jī)是在?beforeCreate?之前執(zhí)行.

          reactive、ref 與 toRefs、isRef

          Vue3.x 可以使用reactive和ref來進(jìn)行數(shù)據(jù)定義。

          //?props?傳入組件對屬性
          //?context?一個(gè)上下文對象,包含了一些有用的屬性:attrs,parent,refs
          setup(props,?context)?{
          ??//?ref?定義數(shù)據(jù)
          ??const?year?=?ref(0);
          ??//?reactive?處理對象的雙向綁定
          ??const?user?=?reactive({?nickname:?"xiaofan",?age:?26,?gender:?"女"?});
          ??setInterval(()?=>?{
          ????year.value++;
          ????user.age++;
          ??},?1000);
          ??return?{
          ????year,
          ????//?使用toRefs,結(jié)構(gòu)解構(gòu)
          ????...toRefs(user),
          ??};
          },
          //?提供isRef,用于檢查一個(gè)對象是否是ref對象

          watchEffect 監(jiān)聽函數(shù)

          • watchEffect 不需要手動(dòng)傳入依賴
          • watchEffect 會先執(zhí)行一次用來自動(dòng)收集依賴
          • watchEffect 無法獲取到變化前的值, 只能獲取變化后的值

          computed可傳入get和set

          用于定義可更改的計(jì)算屬性

          const?plusOne?=?computed({
          ?get:?()?=>?count.value?+?1,
          ?set:?val?=>?{?count.value?=?val?-?1?}
          });

          使用TypeScript和JSX

          setup現(xiàn)在支持返回一個(gè)渲染函數(shù),這個(gè)函數(shù)返回一個(gè)JSX,JSX可以直接使用聲明在setup作用域的響應(yīng)式狀態(tài):

          export?default?{
          ?setup()?{
          ?const?count?=?ref(0);
          ?return?()?=>?(<div>{count.value}</div>);
          ?},
          };

          Vue 跟React 對比?

          相同點(diǎn):

          1. 都有虛擬DOM(Virtual DOM 是一個(gè)映射真實(shí)DOM的JavaScript對象)
          2. 都提供了響應(yīng)式和組件化的視圖組件。

          不同點(diǎn):Vue 是MVVM框架,雙向數(shù)據(jù)綁定,當(dāng)ViewModelModel進(jìn)行更新時(shí),通過數(shù)據(jù)綁定更新到View。

          React是一個(gè)單向數(shù)據(jù)流的庫,狀態(tài)驅(qū)動(dòng)視圖。State --> View --> New State --> New View?ui = render (data)

          模板渲染方式不同。React是通過JSX來渲染模板,而Vue是通過擴(kuò)展的HTML來進(jìn)行模板的渲染。

          組件形式不同,Vue文件里將HTML,JS,CSS組合在一起。react提供class組件和function組

          Vue封裝好了一些v-if,v-for,React什么都是自己實(shí)現(xiàn),自由度更高

          Vue 初始化過程,雙向數(shù)據(jù)綁定原理

          vue.js 則是采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個(gè)屬性的setter,getter,dep.addSub來收集訂閱的依賴,watcher監(jiān)聽數(shù)據(jù)的變化,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)。

          監(jiān)聽器Observer,用來劫持并監(jiān)聽所有屬性,如果有變動(dòng)的,就通知訂閱者。訂閱者Watcher,可以收到屬性的變化通知并執(zhí)行相應(yīng)的函數(shù),從而調(diào)用對應(yīng)update更新視圖。4cb90235cd184571ad8b5e516de8e399.webp

          v-model?指令,它能輕松實(shí)現(xiàn)表單輸入和應(yīng)用狀態(tài)之間的雙向綁定。

          computed:?支持緩存,只有依賴數(shù)據(jù)結(jié)果發(fā)生改變,才會重新進(jìn)行計(jì)算,不支持異步操作,如果一個(gè)屬性依賴其他屬性,多對一,一般用computed

          watch:?數(shù)據(jù)變,直接觸發(fā)相應(yīng)操作,支持異步,監(jiān)聽數(shù)據(jù)必須data中聲明過或者父組件傳遞過來的props中的數(shù)據(jù),當(dāng)數(shù)據(jù)變化時(shí),觸發(fā)其他操作,函數(shù)有兩個(gè)參數(shù)

          vue-router實(shí)現(xiàn)原理

          端路由簡介以及vue-router實(shí)現(xiàn)原理原理核心就是 更新視圖但不重新請求頁面。路徑之間的切換,也就是組件的切換。vue-router實(shí)現(xiàn)單頁面路由跳轉(zhuǎn)模式:hash模式、history模式。根據(jù)設(shè)置mode參數(shù)

          hash模式:通過錨點(diǎn)值的改變,根據(jù)不同的值,渲染指定DOM位置的不同數(shù)據(jù)。每一次改變#后的部分,都會在瀏覽器的訪問歷史中增加一個(gè)記錄,使用”后退”按鈕,就可以回到上一個(gè)位置。history模式:利用?window.history.pushState?API 來完成 URL 跳轉(zhuǎn)而無須重新加載頁面。

          vuex實(shí)現(xiàn)原理:

          Vue.use(vuex)會調(diào)用vuex的install方法

          beforeCreate鉤子前混入vuexInit方法,vuexInit方法實(shí)現(xiàn)了store注入vue組件實(shí)例,并注冊了vuex?store的引用屬性$store

          Vuexstate狀態(tài)是響應(yīng)式,是借助vuedata是響應(yīng)式,將state存入vue實(shí)例組件的data中;

          Vuexgetters則是借助vue的計(jì)算屬性computed實(shí)現(xiàn)數(shù)據(jù)實(shí)時(shí)監(jiān)聽。

          nextTick 的原理以及運(yùn)行機(jī)制?

          nextTick的源碼分析

          vue進(jìn)行DOM更新內(nèi)部也是調(diào)用nextTick來做異步隊(duì)列控制。只要觀察到數(shù)據(jù)變化,Vue 將開啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變。如果同一個(gè) watcher 被多次觸發(fā),只會被推入到隊(duì)列中一次。

          DOM至少會在當(dāng)前事件循環(huán)里面的所有數(shù)據(jù)變化完成之后,再統(tǒng)一更新視圖。而當(dāng)我們自己調(diào)用nextTick的時(shí)候,它就在更新DOM的microtask(微任務(wù)隊(duì)列)后追加了我們自己的回調(diào)函數(shù),

          從而確保我們的代碼在DOM更新后執(zhí)行,同時(shí)也避免了setTimeout可能存在的多次執(zhí)行問題。確保隊(duì)列中的微任務(wù)在一次事件循環(huán)前被執(zhí)行完畢。

          Vue 實(shí)現(xiàn)一個(gè)高階組件

          高階組件就是一個(gè)函數(shù),且該函數(shù)接受一個(gè)組件作為參數(shù),并返回一個(gè)新的組件。在不改變對象自身的前提下在程序運(yùn)行期間動(dòng)態(tài)的給對象添加一些額外的屬性或行為。

          //?高階組件(HOC)接收到的?props?應(yīng)該透傳給被包裝組件即直接將原組件prop傳給包裝組件
          //?高階組件完全可以添加、刪除、修改?props
          export?default?function?Console(BaseComponent)?{
          ??return?{
          ????props:?BaseComponent.props,
          ????mounted()?{
          ??????console.log("高階組件");
          ????},
          ????render(h)?{
          ??????console.log(this);
          ??????//?將?this.$slots?格式化為數(shù)組,因?yàn)?h?函數(shù)第三個(gè)參數(shù)是子節(jié)點(diǎn),是一個(gè)數(shù)組
          ??????const?slots?=?Object.keys(this.$slots)
          ????????.reduce((arr,?key)?=>?arr.concat(this.$slots[key]),?[])
          ????????.map((vnode)?=>?{
          ??????????vnode.context?=?this._self;?//?綁定到高階組件上,vm:解決具名插槽被作為默認(rèn)插槽進(jìn)行渲染
          ??????????return?vnode;
          ????????});
          ?
          ??????//?透傳props、透傳事件、透傳slots
          ??????return?h(
          ????????BaseComponent,
          ????????{
          ??????????on:?this.$listeners,
          ??????????attrs:?this.$attrs,?//?attrs?指的是那些沒有被聲明為?props?的屬性
          ??????????props:?this.$props,
          ????????},
          ????????slots
          ??????);
          ????},
          ??};
          }

          Vue.component()、Vue.use()、this.$xxx()

          Vue.component()方法注冊全局組件。

          • 第一個(gè)參數(shù)是自定義元素名稱,也就是將來在別的組件中使用這個(gè)組件的標(biāo)簽名稱。
          • 第二個(gè)參數(shù)是將要注冊的Vue組件。
          import?Vue?from?'vue';
          //?引入loading組件?
          import?Loading?from?'./loading.vue';
          //?將loading注冊為全局組件,在別的組件中通過<loading>標(biāo)簽使用Loading組件
          Vue.component('loading',?Loading);

          Vue.use注冊插件,這接收一個(gè)參數(shù)。這個(gè)參數(shù)必須具有install方法。Vue.use函數(shù)內(nèi)部會調(diào)用參數(shù)的install方法。

          • 如果插件沒有被注冊過,那么注冊成功之后會給插件添加一個(gè)installed的屬性值為true。Vue.use方法內(nèi)部會檢測插件的installed屬性,從而避免重復(fù)注冊插件。
          • 插件的install方法將接收兩個(gè)參數(shù),第一個(gè)是參數(shù)是Vue,第二個(gè)參數(shù)是配置項(xiàng)options。
          • 在install方法內(nèi)部可以添加全局方法或者屬性
          import?Vue?from?'vue';

          //?這個(gè)插件必須具有install方法
          const?plugin?=?{
          ??install?(Vue,?options)?{
          ????//?添加全局方法或者屬性
          ????Vue.myGlobMethod?=?function?()?{};
          ????//?添加全局指令
          ????Vue.directive();
          ????//?添加混入
          ????Vue.mixin();
          ????//?添加實(shí)例方法
          ????Vue.prototype.$xxx?=?function?()?{};
          ????//?注冊全局組件
          ????Vue.component()
          ??}
          }

          //?Vue.use內(nèi)部會調(diào)用plugin的install方法
          Vue.use(plugin);

          將Hello方法掛載到Vue的prototype上.

          import?Vue?from?'vue';
          import?Hello?from?'./hello.js';
          Vue.prototype.$hello?=?Hello;

          vue組件中就可以this.$hello('hello world')

          Vue父組件傳遞props數(shù)據(jù),子組件修改參數(shù)

          • 父子組件傳值時(shí),父組件傳遞的參數(shù),數(shù)組和對象,子組件接受之后可以直接進(jìn)行修改,并且父組件相應(yīng)的值也會修改??刂婆_中發(fā)出警告。
          • 如果傳遞的值是字符串,直接修改會報(bào)錯(cuò)。單向數(shù)據(jù)流,每次父級組件發(fā)生更新時(shí),子組件中所有的 prop 都將會刷新為最新的值。

          如果子組件想修改prop中數(shù)據(jù):

          1. 定義一個(gè)局部變量,使用prop的值賦值
          2. 定義一個(gè)計(jì)算屬性,處理prop的值并返回

          Vue父子組件生命周期執(zhí)行順序

          加載渲染過程

          父beforeCreate -> 父created -> 父beforeMount-> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

          子組件更新過程

          父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated

          父組件更新過程

          父beforeUpdate -> 父updated

          銷毀過程

          父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

          Vue 自定義指令

          自定義指令提供了幾個(gè)鉤子函數(shù):bind:指令第一次綁定到元素時(shí)調(diào)用inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用update:所在組件的 VNode 更新時(shí)調(diào)用

          使用slot后可以在子組件內(nèi)顯示插入的新標(biāo)簽

          三、webpack 及工程化

          webpack的生命周期,及鉤子

          compiler(整個(gè)生命周期 [k?m?pa?l?r]) 鉤子 https://webpack.docschina.org/api/compiler-hooks/compilation(編譯 [?kɑ?mp??le??n]) 鉤子

          compiler對象包含了Webpack 環(huán)境所有的的配置信息。這個(gè)對象在啟動(dòng) webpack 時(shí)被一次性建立,并配置好所有可操作的設(shè)置,包括 options,loader 和 plugin。當(dāng)在 webpack 環(huán)境中應(yīng)用一個(gè)插件時(shí),插件將收到此 compiler 對象的引用??梢允褂盟鼇碓L問 webpack 的主環(huán)境。

          compilation對象包含了當(dāng)前的模塊資源、編譯生成資源、變化的文件等。當(dāng)運(yùn)行webpack 開發(fā)環(huán)境中間件時(shí),每當(dāng)檢測到一個(gè)文件變化,就會創(chuàng)建一個(gè)新的 compilation,從而生成一組新的編譯資源。compilation 對象也提供了很多關(guān)鍵時(shí)機(jī)的回調(diào),以供插件做自定義處理時(shí)選擇使用。

          compiler代表了整個(gè)webpack從啟動(dòng)到關(guān)閉的生命周期,而compilation?只是代表了一次新的編譯過程

          webpack 編譯過程

          Webpack 的編譯流程是一個(gè)串行的過程,從啟動(dòng)到結(jié)束會依次執(zhí)行以下流程:

          1. 初始化參數(shù):從配置文件和 Shell 語句中讀取與合并參數(shù),得出最終的參數(shù);
          2. 開始編譯:用上一步得到的參數(shù)初始化?Compiler?對象,加載所有配置的插件,執(zhí)行對象的?run方法開始執(zhí)行編譯;
          3. 確定入口:根據(jù)配置中的?entry?找出所有的入口文件;
          4. 編譯模塊:從入口文件出發(fā),調(diào)用所有配置的?Loader?對模塊進(jìn)行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到所有入口依賴的文件都經(jīng)過了本步驟的處理;
          5. 完成模塊編譯:在經(jīng)過第4步使用?Loader?翻譯完所有模塊后,得到了每個(gè)模塊被翻譯后的最終內(nèi)容以及它們之間的依賴關(guān)系;
          6. 輸出資源:根據(jù)入口和模塊之間的依賴關(guān)系,組裝成一個(gè)個(gè)包含多個(gè)模塊的Chunk,再把每個(gè)?Chunk?轉(zhuǎn)換成一個(gè)單獨(dú)的文件加入到輸出列表,這步是可以修改輸出內(nèi)容的最后機(jī)會;
          7. 輸出完成:在確定好輸出內(nèi)容后,根據(jù)配置確定輸出的路徑和文件名,把文件內(nèi)容寫入到文件系統(tǒng)。

          優(yōu)化項(xiàng)目的webpack打包編譯過程

          1.構(gòu)建打點(diǎn):構(gòu)建過程中,每一個(gè)Loader?和?Plugin?的執(zhí)行時(shí)長,在編譯 JS、CSS 的 Loader 以及對這兩類代碼執(zhí)行壓縮操作的 Plugin上消耗時(shí)長 。一款工具:speed-measure-webpack-plugin

          2.緩存:大部分 Loader 都提供了cache?配置項(xiàng)。cache-loader?,將 loader 的編譯結(jié)果寫入硬盤緩存

          3.多核編譯happypack項(xiàng)目接入多核編譯,理解為happypack?將編譯工作灌滿所有線程

          4.抽離,webpack-dll-plugin?將這些靜態(tài)依賴從每一次的構(gòu)建邏輯中抽離出去,靜態(tài)依賴單獨(dú)打包,Externals將不需要打包的靜態(tài)資源從構(gòu)建邏輯中剔除出去,使用CDN 引用

          5.tree-shaking,雖然依賴了某個(gè)模塊,但其實(shí)只使用其中的某些功能。通過?tree-shaking,將沒有使用的模塊剔除,來達(dá)到刪除無用代碼的目的。

          首屏加載優(yōu)化

          路由懶加載:改為用import引用,以函數(shù)的形式動(dòng)態(tài)引入,可以把各自的路由文件分別打包,只有在解析給定的路由時(shí),才會下載路由組件;

          element-ui按需加載:引用實(shí)際上用到的組件 ;

          組件重復(fù)打包CommonsChunkPlugin配置來拆包,把使用2次及以上的包抽離出來,放進(jìn)公共依賴文件,首頁也有復(fù)用的組件,也會下載這個(gè)公共依賴文件;

          gzip: 拆完包之后,再用gzip做一下壓縮,關(guān)閉sourcemap。

          UglifyJsPlugin:??生產(chǎn)環(huán)境,壓縮混淆代碼,移除console代碼

          CDN部署靜態(tài)資源:靜態(tài)請求打在nginx時(shí),將獲取靜態(tài)資源的地址進(jìn)行重定向CDN內(nèi)容分發(fā)網(wǎng)絡(luò)

          移動(dòng)端首屏加載可以使用骨架屏,自定義loading,首頁單獨(dú)做服務(wù)端渲染。

          如何進(jìn)行前端性能優(yōu)化(21種優(yōu)化+7種定位方式)

          webpack 熱更新機(jī)制

          熱更新流程總結(jié):

          • 啟動(dòng)本地server,讓瀏覽器可以請求本地的靜態(tài)資源

          • 頁面首次打開后,服務(wù)端與客戶端通過 websocket建立通信渠道,把下一次的 hash 返回前端

          • 客戶端獲取到hash,這個(gè)hash將作為下一次請求服務(wù)端 hot-update.js 和 hot-update.json的hash

          • 修改頁面代碼后,Webpack 監(jiān)聽到文件修改后,開始編譯,編譯完成后,發(fā)送 build 消息給客戶端

          • 客戶端獲取到hash,成功后客戶端構(gòu)造hot-update.js script鏈接,然后插入主文檔

          • hot-update.js 插入成功后,執(zhí)行hotAPI 的 createRecord 和 reload方法,獲取到 Vue 組件的 render方法,重新 render 組件, 繼而實(shí)現(xiàn) UI 無刷新更新。

          webpack的 loader和plugin介紹,css-loader,style-loader的區(qū)別

          loader?它就是一個(gè)轉(zhuǎn)換器,將A文件進(jìn)行編譯形成B文件,

          plugin?,它就是一個(gè)擴(kuò)展器,來操作的是文件,針對是loader結(jié)束后,webpack打包的整個(gè)過程,它并不直接操作文件,會監(jiān)聽webpack打包過程中的某些節(jié)點(diǎn)(run, build-module, program)

          Babel?能把ES6/ES7的代碼轉(zhuǎn)化成指定瀏覽器能支持的代碼。

          css-loader?的作用是把 css文件進(jìn)行轉(zhuǎn)碼style-loader: 使用<style>css-loader內(nèi)部樣式注入到我們的HTML頁面

          先使用?css-loader轉(zhuǎn)碼,然后再使用?style-loader插入到文件

          如何編寫一個(gè)webpack的plugin?

          https://segmentfault.com/a/1190000037513682

          webpack 插件的組成:

          • 一個(gè) JS 命名函數(shù)或一個(gè)類(可以想下我們平時(shí)使用插件就是?new XXXPlugin()的方式)
          • 在插件類/函數(shù)的 (prototype) 上定義一個(gè) apply 方法。
          • 通過 apply 函數(shù)中傳入 compiler 并插入指定的事件鉤子,在鉤子回調(diào)中取到 compilation 對象
          • 通過 compilation 處理 webpack 內(nèi)部特定的實(shí)例數(shù)據(jù)
          • 如果是插件是異步的,在插件的邏輯編寫完后調(diào)用 webpack 提供的 callback

          為什么 Vite 啟動(dòng)這么快

          Webpack 會先打包,然后啟動(dòng)開發(fā)服務(wù)器,請求服務(wù)器時(shí)直接給予打包結(jié)果。

          而 Vite 是直接啟動(dòng)開發(fā)服務(wù)器,請求哪個(gè)模塊再對該模塊進(jìn)行實(shí)時(shí)編譯。

          Vite 將開發(fā)環(huán)境下的模塊文件,就作為瀏覽器要執(zhí)行的文件,而不是像 Webpack 那樣進(jìn)行打包合并

          由于 Vite 在啟動(dòng)的時(shí)候不需要打包,也就意味著不需要分析模塊的依賴、不需要編譯。因此啟動(dòng)速度非???。當(dāng)瀏覽器請求某個(gè)模塊時(shí),再根據(jù)需要對模塊內(nèi)容進(jìn)行編譯。

          你的腳手架是怎么做的

          使用?download-git-repo?下載倉庫代碼democommander:完整的?node.js?命令行解決方案。聲明program,使用.option()?方法來定義選項(xiàng)Inquirer.js:命令行用戶界面的集合。

          前端監(jiān)控

          前端監(jiān)控通常包括行為監(jiān)控(PV/UV,埋點(diǎn)接口統(tǒng)計(jì))、異常監(jiān)控性能監(jiān)控。

          一個(gè)監(jiān)控系統(tǒng),大致可以分為四個(gè)階段:日志采集、日志存儲、統(tǒng)計(jì)與分析報(bào)告和警告。

          錯(cuò)誤監(jiān)控

          Vue專門的錯(cuò)誤警告的方法?Vue.config.errorHandler,(Vue提供只能捕獲其頁面生命周期內(nèi)的函數(shù),比如created,mounted)

          Vue.config.errorHandler?=?function?(err)?{
          console.error(‘Vue.error’,err.stack)
          //?邏輯處理
          };

          框架:betterjs,fundebug(收費(fèi)) 捕獲錯(cuò)誤的腳本要放置在最前面,確??梢允占藉e(cuò)誤信息 方法:

          1. window.onerror()當(dāng)有js運(yùn)行時(shí)錯(cuò)誤觸發(fā)時(shí),onerror可以接受多個(gè)參數(shù)(message, source, lineno, colno, error)。
          2. window.addEventListener('error'), function(e) {}, true?會比window.onerror先觸發(fā),不能阻止默認(rèn)事件處理函數(shù)的執(zhí)行,但可以全局捕獲資源加載異常的錯(cuò)誤

          前端JS錯(cuò)誤捕獲--sourceMap

          如何監(jiān)控網(wǎng)頁崩潰?**崩潰和卡頓有何差別?**監(jiān)控錯(cuò)誤

          1. Service Worker 有自己獨(dú)立的工作線程,與網(wǎng)頁區(qū)分開,網(wǎng)頁崩潰了,Service Worker 一般情況下不會崩潰;

          2. Service Worker 生命周期一般要比網(wǎng)頁還要長,可以用來監(jiān)控網(wǎng)頁的狀態(tài);

            卡頓:加載中,渲染遇到阻塞

          性能監(jiān)控 && 性能優(yōu)化

          性能指標(biāo):

          • FP(首次繪制)
          • FCP(首次內(nèi)容繪制 First contentful paint)
          • LCP(最大內(nèi)容繪制時(shí)間 Largest contentful paint)
          • FPS(每秒傳輸幀數(shù))
          • TTI(頁面可交互時(shí)間 Time to Interactive)
          • HTTP?請求響應(yīng)時(shí)間
          • DNS?解析時(shí)間
          • TCP?連接時(shí)間

          性能數(shù)據(jù)采集需要使用?window.performance API?, ? JS庫?web-vitalsimport {getLCP} from 'web-vitals';

          ????//?重定向耗時(shí)
          ????redirect:?timing.redirectEnd?-?timing.redirectStart,
          ????//?DOM?渲染耗時(shí)
          ????dom:?timing.domComplete?-?timing.domLoading,
          ????//?頁面加載耗時(shí)
          ????load:?timing.loadEventEnd?-?timing.navigationStart,
          ????//?頁面卸載耗時(shí)
          ????unload:?timing.unloadEventEnd?-?timing.unloadEventStart,
          ????//?請求耗時(shí)
          ????request:?timing.responseEnd?-?timing.requestStart,
          ????//?獲取性能信息時(shí)當(dāng)前時(shí)間
          ????time:?new?Date().getTime(),
          ????//?DNS查詢耗時(shí)
          ????domainLookupEnd?-?domainLookupStart
          ?//?TCP鏈接耗時(shí)
          ????connectEnd?-?connectStart
          ?//?request請求耗時(shí)
          ????responseEnd?-?responseStart
          ?//?解析dom樹耗時(shí)
          ????domComplete?-?domInteractive
          ?//?白屏?xí)r間
          ????domloadng?-?fetchStart
          ?//?onload時(shí)間
          ????loadEventEnd?-?fetchStart

          性能優(yōu)化常用手段:緩存技術(shù)、 ? 預(yù)加載技術(shù)、 ? 渲染方案。

          1. 緩存?:主要有 cdn、瀏覽器緩存、本地緩存以及應(yīng)用離線包

          2. 預(yù)加載?:資源預(yù)拉?。╬refetch)則是另一種性能優(yōu)化的技術(shù)。通過預(yù)拉取可以告訴瀏覽器用戶在未來可能用到哪些資源。

          • prefetch支持預(yù)拉取圖片、腳本或者任何可以被瀏覽器緩存的資源。

            在head里 添加?<linkrel="prefetch"href="image.png">

          • prerender是一個(gè)重量級的選項(xiàng),它可以讓瀏覽器提前加載指定頁面的所有資源。

          • subresource可以用來指定資源是最高優(yōu)先級的。當(dāng)前頁面需要,或者馬上就會用到時(shí)。

          1. 渲染方案
          • 靜態(tài)渲染(SR)
          • 前端渲染(CSR)
          • 服務(wù)端渲染(SSR)
          • 客戶端渲染(NSR):NSR 數(shù)據(jù)請求,首屏數(shù)據(jù)請求和數(shù)據(jù)線上與 webview 的一個(gè)初始化和框架 JS 初始化并行了起來,大大縮短了首屏?xí)r間。

          8add811043d5b60a2dafba64727c6cc7.webp

          常見的六種設(shè)計(jì)模式以及應(yīng)用場景

          https://www.cnblogs.com/whu-2017/p/9471670.html

          觀察者模式的概念

          觀察者模式模式,屬于行為型模式的一種,它定義了一種一對多的依賴關(guān)系,讓多個(gè)觀察者對象同時(shí)監(jiān)聽某一個(gè)主題對象。這個(gè)主體對象在狀態(tài)變化時(shí),會通知所有的觀察者對象。

          發(fā)布訂閱者模式的概念

          發(fā)布-訂閱模式,消息的發(fā)送方,叫做發(fā)布者(publishers),消息不會直接發(fā)送給特定的接收者,叫做訂閱者。意思就是發(fā)布者和訂閱者不知道對方的存在。需要一個(gè)第三方組件,叫做信息中介,它將訂閱者和發(fā)布者串聯(lián)起來,它過濾和分配所有輸入的消息。換句話說,發(fā)布-訂閱模式用來處理不同系統(tǒng)組件的信息交流,即使這些組件不知道對方的存在。

          需要一個(gè)第三方組件,叫做信息中介,它將訂閱者和發(fā)布者串聯(lián)起來

          工廠模式??主要是為創(chuàng)建對象提供了接口。場景:在編碼時(shí)不能預(yù)見需要?jiǎng)?chuàng)建哪種類的實(shí)例。

          代理模式 命令模式

          單例模式

          保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。(window)

          四、Http 及瀏覽器相關(guān)

          七層網(wǎng)絡(luò)模型

          應(yīng)用層、表示層、會話層、傳輸層、網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層、物理層

          TCP:面向連接、傳輸可靠(保證數(shù)據(jù)正確性,保證數(shù)據(jù)順序)、用于傳輸大量數(shù)據(jù)(流模式)、速度慢,建立連接需要開銷較多(時(shí)間,系統(tǒng)資源) 。(應(yīng)用場景:HTP,HTTP,郵件)

          UDP:面向非連接、傳輸不可靠、用于傳輸少量數(shù)據(jù)(數(shù)據(jù)包模式)、速度快 ,可能丟包(應(yīng)用場景:即時(shí)通訊)

          是否連接?????面向連接?????面向非連接
          傳輸可靠性???可靠????????不可靠
          應(yīng)用場合????少量數(shù)據(jù)????傳輸大量數(shù)據(jù)

          https

          客戶端先向服務(wù)器端索要公鑰,然后用公鑰加密信息,服務(wù)器收到密文后,用自己的私鑰解密。服務(wù)器公鑰放在數(shù)字證書中。

          url到加載渲染全過程

          1. DNS域名解析。
          2. TCP三次握手,建立接連。
          3. 發(fā)送HTTP請求報(bào)文。
          4. 服務(wù)器處理請求返回響應(yīng)報(bào)文。
          5. 瀏覽器解析渲染頁面。
          6. 四次揮手,斷開連接。

          DNS 協(xié)議提供通過域名查找 IP地址,或逆向從?IP地址反查域名的服務(wù)。DNS 是一個(gè)網(wǎng)絡(luò)服務(wù)器,我們的域名解析簡單來說就是在 DNS 上記錄一條信息記錄。

          TCP 三次握手,四次揮手:握手揮手都是客戶端發(fā)起,客戶端結(jié)束。三次握手與四次揮手詳解

          負(fù)載均衡:請求在進(jìn)入到真正的應(yīng)用服務(wù)器前,可能還會先經(jīng)過負(fù)責(zé)負(fù)載均衡的機(jī)器,它的作用是將請求合理地分配到多個(gè)服務(wù)器上,轉(zhuǎn)發(fā)HTTP請求;同時(shí)具備具備防攻擊等功能??煞譃镈NS負(fù)載均衡,HTTP負(fù)載均衡,IP負(fù)載均衡,鏈路層負(fù)載均衡等。

          Web Server:請求經(jīng)過前面的負(fù)載均衡后,將進(jìn)入到對應(yīng)服務(wù)器上的 Web Server,比如Apache、Tomcat

          反向代理是工作在 HTTP 上的,一般都是?Nginx。全國各地訪問baidu.com就肯定要通過代理訪問,不可能都訪問百度的那臺服務(wù)器。?(VPN正向代理,代理客戶端)

          瀏覽器解析渲染過程:返回的html傳遞到瀏覽器后,如果有g(shù)zip會先解壓,找出文件編碼格式,外鏈資源的加載 html從上往下解析,遇到j(luò)s,css停止解析渲染,直到j(luò)s執(zhí)行完成。解析HTML,構(gòu)建DOM樹 解析CSS,生成CSS規(guī)則樹 合并DOM樹和CSS規(guī)則,生成render樹去渲染

          不會引起DOM樹變化,頁面布局變化,改變元素樣式的行為叫重繪

          引起DOM樹結(jié)構(gòu)變化,頁面布局變化的行為叫回流

          GUI渲染線程負(fù)責(zé)渲染瀏覽器界面HTML元素,當(dāng)界面需要?重繪(Repaint)?或由于某種操作引發(fā)?回流(reflow)?時(shí),該線程就會執(zhí)行。在Javascript引擎運(yùn)行腳本期間,GUI渲染線程都是處于掛起狀態(tài)的,也就是說被”凍結(jié)”了. 直到JS程序執(zhí)行完成,才會接著執(zhí)行。因此如果JS執(zhí)行的時(shí)間過長,這樣就會造成頁面的渲染不連貫,導(dǎo)致頁面渲染加載阻塞的感覺。JavaScript是可操縱DOM的,如果在修改這些元素屬性同時(shí)渲染界面,渲染前后元素?cái)?shù)據(jù)可能不一致

          GPU繪制多進(jìn)程的瀏覽器:主控進(jìn)程,插件進(jìn)程,GPU,tab頁(瀏覽器內(nèi)核)多線程的瀏覽器內(nèi)核:每一個(gè)tab頁面可以看作是瀏覽器內(nèi)核進(jìn)程,然后這個(gè)進(jìn)程是多線程的。

          它有幾大類子線程:

          • GUI線程
          • JS引擎線程
          • 事件觸發(fā)線程
          • 定時(shí)器線程
          • HTTP請求線程

          http1 跟HTTP2

          http2

          多路復(fù)用:相同域名多個(gè)請求,共享同一個(gè)TCP連接,降低了延遲

          請求優(yōu)先級:給每個(gè)request設(shè)置優(yōu)先級

          二進(jìn)制傳輸;之前是用純文本傳輸

          數(shù)據(jù)流:數(shù)據(jù)包不是按順序發(fā)送,對數(shù)據(jù)包做標(biāo)記。每個(gè)請求或回應(yīng)的所有數(shù)據(jù)包成為一個(gè)數(shù)據(jù)流,

          服務(wù)端推送:可以主動(dòng)向客戶端發(fā)送消息。

          頭部壓縮:減少包的大小跟數(shù)量

          HTTP/1.1 中的管道( pipeline)傳輸中如果有一個(gè)請求阻塞了,那么隊(duì)列后請求也統(tǒng)統(tǒng)被阻塞住了 HTTP/2 多請求復(fù)用一個(gè)TCP連接,一旦發(fā)生丟包,就會阻塞住所有的 HTTP 請求。HTTP/3 把 HTTP 下層的 TCP 協(xié)議改成了?UDP!http1 keep alive 串行傳輸

          http 中的 keep-alive 有什么作用

          響應(yīng)頭中設(shè)置?keep-alive?可以在一個(gè) TCP 連接上發(fā)送多個(gè) http?請求

          瀏覽器緩存策略

          強(qiáng)緩存:cache-control;no-cache max-age=<10000000>;expires;其中Cache-Conctrol的優(yōu)先級比Expires高;

          控制強(qiáng)制緩存的字段分別是Expires和Cache-Control,如果客戶端的時(shí)間小于Expires的值時(shí),直接使用緩存結(jié)果。

          協(xié)商緩存:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的優(yōu)先級比Last-Modified / 首次請求,服務(wù)器會在返回的響應(yīng)頭中加上Last-Modified字段,表示資源最后修改的時(shí)間。

          瀏覽器再次請求時(shí),請求頭中會帶上If-Modified-Since字段,比較兩個(gè)字段,一樣則證明資源未修改,返回304,否則重新返回資源,狀態(tài)碼為200;

          垃圾回收機(jī)制:

          標(biāo)記清除:進(jìn)入執(zhí)行環(huán)境的變量都被標(biāo)記,然后執(zhí)行完,清除這些標(biāo)記跟變量。查看變量是否被引用。

          引用計(jì)數(shù):會記錄每個(gè)值被引用的次數(shù),當(dāng)引用次數(shù)變成0后,就會被釋放掉。

          前端安全

          同源策略:如果兩個(gè) URL 的協(xié)議、域名和端口都相同,我們就稱這兩個(gè) URL 同源。因?yàn)闉g覽器有cookies。

          • XSS:跨站腳本攻擊(Cross Site Scripting)?input, textarea等所有可能輸入文本信息的區(qū)域,輸入<script src="http://惡意網(wǎng)站"></script>等,提交后信息會存在服務(wù)器中 。

          • CSRF:跨站請求偽造 。引誘用戶打開黑客的網(wǎng)站,在黑客的網(wǎng)站中,利用用戶的登錄狀態(tài)發(fā)起的跨站請求。

            A站點(diǎn)imgsrc=B站點(diǎn)的請求接口,可以訪問;解決:referer攜帶請求來源

            訪問該頁面后,表單自動(dòng)提交, 模擬完成了一次POST操作,發(fā)送post請求

            解決:后端注入一個(gè)隨機(jī)串Cookie,前端請求取出隨機(jī)串添加傳給后端。

          • http 劫持:電信運(yùn)營商劫持

          • SQL注入

          • 點(diǎn)擊劫持:誘使用戶點(diǎn)擊看似無害的按鈕(實(shí)則點(diǎn)擊了透明?iframe中的按鈕) ,解決后端請求頭加一個(gè)字段?X-Frame-Options

          • 文件上傳漏洞?:服務(wù)器未校驗(yàn)上傳的文件

          五、CSS 及 HTML

          什么是BFC(塊級格式化上下文)、IFC(內(nèi)聯(lián)格式化上下文 )、FFC(彈性盒模型)

          BFC(Block formatting context),即塊級格式化上下文,它作為HTML頁面上的一個(gè)獨(dú)立渲染區(qū)域,只有區(qū)域內(nèi)元素參與渲染,且不會影響其外部元素。簡單來說,可以將 BFC 看做是一個(gè)“圍城”,外面的元素進(jìn)不來,里面的元素出不去(互不干擾)。

          一個(gè)決定如何渲染元素的容器 ,渲染規(guī)則 :

          • 1、內(nèi)部的塊級元素會在垂直方向,一個(gè)接一個(gè)地放置。

          • 2、塊級元素垂直方向的距離由margin決定。屬于同一個(gè)BFC的兩個(gè)相鄰塊級元素的margin會發(fā)生重疊。

          • 3、對于從左往右的格式化,每個(gè)元素(塊級元素與行內(nèi)元素)的左邊緣,與包含塊的左邊緣相接觸,(對于從右往左的格式化則相反)。即使包含塊中的元素存在浮動(dòng)也是如此,除非其中元素再生成一個(gè)BFC。

          • 4、BFC的區(qū)域不會與浮動(dòng)元素重疊。

          • 5、BFC是一個(gè)隔離的獨(dú)立容器,容器里面的子元素和外面的元素互不影響。

          • 6、計(jì)算BFC容器的高度時(shí),浮動(dòng)元素也參與計(jì)算。

          形成BFC的條件:

          1、浮動(dòng)元素,float 除 none 以外的值;

          2、定位元素,position(absolute,fixed);

          3、display 為以下其中之一的值 inline-block,table-cell,table-caption;

          4、overflow 除了 visible 以外的值(hidden,auto,scroll);

          BFC 一般用來解決以下幾個(gè)問題

          • 邊距重疊問題
          • 消除浮動(dòng)問題
          • 自適應(yīng)布局問題

          flex: 0 1 auto;?是什么意思?

          元素會根據(jù)自身寬高設(shè)置尺寸。它會縮短自身以適應(yīng)?flex?容器,但不會伸長并吸收flex?容器中的額外自由空間來適應(yīng)?flex?容器?。水平的主軸(main axis)和垂直的交叉軸(cross axis)幾個(gè)屬性決定按哪個(gè)軸的排列方向

          • flex-grow:?0??一個(gè)無單位數(shù)(): 它會被當(dāng)作<flex-grow>的值。
          • flex-shrink:?1??一個(gè)有效的**寬度(width)**值: 它會被當(dāng)作?<flex-basis>的值。
          • flex-basis:?auto??關(guān)鍵字none,autoinitial.

          放大比例、縮小比例、分配多余空間之前占據(jù)的主軸空間。

          避免CSS全局污染

          1. scoped 屬性
          2. css in js
          const?styles?=?{
          ??bar:?{
          ????backgroundColor:?'#000'
          ??}
          }
          const?example?=?(props)=>{
          ??<div?style={styles.bar}?/>
          }
          1. CSS Modules
          2. 使用less,盡量少使用全局對選擇器
          //?選擇器上>要記得寫,免得污染所有ul下面的li
          ul{
          ??>li{
          ??? color:red;
          ??}
          }

          CSS Modules

          阮一峰 CSS Modules

          CSS Modules是一種構(gòu)建步驟中的一個(gè)進(jìn)程。通過構(gòu)建工具來使指定class達(dá)到scope的過程。

          CSS Modules?允許使用::global(.className)的語法,聲明一個(gè)全局規(guī)則。凡是這樣聲明的class,都不會被編譯成哈希字符串:local(className): 做 localIdentName 規(guī)則處理,編譯唯一哈希類名。

          CSS Modules使用特點(diǎn):

          • 不使用選擇器,只使用 class 名來定義樣式
          • 不層疊多個(gè) class,只使用一個(gè) class 把所有樣式定義好
          • 不嵌套class

          盒子模型和?box-sizing?屬性

          width: 160px; padding: 20px; border: 8px solid orange;標(biāo)準(zhǔn) box-sizing:?content-box;?元素的總寬度 = 160 + 202 + 82; IE的?border-box:總寬度160

          margin/padding百分比的值時(shí) ,基于父元素的寬度和高度的。

          css繪制三角形

          1. 通過border 處理
          //?border?處理
          .class?{
          ????width:?0;
          ????height:?0;
          ????border-left:?50px?solid?transparent;
          ????border-right:?50px?solid?transparent;
          ????border-bottom:?100px?solid?red;
          }
          //?寬高+border
          div?{
          ????width:?50px;
          ????height:?50px;
          ????border:?2px?solid?orange;
          }
          1. clip-path裁剪獲得
          div{
          ?clip-path:?polygon(0?100%,?50%?0,?100%?100%);
          }
          1. 漸變linear-gradient 實(shí)現(xiàn)
          div?{
          ??width:?200px;
          ??height:?200px;
          ??background:linear-gradient(to?bottom?right,?#fff?0%,?#fff?49.9%,?rgba(148,88,255,1)?50%,rgba(185,88,255,1)?100%);
          }

          CSS實(shí)現(xiàn)了三角形后如何給三角形添加陰影

          ???

          CSS兩列布局的N種實(shí)現(xiàn)

          兩列布局分為兩種,一種是左側(cè)定寬、右側(cè)自適應(yīng),另一種是兩列都自適應(yīng)(即左側(cè)寬度由子元素決定,右側(cè)補(bǔ)齊剩余空間)。

          1. 左側(cè)定寬、右側(cè)自適應(yīng)如何實(shí)現(xiàn)
          //?兩個(gè)元素都設(shè)置dislpay:inline-block
          .left?{
          ????display:?inline-block;
          ????width:?100px;
          ????height:?200px;
          ????background-color:?red;
          ????vertical-align:?top;
          }
          .right?{
          ????display:?inline-block;
          ????width:?calc(100%?-?100px);
          ????height:?400px;
          ????background-color:?blue;
          ????vertical-align:?top;
          }
          //?兩個(gè)元素設(shè)置浮動(dòng),右側(cè)自適應(yīng)元素寬度使用calc函數(shù)計(jì)算
          .left{
          ????float:?left;
          ????width:?100px;
          ????height:?200px;
          ????background-color:?red;
          }
          .right{
          ????float:?left;
          ????width:?calc(100%?-?100px);
          ????height:?400px;
          ????background-color:?blue;
          }
          //?父元素設(shè)置displayflex,自適應(yīng)元素設(shè)置flex:1
          .box{
          ????height:?600px;
          ????width:?100%;
          ????display:?flex;
          }
          .left{
          ????width:?100px;
          ????height:?200px;
          ????background-color:?red;
          }
          .right{
          ????flex:?1;
          ????height:?400px;
          ????background-color:?blue;
          }
          //?父元素相對定位,左側(cè)元素絕對定位,右側(cè)自適應(yīng)元素設(shè)置margin-left的值大于定寬元素的寬度
          .left{
          ????position:?absolute;
          ????width:?100px;
          ????height:?200px;
          ????background-color:?red;
          }
          .right{
          ????margin-left:?100px;
          ????height:?400px;
          ????background-color:?blue;
          }
          1. 左右兩側(cè)元素都自適應(yīng)
          //?flex布局?同上
          //?父元素設(shè)置displaygrid;?grid-template-columns:auto?1fr;(這個(gè)屬性定義列寬,auto關(guān)鍵字表示由瀏覽器自己決定長度。fr是一個(gè)相對尺寸單位,表示剩余空間做等分)grid-gap:20px(行間距)
          .parent{
          ????display:grid;
          ????grid-template-columns:auto?1fr;
          ????grid-gap:20px
          }?
          .left{
          ????background-color:?red;
          ????height:?200px;
          }
          .right{
          ????height:300px;
          ????background-color:?blue;
          }
          //?浮動(dòng)+BFC???父元素設(shè)置overflow:hidden,左側(cè)定寬元素浮動(dòng),右側(cè)自適應(yīng)元素設(shè)置overflow:auto創(chuàng)建BFC
          .box{
          ????height:?600px;
          ????width:?100%;
          ????overflow:?hidden;
          }
          .left{
          ????float:?left;
          ????width:?100px;
          ????height:?200px;
          ????background-color:?red;
          }
          .right{
          ????overflow:?auto;
          ????height:?400px;
          ????background-color:?blue;
          }

          CSS三列布局

          1. float布局:左邊左浮動(dòng),右邊右浮動(dòng),中間margin:0 100px;

          2. Position布局: 左邊left:0; 右邊right:0; 中間left: 100px; right: 100px;

          3. table布局: 父元素 display: table; 左右 width: 100px; 三個(gè)元素display: table-cell;

          4. 彈性(flex)布局:父元素 display: flex; 左右 width: 100px;

          5. 網(wǎng)格(gird)布局:

          //?gird提供了?gird-template-columnsgrid-template-rows屬性讓我們設(shè)置行和列的高、寬
          .div{
          ????width:?100%;
          ????display:?grid;
          ????grid-template-rows:?100px;
          ????grid-template-columns:?300px?auto?300px;
          }

          六、常見場景及問題

          app與H5 如何通訊交互的?

          //?兼容IOS和安卓
          callMobile(parameters,messageHandlerName)?{
          ??//handlerInterface由iOS addScriptMessageHandler與andorid addJavascriptInterface 代碼注入而來。
          ??if?(/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent))?{
          ????//?alert('ios')
          ????window.webkit.messageHandlers[messageHandlerName].postMessage(JSON.stringify(parameters))
          ??}?else?{
          ????//?alert('安卓')
          ????//安卓傳輸不了js?json對象,只能傳輸string
          ????window.webkit[messageHandlerName](JSON.stringify(parameters))
          ??}
          }

          由app將原生方法注入到window上供js調(diào)用

          messageHandlerName?約定的通信方法parameters?需要傳入的參數(shù)

          移動(dòng)端適配方案

          rem是相對于HTML的根元素em相對于父級元素的字體大小。VW,VH?屏幕寬度高度的高分比

          //按照寬度375圖算,?1rem?=?100px;
          (function?(win,?doc)?{
          ???function?changeSize()?{
          ?????doc.documentElement.style.fontSize?=?doc.documentElement.clientWidth?/?3.75?+?'px';
          ????console.log(100?*?doc.documentElement.clientWidht?/?3.75)
          ???}
          ???changeSize();
          ???win.addEventListener('resize',?changeSize,?false);

          })(window,?document);

          七、代碼編程相關(guān)

          實(shí)現(xiàn)發(fā)布訂閱

          /*?Pubsub?*/
          function?Pubsub(){
          ??//存放事件和對應(yīng)的處理方法
          ??this.handles?=?{};
          }

          Pubsub.prototype?=?{
          ??//傳入事件類型type和事件處理handle
          ??on:?function?(type,?handle)?{
          ????if(!this.handles[type]){
          ??????this.handles[type]?=?[];
          ????}
          ????this.handles[type].push(handle);
          ??},
          ??emit:?function?()?{
          ????//通過傳入?yún)?shù)獲取事件類型
          ????//將arguments轉(zhuǎn)為真數(shù)組
          ????var?type?=?Array.prototype.shift.call(arguments);
          ????if(!this.handles[type]){
          ??????return?false;
          ????}
          ????for?(var?i?=?0;?i?<?this.handles[type].length;?i++)?{
          ??????var?handle?=?this.handles[type][i];
          ??????//執(zhí)行事件
          ??????handle.apply(this,?arguments);
          ????}
          ??},
          ??off:?function?(type,?handle)?{
          ????handles?=?this.handles[type];
          ????if(handles){
          ??????if(!handle){
          ????????handles.length?=?0;//清空數(shù)組
          ??????}else{
          ??????for?(var?i?=?0;?i?<?handles.length;?i++)?{
          ????????var?_handle?=?handles[i];
          ????????if(_handle?===?handle){
          ??????????//從數(shù)組中刪除
          ??????????handles.splice(i,1);
          ????????}
          ??????}
          ????}
          ??}??
          }

          promise怎么實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用跟返回不同的狀態(tài)

          //?MyPromise.js

          //?先定義三個(gè)常量表示狀態(tài)
          const?PENDING?=?'pending';
          const?FULFILLED?=?'fulfilled';
          const?REJECTED?=?'rejected';

          //?新建?MyPromise?類
          class?MyPromise?{
          ??constructor(executor){
          ????//?executor?是一個(gè)執(zhí)行器,進(jìn)入會立即執(zhí)行
          ????//?并傳入resolve和reject方法
          ????executor(this.resolve,?this.reject)
          ??}

          ??//?儲存狀態(tài)的變量,初始值是?pending
          ??status?=?PENDING;

          ??// resolve和reject為什么要用箭頭函數(shù)?
          ??//?如果直接調(diào)用的話,普通函數(shù)this指向的是window或者undefined
          ??//?用箭頭函數(shù)就可以讓this指向當(dāng)前實(shí)例對象
          ??//?成功之后的值
          ??value?=?null;
          ??//?失敗之后的原因
          ??reason?=?null;

          ??//?更改成功后的狀態(tài)
          ??resolve?=?(value)?=>?{
          ????//?只有狀態(tài)是等待,才執(zhí)行狀態(tài)修改
          ????if?(this.status?===?PENDING)?{
          ??????//?狀態(tài)修改為成功
          ??????this.status?=?FULFILLED;
          ??????//?保存成功之后的值
          ??????this.value?=?value;
          ????}
          ??}

          ??//?更改失敗后的狀態(tài)
          ??reject?=?(reason)?=>?{
          ????//?只有狀態(tài)是等待,才執(zhí)行狀態(tài)修改
          ????if?(this.status?===?PENDING)?{
          ??????//?狀態(tài)成功為失敗
          ??????this.status?=?REJECTED;
          ??????//?保存失敗后的原因
          ??????this.reason?=?reason;
          ????}
          ??}

          ????then(onFulfilled,?onRejected)?{
          ????//?判斷狀態(tài)
          ????if?(this.status?===?FULFILLED)?{
          ??????//?調(diào)用成功回調(diào),并且把值返回
          ??????onFulfilled(this.value);
          ????}?else?if?(this.status?===?REJECTED)?{
          ??????//?調(diào)用失敗回調(diào),并且把原因返回
          ??????onRejected(this.reason);
          ????}
          ??}

          }

          實(shí)現(xiàn)Promise.all

          //?Promise.all
          function?all(promises)?{
          ??let?len?=?promises.length,?res?=?[]
          ??if?(len)?{
          ????return?new?Promise(function?(resolve,?reject)?{
          ????????for(let?i=0;?i?<?len;?i++){
          ????????????let?promise?=?promises[i];
          ????????????promise.then(response?=>?{
          ????????????????res[i]?=?response

          ????????????????//?當(dāng)返回結(jié)果為最后一個(gè)時(shí)
          ????????????????if?(res.length?===?len)?{
          ????????????????????resolve(res)
          ????????????????}

          ????????????},?error?=>?{
          ????????????????reject(error)
          ????????????})

          ????????}
          ????})
          }

          對象數(shù)組轉(zhuǎn)換成tree數(shù)組

          >?將entries?按照?level?轉(zhuǎn)換成?result?數(shù)據(jù)結(jié)構(gòu)

          const?entries?=?[
          ????{
          ????????"province":?"浙江",?"city":?"杭州",?"name":?"西湖"
          ????},?{
          ????????"province":?"四川",?"city":?"成都",?"name":?"錦里"
          ????},?{
          ????????"province":?"四川",?"city":?"成都",?"name":?"方所"
          ????},?{
          ????????"province":?"四川",?"city":?"阿壩",?"name":?"九寨溝"
          ????}
          ];
          ?
          const?level?=?["province",?"city",?"name"];

          const??result?=?[
          ?{
          ??value:'浙江',
          ??children:[
          ???{
          ????value:'杭州',
          ????children:[
          ?????{
          ??????value:'西湖'
          ?????}
          ????]
          ???}
          ??]
          ?},
          ?{
          ??value:'四川',
          ??children:[
          ???{
          ????value:'成都',
          ????children:[
          ?????{
          ??????value:'錦里'
          ?????},
          ?????{
          ??????value:'方所'
          ?????}
          ????]
          ???},
          ???{
          ????value:'阿壩',
          ????children:[
          ?????{
          ??????value:'九寨溝'
          ?????}
          ????]
          ???}
          ??]
          ?},
          ]

          思路:涉及到樹形數(shù)組,采用遞歸遍歷的方式

          function?transfrom(list,?level)?{
          ??const?res?=?[];
          ??list.forEach(item?=>?{
          ????pushItem(res,?item,?0);
          ??});

          ??function?pushItem(arr,?obj,?i)?{
          ????const?o?=?{
          ??????value:?obj[level[i]],
          ??????children:?[],
          ????};
          ????//?判斷傳入數(shù)組里是否有value等于要傳入的項(xiàng)
          ????const?hasItem?=?arr.find(el?=>?el.value?===?obj[level[i]]);
          ????let?nowArr;
          ????if(hasItem)?{
          ??????//?存在,則下一次遍歷傳入存在項(xiàng)的children
          ??????nowArr?=?hasItem.children;
          ????}else{
          ??????//?不存在?壓入arr,下一次遍歷傳入此項(xiàng)的children
          ??????arr.push(o);
          ??????nowArr?=?o.children;
          ????}
          ????if(i?===?level.length?-?1)?delete?o.children;
          ????i++;
          ????if(i?<?level.length)?{
          ??????//?遞歸進(jìn)行層級的遍歷
          ??????pushItem(nowArr,?obj,?i);
          ????}
          ??}
          }

          transfrom(entries,?level);

          JS instanceof 方法原生實(shí)現(xiàn)

          簡單用法

          function?Fn?()?{}
          const?fn?=?new?Fn()
          fn?instanceof?Fn??//?true

          實(shí)現(xiàn)如下:

          //?left?instanceof?right
          function?_instanceof(left,?right)?{
          ??//?構(gòu)造函數(shù)原型
          ??const?prototype?=?right.prototype
          ??//?實(shí)列對象屬性,指向其構(gòu)造函數(shù)原型
          ??left?=?left.__proto__
          ??//?查實(shí)原型鏈
          ??while?(true)?{
          ????//?如果為null,說明原型鏈已經(jīng)查找到最頂層了,真接返回false
          ????if?(left?===?null)?{
          ??????return?false
          ????}
          ????//?查找到原型
          ????if?(prototype?===?left){
          ??????return?true
          ????}
          ????//?繼續(xù)向上查找
          ????left?=?left.__proto__
          ??}
          }

          const?str?=?"abc"
          _instanceof(str,?String)?//?true

          編程題

          將有同樣元素的數(shù)組進(jìn)行合并

          //?例如:
          const?arr?=?[
          ????['a',?'b',?'c'],
          ????['a',?'d'],
          ????['d',?'e'],
          ????['f',?'g'],
          ????['h',?'g'],
          ????['i']
          ]
          //?運(yùn)行后的返回結(jié)果是:
          [
          ????['a',?'b',?'c',?'d',?'e'],
          ????['f',?'g',?'h'],
          ????['i']
          ]
          //?思路一:
          const?arr?=?[['a',?'b',?'c'],?['a',?'d'],?['d',?'e'],?['f',?'g'],?['h',?'g'],?['i']]
          function?transform(arr){
          ????let?res?=?[]
          ????arr?=?arr.map(el?=>?el.sort()).sort()
          ????const?item?=?arr.reduce((pre,?cur)?=>?{
          ??????if?(cur.some(el?=>?pre?&&?pre.includes(el)))?{
          ????????pre?=?pre.concat(cur)
          ??????}?else?{
          ????????res.push(pre)
          ????????pre?=?cur
          ??????}
          ??????return?[...new?Set(pre)]
          ????})
          ????res.push(item)
          ????return?res;
          }
          transform(arr)
          //?console.log(transform(arr));

          //?思路二:?

          function?r?(arr)?{
          ??const?map?=?new?Map()
          ??arr.forEach((array,?index)?=>?{
          ????const?findAlp?=?array.find((v)?=>?map.get(v))
          ????if?(findAlp)?{
          ??????const?set?=?map.get(findAlp)
          ??????array.forEach((alp)?=>?{
          ????????set.add(alp)
          ????????const?findAlp2?=?map.get(alp)
          ????????if?(findAlp2?&&?findAlp2?!==?set)?{
          ??????????for(const?v?of?findAlp2.values()){
          ????????????set.add(v)
          ????????????map.set(v,?set)
          ??????????}
          ????????}
          ????????map.set(alp,?set)
          ??????})
          ????}?else?{
          ??????const?set?=?new?Set(arr[index])
          ??????array.forEach((alp)?=>?map.set(alp,?set))
          ????}
          ??})
          ??const?set?=?new?Set()
          ??const?ret?=?[]
          ??for?(const?[key,?value]?of?map.entries())?{
          ????if?(set.has(value))?continue
          ????set.add(value)
          ????ret.push([...value])
          ??}
          ??return?ret
          }

          本文來自作者@幾米陽光

          https://juejin.cn/post/6991724298197008421



          八、總結(jié)

          在我們閱讀完官方文檔后,我們一定會進(jìn)行更深層次的學(xué)習(xí),比如看下框架底層是如何運(yùn)行的,以及源碼的閱讀。? ? 這里廣東靚仔給下一些小建議:
          • 在看源碼前,我們先去官方文檔復(fù)習(xí)下框架設(shè)計(jì)理念、源碼分層設(shè)計(jì)
          • 閱讀下框架官方開發(fā)人員寫的相關(guān)文章
          • 借助框架的調(diào)用棧來進(jìn)行源碼的閱讀,通過這個(gè)執(zhí)行流程,我們就完整的對源碼進(jìn)行了一個(gè)初步的了解
          • 接下來再對源碼執(zhí)行過程中涉及的所有函數(shù)邏輯梳理一遍

          關(guān)注我,一起攜手進(jìn)階

          歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~

          瀏覽 51
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  av天天av天天爽 | 黄片艹逼 | av在线青青草 | 老女人毛片 | 黄色视频在线免费观看视频 |