手撕釘釘前端考試卷,offer,拿來吧你~

前言

出卷人:子弈(阿里釘釘團隊)
姓名:程序員思語
班級:掘金
年級:前端3年級

子弈大佬出面試指南的本意也是幫助你們掃盲,讓你們去了解一些可能本身沒有接觸過或者本身不知道的東西,這裏面的大部分題目實際上是在不斷深刻溝通的語境裏產生的,也不影響對面試者的面試判斷,純粹屬於那種面着面着以爲不錯,以爲他可以答上來而給予的加分一問,不是你們理解的刁難人的意思。javascript

因爲文章字數限制,不少題目沒法詳盡和深刻回答(純粹是懶,就是想一夜趕出來文章,嘿!),本人也只是簡略寫一下,並且非標準答案,好比問瀏覽器URL請求流程,那至少能跟面試官扯幾個小時。有些題目不算是題目又或者比較籠統,很差回答,就略過,你們看看就圖一樂,別當真。非標準答案,css

歡迎留言,接受任何批評和建議~html

題目

1.列舉你所瞭解的計算機存儲設備類型?

答:前端

  • 隨機存儲器 RAM

SRAM、DRAM(SDRAM、RDRAM、CDRAM 等)vue

  • 只讀存儲器 ROM

MROM、PROM、EPROM、EEPROMjava

2.通常代碼存儲在計算機的哪一個設備中?代碼在 CPU 中是如何運行的?

答: 1)易失性執行以前,咱們的代碼主要存儲在內存中。 ②CPU讀取內存中的數據並放在寄存器內,將寄存器中的數據寫入內存並進行有序的四則運算、相關指令,在此過程當中,寄存器主要用於存放計算數據,運算器負責操做寄存器中的數據。node

3.什麼是指令和指令集?

答: 指令通常是指機器指令,是計算機可完成一個獨立計算邏輯所要執行的的命令;一臺常規的計算機的全部指令的集合,就是該計算機的指令集。react

4.JavaScript 是如何運行的?解釋型語言和編譯型語言的差別是什麼?

答: ①JS代碼->解析成 AST (期間伴隨詞法分析、語法分析)->生成字節碼(V8)->生成機器碼(編譯器)jquery

②不少資料會說,JavaScript、Python、Ruby都是"解釋型語言",是經過解釋器來實現的。這麼說其實很容易引發誤解:語言通常只會定義其抽象語義,而不會強制性要求採用某種實現方式。webpack

例如說C通常被認爲是「編譯型語言」,但C的解釋器也是存在的,例如Ch。一樣,C++也有解釋器版本的實現,例如Cint。

通常被稱爲「解釋型語言」的是主流實現爲解釋器的語言,但並非說它就沒法編譯。例如說常常被認爲是「解釋型語言」的Scheme就有好幾種編譯器實現,其中率先支持R6RS規範的大部份內容的是Ikarus,支持在x86上編譯Scheme;它最終不是生成某種虛擬機的字節碼,而是直接生成x86機器碼。

解釋器就是個黑箱,輸入是源碼,輸出就是輸入程序的執行結果,對用戶來講中間沒有獨立的「編譯」步驟。這很是抽象,內部是怎麼實現的都不要緊,只要能實現語義就行。你能夠寫一個C語言的解釋器,裏面只是先用普通的C編譯器把源碼編譯爲in-memory image,而後直接調用那個image去獲得運行結果;用戶拿過去,發現直接輸入源碼能夠獲得源程序對應的運行結果就知足需求了,無需在乎解釋器這個「黑箱子」裏究竟是什麼。

實際上不少解釋器內部是以「編譯器+虛擬機」的方式來實現的,先經過編譯器將源碼轉換爲AST或者字節碼,而後由虛擬機去完成實際的執行。所謂「解釋型語言」並非不用編譯,而只是不須要用戶顯式去使用編譯器獲得可執行代碼而已。

這道題扯多了,確定有掘金大佬來槓我。個人觀點是若是一種語言的主流實現是解釋器,其內部是編譯器+虛擬機,而虛擬機又是採用解釋方式實現的,或者內部實現是編譯器+樹遍歷解釋器,那它就是名副其實的「解釋型語言」。若是內部用的虛擬機是用編譯方式實現的,其實跟廣泛印象中的"解釋器"仍是挺不一樣的。

能夠舉這樣一個例子:ActionScript 3,通常都被認爲是「解釋型語言」對吧?但這種觀點究竟是把FlashPlayer總體當作一個解釋器,於是AS3是"解釋型語言"」"呢?仍是認爲FlashPlayer中的虛擬機採用解釋執行方案,於是AS3是解釋型語言呢?

5.簡單描述一下 Babel 的編譯過程?

答: 首先,Babel的做用是 從一種源碼到另外一種源碼,充當轉換編譯器的做用,能夠簡述爲 解析(解析JS代碼)->轉換(解析和修改AST)->重建(將修改後的AST轉換成另外一種JS代碼)

6.JavaScript 中的數組和函數在內存中是如何存儲的?

答: ①數組,JS裏的數組主要就是 以連續內存形式存儲的FixedArray、以哈希表形式存儲的HashTable

②函數,函數屬於引用數據類型,存儲在堆中,在棧內存中只是存了一個地址來表示對堆內存中的引用。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中得到實體。

7.瀏覽器和 Node.js 中的事件循環機制有什麼區別?

答: ①瀏覽器中的事件循環: macrotasks(宏任務):

  • script(總體代碼)
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI rendering
  • event listner

microtasks(微任務):

  • process.nextTick
  • Promises
  • Object.observe
  • MutationObserver

在瀏覽器裏,每當一個被監聽的事件發生時,事件監聽器綁定的相關任務就會被添加進回調隊列。經過事件產生的任務是異步任務,常見的事件任務包括:

  • 用戶交互事件產生的事件任務,好比輸入操做;
  • 計時器產生的事件任務,好比setTimeout;
  • 異步請求產生的事件任務,好比 HTTP 請求。

主線程運行的時候,會產生堆(heap)和棧(stack),其中堆爲內存、棧爲函數調用棧。咱們能看到,Event Loop 負責執行代碼、收集和處理事件以及執行隊列中的子任務,具體包括如下過程。

  • JavaScript 有一個主線程和調用棧,全部的任務最終都會被放到調用棧等待主線程執行。
  • 同步任務會被放在調用棧中,按照順序等待主線程依次執行。
  • 主線程以外存在一個回調隊列,回調隊列中的異步任務最終會在主線程中以調用棧的方式運行。
  • 同步任務都在主線程上執行,棧中代碼在執行的時候會調用瀏覽器的 API,此時會產生一些異步任務。
  • 異步任務會在有告終果(好比被監聽的事件發生時)後,將異步任務以及關聯的回調函數放入回調隊列中。
  • 調用棧中任務執行完畢後,此時主線程處於空閒狀態,會從回調隊列中獲取任務進行處理。
  • 上述過程會不斷重複,這就是 JavaScript 的運行機制,稱爲事件循環機制(Event Loop)。

②NodeJs中的事件循環:

  • timersj階段:這個階段執行timer(setTimeout、setInterval)的回調
  • I/O callbacks:執行一些系統調用錯誤,好比網絡通訊的錯誤回調
  • idle,prepare:僅node內部使用
  • poll:獲取新的I/O事件, 適當的條件下node將阻塞在這裏
  • check:執行 setImmediate() 的回調
  • close callbacks:執行 socket 的 close 事件回調

③區別: 瀏覽器環境下,microtask的任務隊列是每一個macrotask執行完以後執行。而在Node.js中,microtask會在事件循環的各個階段之間執行,也就是一個階段執行完畢,就會去執行microtask隊列的任務。若是是node11版本一旦執行一個階段裏的一個宏任務(setTimeout,setInterval和setImmediate)就馬上執行微任務隊列,這就跟瀏覽器端運行一致。

setTimeout(()=>{
    console.log('timer1')
    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)
setTimeout(()=>{
    console.log('timer2')
    Promise.resolve().then(function() {
        console.log('promise2')
    })
}, 0)

// 瀏覽器環境:
timer1=>promise1=>timer2=>promise2

// node V11以後
timer1=>promise1=>timer2=>promise2

// node 10及其以前
timer1=>promise1=>timer2=>promise2 (若是是第二個定時器還未在完成隊列中)
timer1=>timer2=>promise1=>promise2 (若是是第二個定時器已經在完成隊列中)
複製代碼
8.ES6 Modules 相對於 CommonJS 的優點是什麼?

答:

  • CommonJS和ES6 Module均可以對引入的對象進行賦值,即對對象內部屬性的值進行改變;
  • CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。即ES6 Module只存只讀,不能改變其值,具體點就是指針指向不能變;
  • CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。
  • CommonJS 模塊的require()是同步加載模塊,ES6 模塊的import命令是異步加載,有一個獨立的模塊依賴的解析階段。
  • import 的接口是 read-only(只讀狀態),不能修改其變量值。 即不能修改其變量的指針指向,但能夠改變變量內部指針指向,能夠對 commonJS 對從新賦值(改變指針指向),可是對 ES6 Module 賦值會編譯報錯。

優點: CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完纔會生成。而 ES6 Modules不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成

9.高級程序設計語言是如何編譯成機器語言的?

答: 高級語言代碼->解析成 AST (期間伴隨詞法分析、語法分析)->生成字節碼(V8)->生成機器碼(編譯器)

10.編譯器通常由哪幾個階段組成?數據類型檢查通常在什麼階段進行?

答: 編譯器通常由4個階段工做完成:

  • Parse 階段:V8 引擎負責將 JS 代碼轉換成 AST(抽象語法樹);

  • Ignition 階段:解釋器將 AST 轉換爲字節碼,解析執行字節碼也會爲下一個階段優化編譯提供須要的信息;

  • TurboFan 階段:編譯器利用上個階段收集的信息,將字節碼優化爲能夠執行的機器碼;

  • Orinoco 階段:垃圾回收階段,將程序中再也不使用的內存空間進行回收。

數據類型檢查通常在 Parse 階段以前 就進行了,由於在生成AST以前 就要進行語法分析,提取出句子的結構。廣義來講輸入通常是程序的源碼,輸出通常是語法樹(syntax tree,也叫parse tree等)或抽象語法樹(abstract syntax tree,AST)。進一步剝開來,廣義的解析器裏通常會有掃描器(scanner,也叫tokenizer或者lexical analyzer,詞法分析器),以及狹義的解析器(parser,也叫syntax analyzer,語法分析器)。掃描器的輸入通常是文本,通過詞法分析,輸出是將文本切割爲單詞的流。狹義的解析器輸入是單詞的流,通過語法分析,輸出是語法樹或者精簡過的AST。

11.編譯過程當中虛擬機的做用是什麼?

答:虛擬機(VM),其意義是實現高級語言的語義。VM既然被稱爲「機器」,通常認爲輸入是知足某種指令集架構(instruction set architecture,ISA)的指令序列,中間轉換爲目標ISA的指令序列並加以執行,輸出爲程序的執行結果的,就是VM。源與目標ISA能夠是同一種,這是所謂 same-ISA VM。 虛擬機 並非神奇的就能執行代碼了,它也得采用某種方式去實現輸入程序的語義,而且一樣有幾種選擇:「編譯」,例如微軟的.NET中的CLR;「解釋」,例如CPython、CRuby 1.9,許多老的JavaScript引擎等;也有介於二者之間的混合式,例如Sun的JVM,HotSpot。若是採用編譯方式,VM會把輸入的指令先轉換爲某種能被底下的系統直接執行的形式(通常就是native code),而後再執行之;若是採用解釋方式,則VM會把輸入的指令逐條直接執行。

12.什麼是中間代碼(IR),它的做用是什麼?

答: IR是由LLVM生成的中間代碼,做用是優化編譯器或VM,使優化後的機器代碼執行效率更高,同時避免緩存編譯後的二進制代碼佔用更多的內存。。

13.什麼是交叉編譯?

答: 是指是在一個平臺上生成另外一個平臺上的可執行代碼。

14.發佈 / 訂閱模式和觀察者模式的區別是什麼?

答: 在觀察者模式中,被觀察者一般會維護一個觀察者列表。當被觀察者的狀態發生改變時,就會通知觀察者。

在發佈訂閱模式中,具體發佈者會動態維護一個訂閱者的列表:可在運行時根據程序須要開始或中止發佈給對應訂閱者的事件通知。

區別在於發佈者自己並不維護訂閱列表(它不會像觀察者同樣主動維護一個列表),它會將工做委派給具體發佈者(至關於祕書,任何人想知道個人事情,直接問個人祕書就能夠了);訂閱者在接收到發佈者的消息後,會委派具體的訂閱者來進行相關的處理。

15.裝飾器模式通常會在什麼場合使用?

答: 裝飾器模式通常是指容許動態地向一個現有的對象添加新的功能,同時又不改變其結構,至關於對現有的對象進行了一個包裝。

使用場景不少,好比之前寫jQ項目,能夠本身快速動態拓展jQ上面的方法,或者vue的自定義指令,主要是但願經過繼承的方式擴展老舊功能。

16.談談你對大型項目的代碼解耦設計理解?什麼是 Ioc?通常 DI 採用什麼設計模式實現?

答: ①代碼解耦、必定要按模塊劃分而不是按功能劃分

  • 各個模塊的生命週期(初始化、銷燬)統一由框架進行管理:經過提供通用類Disposable,統一管理相關資源的註冊和銷燬。

  • 模塊間不直接引入和調用,而是經過聲明依賴的方式,從框架中獲取相應的服務並使用。

  • 不直接使用全局事件進行通訊,而是經過訂閱具體服務的方式來處理:經過使用一樣的方式this._register()註冊事件和訂閱事件,將事件相關資源的處理統一掛載到dispose()方法中

②各個部分各個模塊開發職責的仔細拆分 ③代碼開發儘快組件化、提升可複用性,避免業務邏輯過分耦合臃腫,最終難以拓展

Ioc是指依賴注入,簡單理解就是藉助於"第三方"實現具備依賴關係的對象之間的解耦。通常使用代理模式。

17.列舉你所瞭解的編程範式?

答:聲明式、命令式、函數式

18.什麼是面向切面(AOP)的編程?

答:面向切面編程是面向對象中的一種方式而已。在代碼執行過程當中,動態嵌入其餘代碼,叫作面向切面編程。

19.什麼是函數式編程?什麼是響應式編程?什麼是函數響應式編程?

答: 函數式編程是面向數學的抽象,關心數據(代數結構)之間的映射關係。函數式編程將計算描述爲一種表達式求值。

響應式編程是一種基於數據流和變化傳遞的聲明式的編程範式。

函數響應式編程是一種混合體,響應式編程思想爲體, 函數式編程思想爲用。

20.如何實現一個上中下三行佈局,頂部和底部最小高度是 100px,中間自適應?

答:

<div class="layout">
  <div class="top">top</div>
  <div class="content">content</div>
  <div class="bottom">bottom</div>
</div>
複製代碼
html,
body {
  padding: 0;
  margin: 0;
  height: 100%;
  /*定義頁面總體高度爲100%,重要*/
}
.layout {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}
.top {
  height: 100px;
  background: red;
}
.content {
  flex: 1;
  background: yellow;
}
.bottom {
  height: 100px;
  background: blue;
}
複製代碼
21.如何判斷一個元素 CSS 樣式溢出,從而能夠選擇性的加 title 或者 Tooltip?

答: 能夠用元素的scrollHeight屬性和clientHeight屬性來判斷, 當scrollHeight大於clientHeight的時候,元素就是能夠垂直滾動的;若是檢測水平滾動的話,能夠用scrollWidth和clientWidth。

22.如何讓 CSS 元素左側自動溢出(... 溢出在左側)?

答: 左側寬度自動增加,右側寬度自動增加而且不可溢出省略。當左側文字長度超出的時候,左側文字溢出省略。 在 css 有個 direction 屬性,把文本方向設置爲從右向左:direction: rtl

/* css */
.footer {
  width: 300px;
  height: 20px;
  display: flex;
  overflow: hidden;
}
.left {
  background: #3cc8b4;
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 50px;
}
.right {
  background: #9bc;
  max-width: 250px;
}
.right-ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
複製代碼
/* html */
<div class="footer">
   <div class="left">
     leftleftleftleftleftleftleftleftleftleftleftleftleft
  </div>
  <div class="right">
    <div class="right-ellipsis">
      rightrightrightrightrightrightrightrightright
    </div>
  </div>
</div>
複製代碼
23.什麼是沙箱?瀏覽器的沙箱有什麼做用?

答: 沙箱設計的目的是爲了讓不可信的代碼運行在必定的環境中,從而限制這些代碼訪問隔離區以外的資源。

24.如何處理瀏覽器中表單項的密碼自動填充問題?

答: 表單中當input是password類型時,打開瀏覽器會自動填充瀏覽器存儲的密碼,在input中加入autocomplete="new-password"便可解決。之因此new-password可以解決off失效的緣由是autocomplete屬性的有效值只有on和off,默認值是on,若是autocomplete的屬性是除on和off外的值,那麼就是個無效值,那麼瀏覽器就會放棄對該屬性的執行。

<input type="password" name="password" placeholder="請輸入密碼" autocomplete="new-password"/>
複製代碼
25.Hash 和 History 路由的區別和優缺點?

答: hash 路由模式的實現主要是基於下面幾個特性:

  • URL 中 hash 值只是客戶端的一種狀態,也就是說當向服務器端發出請求時,hash 部分不會被髮送;
  • hash 值的改變,都會在瀏覽器的訪問歷史中增長一個記錄。所以咱們能經過瀏覽器的回退、前進按鈕控制hash 的切換;
  • 能夠經過 a 標籤,並設置 href 屬性,當用戶點擊這個標籤後,URL 的 hash 值會發生改變;或者使用  JavaScript 來對 loaction.hash 進行賦值,改變 URL 的 hash 值;
  • 咱們能夠使用 hashchange 事件來監聽 hash 值的變化,從而對頁面進行跳轉(渲染)。

history 路由模式的實現主要基於存在下面幾個特性:

  • pushState 和 repalceState 兩個 API 來操做實現 URL 的變化 ;
  • 咱們能夠使用 popstate 事件來監聽 url 的變化,從而對頁面進行跳轉(渲染);
  • history.pushState() 或 history.replaceState() 不會觸發 popstate 事件,這時咱們須要手動觸發頁面跳轉(渲染)。
26.JavaScript 中的 const 數組能夠進行 push 操做嗎?爲何?

答: 能夠,也能夠進行splice()操做。

const聲明建立一個值的只讀引用。但這並不意味着它所持有的值是不可變的,只是變量標識符不能從新分配。例如,在引用內容是對象的狀況下,這意味着能夠改變對象的內容(例如,其參數)。

27.JavaScript 中對象的屬性描述符有哪些?分別有什麼做用?

答:

  • Configurable(可配置性)

可配置性決定是否能夠使用delete刪除屬性,以及是否能夠修改屬性描述符的特性,默認值爲true

  • Enumerable(可枚舉性)

可枚舉性決定屬性是否出如今對象的屬性枚舉中,好比是否能夠經過for-in循環返回該屬性,默認值爲true

  • Writable(可寫性)

可寫性決定是否能夠修改屬性的值,默認值爲true

  • Value(屬性值)

屬性值包含這個屬性的數據值,讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。默認值爲undefined

  • getter

在讀取屬性時調用的函數。默認值爲undefined

  • setter

在寫入屬性時調用的函數。默認值爲undefined

28.JavaScript 中 console 有哪些 api ?

答: 我只用過console.clear()、console.log()、console.info()、console.warn()、console.error()、console.time()、console.timeEnd()。其餘的不知道,我也不經常使用

29.簡單對比一下 Callback、Promise、Generator、Async 幾個異步 API 的優劣?

答: 首先callback不是異步API,它是早年JS異步編程實現的一種手段。

Promise是社區爲了解決回調地獄的問題在ES6版本提出的一種解決方案;

Generator也是一種異步編程解決方案,它最大的特色就是能夠交出函數的執行權,Generator 函數能夠看出是異步任務的容器,須要暫停的地方,都用 yield 語法來標註;

Async/await是 ES7 中提出的新的異步解決方案,async 是 Generator 函數的語法糖,async/await 的優勢是代碼清晰(不像使用 Promise 的時候須要寫不少 then 的方法鏈)。async/await 不只僅是 JS 的異步編程的一種方式,其可讀性也接近於同步代碼,讓人更容易理解。

30.Object.defineProperty 有哪幾個參數?各自都有什麼做用?

答: 首先它的用法是 Object.defineProperty(object, propertyname, descriptor)

  • object 必需。 要在其上添加或修改屬性的對象。 這多是一個本機 JavaScript對象(即用戶定義的對象或內置對象)或 DOM 對象。
  • propertyname 必需。 一個包含屬性名稱的字符串。
  • descriptor 必需。 屬性描述符。 它能夠針對數據屬性或訪問器屬性。

它內部的descriptor參數以下:

  • value

屬性的值,默認爲 undefined。

  • writable

該屬性是否可寫,若是設置成 false,則任何對該屬性改寫的操做都無效(但不會報錯),對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。

  • configurable

若是爲false,則任未嘗試刪除目標屬性或修改屬性如下特性(writable, configurable, enumerable)的行爲將被無效化,對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。 。

  • enumerable

是否能在for-in循環中遍歷出來或在Object.keys中列舉出來。對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。

  • get

一旦目標對象訪問該屬性,就會調用這個方法,並返回結果。默認爲 undefined。

  • set

一旦目標對象設置該屬性,就會調用這個方法。默認爲 undefined。

31.Object.defineProperty 和 ES6 的 Proxy 有什麼區別?

答: Proxy的優點以下

  • Proxy能夠直接監聽整個對象而非屬性。
  • Proxy能夠直接監聽數組的變化。
  • Proxy有13中攔截方法,如ownKeys、deleteProperty、has 等是 Object.defineProperty 不具有的。
  • Proxy返回的是一個新對象,咱們能夠只操做新的對象達到目的,而Object.defineProperty只能遍歷對象屬性直接修改;
  • Proxy作爲新標準將受到瀏覽器產商重點持續的性能優化,也就是傳說中的新標準的性能紅利。

Object.defineProperty 的優點以下

  • 兼容性好,支持 IE9,而 Proxy 的存在瀏覽器兼容性問題,並且沒法用 polyfill 磨平。

Object.defineProperty 不足在於:

  • Object.defineProperty 只能劫持對象的屬性,所以咱們須要對每一個對象的每一個屬性進行遍歷。
  • Object.defineProperty不能監聽數組。是經過重寫數據的那7個能夠改變數據的方法來對數組進行監聽的。
  • Object.defineProperty 也不能對 es6 新產生的 Map,Set 這些數據結構作出監聽。
  • Object.defineProperty也不能監聽新增和刪除操做,經過 Vue.set()和 Vue.delete來實現響應式的。
32.ES6 中 Symbol、Map、Decorator 的使用場景有哪些?或者你在哪些庫的源碼裏見過這些 API 的使用?

答: 使用場景太多了,業務上也每天用,略。

33.爲何要使用 TypeScript ? TypeScript 相對於 JavaScript 的優點是什麼?

答: 首先,不必定非要用TS,大型業務產品、多人協做寫大堆的業務代碼不適合TS。

優點: 1.爲JavaScript、IDE和實踐(如靜態檢查)提供了高效的開發工具。(主要) 2.其餘的好比強大的類型系統,泛型支持、模塊支持等等(次要)

34.TypeScript 中 const 和 readonly 的區別?枚舉和常量枚舉的區別?接口和類型別名的區別?

答: const 和 readonly 的區別:

  • const是一個編譯期常量, readonly是一個運行時常量
  • const只能聲明基元類型,枚舉類型,字符串類型。readonly則無限制
  • const天生爲靜態數據,無需再添加static標識
  • readonly是運行時變量,只能賦值一次。特例是能夠定義時賦值一次,構造函數中再賦值一次

枚舉和常量枚舉的區別: 常量枚舉經過在枚舉上使用 const 修飾符來定義,常量枚舉不一樣於常規的枚舉,他們會在編譯階段被刪除。常量枚舉成員在使用的地方會被內聯進來,之因此能夠這麼作是由於,常量枚舉不容許包含計算成員;如上例所示,在運行時是沒有 Size 變量的,所以常量枚舉會帶來一個對性能的提高。

接口和類型別名的區別:

  • 類型別名能夠用於其它類型 (聯合類型、元組類型、基本類型(原始值)),interface不支持
  • nterface 能夠屢次定義 並被視爲合併全部聲明成員 type 不支持
  • type 能使用 in 關鍵字生成映射類型,但 interface 不行。
  • 默認導出方式不一樣
35.TypeScript 中 any 類型的做用是什麼?

答: Any就是任意類型,能夠將 TypeScript 進化成強大的 AnyScript。

36.TypeScript 中 any、never、unknown 和 void 有什麼區別?

答: any 顧名思義就是任意類型。 never 表示永不存在的值的類型。 unknown 表示未知類型,即寫代碼的時候還不清楚會獲得怎樣的數據類型,它能被賦值爲任何類型,但不能被賦值給除了 any 和 unknown 以外的其餘類型,同時,不容許執行 unknown 類型變量的方法(any 能夠)。 void 表示無任何類型,正好與 any 相反,沒有類型,若是是函數則應沒有返回值或者返回 undefined

37.TypeScript 中 interface 能夠給 Function / Array / Class(Indexable)作聲明嗎?

答: 能夠,interface 可以描述 JavaScript 對象的任何形式,包括函數。

interface 也能夠被 class 類 implements,這裏至關於聲明瞭一個 interface 包含了各類屬性,須要 class 去實現,注意給類自己聲明類型,其實就是給構造器進行類型聲明,不能添加其餘屬性。

38.TypeScript 中能夠使用 String、Number、Boolean、Symbol、Object 等給類型作聲明嗎?

答: 能夠

39.TypeScript 中的 this 和 JavaScript 中的 this 有什麼差別?

答: this 沒法在未聲明的狀況下使用,在編寫函數是須要在函數裏首位聲明this

40.TypeScript 中使用 Unions 時有哪些注意事項?

答: 聯合類型表示取值能夠爲多種類型中的一種,當 TypeScript 不肯定一個聯合類型的變量究竟是哪一個類型的時候,咱們只能訪問此聯合類型的全部類型裏共有的屬性或方法(交集)。

41.TypeScript 如何設計 Class 的聲明?

答: TypeScript 類型聲明很是靈活,這也意味着一千個莎士比亞就能寫出一千個哈姆雷特。在團隊協做中,爲了更好的可維護性, 咱們應該儘量地踐行如下3條原則:

  • 泛型優於聯合類型

第一,類型定義使 getSmallPet變得侷限。從代碼邏輯看,它的做用是返回一個不下蛋的動物,返回的類型指向的是Fish或Bird。但我若是隻想在一羣鳥中挑出一個不下蛋的鳥呢?經過調用這個方法,我只能獲得一個 多是Fish、或者是Bird的神奇生物。

第二,代碼重複、難以擴展。好比,我想再增長一個烏龜,我必須找到全部相似 Fish | Bird 的地方,而後把它修改成 Fish | Bird | Turtle

第三,類型簽名沒法提供邏輯相關性。咱們再審視一下類型簽名,徹底沒法看出這裏爲何是 Fish | Bird 而不是其餘動物,它們兩個到底和邏輯有什麼關係纔可以被放在這裏

  • 善用typeof推導優於自定義類型
  • 善用內置工具函數優於重複聲明
42.TypeScript 中如何聯合枚舉類型的 Key?

答: 用 mapped type,用完以後不能加額外的屬性,用類型並運算解決。

type Props = {
    [key in Link]: U;
} & { type: string;}
複製代碼
43.TypeScript 中 ?.、??、!.、_、** 等符號的含義?

答: ?:表示該屬性或參數爲可選項 !:表示強制解析(告訴typescript編譯器,這裏必定有值),變量後使用 !:表示類型推斷排除null、undefined ?? 若是??運算符左側的表達式求值爲undefined或null,則返回其右側的值;不然,返回其左側的值

44.TypeScript 中預約義的有條件類型有哪些?

答:

Exclude<T, U> -- 從T中剔除能夠賦值給U的類型。
Extract<T, U> -- 提取T中能夠賦值給U的類型。
NonNullable<T> -- 從T中剔除nullundefined。
ReturnType<T> -- 獲取函數返回值類型。
InstanceType<T> -- 獲取構造函數類型的實例類型。
複製代碼
45.簡單介紹一下 TypeScript 模塊的加載機制?

答: Typescrit的模塊機制與es6的模塊基本相似,也提供了轉換爲amd,es6,umd,commonjs,system的轉換。typescript的按需加載,也叫動態加載,編譯器會檢測是否每一個模塊都會在生成的JavaScript中用到。 若是一個模塊標識符只在類型註解部分使用,而且徹底沒有在表達式中使用時,就不會生成require這個模塊的代碼。 省略掉沒有用到的引用對性能提高是頗有益的,並同時提供了選擇性加載模塊的能力。這種模式的核心是import id = require("...")語句可讓咱們訪問模塊導出的類型。 模塊加載器會被動態調用(經過require)。

模塊加載的最佳實踐

  • 一、儘量地在頂層導出

用戶應該更容易地使用你模塊導出的內容。 嵌套層次過多會變得難以處理,所以仔細考慮一下如何組織你的代碼。

  • 二、模塊裏避免使用命名空間

模塊中使用命名空間是沒必要要的,在模塊中導出的東西確定不能重名,而導入時使用者確定會爲其命名或者直接使用,也不存在重名,使用命名空間是多餘的。

  • 三、若是僅導出單個 class 或 function,使用 export default。如剛纔所說,default是比較好的實踐。

  • 四、若是要導出多個對象,把它們放在頂層裏導出

  • 五、導入時明確地列出導入的名字

  • 六、導入大量模塊時使用命名空間

  • 七、使用從新導出進行擴展

你可能常常須要去擴展一個模塊的功能。 JS裏經常使用的一個模式是JQuery那樣去擴展原對象。 如咱們以前提到的,模塊不會像全局命名空間對象那樣去合併。 推薦的方案是不要去改變原來的對象,而是導出一個新的實體來提供新的功能。

46.簡單聊聊你對 TypeScript 類型兼容性的理解?抗變、雙變、協變和逆變的簡單理解?

答: TypeScript裏的類型兼容性是基於結構子類型的。 結構類型是一種只使用其成員來描述類型的方式。 它正好與名)類型造成對比。TypeScript的結構性子類型是根據JavaScript代碼的典型寫法來設計的。 由於JavaScript裏普遍地使用匿名對象,例如函數表達式和對象字面量,因此使用結構類型系統來描述這些類型比使用名義類型系統更好。

  • 協變 (Covariant) :協變表示Comp<T>類型兼容和T的一致。

  • 逆變 (Contravariant) :逆變表示Comp<T>類型兼容和T相反。

  • 雙向協變 (Covariant) :雙向協變表示Comp<T>類型雙向兼容。

  • 不變 (Bivariant) :不變表示Comp<T>雙向都不兼容。

47.TypeScript 中對象展開會有什麼反作用嗎?

答: 展開操做符正與解構相反。 它容許你將一個數組展開爲另外一個數組,或將一個對象展開爲另外一個對象。對象的展開比數組的展開要複雜的多。 像數組展開同樣,它是從左至右進行處理,但結果仍爲對象。 這就意味着出如今展開對象後面的屬性會覆蓋前面的屬性。對象展開還有其它一些意想不到的限制。 首先,它僅包含對象 自身的可枚舉屬性。 大致上是說當你展開一個對象實例時,你會丟失其方法:

48.TypeScript 中 interface、type、enum 聲明有做用域的功能嗎?

答: 有,叫類做用域,類變量 也能夠稱爲 字段。類變量 聲明在一個類裏頭,但在類的方法外面,能夠經過類的實例化對象來訪問靜態變量 靜態的類變量,靜態的變量能夠經過類名直接訪問

49.TypeScript 中同名的 interface 或者同名的 interface 和 class 能夠合併嗎?

答: 同名interface接口會自動合併,interface同名的class也會自動聚合。 但type不能自動聚合,由於type聲明不能重名。

50.如何使 TypeScript 項目引入並識別編譯爲 JavaScript 的 npm 庫包?

答: 能夠選擇安裝其npm包的typescript版本,npm install @types/包名 --save,通常都是這樣命名。 若是是本身寫的js庫  能夠單獨編寫.d.ts文件

51.TypeScript 的 tsconfig.json 中有哪些配置項信息?

答:

{
   "files": [  # 指定須要編譯文件,相對配置文件所在
        "core.ts",
        "sys.ts",
        "types.ts",
        "scanner.ts",
        "parser.ts",
        "utilities.ts",
        "binder.ts",
        "checker.ts",
        "emitter.ts",
        "program.ts",
        "commandLineParser.ts",
        "tsc.ts",
        "diagnosticInformationMap.generated.ts"
    ],
    "exclude": [ # 指定不須要編譯文件
        "node_modules",
        "**/*.spec.ts"
    ],
    "include": [ # 指定須要編譯文件; 不配置files,include,默認除了exclude的全部.ts,.d.ts,.tsx
        "src/**/*"
    ],
	# 指定基礎配置文件路徑 大部分配置 compilerOptions, files, include, and exclude。切忌循環引用。
	"extends": "./configs/base",
    "compilerOptions": {   # 告知TypeScript 編譯器怎麼編譯
		"baseUrl": "./",
		"paths": {   # 相對於baseUrl配置 
			"jquery": ["node_modules/jquery/dist/jquery"] ,
			 "*": [
					"*",
					"generated/*"
			 ]
		},
		"rootDirs":[ # 找平路徑配置依賴
				"src/views",
				"generated/templates/views"
	    ],
        "module": "commonjs",
        "noImplicitAny": true,
        "removeComments": true, # 移除代碼註解 
        "preserveConstEnums": true,
        "sourceMap": true
		"types": []  #不會自動導入@types定義的包
		"noResolve":true , # 不會自動導入import 依賴, 編譯會報錯
		"downlevelIteration":true # 進行js 語法降級 for..of 
		"module": "esnext",
       "moduleResolution": "node",
		"strictNullChecks": true  # 開啓null,檢測
		"target":'ES5'
		"strictBindCallApply":true
		"skipLibCheck":true, 
    },
	# 以上屬性,爲經常使用配置屬性
	"compileOnSave": false, # 整個工程而言,須要編譯器支持,譬如Visual Studio 2015 with TypeScript 1.8.4+
	"typeAcquisition":{   # 整個工程的類型定義.d.ts
		    "enable":false,  # 默認值 false 
			"include" : ["jquery", "lodash"] 
			"exclue":["jquery", "lodash" ]
	},
   "references":[{ # 引用的工程 
	   path: 'xxxx'
	 }]  
}
複製代碼
52.TypeScript 中如何設置模塊導入的路徑別名?

答: 經過tsconfig.json中的paths項來配置

53.React Class 組件有哪些周期函數?分別有什麼做用?

答:

  • constructor() 掛載類組件的時候,先執行構造函數

  • static getDerivedStateFromProps() 會在調用 render 方法以前調用,而且在初始掛載及後續更新時都會被調用。它應返回一個對象來更新 state,若是返回 null 則不更新任何內容。

  • render() 渲染真實的DOM節點

  • componentDidMount()會在組件掛載後(插入 DOM 樹中)當即調用。依賴於 DOM 節點的初始化應該放在這裏。如需經過網絡請求獲取數據,此處是實例化請求的好地方。

更新:

  • static getDerivedStateFromProps() 同一次掛載時的 getDerivedStateFromProps() 一致

  • shouldComponentUpdate() 能夠在這裏進行性能優化,減小淺層比較

  • render() 插入真實的DOM節點樹上

  • getSnapshotBeforeUpdate() 能在最近一次渲染中,從以前的DOM拿到一些有用的信息,好比滾動位置等

  • componentDidUpdate() 當組件更新後,能夠在此處對 DOM 進行操做。若是你對更新先後的 props 進行了比較,也能夠選擇在此處進行網絡請求。(例如,當 props 未發生變化時,則不會執行網絡請求)

卸載:

  • componentWillUnmount()  這裏是卸載及銷燬組件前的調用方法 能夠在這裏清空一些數據,好比取消網絡請求、 componentDidmount中建立的一些數據等等
54.React Class 組件中請求能夠在 componentWillMount 中發起嗎?爲何?

答: 看狀況,若是是服務端渲染會拿不到數據。

componentWillMount方法的調用在constructor以後,在render以前,在這方法裏的代碼調用setState方法不會觸發重渲染,因此它通常不會用來做加載數據之用,它也不多被使用到。

通常的從後臺(服務器)獲取的數據,都會與組件上要用的數據加載有關,因此都在componentDidMount方法裏面做。雖然與組件上的數據無關的加載,也能夠在constructor裏做,但constructor是做組件state初紿化工做,並非設計來做加載數據這工做的,因此全部有反作用的代碼都會集中在componentDidMount方法裏。

55.React Class 組件和 React Hook 的區別有哪些?

答: Hook代碼可讀性更強,本來同一塊功能的代碼邏輯被拆分在了不一樣的生命週期函數中,容易使開發者不利於維護和迭代,經過 React Hooks 能夠將功能代碼聚合,方便閱讀維護;

組件樹層級變淺,在本來的代碼中,咱們常用 HOC/render/Props 等方式來複用組件的狀態,加強功能等,無疑增長了組件樹層數及渲染,而在 React Hooks 中,這些功能均可以經過強大的自定義的 Hooks 來實。

hooks組件實際上是下降了react開發的使用難度的,讓新手能夠在不使用class組件的狀況下依然能夠進行項目開發。

56.React 中高階函數和自定義 Hook 的優缺點?

答:

高階組件實際上就是把一個組件當參數傳入,再返回一個新的組件出來。業務過分封裝的高階組件,可能會致使組件層次嵌套變深。

而自定義 Hook 能夠不用使用高階組件依然能夠進行功能複用。

57.簡要說明 React Hook 中 useState 和 useEffect 的運行原理?

答: useState返回一個有狀態值和一個函數來更新它。在初始渲染期間,返回的狀態(狀態)與做爲第一個參數(initialState)傳遞的值相同。setState 函數用於更新狀態。它接受一個新的狀態值,並排隊等待從新渲染該組件。 在更新過程當中,

  • 首次渲染,render()

  • render會調用App函數,獲得虛擬DIV,建立真實DIV

  • 用戶點擊Button,調用setN(n+1),render函數被再一次調用

  • render進一步調用App函數,獲得虛擬DIV,Diff,更新真實DIV

  • 每一次setN都會再次調用render,進而調用App

而useEffect的運行流程

  • 初次渲染的時候,按照 useState,useEffect 的順序,把 state,deps 等按順序塞到 memoizedState 數組中。

  • 更新的時候,按照順序,從 memoizedState 中把上次記錄的值拿出來。

  • useState,useEffect 和使用的不是同一個數據

  • 核心就在於每次更新把cursor賦值爲零,而後更新時按照hooks順序,依次從 memoizedState 中把上次記錄的值拿出來,useEffect接受useState(返回新值)和舊值進行比較

58.React 如何發現重渲染、什麼緣由容易形成重渲染、如何避免重渲染?

答: 當內部data發生改變,state發生改變(經過調用this.setState()) 以及父組件傳過來的props發生改變時,會致使組件從新渲染。

react生命週期中有這樣一個鉤子,叫shouldComponentUpdate函數,是重渲染時render()函數調用前被調用的函數,兩個參數 nextProps和nextState ,分別表示下一個props和state的值。當函數返回false時,阻止接下來的render()函數的調用,阻止組件重渲染,返回true時,組件照常渲染。 先後不改變state的值的setState和無數據交換的父組件的重渲染都會致使組件的重渲染,但咱們能夠經過shouldComponentUpdate來阻止這兩種狀況,shouldComponentUpdate並非完美的,只能阻止扁平的對象,這時候能夠考慮Immutable.js(Immutable.js 的基本原則是對於不變的對象返回相同的引用,而對於變化的對象,返回新的引用)或者PureRenderMixin 插件。

59.React Hook 中 useEffect 有哪些參數,如何檢測數組依賴項的變化?

答: 不傳參數、空數組、有一個或者多個值得數組、返回一個函數。

useEffect的第二個參數可用於定義其依賴的全部變量。若是其中一個變量發生變化,則useEffect會再次運行。若是包含變量的數組爲空,則在更新組件時useEffect不會再執行,由於它不會監放任何變量的變動。

60.React 的 useEffect 是如何監聽數組依賴項的變化的?

答: useEffect的第二個參數可用於定義其依賴的全部變量。若是其中一個變量發生變化,則useEffect會再次運行。若是包含變量的數組爲空,則在更新組件時useEffect不會再執行,由於它不會監放任何變量的變動。

61.React Hook 和閉包有什麼關聯關係?

答:

首先閉包是由函數以及建立該函數的詞法環境組合而成。這個詞法環境包含了該閉包建立時所能訪問的全部局部變量。劃重點是閉包建立時的變量值,閉包建立以後即便這些變量值改變了也不會影響到閉包內保存的這個變量。

而useEffect、useMemo、useCallback都是自帶閉包的。每一次組件的渲染,它們都會捕獲當前組件函數上下文中的狀態(state, props),因此每一次這三種hooks的執行,反映的也都是當前的狀態,你沒法使用它們來捕獲上一次的狀態。

對 Hook 過期閉包的解決辦法:

  • 添加依賴項

注意依賴項爲空和不傳依賴項是兩個概念,前者是傳了依賴項但它是一個空數組,後者是直接不傳這個參數。前者只有依賴項改變時纔會執行函數,後者只要組件數據改變了就執行。

  • 以函數的形式更新state

以函數的形式更新state,同 react 的 setState 同樣,useState Hook 也能夠經過函數的形式來修改 state,而且使用當前的 state 值做爲函數參數。

  • 使用useRef

經過 useRef 生成的對象來綁定 state,這樣更新 state 的時候就能夠不用依賴於該 state,而是直接在該綁定對象上的基礎上更新便可。

  • 使用useReducer

useReducer 能夠達到和使用函數形式更新的 useState 同樣的效果,也是在更新時在當前的 state 基礎上進行操做。

62.React 中 useState 是如何作數據初始化的?

答: 一個函數組件,在react執行渲染時該函數都會被調用,因此函數內的useState在每次都會被調用。useState在不一樣階段,其對應的實現不同,在onMount階段:初始化state;在onUpdate階段:更新state。useState返回的是一個數組,數組的第二項是一個函數,該函數每次被調用後,都會觸發react的更新。

63.列舉你經常使用的 React 性能優化技巧?

答:

  • 使用 shouldComponentUpdate 規避冗餘的更新邏輯

  • PureComponent + Immutable.js

  • React.memo 與 useMemo

64.Vue 2.x 模板中的指令是如何解析實現的?

答: 指令本質上就是一個 JavaScript 對象,對象上掛着一些鉤子函數,不管是官方提供的指令,仍是自定義指令,一個指令從第一次被綁定到元素上到最終與被綁定的元素解綁,它會通過如下幾種狀態:

  • bind:只調用一次,指令第一次綁定到元素時調用。在這裏能夠進行一次性的初始化設置。
  • inserted:被綁定元素插入父節點時調用 (僅保證父節點存在,但不必定已被插入文檔中)。
  • update:所在組件的 VNode 更新時調用,可是可能發生在其子 VNode 更新以前。
  • componentUpdated:指令所在組件的 VNode 及其子 VNode 所有更新後調用。
  • unbind:只調用一次,指令與元素解綁時調用。

了每一個狀態的鉤子函數,這樣咱們就可讓指令在不一樣狀態下作不一樣的事情。當虛擬DOM渲染更新的時候會觸發create、update、destory這三個鉤子函數,從而就會執行updateDirectives函數來處理指令的相關邏輯,執行指令函數,讓指令生效。

65.簡要說明 Vue 2.x 的全鏈路運做機制?

答:

  • 初始化以及掛載init, mount
  • 在進行模板編譯compile,將template編譯爲渲染函數render function
  • 執行render function生成Virtual DOM, render function => VNode tree
  • 再進行響應式依賴收集,render function => getter, setter => Watcher.update => patch。以及使用隊列進行異步更新的策略。
  • 最後經過diff算法後進行patch更新視圖
66.簡單介紹一下 Element UI 的框架設計?

答:

支持 npm 方式和 cdn 方式,並支持按需引入、支持多語言、文檔詳盡、組件豐富。(不瞭解)

67.如何理解 Vue 是一個漸進式框架?

答:

漸進式表明的含義是:沒有多作職責以外的事。

你能夠使用jsx開發,你也能夠寫template;你能夠使用vue全家桶,你也能夠把它作爲某個業務的輕量視圖,隨你,不強求不主張。

68.Vue 裏實現跨組件通訊的方式有哪些?

答:

  • 父子通訊:

父向子傳遞數據是經過 props,子向父是經過 events( e m i t );經過父鏈 / 子鏈也能夠通訊( emit);經過父鏈 / 子鏈也能夠通訊( parent / c h i l d r e n ); r e f 也能夠訪問組件實例; p r o v i d e / i n j e c t A P I children);ref 也能夠訪問組件實例;provide / inject API; attrs/$listeners

  • 兄弟通訊:

Bus;Vuex

  • 跨級通訊:

Bus;Vuex;provide / inject API、 a t t r s / attrs/ listeners

69.Vue 中響應式數據是如何作到對某個對象的深層次屬性的監聽的?

答:

使用watch而且搭配deep:true 就能夠實現對對象的深度監聽

70.MVVM、MVC 和 MVP 的區別是什麼?各自有什麼應用場景?、

答:

MVC 是一種使用 MVC(Model View Controller 模型-視圖-控制器)設計建立 Web 應用程序的模式。

  • 耦合性低
  • 重用性高
  • 生命週期成本低

MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。

  • 模型與視圖徹底分離,咱們能夠修改視圖而不影響模型
  • 能夠更高效地使用模型,由於全部的交互都發生在一個地方——Presenter內部
  • 咱們能夠將一個Presenter用於多個視圖,而不須要改變Presenter的邏輯。這個特性很是的有用,由於視圖的變化老是比模型的變化頻繁。
  • 若是咱們把邏輯放在Presenter中,那麼咱們就能夠脫離用戶接口來測試這些邏輯(單元測試)

MVVM 本質上就是MVC 的改進版。MVVM 就是將其中的View 的狀態和行爲抽象化,讓咱們將視圖 UI 和業務邏輯分開。

  • 低耦合
  • 可重用性
  • 獨立開發
  • 可測試
71.什麼是 MVVM 框架?

答: MVVM,特色是採用雙向綁定(data-binding): View的 變更,自動反映在View Model,反之亦然。這樣開發者就不用處理接收事件和View更新的工做,框架已經幫你作好了。

72.Vue CLI 3.x 有哪些功能?Vue CLI 3.x 的插件系統瞭解?

答:

插件系統是給vue項目提供可選功能的npm包,如:Babel/TypeScript 轉譯、ESLint 集成、unit和 e2e測試 等

73.Vue CLI 3.x 中的 Webpack 是如何組裝處理的?

答:

對比vue-cli2,cli3 最主要的就是生成的項目中,進行webpack配置的文件沒有了。cli3的腳手架封裝了webpack絕大部分配置,使得生成的項目更加清晰,可是在開發中免不了會有本身的個性需求,來添加一些本身的項目配置,此時只需在項目的根目錄下新建一個vue.config.js文件便可。而webpack中是經過 resolve.alias 來實現此功能的。在vue.config.js中修改webpack的配置,能夠經過configureWebpack方法。

74.Vue 2.x 如何支持 TypeScript 語法?

答:

  • 配置ts-loader,tsconfig
  • 增長類型擴展,讓ts識別vue文件
  • vue文件中script裏面換成ts寫法, 須要增長几個ts擴展的package, 好比vue-property-decorator
75.如何配置環境使得 JavaScript 項目能夠支持 TypeScript 語法?

答:

  • 利用 Babel 的 @babel/plugin-transform-typescript 插件來實現。
  • 利用 ts-loader 結合官方 typescript 庫來實現。
76.如何對 TypeScript 進行 Lint 校驗?ESLint 和 TSLint 有什麼區別?

答:

ESLint 和 TSLint 都是 Javascript 的語法檢查器,一般使用 ESLint 或 TSLint 用於解決團隊開發上不一樣代碼風格所帶來的一系列不和諧的問題。

ESLint 支持幾種格式的配置文件:

  • JavaScript:使用 .eslintrc.js 而後輸出一個配置對象。
  • YAML:使用 .eslintrc.yaml 或 .eslintrc.yml 去定義配置的結構。
  • JSON:使用 .eslintrc.json 去定義配置的結構,ESLint 的 JSON 文件容許 JavaScript 風格的註釋。
  • (棄用):使用 .eslintrc,能夠使 JSON 也能夠是 YAML。

package.json:在 package.json 裏建立一個 eslintConfig 屬性,在那裏定義你的配置。

TSLint 是配合 Typescript 來使用的。TSLint 執行規則的方式存在一些框架問題,從而影響性能,而修復這些問題會破壞現有的規則。ESLint 的性能更好,而且社區用戶一般擁有 ESLint 的規則配置(好比 React 和 Vue 的配置),而不會擁有 TSLint 的規則配置。

77.Node.js 如何支持 TypeScript 語法?

答:

Node是基於Chrome V8引擎開發的能使JavaScript在服務器端運行的運行時環境,TS最終編譯成JS,而後生成字節碼->機器碼。 Node.js支持TS語法,有什麼疑問嗎,畢竟最終都是編譯成JS?

78.TypeScript 如何自動生成庫包的聲明文件?

答:

編譯選項,在這裏找到與生成相應的 .d.ts 文件和 聲明文件 相關的選項,其中包括:

  • declaration
  • declarationDir
  • types
  • typeRoots

配置完 tsconfig.json 文件後,再次執行 npm run build 會在項目根目錄下生成 types 文件夾,該文件夾主要存放自動生成的 TypeScript 聲明文件。

79.Babel 對於 TypeScript 的支持有哪些限制?

答:

在使用babel-preset-typescript能夠使 JavaScript 與 TypeScript 並存,且在編譯過程是同一階段進行的。透過 Babel preset 配置,能夠更容易的實現咱們所需的JS項目平滑轉移到 TS項目過程。

可是,這對Babel有必定的要求,而不一樣版本的Babel對於 monorepo 存在相容性問題,特別是要從 subrepo 引用 module 時,會致使 Babel 的配置沒法正確取得。

80.Webpack 中 Loader 和 Plugin 的區別是什麼?

答:

在webpack中 Loader 就是負責完成項目中各類各樣資源模塊的加載,從而實現總體項目的模塊化,而 Plugin 則是用來解決項目中除了資源模塊打包之外的其餘自動化工做,對比 Loader 只是在模塊的加載環節工做,而插件的做用範圍幾乎能夠觸及 Webpack 工做的每個環節。

81.在 Webpack 中是如何作到支持相似於 JSX 語法的 Sourcemap 定位?

答:

// 見試卷背面
複製代碼
82.發佈 Npm 包如何指定引入地址?

答:

// 見試卷背面
複製代碼
83.如何發佈開發項目的特定文件夾爲 Npm 包的根目錄?

答:

通常狀況下,npm包的根目錄時node_modules,能夠使用package.json的directories屬性裏的directories.lib,更改 Npm 包的根目錄。

84.如何發佈一個支持 Tree Shaking 機制的 Npm 包?

答:

一般人們在使用打包工具的 babel 插件編譯代碼時都會屏蔽掉 node_modules 目錄下的文件。由於按照約定你們發佈到 npm 的模塊代碼都是基於 ES5 規範的,所以配置 babel 插件屏蔽 node_modules 目錄能夠極大的提升編譯速度。但用戶若是使用了咱們發佈的基於 ES6 規範的包就必須配置複雜的屏蔽規則以便把咱們的包加入編譯的白名單。

若是用戶是在 NodeJS 環境使用咱們的包,那麼極有可能連打包這一步驟都沒有。若是用戶的 NodeJS 環境又恰巧不支持 ES6 模塊規範,那麼就會致使代碼報錯。

基於以上兩個緣由,pkg.module 字段要指向的應該是一個基於 ES6 模塊規範的使用ES5語法書寫的模塊。基於 ES6 模塊規範是爲了用戶在使用咱們的包時能夠享受 Tree Shaking 帶來的好處;使用 ES5 語法書寫是爲了用戶在配置 babel 插件時能夠放心的屏蔽 node_modules 目錄。至關於在一個包內同時發佈了兩種模塊規範的版本。

當打包工具遇到咱們的模塊時:

  • 若是它已經支持 pkg.module 字段則會優先使用 ES6 模塊規範的版本,這樣能夠啓用 Tree Shaking 機制。
  • 若是它還不識別 pkg.module 字段則會使用咱們已經編譯成 CommonJS 規範的版本,也不會阻礙打包流程。
85.Npm 包中 peerDependencies 的做用是什麼?

答:

peerDependencies的目的是提示宿主環境去安裝知足插件peerDependencies所指定依賴的包,而後在插件import或者require所依賴的包的時候,永遠都是引用宿主環境統一安裝的npm包,最終解決插件與所依賴包不一致的問題。

86.如何優雅的調試須要發佈的 Npm 包?

答:

  • 在須要調試的npm包目錄下結構下的控制檯輸入npm link 這個命令會把當前包映射到本地的一個全局的npm包裏面;
  • 在引用的目錄結構下的控制檯輸入 npm link 包名稱 這個命令會把本地引用的這個npm包的路徑定位到全局的npm包下;
  • 全局的npm包至關於一箇中轉站,在編輯區域與引用區域之間中轉。
87.在設計一些庫包時如何生成版本日誌?

答:

npm run changelog 自動生成的版本日誌信息

88.瞭解 Git (Submodule)子模塊嗎?簡單介紹一下 Git 子模塊的做用?

答:

子模塊是進行開發和需求進行對接將需求文檔做爲子模塊項目,嵌入開發人員的項目中。子模塊的使用既能夠減小需求或設計人員的git操做,又能夠及時的將doc文檔發佈到項目的目錄文件下,並且不會對開發人員的項目產生任何影響。

89.Git 如何修改已經提交的 Commit 信息?

答:

  • git rebase -i <commit id> 列出 commit 列表
  • 找到須要修改的 commit 記錄,把 pick 修改成 edit 或 e,:wq 保存退出
  • 修改 commit 的具體信息git commit --amend,保存並繼續下一條git rebase --continue,直到所有完成
  • 中間也可跳過或退出git rebase (--skip | --abort)
90.Git 如何撤銷 Commit 並保存以前的修改?

答:

  • 查看commit git log --pretty=oneline
  • 撤銷到上一個commit,可是保存當前的修改。 git reset --soft <commit>
  • 修改爲功。重建分支,進行提交。
91.Git 如何 ignore 被 commit 過的文件?

答:

  • 刪除 track 的文件 (已經 commit 的文件)
  • 在 .gitignore 文件中添加忽略規則

在 .gitignore 文件中添加 ignore 條目, 如: .DS_Store 提交 .gitignore 文件: git commit -a -m "添加ignore規則"

  • 推送到遠程倉庫讓 ignore 規則對於其餘開發者也能生效
92.在使用 Git 的時候如何規範 Git 的提交說明(Commit 信息)?

答:

用Commitizen,Commitizen 是一個撰寫符合上面 Commit Message 標準的一款工具。 在push操做時檢查commit的信息,使用正則檢查是否匹配(好比使用angular的git規範),不符合的不容許Push。

93.簡述符合 Angular 規範的提交說明的結構組成?

答:

commit格式以下:

<type>: <subject>
<BLANK LINE>
<body>
type - 提交 commit 的類型
複製代碼

feat: 新功能

fix: 修復問題

docs: 修改文檔

style: 修改代碼格式(不影響邏輯功能,好比格式化、補充分號等等)

refactor: 重構代碼(fix bug或增長新功能不屬於此範圍)

perf: 提高頁面性能

test: 增長/修改測試用例

chore: 修改工具相關(包括但不限於文檔、代碼生成等, 好比修改了README,webpack配置文件等等)

deps: 升級依賴

subject - 用一句話清楚的描述此次提交作了什麼

body - 補充subject,適當增長緣由、目的等相關因素,可選。

94.Commit 信息如何和 Github Issues 關聯?

答:

當你提交一個commit的時候在commit message裏面使用#issue, 好比#8, github就會自動關聯issue 8跟這個commit. 固然在github上面寫comment的時候使用這個也是有效的,在confirm merge的時候能夠使用一下命令來關閉相關issue。

fixes #xxx
fixed #xxx
fix #xxx
closes #xxx
close #xxx
closed #xxx
複製代碼
95.Git Hook 在項目中哪些做用?

答:

Git Hooks是定製化的腳本程序,因此它實現的功能與相應的git動做相關,以下幾個簡單例子:

  • 多人開發代碼語法、規範強制統一
  • commit message 格式化、是否符合某種規範
  • 若是有須要,測試用例的檢測
  • 服務器代碼有新的更新的時候通知全部開發成員
  • 代碼提交後的項目自動打包(git receive以後)
  • 等等...
96.Git Hook 中客戶端和服務端鉤子各自用於什麼做用?

答:

客戶端鉤子由諸如提交和合並這樣的操做所調用, 而服務器端鉤子做用於諸如接收被推送的提交這樣的聯網操做。

97.Git Hook 中經常使用的鉤子有哪些?

答:

ClientSide hooks:

  • pre-commit,當執行commit動做時先執行此hook,能夠用此hook作一些檢查,好比代碼風格檢查,或者先跑測試。

  • prepare-commit-msg, 當commit時須要輸入message前會觸發此hook,能夠用此hook來定製本身的default message信息。

  • commit-msg,當用戶輸入commit的message後被觸發,能夠用此hook校驗message的信息,好比是否符合規定,有沒有cr等。

  • post-commit, 當commit完成後被觸發,能夠用此hook發送 notification 等。

  • pre-rebase, rebase以前會被觸發,能夠用此hook來拒絕全部的已經push的commits進行rebase操做。

  • post-merge, 當merge成功後,會觸發此hook。

  • pre-push, 當push時,remote refs被更新,可是在全部的objects傳輸前被觸發。

  • pre-auto-gc, 當git gc –auto執行前被觸發。在垃圾回收以前作一些驗證或備份是挺不錯的。

ServerSide hooks:

  • pre-receive, 當收到push動做以前會被執行。

  • update, 也是收到push動做以前被執行,可是有可能被執行屢次,每一個branch一次。

  • post-receive, 當push動做已經完成的時候會被觸發,能夠用此hook來 push notification等,好比發郵件,通知持續構建服務器等。

98.pre-commit 和 commit-msg 鉤子的區別是什麼?各自可用於作什麼?

答:

pre-commit是客戶端hooks之一,也是接下來要介紹的鉤子。pre-commit在git add提交以後,而後執行git commit時執行,腳本執行沒報錯就繼續提交,反之就駁回提交的操做。 這個鉤子中能夠實現:對將要提交的代碼進行檢查、優化代碼格式、或者對提交的圖片進行壓縮等等任務。

Git 每次提交代碼,都要寫 Commit message(提交說明),不然就不容許提交。

99.husky 以及 ghook 等工具製做 Git Hook 的原理是什麼?

答:

代碼提交以前會經過 husky 配合 git hook 進行提交信息校驗,一旦提交信息不符合 Angular 規範,則提交會失敗。

100.如何設計一個通用的 Git Hook ?

答:

藉助Commitizen,使用 git cz 代替 git commit 進行復合 Angular 規範的 Commit Message 信息提交,規範團隊的git規範。代碼提交以前會經過 husky 配合 git hook 進行提交信息校驗,一旦提交信息不符合 團隊的git規範,正則匹配失敗,則提交會失敗。

101.Git Hook 能夠採用 Node 腳本進行設計嗎?如何作到?

答:

能夠。鉤子都被存儲在 Git 目錄下的 hooks 子目錄中。 也即絕大部分項目中的 .git/hooks 。 當你用 git init 初始化一個新版本庫時,Git 默認會在這個目錄中放置一些示例腳本。這些腳本除了自己能夠被調用外,它們還透露了被觸發時所傳入的參數。 全部的示例都是 shell 腳本,其中一些還混雜了 Perl 代碼,不過,任何正確命名的可執行腳本均可以正常使用 —— 你能夠用 Ruby 或 Python,或其它語言編寫它們。 這些示例的名字都是以 .sample 結尾,若是你想啓用它們,得先移除這個後綴。

好比說用 Node.js 來寫一個拒絕提交沒有被解決的衝突的文件的鉤子,寫這個鉤子的初衷是由於在多人合做項目中,老是不免會遇到文件衝突的狀況,而有些同事沒有找到所有的衝突文件並一一解決,這個鉤子就會在 commit 的時候檢查是否有衝突,若是有衝突,就會把全部衝突找到,並提示出錯文件後,拒絕 commit。

#!/usr/bin/env node
// 在 commit 以前檢查是否有衝突,若是有衝突就 process.exit(1)

const execSync = require('child_process').execSync

// git 對全部衝突的地方都會生成下面這種格式的信息,因此寫個檢測衝突文件的正則
const isConflictRegular = "^<<<<<<<\\s|^=======$|^>>>>>>>\\s"

let results

try {
 // git grep 命令會執行 perl 的正則匹配全部知足衝突條件的文件
    results = execSync(`git grep -n -P "${isConflictRegular}"`, {encoding: 'utf-8'})
} catch (e) {
    console.log('沒有發現衝突,等待 commit')
    process.exit(0)
}

if(results) {
    console.error('發現衝突,請解決後再提交,衝突文件:')
    console.error(results.trim())
    process.exit(1)
}

process.exit(0)
複製代碼

把這個文件拷貝到 .git/hooks/pre-commit 下,並執行 chmod 777 pre-commit 就能夠在每次 commit 的狀況下檢查以前文件是否有衝突。

102.如何確保別人上傳的代碼沒有 Lint 錯誤?如何確保代碼構建沒有 Lint 錯誤?

答:

在使用cli構建項目時,勾選 Use ESLint to lint your code。 在 .eslintrc.js 文件裏,找到文件中的rules,咱們能夠在其中定義一些代碼檢查的規則

'semi': ['error', 'always']
複製代碼

經常使用規則

'rules': {
      "comma-dangle": ["error", "never"], //是否容許對象中出現結尾逗號
      "no-cond-assign": 2, //條件語句的條件中不容許出現賦值運算符
      "no-console": 2, //不容許出現console語句
      "no-constant-condition": 2, //條件語句的條件中不容許出現恆定不變的量
      "no-control-regex": 2, //正則表達式中不容許出現控制字符
      "no-debugger": 2, //不容許出現debugger語句
      "no-dupe-args": 2, //函數定義的時候不容許出現重複的參數
      "no-dupe-keys": 2, //對象中不容許出現重複的鍵
      "no-duplicate-case": 2, //switch語句中不容許出現重複的case標籤
      "no-empty": 2, //不容許出現空的代碼塊
      "no-empty-character-class": 2, //正則表達式中不容許出現空的字符組
      "no-ex-assign": 2, //在try catch語句中不容許從新分配異常變量
      "no-extra-boolean-cast": 2, //不容許出現沒必要要的布爾值轉換
      "no-extra-parens": 0, //不容許出現沒必要要的圓括號
      "no-extra-semi": 2, //不容許出現沒必要要的分號
      "no-func-assign": 2, //不容許從新分配函數聲明
      "no-inner-declarations": ["error", "functions"], //不容許在嵌套代碼塊裏聲明函數
      "no-invalid-regexp": 2, //不容許在RegExp構造函數裏出現無效的正則表達式
      "no-irregular-whitespace": 2, //不容許出現不規則的空格
      "no-negated-in-lhs": 2, //不容許在in表達式語句中對最左邊的運算數使用取反操做
      "no-obj-calls": 2, //不容許把全局對象屬性當作函數來調用
      "no-regex-spaces": 2, //正則表達式中不容許出現多個連續空格
      "quote-props": 2, //對象中的屬性名是否須要用引號引發來
      "no-sparse-arrays": 2, //數組中不容許出現空位置
      "no-unreachable": 2, //在return,throw,continue,break語句後不容許出現不可能到達的語句
      "use-isnan": 2, //要求檢查NaN的時候使用isNaN()
      "valid-jsdoc": ["error", {
          "requireReturn": false,
          "requireParamDescription": false,
          "requireReturnDescription": true
      }], //強制JSDoc註釋
      "valid-typeof": ["error", {
          "requireStringLiterals": true
      }], //在使用typeof表達式比較的時候強制使用有效的字符串
      "block-scoped-var": 2, //將變量聲明放在合適的代碼塊裏
      "complexity": 0, //限制條件語句的複雜度
      "consistent-return": 2, //不管有沒有返回值都強制要求return語句返回一個值
      "curly": ["error", "all"], //強制使用花括號的風格
      "default-case": 0, //在switch語句中須要有default語句
      "dot-notation": ["error", {"allowKeywords": false, "allowPattern": ""}], //獲取對象屬性的時候使用點號
      "eqeqeq": ["error", "smart"], //比較的時候使用嚴格等於
      "no-alert": 1, //不容許使用alert,confirm,prompt語句
      "no-caller": 2, //不容許使用arguments.callee和arguments.caller屬性
      "guard-for-in": 0, //監視for in循環,防止出現不可預料的狀況
      "no-div-regex": 2, //不能使用看起來像除法的正則表達式
      "no-else-return": 0, //若是if語句有return,else裏的return不用放在else裏
      "no-labels": ["error", {
          "allowLoop": false,
          "allowSwitch": false
      }], //不容許標籤語句
      "no-eq-null": 2, //不容許對null用==或者!=
      "no-eval": 2, //不容許使用eval()
      "no-extend-native": 2, //不容許擴展原生對象
      "no-extra-bind": 2, //不容許沒必要要的函數綁定
      "no-fallthrough": 2, //不容許switch按順序所有執行全部case
      "no-floating-decimal": 2, //不容許浮點數缺失數字
      "no-implied-eval": 2, //不容許使用隱式eval()
      "no-iterator": 2, //不容許使用__iterator__屬性
      "no-lone-blocks": 2, //不容許沒必要要的嵌套代碼塊
      "no-loop-func": 2, //不容許在循環語句中進行函數聲明
      "no-multi-spaces": 2, //不容許出現多餘的空格
      "no-multi-str": 2, //不容許用\來讓字符串換行
      "no-global-assign": 2, //不容許從新分配原生對象
      "no-new": 2, //不容許new一個實例後不賦值或者不比較
      "no-new-func": 2, //不容許使用new Function
      "no-new-wrappers": 2, //不容許使用new String,Number和Boolean對象
      "no-octal": 2, //不容許使用八進制字面值
      "no-octal-escape": 2, //不容許使用八進制轉義序列
      "no-param-reassign": 0, //不容許從新分配函數參數"no-proto": 2, //不容許使用__proto__屬性
      "no-redeclare": 2, //不容許變量重複聲明
      "no-return-assign": 2, //不容許在return語句中使用分配語句
      "no-script-url": 2, //不容許使用javascript:void(0)
      "no-self-compare": 2, //不容許本身和本身比較
      "no-sequences": 2, //不容許使用逗號表達式
      "no-throw-literal": 2, //不容許拋出字面量錯誤 throw "error"
      "no-unused-expressions": 2, //不容許無用的表達式
      "no-void": 2, //不容許void操做符
      "no-warning-comments": [1, {"terms": ["todo", "fixme", "any other term"]}], //不容許警告備註
      "no-with": 2, //不容許使用with語句
      "radix": 1, //使用parseInt時強制使用基數來指定是十進制仍是其餘進制
      "vars-on-top": 0, //var必須放在做用域頂部
      "wrap-iife": [2, "any"], //當即執行表達式的括號風格
      "yoda": [2, "never", {"exceptRange": true}], //不容許在if條件中使用yoda條件
      "strict": [2, "function"], //使用嚴格模式
      "no-catch-shadow": 2, //不容許try catch語句接受的err變量與外部變量重名"no-delete-var": 2, //不容許使用delete操做符
      "no-label-var": 2, //不容許標籤和變量同名
      "no-shadow": 2, //外部做用域中的變量不能與它所包含的做用域中的變量或參數同名
      "no-shadow-restricted-names": 2, //js關鍵字和保留字不能做爲函數名或者變量名
      "no-undef": 2, //不容許未聲明的變量
      "no-undef-init": 2, //不容許初始化變量時給變量賦值undefined
      "no-undefined": 2, //不容許把undefined當作標識符使用
      "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], //不容許有聲明後未使用的變量或者參數
      "no-use-before-define": [2, "nofunc"], //不容許在未定義以前就使用變量"indent": 2, //強制一致的縮進風格
      "brace-style": [2, "1tbs", { "allowSingleLine": false}], //大括號風格
      "camelcase": [2, {"properties": "never"}], //強制駝峯命名規則
      "comma-style": [2, "last"], //逗號風格
      "consistent-this": [0, "self"], //當獲取當前環境的this是用同樣的風格
      "eol-last": 2, //文件以換行符結束
      "func-names": 0, //函數表達式必須有名字
      "func-style": 0, //函數風格,規定只能使用函數聲明或者函數表達式
      "key-spacing": [2, {"beforeColon": false, "afterColon": true}], //對象字面量中冒號的先後空格
      "max-nested-callbacks": 0, //回調嵌套深度
      "new-cap": [2, {"newIsCap": true, "capIsNew": false}], //構造函數名字首字母要大寫
      "new-parens": 2, //new時構造函數必須有小括號
      "newline-after-var": 0, //變量聲明後必須空一行
      "no-array-constructor": 2, //不容許使用數組構造器
      "no-inline-comments": 0, //不容許行內註釋
      "no-lonely-if": 0, //不容許else語句內只有if語句
      "no-mixed-spaces-and-tabs": [2, "smart-tabs"], //不容許混用tab和空格
      "no-multiple-empty-lines": [2, {"max": 2}], //空行最多不能超過兩行
      "no-nested-ternary": 2, //不容許使用嵌套的三目運算符
      "no-new-object": 2, //禁止使用new Object()
      "fun-call-spacing": 2, //函數調用時,函數名與()之間不能有空格
      "no-ternary": 0, //不容許使用三目運算符
      "no-trailing-spaces": 2, //一行最後不容許有空格
      "no-underscore-dangle": 2, //不容許標識符如下劃線開頭
      "no-extra-parens": 0, //不容許出現多餘的括號
      "one-var": 0, //強制變量聲明放在一塊兒
      "operator-assignment": 0, //賦值運算符的風格
      "padded-blocks": [2, "never"], //塊內行首行尾是否空行
      "quote-props": 0, //對象字面量中屬性名加引號
      "quotes": [1, "single", "avoid-escape"], //引號風格
      "semi": [2, "always"], //強制語句分號結尾
      "semi-spacing": [2, {"before": false, "after": true}], //分後先後空格
      "sort-vars": 0, //變量聲明時排序
      "space-before-blocks": [2, "always"], //塊前的空格
      "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], //函數定義時括號前的空格
      "space-infix-ops": [2, {"int32Hint": true}], //操做符周圍的空格
      "keyword-spacing": 2, //關鍵字先後的空格
      "space-unary-ops": [2, { "words": true, "nonwords": false}], //一元運算符先後不要加空格
      "wrap-regex": 2, //正則表達式字面量用括號括起來
      "no-var": 0, //使用let和const代替var
      "generator-star-spacing": [2, "both"], //生成器函數先後空格
      "max-depth": 0, //嵌套塊深度
      "max-len": 0, //一行最大長度,單位爲字符
      "max-params": 0, //函數最多能有多少個參數
      "max-statements": 0, //函數內最多有幾個聲明
      "no-bitwise": 0, //不容許使用位運算符
      "no-plusplus": 0 //不容許使用++ --運算符
  }
複製代碼
103.如何在 Vs Code 中進行 Lint 校驗提示?如何在 Vs Code 中進行 Lint 保存格式化?

答:

  • 打開終端,運行npm install eslint -g全局安裝ESLint。
  • vscode安裝插件
  • scode 擴展設置
104.ESLint 和 Prettier 的區別是什麼?二者在一塊兒工做時會產生問題嗎?

答:

這倆解決的不是一個問題,ESLint 主要解決的是代碼質量問題;Prettier主要解決的是代碼風格問題。 二者在一塊兒會產生問題。

須要解決: 首先咱們須要使用 eslint-config-prettier 來關掉 (disable) 全部和 Prettier 衝突的 ESLint 的配置(這部分配置就是上面說的,格式問題的配置,因此關掉不會有問題),方法就是在 .eslintrc 裏面將 prettier 設爲最後一個 extends。

// .eslintrc 
{      
    "extends": ["prettier"] // prettier 必定要是最後一個,才能確保覆蓋 
}
複製代碼

(可選,推薦) 而後再啓用 eslint-plugin-prettier ,將 prettier 的 rules 以插件的形式加入到 ESLint 裏面。這裏插一句,爲何"可選" ?當你使用 Prettier + ESLint 的時候,其實格式問題兩個都有參與,disable ESLint 以後,其實格式的問題已經所有由 prettier 接手了。那咱們爲何還要這個 plugin?實際上是由於咱們指望報錯的來源依舊是 ESLint ,使用這個,至關於把 Prettier 推薦的格式問題的配置以 ESLint rules 的方式寫入,這樣至關於能夠統一代碼問題的來源。

// .eslintrc 
{      
    "plugins": ["prettier"],      
    "rules": {        
        "prettier/prettier": "error"      
    }    
}
複製代碼

將上面兩個步驟和在一塊兒就是下面的配置,也是官方的推薦配置

// .eslintrc
{
  "extends": ["plugin:prettier/recommended"]
}
複製代碼
105.如何有效的識別 ESLint 和 Prettier 可能產生衝突的格式規則?如何解決此類規則衝突問題?

答:

  • 鼠標右鍵,選擇文檔格式設置方式。
  • 在彈出的下拉列表中選擇prettier。這時已經使用了prettier格式化了代碼。
  • ctrl+shift+p,而後下拉列表中選擇格式化文檔。

假設你的默認格式化程序是prettier,那麼稍做改變便可,相信你已經知道怎樣操做,再也不贅述。 這樣,你先使用prettier格式化了代碼,再使用eslint去糾正了不符合eslint規則的部分,就實現了二者衝突的解決。

106.在一般的腳手架項目中進行熱更新(hot module replacement)時如何作到 ESLint 實時打印校驗錯誤信息?

答:

Ctrl+s / command+s 時自動修復代碼的格式錯誤,自動修復的規則是讀取項目根目錄的Eslint規則。

107.談談你對 SourceMap 的瞭解?

答:

sourceMap就是一個信息文件,裏面儲存着打包前的位置信息。也就是說,轉換後的代碼的每個位置,所對應的轉換前的位置。有了它,出錯的時候,瀏覽器控制檯將直接顯示原始代碼出錯的位置,而不是轉換後的代碼,點擊出錯信息將直接跳轉到原始代碼位置。方便定位和解決問題。

108.如何調試 Node.js 代碼?如何調試 Node.js TypeScript 代碼?在瀏覽器中如何調試 Node.js 代碼?

答:

從nodejs8開始,node去掉了_debugger , 內部集成了inspect , 以往使用node-inspect實現的在線調試再也不可用.node8開始要用新方法了。

  • 在服務端用inspect模式運行nodejs
node --inspect-brk=0.0.0.0:8080 index.js
複製代碼
  • 打開chrome瀏覽器 地址欄輸入chrome://inspect,在彈出的界面中輸入ip:port便可調試。
109.列舉你知道的全部構建工具並說說這些工具的優缺點?這些構建工具在不一樣的場景下應該如何選型?

答:

Grunt、Gulp、Webpack、vite、Rollup

110.VS Code 配置中的用戶和工做區有什麼區別?

答:

VS Code提供了兩種設置方式:

  • 用戶設置: 這種方式進行的設置,會應用於該用戶打開的全部工程;

  • 工做空間設置:工做空間是指使用VS Code打開的某個文件夾,在該文件夾下會建立一個名爲.vscode的隱藏文件夾,裏面包含着僅適用於當前目錄的VS Code的設置,工做空間的設置會覆蓋用戶的設置。

"用戶設置"會應用於用戶打開的全部工程;

"工做區設置"僅適用於當前目錄的VS Code的設置。

111.VS Code 的插件能夠只對當前項目生效嗎?

答:

固然不是。

112.你所知道的測試有哪些測試類型?

答:

功能測試,性能測試,界面測試

113.你所知道的測試框架有哪些?

答:

Selenium、cypress、Appium 、Requests、Jmeter、Mitmproxy

114.什麼是 e2e 測試?有哪些 e2e 的測試框架?

答:

端到端測試;

cypress 、Selenium 、puppeteer、nightwatch

115.假設如今有一個插入排序算法,如何對該算法進行單元測試?

答:

// 見試卷背面
複製代碼
116.CDN 服務如何實現網絡加速?

答:

CDN的工做原理就是將您源站的資源緩存到位於全球各地的CDN節點上,用戶請求資源時,就近返回節點上緩存的資源,而不須要每一個用戶的請求都回您的源站獲取,避免網絡擁塞、緩解源站壓力,保證用戶訪問資源的速度和體驗。

117.WebSocket 使用的是 TCP 仍是 UDP 協議?

答:

是基於TCP的,websocket的協議是在TCP/IP協議簇的應用層,和http在同一層。

118.什麼是單工、半雙工和全雙工通訊?

答:

  • 單工數據傳輸只支持數據在一個方向上傳輸;
  • 半雙工數據傳輸容許數據在兩個方向上傳輸,可是,在某一時刻,只容許數據在一個方向上傳輸,它其實是一種切換方向的單工通訊;
  • 全雙工數據通訊容許數據同時在兩個方向上傳輸,所以,全雙工通訊是兩個單工通訊方式的結合,它要求發送設備和接收設備都有獨立的接收和發送能力。
119.簡單描述 HTTP 協議發送一個帶域名的 URL 請求的協議傳輸過程?(DNS、TCP、IP、鏈路)

答: 略(內容太多)

簡單回答一個不全的答案,剩下須要深刻的等面試官一個一個問:

  • 瀏覽器經過請求獲得一個HTML文本
  • 渲染進程解析HTML文本,構建DOM樹
  • 解析HTML的同時,若是遇到內聯樣式或者樣式腳本,則下載並構建樣式規則(stytle rules),若遇到JavaScript腳本,則會下載執行腳本。
  • DOM樹和樣式規則構建完成以後,渲染進程將二者合併成渲染樹(render tree)
  • 渲染進程開始對渲染樹進行佈局,生成佈局樹(layout tree)
  • 渲染進程對佈局樹進行繪製,生成繪製記錄
  • 渲染進程的對佈局樹進行分層,分別柵格化每一層,並獲得合成幀
  • 渲染進程將合成幀信息發送給GPU進程顯示到頁面中
120.什麼是正向代理?什麼是反向代理?

答:

代理其本質上能夠理解爲中介。當A和B不方便進行交互時,每每會引入一箇中間角色C,那麼C即是中介,即是代理。

正向代理服務器一般位於客戶端和服務器之間,相似一個跳板機,經過代理服務器能夠訪問到目標服務器。

正向代理時,一般,客戶端發送對目標服務器的請求,代理服務器在中間將請求轉發給目標服務器,並將結果返回給客戶端。

反向代理與正向代理剛好相反,代理服務位於服務器端。

對客戶端來講,反向代理服務器就好像是目標服務器。反向代理服務器接收客戶端發來的請求,而後將其分發到內網的服務器,並將內網服務器返回的結果返回給客戶端。

整個過程客戶端並不會感知到反向代理後面的服務,也不須要客戶端作任何設置,只須要把反向代理服務器當成真正的服務器就行。

121.Cookie 能夠在服務端生成嗎?Cookie 在服務端生成後的工做流程是什麼樣的?

答:

能夠。 HTTP 協議中的 Cookie 包括 Web Cookie 和瀏覽器 Cookie,它是服務器發送到 Web 瀏覽器的一小塊數據。服務器發送到瀏覽器的 Cookie,瀏覽器會進行存儲,並與下一個請求一塊兒發送到服務器。一般,它用於判斷兩個請求是否來自於同一個瀏覽器,例如用戶保持登陸狀態。

122.Session、Cookie 的區別和關聯?如何進行臨時性和永久性的 Session 存儲?

答:

  • Session

客戶端請求服務端,服務端會爲此次請求開闢一塊內存空間,這個對象即是 Session 對象,存儲結構爲 ConcurrentHashMap。Session 彌補了 HTTP 無狀態特性,服務器能夠利用 Session 存儲客戶端在同一個會話期間的一些操做記錄。

  • Cookies

HTTP 協議中的 Cookie 包括 Web Cookie 和瀏覽器 Cookie,它是服務器發送到 Web 瀏覽器的一小塊數據。服務器發送到瀏覽器的 Cookie,瀏覽器會進行存儲,並與下一個請求一塊兒發送到服務器。一般,它用於判斷兩個請求是否來自於同一個瀏覽器,例如用戶保持登陸狀態。

服務器端session,若是你不指定session的存儲時間,在你打開的瀏覽器中存儲的值,是能夠在新打開的框口內獲得的,關閉後就自動消失(消失的實際上是session_id,由於session的機制是依賴於cookie的(還能夠依賴其餘的)。

123.設置 Cookie 時候如何防止 XSS 攻擊?

答:

在服務器端設置cookie的時候設置 http-only, 這樣就能夠防止用戶經過JS獲取cookie。對cookie的讀寫或發送通常有以下字段進行設置:

  • http-only: 只容許http或https請求讀取cookie、JS代碼是沒法讀取cookie的(document.cookie會顯示http-only的cookie項被自動過濾掉)。發送請求時自動發送cookie.
  • secure-only: 只容許https請求讀取,發送請求時自動發送cookie。
  • host-only: 只容許主機域名與domain設置完成一致的網站才能訪問該cookie。

設置Cookie,能夠防止攻擊者拿到正經常使用戶的Cookie冒充身份非法調用網站接口。

124.簡單描述一下用戶免登錄的實現過程?可能會出現哪些安全性問題?通常如何對用戶登陸的密碼進行加密?

答:

在用戶第一次登陸成功的時候,後端會返回一個 Token,這個值Token 主要的做用就是用於識別用戶的身份。至關於帳號密碼。正常狀況下,前端給後端發送請求的時候,後端都須要先判斷用戶的身份,來返回相應的數據給用戶。獲取到Token後,你須要把 Token 存在 Cookie中。接着向服務器發送請求時,你從 Cookie 中取出 Token,在請求頭中攜帶上 Token 。Token過時時間設置足夠長,只要token沒過時,這段時間用戶都是免登陸。

安全問題:其餘人使用本機,實現免登陸,沒法在每次使用應用時驗證用戶的身份。提供了便捷,失去了安全校驗。

對用戶登陸的密碼進行加密,密碼MD5化,不使用明文傳輸。

125.HTTP 中提高傳輸速率的方式有哪些?經常使用的內容編碼方式有哪些?

答:

  • 使用壓縮技術把實體主體壓小,在客戶端再把數據解析。
  • 使用分塊傳輸編碼,將實體主體分塊傳輸,當瀏覽器解析到實體主體就可以顯示了。

經常使用的內容編碼方式:

  • 非歸零碼
  • 曼徹斯特編碼
  • 差分曼徹斯特編碼
126.傳輸圖片的過程當中若是忽然中斷,如何在恢復後從以前的中斷中恢復傳輸?

答:

文件的斷點續傳,

前端工做

  • 爲每個文件切割塊添加不一樣的標識
  • 當上傳成功的以後,記錄上傳成功的標識
  • 當咱們暫停或者發送失敗後,能夠從新發送沒有上傳成功的切割文件

後端工做

  • 接收每個切割文件,並在接收成功後,存到指定位置,並告訴前端接收成功
  • 收到合併信號,將全部的切割文件排序,合併,生成最終的大文件,而後刪除切割小文件,並告知前端大文件的地址
127.什麼是代理?什麼是網關?代理和網關的做用是什麼?

答:

代理是中間人,使用代理的主機發出的IP報文的目的IP是代理的,可是會在應用層裏明確告訴代理,本身真實需求是什麼。 網關即Gateway,它是鏈接基於不一樣通訊協議的網絡的設備,使文件能夠在這些網絡之間傳輸。

128.HTTPS 相比 HTTP 爲何更加安全可靠?

答:

由於 HTTPS 保證了傳輸安全,防止傳輸過程被監聽、防止數據被竊取,能夠確認網站的真實性(具體細節二面再說)。不過須要注意的是,即使使用 HTTPS 仍可能會被抓包,由於HTTPS 只防止用戶在不知情的狀況下通訊被監聽,若是用戶主動授信,是能夠構建「中間人」網絡,代理軟件能夠對傳輸內容進行解密。

129.什麼是對稱密鑰(共享密鑰)加密?什麼是非對稱密鑰(公開密鑰)加密?哪一個更加安全?

答:

傳統的對稱式加密須要通信雙方都保存同一份密鑰,經過這份密鑰進行加密和解密。因此非對稱加密也稱爲單密鑰加密。 在非對稱加密中,加密和解密使用的是不一樣的密鑰。非對稱加密中的密鑰分爲公鑰和私鑰。公鑰顧名思義就是公開的,任何人均可以經過公鑰進行信息加密,可是隻有用戶私鑰的人才能完成信息解密。非對稱加密帶來了一個好處,避免了對稱式加密須要傳輸和保存同一份密鑰的痛苦。

非對稱加密必定比對稱加密機密性更高嗎? 不必定, 由於機密性高低是根據祕鑰長度而變化的。並且非對稱加密最大的問題,就是性能較差,沒法應用於長期的通訊。

130.你以爲 HTTP 協議目前存在哪些缺點?

答:

HTTP不具有必要的安全功能,與最初的設計相比,現今的Web網站應用的HTTP協議的使用方式已發生了翻天覆地的變化。幾乎現今全部的Web網站都會使用會話(session)管理、加密處理等安全性方面的功能,而HTTP協議內並不具有這些功能。

從總體上看,HTTP就是一個通用的單純協議機制。所以它具有較多優點,可是在安全性方面則呈劣勢。 就拿遠程登陸時會用到的SSH協議來講,SSH具有協議級別的認證及會話管理等功能,HTTP協議則沒有。另外在架設SSH服務方面,任何人均可以輕易地建立安全等級高的服務,而HTTP即便已架設好服務器,但若想提供服務器基礎上的Web應用,不少狀況下都須要從新開發。

所以,開發者須要自行設計並開發認證及會話管理功能來知足Web應用的安全。而自行設計就意味着會出現各類形形色色的實現。結果,安全等級並不完備,可仍在運做的Web應用背後卻隱藏着各類容易被攻擊者濫用的安全漏洞的Bug。

131.在 React 中如何識別一個表單項裏的表單作到了最小粒度 / 代價的渲染?

答:

首先,最小粒度 / 代價的渲染的表單項or組件應該具有什麼樣的特性:

  • 簡單易用
  • 父組件可經過代碼操做表單數據
  • 避免沒必要要的組件重繪
  • 支持自定義組件
  • 支持表單校驗

而這個表單項or組件實現起來主要分爲三部分:

  • Form:用於傳遞表單上下文。
  • Field: 表單域組件,用於自動傳入value和onChange到表單組件。
  • FormStore: 存儲表單數據,封裝相關操做。

爲了能減小使用ref,同時又能操做表單數據(取值、修改值、手動校驗等),我將用於存儲數據的FormStore,從Form組件中分離出來,經過new FormStore()建立並手動傳入Form組件。

符合以上標準,就能夠認爲這個表單作到了最小粒度/最小代價的渲染。

對於有大量表單的頁面,能夠使用Lighthouse做爲衡量工具,來排查和優化頁面。

132.在 React 的開發的過程當中你能想到哪些控制渲染成本的方法?

答:

  • 使用 shouldComponentUpdate 規避冗餘的更新邏輯

  • PureComponent + Immutable.js

  • React.memo 與 useMemo

使用 useMemo,咱們能夠對函數組件的執行邏輯進行更加細粒度的管控(尤爲是定向規避掉一些高開銷的計算),同時也彌補了 React.memo 沒法感知函數內部狀態的遺憾,這對咱們總體的性能提高是大有裨益的。

133.Vue CLI 3.x 的插件系統是如何設計的?

答:

Vue-cli@3.0 採用了一套基於插件的架構,它將部分核心功能收斂至 CLI 內部,同時對開發者暴露可拓展的 API 以供開發者對 CLI 的功能進行靈活的拓展和配置。

整個插件系統當中包含2個重要的組成部分:

  • @vue/cli,提供 cli 命令服務,例如vue create建立一個新的項目;

@vue/cli 提供 vue cli 命令,負責偏好設置,生成模板、安裝插件依賴的工做,例如 vue create <projectName> vue add <pluginName>

  • @vue/cli-service,提供了本地開發構建服務。

@vue/cli-service 做爲 @vue/cli 整個插件系統當中的內部核心插件,提供了 webpack 配置更新,本地開發構建服務

前者主要完成了對於插件的依賴管理,項目模板的拓展等,後者主要是提供了在運行時本地開發構建的服務,同時後者也做爲 @vue/cli 整個插件系統當中的內部核心插件而存在。在插件系統內部也對核心功能進行了插件化的拆解,例如 @vue/cli-service 內置的基礎 webpack 配置,npm script 命令等。

@vue/cli-service 插件系統當中幾個核心的模塊:

  • Service.js 提供服務的基類,它提供了 @vue/cli 生態當中本地開發構建時:插件加載(包括內部插件和項目應用插件)、插件的初始化,它的單例被全部的插件所共享,插件使用它的單例去完成 webpack 的更新。
  • PluginAPI.js 提供供插件使用的對象接口,它和插件是一一對應的關係。全部供 @vue/cli-service 使用的本地開發構建的插件接收的第一個參數都是 PluginAPI 的實例( api ),插件使用這個實例去完成 CLI 命令的註冊及對應服務的執行、webpack 配置的更新等。
134.Webpack 中的插件機制是如何設計的?

答:

Webpack 插件機制的目的是爲了加強 Webpack 在項目自動化構建方面的能力。在webpack中 Loader 就是負責完成項目中各類各樣資源模塊的加載,從而實現總體項目的模塊化,而 Plugin 則是用來解決項目中除了資源模塊打包之外的其餘自動化工做,對比 Loader 只是在模塊的加載環節工做,而插件的做用範圍幾乎能夠觸及 Webpack 工做的每個環節。

Webpack 的插件機制就是咱們在軟件開發中最多見的鉤子機制。鉤子機制也特別容易理解,它有點相似於 Web 中的事件。在 Webpack 整個工做過程會有不少環節,爲了便於插件的擴展,Webpack 幾乎在每個環節都埋下了一個鉤子。這樣咱們在開發插件的時候,經過往這些不一樣節點上掛載不一樣的任務,就能夠輕鬆擴展 Webpack 的能力。

135.\r\n(CRLF) 和 \n (LF)的區別是什麼?(Vs Code 的右下角能夠切換)

答:

\r\n 表示回車並換行 (則會將打印紙張上移一行,且下一個打字位置將回到該行的最左側) \n 表示換行

136./dev/null 的做用是啥?

答:

/dev/null:表示 的是一個黑洞,一般用於丟棄不須要的數據輸出, 或者用於輸入流的空文件

  • 將無用的輸出流寫入到黑洞丟棄。

curl -Iwww.baidu.com 2>/dev/null | head -l 錯誤信息定位到黑洞

  • 清空文件

cat /dev/null > /home/omc/h.txt

  • 在書寫定時任務總,規範的寫法就是將全部定時任務腳本結尾加上>/dev/null 2>&1,讓全部的輸出流(包括錯誤的和正確的)都定向到空設備丟棄。
137.如何在 Mac 的終端中設置一個命令的別名?

答:

  • 打開編輯.bash_profile
  • 配置別名
alias dev="npm run dev"
複製代碼
  • 保存以後從新打開terminal或者執行
138.如何在 Windows 中設置環境變量?

答:

  • 查看當前全部可用的環境變量:輸入 set 便可查看。
  • 查看某個環境變量:輸入 「set 變量名」便可,好比想查看path變量的值,即輸入 set path
  • 修改環境變量 :輸入 「set 變量名=變量內容」便可
  • 修改變量(設置爲空:若是想將某一變量設置爲空,輸入「set 變量名=」便可。 修改爲其餘值:輸入「set 變量名=%變量名%;變量內容」)
139.Mac 的文件操做系統默認區分文件路徑的大小寫嗎?

答:

默認不區分,想要區分,就在重裝系統or單獨分區的時候,選擇 MAC OS 擴展(區分大小寫,日誌格式)

140.編寫 Shell 腳本時如何設置文件的絕對路徑?

答:

SHELL_FOLDER=$(dirname $(readlink -f "$0"))
複製代碼
141.Session、Cookie 的區別和關聯?如何進行臨時性和永久性的 Session 存儲?

答:

  • Session

客戶端請求服務端,服務端會爲此次請求開闢一塊內存空間,這個對象即是 Session 對象,存儲結構爲 ConcurrentHashMap。Session 彌補了 HTTP 無狀態特性,服務器能夠利用 Session 存儲客戶端在同一個會話期間的一些操做記錄。

  • Cookies

HTTP 協議中的 Cookie 包括 Web Cookie 和瀏覽器 Cookie,它是服務器發送到 Web 瀏覽器的一小塊數據。服務器發送到瀏覽器的 Cookie,瀏覽器會進行存儲,並與下一個請求一塊兒發送到服務器。一般,它用於判斷兩個請求是否來自於同一個瀏覽器,例如用戶保持登陸狀態。

服務器端session,若是你不指定session的存儲時間,在你打開的瀏覽器中存儲的值,是能夠在新打開的框口內獲得的,關閉後就自動消失(消失的實際上是session_id,由於session的機制是依賴於cookie的(還能夠依賴其餘的)。

142.如何部署 Node.js 應用?如何處理負載均衡中 Session 的一致性問題?

答:

題目太大,篇幅有限,略。

143.如何提高 Node.js 代碼的運行穩定性?

答:

  • 保障進程安全

因爲一個用戶的異常訪問或者數據異常,加上沒有作好異常處理和安全保護,直接致使了整個 Node.js 服務重啓了,從而中斷了全部人的請求,用戶體驗很是差。 ①因爲 Node.js 使用的是 JavaScript,而JavaScript 是一個弱類型語言,所以在現網常常會引起一些由代碼邏輯的異常致使的進程異常退出。 ②其次在 Node.js 中也常常會由於內存的使用不當,致使內存泄漏,當在 64 位系統中達到 1.4 G(32 位系統 0.7 G)時,Node.js 就會異常崩潰。 ③再而因爲Node.js 的 I/O 較多也較爲頻繁,當啓用較多 I/O 句柄,可是沒有及時釋放,一樣會引起進程問題。

  • parameters error

關於 JSON.parse 不少時候咱們都比較天然地將其餘接口或者第三方的數據拿來解析,可是這裏每每會忽略其非 JSON 字符串的問題,在這裏須要進行try catch 異常判斷。

  • other errors

當前 Node.js 的 Promise 應用愈來愈普遍了,所以對於 Promise 的 catch 也應該多進行重視,對於每一個 Promise 都應該要處理其異常 catch 邏輯,否則系統會提示 warning 信息。 還有一些常見的長鏈接的服務,好比 Socket、Redis、Memcache 等等,咱們須要在鏈接異常時進行處理,若是沒有處理一樣會致使異常,好比 Socket 提供了 Socket.on(‘error’) 的監聽。

  • 注意服務異常方面的內存泄露

設置最大臨時緩存數,超出則不使用緩存;設置最大緩存句柄數,超出則不使用緩存;定時清理當前的臨時緩存和句柄緩存。

  • 避免全局變量

通常狀況下不建議使用全局變量,全局變量必需要有必定的上限和清理規則才能保證服務的安全。

  • 避免單例模塊中的變量內存泄漏

要注意一個點,有些模塊咱們使用單例的模式,就是在每次 require 後都返回這個對象,這種狀況也比較容易引起內存泄漏的問題。由於單例模式會引起每一個用戶訪問的數據的疊加。

  • 主動關閉打開文件後未關閉的狀況

通常打開文件句柄後,咱們都應該主動關閉,若是未主動關閉,就會致使文件句柄愈來愈多,從而引起句柄泄漏問題。

144.GraphQL 與 Restful 的區別,它有什麼優勢?

答:

QraphQL是對後端REST API向業務層的聚合與裁剪,REST更關注對業務細粒度的拆分與重用。

其實就是增長了一箇中間層對前端的請求和響應作預處理和後處理,前端的工做少了,後端的工做也沒多,卻加入了中端的依賴,好處是避免前端和後端的屢次遠距離的交互。 而graphql存在一個很難控制的問題就是查詢複雜度。在開發過程當中須要把控好解析粒度,而就目前主流關係型數據庫,restful api依舊是最好的選擇。graphql準確的說在查詢圖結構數據時更有優點,這也是其名稱的主意。

145.Vue SSR 的工做原理?Vuex 的數據如何同構渲染?

答:

在Vue SSR中,建立Vue實例、建立store和建立router都是套了一層工廠函數的,目的就是避免數據的交叉污染。在服務端只能執行生命週期中的created和beforeCreate,緣由是在服務端是沒法操縱dom。服務端渲染和客戶端渲染不一樣,須要建立兩個entry分別跑在服務端和客戶端,而且須要webpack對其分別打包;SSR服務端請求不帶cookie,須要手動拿到瀏覽器的cookie傳給服務端的請求。SSR要求dom結構規範,由於瀏覽器會自動給HTML添加一些結構好比tbody,可是客戶端進行混淆服務端放回的HTML時,不會添加這些標籤,致使混淆後的HTML和瀏覽器渲染的HTML不匹配。

對於同構應用來講,咱們必須實現客戶端與服務端的路由、模型組件、數據模型的共享。Vuex是實現咱們客戶端和服務端的狀態共享的關鍵,咱們能夠不使用vuex,可是咱們得去實現一套數據預取的邏輯;能夠嘗試封裝一個能夠給組件們共享的EventBus,在main.js中export出咱們的EventBus以便兩個entry使用,接下來是咱們的兩個entry了。server用來匹配咱們的組件並調用組件的asyncData方法去獲取數據,client用來將預渲染的數據存儲到咱們eventBus中的data中。這樣就至關於實現類Vuex的功能。

146.SSR 技術和 SPA 技術的各自的優缺點是什麼?

答:

SSR:

  • 更利於SEO。
  • 更利於首屏渲染
  • 服務端壓力較大

SPA:

  • 頁面之間的切換很是快
  • 必定程度上減小了後端服務器的壓力(不用管頁面邏輯和渲染)
  • 後端程序只須要提供API,徹底不用管客戶端究竟是Web界面仍是手機等
  • 不利於SEO
  • 首屏加載壓力大
147.如何處理 Node.js 渲染 HTML 壓力過大問題?

答:

暫時沒遇到慢的狀況,沒法回答,放棄此題。

148.你所知道的 CI / CD 工具備哪些?在項目中有接觸過相似的流程嗎?

答:

CICD,持續集成和持續交付,各個部門合做同一個項目時,各類管理的倉庫發生變動,就會自動對代碼進行測試和構建,等Pipline跑完以後,反饋運行結果,自動打出最新的master包。

經常使用的CICD工具備Jenkins 、Travis 等等。

149.若是讓你實現一個 Web 前端的 CI / CD 工程研發平臺,你會如何設計?

答:

題目太大,篇幅有限,略。

150.若是咱們須要將已有項目中的線上產物資源(例如圖片)轉換成本地私有化資源,你有什麼解決方案?

答:

不知道這道題是想問我如何把線上已有的資源從新蒐集成本地的素材中心,仍是單純的把 顯示資源換成本地的。 我對這道題目的理解是: 好比一個頁面內有一個el-table,5000頁,每頁20個商品,你也不知道每一個商品的連接,每一個商品後面的URL尚未規律,此時對於開發者而言,你所知道的也就是頁面的節點、接口的API這兩個關鍵信息了。想要把這20*5000個商品的商品詳情圖從新整理本地素材中心,那就手寫一個爬蟲,一次性爬完,把全部的資源路徑拿下來。或者寫一個chrome插件一鍵收集。

若是隻是單純的想把本應用的生產環境靜態資源換成本地的,直接找到 應用的靜態資源連接,找到它的baseUrl,換成本地的dev環境下的baseUrl就能夠了。通常項目的靜態資源和服務端服務都是分開發的,二者獨立,分開推送。而且每次發版,靜態資源都有本身所屬的版本號。

151.如何使用 Vue CLI 3.x 定製一個腳手架?好比內部自動集成了 i18n、 axios、Element UI、路由守衛等?

答:

// 見試卷背面
複製代碼
152.Jenkins 如何配合 Node.js 腳本進行 CI / CD 設計?

答:

// 見試卷背面
複製代碼
153.若是讓你設計一個通用的項目腳手架,你會如何設計?一個通用的腳手架通常須要具有哪些能力?

答:

參考vue-cli,至少知足如下幾點:

  • 解耦:腳手架與模板分離
  • 腳手架負責構建流程,經過命令行與用戶交互,獲取項目信息
  • 模板負責統一項目結構、工做流程、依賴項管理
  • 腳手架須要檢測模板的版本是否有更新,支持模板的刪除與新建
154.若是讓你設計一個通用的工具庫,你會如何設計?一個通用的工具庫通常須要具有哪些能力?

答:

至少知足4個條件

  • 選擇通用、合適、便捷的構建工具,方便打包代碼,而且易於調試;
  • 注重代碼質量和開發效率,有類型推斷及靜態檢查能力(提早寫好TS);
  • api簡單易用,易於上手,文檔實時更新;
  • 易於開發者拓展、版本升級保持向前兼容。
155.假設你本身實現的 React 或 Vue 的組件庫要設計演示文檔,你會如何設計?設計的文檔須要實現哪些功能?

答:

寫代碼以前就要寫好文檔,沒有文檔的組件庫=白寫。文檔不只僅是給使用者看的,同時也是維護者給本身產品的一種宏觀把控。 文檔第一要實時、簡潔,易於上手;第二要作好版本控制,不管是Vue組件庫,仍是小程序組件庫,不少時候都要考慮大環境的限制(好比Vue的api變更、小程序基礎庫的變更),要實時更新和兼容,遇到沒法兼容的版本隔閡,要特別說明,適當的地方throw waring;第三,要支持按需加載和通用性和高拓展性,我曾經也只是一個組件庫api調用大師,如今也在維護本身的組件庫,我深知UI庫最大的問題不是樣式不全,而是別人以爲你的很差用,爲了用你一個toast而要下載整個組件庫,得不償失,按需加載很重要,支持拓展更重要,方便其餘的開發者在你的組件庫上打補丁。

156.在設計工具庫包的時候你是如何設計 API 文檔的?

答:

這個不算是問題,設計和整理API文檔,能體現維護者對本身產品的態度,在開發之時就要開始整理,而不是等所有開發完。在寫API文檔的同時還能夠幫助你從旁觀者的角度審覈你的產品功能。具體API文檔的實現方案,能夠使用vuepress、rap2等等之類的。

157.什麼是單點登陸?如何作單點登陸?

答: 單點登陸是指在同一賬號平臺下的多個應用系統中,用戶只需登陸一次,就可訪問全部相互信任的應用系統。好比你在網頁中登陸了百度雲盤,隨後你再去貼吧發帖 是不須要二次登陸的。

單點登陸的本質就是在多個應用系統中共享登陸狀態。若是用戶的登陸狀態是記錄在 Session 中的,要實現共享登陸狀態,就要先共享 Session,好比能夠將 Session 序列化到 Redis 中,讓多個應用系統共享同一個 Redis,直接讀取 Redis 來獲取 Session。

由於不一樣的應用系統有着不一樣的域名,儘管 Session 共享了,可是一個企業不一樣應用的域名不一樣,依然可能出現跨站or跨域。

前端方面的實現方式:

  • 父域 Cookie
  • 認證中心
  • LocalStorage 跨域
158.如何作一個項目的國際化方案?

答:

方式有2類,

1、經過i18等插件,在項目代碼中定義語言環境,經過通用的語言翻譯函數,提供的功能就是靜態和動態文案的翻譯。主要思路就是經過構建工具去完成樣式, 圖片替換, class屬性等的替換工做,在業務代碼中不會出現過多的因國際化而多出的變量名,同時使用一個通用的翻譯函數去完成靜態文案及動態文案的翻譯工做,而不用使用不一樣框架提供的相應的國際化插件。

2、在項目中不使用插件,而是封裝一套通用的翻譯函數,在後臺服務那邊定義語言環境,經過API服務調用不一樣的主題和語言環境,返回不一樣的字段和頁面內容。(項目代碼中無中文,全是用翻譯函數包裹後的變量)

159.如何作一個項目的監控和埋點方案?

答:

監控是作法,埋點是工具,數據分析是目的。這三者一條道上的不一樣角色。

監控什麼,通常前端項目所要監控的數據主要分爲5種:

  • 系統的生命週期數據,可用於觀察頁面性能狀況、總體訪問狀況等。

  • HTTP 測速數據,可用於觀察外部服務調用狀況 、頁面性能優化等。

  • 系統異常數據,可用於觀察系統穩定性、系統異常問題。

  • 用戶行爲數據,可用於觀察頁面穩定性、總體訪問狀況等。

  • 用戶日誌,用於進行用戶反饋的問題排查。

前端常見的埋點方案有三種:代碼埋點、可視化埋點、無痕埋點。 無論使用哪一種埋點方式,咱們都須要對數據進行標準化處理。因爲最終的數據須要落盤到服務端並進行計算和監控,所以咱們須要將採集的數據,按照與服務端約定好的協議格式來進行轉換。

爲了快速發現並定位問題,咱們須要將這些埋點的數據、運行的日誌上報發送到服務端,服務端再進行轉換、存儲、計算和監控。通常採用new一個gif的形式上報,同時爲了不數據的上報過於頻繁、增長服務端的壓力,咱們能夠在本地進行數據的整合,好比經過隊列或數組的方式進行維護,而後選擇如下方式/時機進行上報。

插一句話,有些場景咱們須要知道用戶設備崩潰or異常的數據狀況,但每每這種狀況下埋點沒有觸發,或者該觸發的時候用戶退出應用了,因此這裏建議把須要埋點上報的數據離線上傳,就是把埋點上報隊列,放在緩存裏,慢慢吐泡泡,同時不跟主業務流程中的API服務競爭資源。

數據上報完成後,通常來講須要搭建可視化的管理端,來對這些數據進行直觀的監控。在平常監控中,咱們還會經過對監控數據、配置告警閾值等方式,結合郵件、機器人等方式推送到相關的人員,來及時發現並解決問題。好比若是5分鐘的異常數量大於閥值,就釘釘or短信報警,讓相關同事及時修復功能異常。若是想要作得更多,咱們甚至能夠結合其餘系統來進行協做,好比關聯 BUG 管理系統、自動生成 BUG 單,將 BUG 單綁定到對應的版本分支上,經過提交對應的修復分支、進行測試驗證後,自動地扭轉 BUG 單狀態,等等。

160.如何建設項目的穩定性(監控、灰度、錯誤降級、回滾...)?

答:

我沒有當過Leader,如下純從隊友角度回答。

事前:進行合理的分工排期,對項目風險進行把控,學會自測,不要把全部的風險都拋給測試同窗處理。

過後:對發佈上線的產品及時監控,觀察是否運行正常,是否符合預期。遇到上線後帶了bug,或者上線版本異常,此時首要目標時恢復業務,及時回滾,不要嘗試用新版本覆蓋。

覆盤:及時發現本身的問題並改進,避免掉進同一個坑;讓團隊成員和管理者知道本身在作什麼;整理沉澱和分享項目經驗,不奢求讓整個團隊都獲得成長,至少本身在這個產品開發週期內有所收穫。

161.通常管理後臺型的應用須要考慮哪些性能方面的優化?

答:

通常的管理後臺應用,基本就是Vue+Element,或者React+antd這樣的組件,而且業務仍是toB,to內部使用的這種,通常都是SPA,在性能方面,也不是那麼講究, 代碼角度的優化:

  • 各個模塊間解耦,將各個模塊統一交由框架處理,梳理各個模塊的職責,明確每一個模塊負責的工做和提供的功能,肯定各個模塊間的邊界和調用方式。
  • 模塊以服務的方式進行註冊,經過聲明依賴的方式來獲取須要使用的服務,框架會對模塊間依賴關係進行分析,判斷某個服務是否須要初始化和銷燬,從而避免了沒必要要的服務被加載。
  • 拆公共庫、拆組件庫,代碼複用,對重複邏輯進行組件和公共庫的抽象和封裝。避免出現一個方法重複處處copy的狀況;UI庫按需引用,移除沒必要要的代碼(好比使用 Tree-shaking)。異步加載模塊,根據業務須要將模塊拆分紅多個步驟加載(好比優化依賴注入框架對模塊分批初始化)。
  • 差別化服務,對於不一樣場景只加載所須要的模塊內容(好比讀寫分離)。

工程角度的優化:

  • 靜態資源使用 CDN
  • 壓縮文件
  • 對於圖表比較多的CRM管理平臺應用,考慮圖片優化,好比精靈圖、懶加載
  • HTTP2
  • 經過 webpack 按需加載代碼,提取第三庫代碼,減小 ES6 轉爲 ES5 的冗餘代碼

騷操做禁區

  • 不要在後臺管理儀表盤首頁一次性請求大量的echart圖表數據,若是這個頁面的訪問量很少,不要嘗試在登陸後自動跳轉到這個頁面,除非它真的很快,否則就是無效加載。
  • 前端不要嘗試請求 第0頁第100000條 數據來嘗試讀取某個表的所有數據,問後端要接口,別本身浪,等頁面卡死了,運營拿小鍋鍋敲你頭。
162.簡述一些提高項目體驗的案例和技術方案(骨架屏、Loading 處理、緩存、錯誤降級、請求重試...)?

答:

  • 直播活動,商品詳情頁訪問量大,頁面加骨骼屏,等待時間過長時loading引導客戶先去其餘活動轉轉,不讓用戶盲等。
  • 對於不管是H5仍是小程序,埋點數據所有走離線上傳,把埋點隊列裏的埋點請求所有放在緩存裏,一個一個慢慢發送慢慢吐泡泡,既保證了不與主流程業務請求競爭,也不會由於用戶設備退出應用閃退,而偵測不到數據,由於下次進入時只要緩存裏有沒吐完的泡泡(埋點請求隊列)就繼續發送。
  • 應用的業務http請求封裝一層 熔斷任務隊列,增長重試機制,增長token自動刷新請求等等。
  • 應用頁面適當的緩動動畫,不只能夠爲請求數據爭取時間,還能夠提升用戶體驗,不那麼突兀。
  • H5可以使用龍骨動畫、spine動畫,小程序能夠使用幀動畫、css animation提升頁面的動感,以及必定的互動營銷效果。從實現和編碼方面都比裸寫DOM要好得多。
163.假設須要對頁面設計一個水印方案,你會如何設計?

答:

  • 經過canvas生成水印,用canvas來生成base64圖片,經過CanIUse網站查詢兼容性。
  • 經過SVG生成水印,相比Canvas,SVG有更好的瀏覽器兼容性,使用SVG生成水印的方式與Canvas的方式相似,只是base64Url的生成方式換成了SVG。
  • 服務端畫圖,經過NodeJS生成水印。前端發一個請求,參數帶上水印內容,後臺返回圖片內容。
164.如何設計一個通用的 JSON Schema 協議使其能夠動態渲染一個通用的聯動表單?

答:

// 見試卷背面
複製代碼
165.通常的低代碼平臺須要具有哪些能力?

答:

阿里的imgCook、雲鳳蝶、京東的通天塔、滿幫的碼良、徐小夕的H5 Doring等等,無一例外,至少都知足一下幾種能力:

  • 面向業務的軟件設計模式,低代碼主要是給運營用的,經過模塊or組件的使用,給運營人員提供物料,搭建他們本身想要的營銷活動頁面。
  • 可以提供可複用業務組件的知識庫,組件之間能夠相互聯合,組成新的業務組件,而不是單一獨立的基礎組件,可複用性、可拓展性很是高。
  • 可以方便實現與第三方系統整合的流程整合能力與數據整合能力等,可以以SDK,或者npm包的形式嵌入到其餘應用,或者其餘應用嵌入進來,支持數據之間的整合。
  • 可以支持多種部署模式,不受平臺自己的限制
  • 支持高度可配置化,以知足不一樣環境、不一樣規模、不一樣業務場景的特定要求
166.使用 TypeScript 語法將沒有層級的扁平數據轉換成樹形結構的數據

答:

interface Config {
	id?: string
	pid?: string
	children?: string
}

/** * constrcut 方法 * 根據提供的 id, pid 和 children 將一個個節點構建成一棵或者多棵樹 * @param nodes 節點對象 * @param config 配置對象 */
export function construct(nodes: object[], config?: Config) {
	const id = config && config.id || 'id'
	const pid = config && config.pid || 'pid'
	const children = config && config.children || 'children'

	const idMap = {}
	const jsonTree = []

	nodes.forEach((v) => { v && (idMap[v[id]] = v) })
	nodes.forEach((v) => {
		if (v) {
			let parent = idMap[v[pid]]
			if (parent) {
				!parent[children] && (parent[children] = [])
				parent[children].push(v)
			} else {
				jsonTree.push(v)
			}
		}
	})

	return jsonTree
}

/** * destruct 方法 * 根據配置的 id, pid 和 children 把解構化的樹型對象拆解爲一個個節點 * @param forest 單個或者多個樹型對象 * @param config 配置 */
export function destruct(forest: object[] | object, config?: Config) {
	const id = config && config.id || 'id'
	const pid = config && config.pid || 'pid'
	const children = config && config.children || 'children'

	function flatTree(tree: object) {
		const queue = [tree]
		const result = []
		while (queue.length) {
			let currentNode = queue.shift()
			if (currentNode.hasOwnProperty(id)) {
				if (!currentNode.hasOwnProperty(pid)) {
					currentNode = { ...currentNode, [pid]: null }
				}
				if (currentNode[children]) {
					currentNode[children].forEach((v) => { v && queue.push({ ...v, [pid]: currentNode[id] }) })
				}
				result.push(currentNode)
				delete currentNode[children]
			} else {
				throw new Error('you need to specify the [id] of the json tree')
			}
		}
		return result
	}

	if (Array.isArray(forest)) {
		return forest.map((v) => flatTree(v)).reduce((pre, cur) => pre.concat(cur))
	} else {
		return flatTree(forest)
	}
}

export default {
	construct,
	destruct,
}
複製代碼
167.實現一個簡易的模板引擎

答: 模板引擎的做用是將模板文件轉換成另外一種模板格式的工具,引擎內部流程是把模板文件裏的內容當成字符串傳入到模板引擎中,而後模板引擎根據必定語法對該字符串進行解析處理,而後返回一個函數,以後咱們在執行函數時把數據傳輸進去,便可拿到根據模板和數據獲得的新字符串。

模板 ----> 輸入到模板引擎 ----> 生成函數 ----> 把數據當成參數,執行該函數 ----> 輸出結果。

立刻開始實現一個知足 變量定義、插值、條件判斷、數組遍歷 功能的建議模板引擎。

"use strict";

var __PARSE__ = (function () {
  /* 語法正則 */
  const regmap = [
    // if語句開始
    {
      reg: /^if\s+(.+)/i,
      val: (all, condition) => {
        return `if(${condition}) {`;
      },
    },
    // elseif 語句開始
    {
      reg: /^elseif\s+(.+)/i,
      val: (all, condition) => {
        return `} else if(${condition}) {`;
      },
    },
    // else語句結束
    { reg: /^else/i, val: "} else {" },
    // if語句結束
    { reg: /^\/\s*if/i, val: "}" },
    // list語句開始
    {
      reg: /^list\s+([\S]+)\s+as\s+([\S]+)/i,
      val: (all, arr, item) => {
        return `for(var __INDEX__=0;__INDEX__<${arr}.length;__INDEX__++) {var ${item}=${arr}[__INDEX__];var ${item}_index=__INDEX__;`;
      },
    },
    // list語句結束
    { reg: /^\/\s*list/i, val: "}" },
    // var 語句
    {
      reg: /^var\s+(.+)/i,
      val: (all, expr) => {
        return `var ${expr};`;
      },
    },
  ];

  /** 默認的過濾器 */
  const defaultFilter = {
    // 防注入用
    escape: (str) => {
      // 防注入轉碼映射表
      var escapeMap = {
        "<": "&lt;",
        ">": "&gt;",
        "&": "&amp;",
        " ": "&nbsp;",
        '"': "&quot;",
        "'": "&#39;",
        "\n": "<br/>",
        "\r": "",
      };

      return str.replace(/\<|\>|\&|\r|\n|\s|\'|\"/g, (one) => {
        return escapeMap[one];
      });
    },
  };

  /* 轉換模板語句 */
  let transStm = function (stmJs) {
    stmJs = stmJs.trim();
    for (let item of regmap) {
      if (item.reg.test(stmJs)) {
        return typeof item.val === "function"
          ? stmJs.replace(item.reg, item.val)
          : item.val;
      }
    }
  };

  /* 解析模板 */
  let doParseTemplate = function (content, data, filter) {
    content = content
      .replace(/\t/g, " ")
      .replace(/\n/g, "\\n")
      .replace(/\r/g, "\\r");

    // 初始化模板生成器結構
    let out = [];
    let struct = [
      "try { var OUT = [];",
      "", //放置模板生成器佔位符
      "return OUT.join(''); } catch(e) { throw e; }",
    ];

    // 初始化模板變量
    let vars = [];
    Object.keys(data).forEach((name) => {
      vars.push(`var ${name} = DATA['${name}'];`);
    });
    out.push(vars.join(""));

    // 初始化過濾器
    let filters = ["var FILTERS = {};"];
    Object.keys(filter).forEach((name) => {
      if (typeof filter[name] === "function") {
        filters.push(`FILTERS['${name}'] = FILTER['${name}'];`);
      }
    });
    out.push(filters.join(""));

    // 解析模板內容
    let beg = 0; // 解析文段起始位置
    let stmbeg = 0; // 表達式起始位置
    let stmend = 0; // 表達式結束位置
    let len = content.length;
    let preCode = ""; // 表達式前的代碼
    let endCode = ""; // 最後一段代碼
    let stmJs = ""; // 表達式
    while (beg < len) {
      /* 開始符 */
      stmbeg = content.indexOf("{", beg);
      while (content.charAt(stmbeg - 1) === "\\") {
        // 遇到轉義的狀況
        stmbeg = content.indexOf("{", stmbeg + 1);
      }
      if (stmbeg === -1) {
        // 到達最後一段代碼
        endCode = content.substr(beg);
        out.push("OUT.push('" + endCode + "');");
        break;
      }

      /* 結束符 */
      stmend = content.indexOf("}", stmbeg);
      while (content.charAt(stmend - 1) === "\\") {
        // 遇到轉義的狀況
        stmend = content.indexOf("}", stmend + 1);
      }
      if (stmend === -1) {
        // 沒有結束符
        break;
      }

      // 開始符以前代碼
      preCode = content.substring(beg, stmbeg);

      if (content.charAt(stmbeg - 1) === "$") {
        // 針對變量取值
        out.push(`OUT.push(\'${preCode.substr(0, preCode.length - 1)}\');`);
        stmJs = content.substring(stmbeg + 1, stmend);

        // 處理過濾器
        let tmp = "";
        stmJs.split("|").forEach((item, index) => {
          if (index === 0) {
            // 變量,強制轉碼
            tmp = item;
          } else {
            // 過濾器
            let farr = item.split(":");
            tmp = `FILTERS['${farr[0]}'](${tmp}`;

            if (farr[1]) {
              // 帶變量的過濾器
              farr[1].split(",").forEach((fitem) => {
                tmp = `${tmp}, ${fitem}`;
              });
            }

            tmp = `${tmp})`; // 追加結尾
          }
        });

        out.push(`OUT.push((${tmp}).toString());`);
      } else {
        // 針對js語句
        out.push(`OUT.push(\'${preCode}\');`);
        stmJs = content.substring(stmbeg + 1, stmend);
        out.push(transStm(stmJs));
      }
      beg = stmend + 1;
    }

    // 合併內容
    struct[1] = out.join("");
    return new Function("DATA", "FILTER", struct.join(""));
  };

  /** 根據模板數據生成代碼 */
  return function (content, data, filter) {
    try {
      data = data || {};
      filter = Object.assign({}, defaultFilter, filter);
      // 解析模板生成代碼生成器
      let f = doParseTemplate(content, data, filter);
      return f(data, filter);
    } catch (ex) {
      return ex.stack;
    }
  };
})();

if (typeof module !== "undefined" && typeof exports === "object") {
  module.exports = __PARSE__;
} else {
  window.parse = __PARSE__;
}
複製代碼
168.簡單實現一個發佈 / 訂閱模式

答:

const eventEmitter = {
  list: {},

  /** 訂閱 */
  on(event, fn) {
    let _this = this;
    (_this.list[event] || (_this.list[event] = [])).push(fn);
    return _this;
  },

  /** 監聽一次 */
  once(event, fn) {
    // 先綁定,調用後刪除
    let _this = this;
    function on() {
      _this.off(event, on);
      fn.apply(_this, arguments);
    }
    on.fn = fn;
    _this.on(event, on);
    return _this;
  },

  /** 卸載 */
  off(event, fn) {
    let _this = this;
    let fns = _this.list[event];
    if (!fns) return false;
    if (!fn) {
      fns && (fns.length = 0);
    } else {
      let cb;
      for (let i = 0, cbLen = fns.length; i < cbLen; i++) {
        cb = fns[i];
        if (cb === fn || cb.fn === fn) {
          fns.splice(i, 1);
          break;
        }
      }
    }
    return _this;
  },

  /** 發佈 */
  emit() {
    let _this = this;
    let event = [].shift.call(arguments),
      fns = [..._this.list[event]];
    if (!fns || fns.length === 0) {
      return false;
    }
    fns.forEach((fn) => {
      fn.apply(_this, arguments);
    });
    return _this;
  },
};
複製代碼
169.匹配出字符串中 const a = require('xxx') 中的 xxx

答:

// 見試卷背面
複製代碼

參考文獻

考試大綱以下

基礎知識主要包含如下幾個方面:

  • 基礎:計算機原理、編譯原理、數據結構、算法、設計模式、編程範式等基本知識瞭解

  • 語法:JavaScript、ECMAScript、CSS、TypeScript、HTML、Node.js 等語法的瞭解和使用

  • 框架:React、Vue、Egg、Koa、Express、Webpack 等原理的瞭解和使用

  • 工程:編譯工具、格式工具、Git、NPM、單元測試、Nginx、PM二、CI / CD 瞭解和使用

  • 網絡:HTTP、TCP、UDP、WebSocket、Cookie、Session、跨域、緩存、協議的瞭解

  • 性能:編譯性能、監控、白屏檢測、SEO、Service Worker 等了解

  • 插件:Chrome 、Vue CLI 、Webpack 等插件設計思路的理解

  • 系統:Mac、Windows、Linux 系統配置的實踐

  • 後端:Redis 緩存、數據庫、Graphql、SSR、模板引擎等了解和使用

批卷

得分:_____

相關文章
相關標籤/搜索