子弈大佬出面試指南的本意也是幫助你們掃盲,讓你們去了解一些可能本身沒有接觸過或者本身不知道的東西,這裏面的大部分題目實際上是在不斷深刻溝通的語境裏產生的,也不影響對面試者的面試判斷,純粹屬於那種面着面着以爲不錯,以爲他可以答上來而給予的加分一問,不是你們理解的刁難人的意思。javascript
因爲文章字數限制,不少題目沒法詳盡和深刻回答(純粹是懶,就是想一夜趕出來文章,嘿!),本人也只是簡略寫一下,並且非標準答案,好比問瀏覽器URL請求流程,那至少能跟面試官扯幾個小時。有些題目不算是題目又或者比較籠統,很差回答,就略過,你們看看就圖一樂,別當真。非標準答案,css
歡迎留言,接受任何批評和建議~
html
答:前端
SRAM、DRAM(SDRAM、RDRAM、CDRAM 等)vue
MROM、PROM、EPROM、EEPROMjava
答: 1)易失性執行以前,咱們的代碼主要
存儲在內存中。 ②CPU讀取內存中的數據並放在寄存器內,將寄存器中的數據寫入內存並進行有序的四則運算、相關指令,在此過程當中,寄存器主要用於存放計算數據,運算器負責操做寄存器中的數據。node
答: 指令通常是指機器指令,是計算機可完成一個獨立計算邏輯所要執行的的命令;一臺常規的計算機的全部指令的集合,就是該計算機的指令集。react
答: ①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是解釋型語言呢?
答: 首先,Babel的做用是 從一種源碼到另外一種源碼,充當轉換編譯器的做用,能夠簡述爲 解析
(解析JS代碼)->轉換
(解析和修改AST)->重建
(將修改後的AST轉換成另外一種JS代碼)
答: ①數組,JS裏的數組主要就是 以連續內存形式存儲的FixedArray
、以哈希表形式存儲的HashTable
。
②函數,函數屬於引用數據類型,存儲在堆中,在棧內存中只是存了一個地址來表示對堆內存中的引用。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中得到實體。
答: ①瀏覽器中的事件循環: macrotasks(宏任務):
script(總體代碼)
setTimeout
setInterval
setImmediate
I/O
UI rendering
event listner
microtasks(微任務):
process.nextTick
Promises
Object.observe
MutationObserver
在瀏覽器裏,每當一個被監聽的事件發生時,事件監聽器綁定的相關任務就會被添加進回調隊列。經過事件產生的任務是異步任務,常見的事件任務包括:
主線程運行的時候,會產生堆(heap)和棧(stack),其中堆爲內存、棧爲函數調用棧。咱們能看到,Event Loop 負責執行代碼、收集和處理事件以及執行隊列中的子任務,具體包括如下過程。
②NodeJs中的事件循環:
③區別: 瀏覽器環境下,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 (若是是第二個定時器已經在完成隊列中)
複製代碼
答:
優點: CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完纔會生成。而 ES6 Modules不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成
。
答: 高級語言代碼->解析成 AST (期間伴隨詞法分析、語法分析)->生成字節碼(V8)->生成機器碼(編譯器)
答: 編譯器通常由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。
答:虛擬機(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會把輸入的指令逐條直接執行。
答: IR是由LLVM生成的中間代碼,做用是優化編譯器或VM,使優化後的機器代碼執行效率更高,同時避免緩存編譯後的二進制代碼佔用更多的內存。。
答: 是指是在一個平臺上生成另外一個平臺上的可執行代碼。
答: 在觀察者模式中,被觀察者一般會維護一個觀察者列表。當被觀察者的狀態發生改變時,就會通知觀察者。
在發佈訂閱模式中,具體發佈者會動態維護一個訂閱者的列表:可在運行時根據程序須要開始或中止發佈給對應訂閱者的事件通知。
區別在於發佈者自己並不維護訂閱列表(它不會像觀察者同樣主動維護一個列表),它會將工做委派給具體發佈者(至關於祕書,任何人想知道個人事情,直接問個人祕書就能夠了);訂閱者在接收到發佈者的消息後,會委派具體的訂閱者來進行相關的處理。
答: 裝飾器模式通常是指容許動態地向一個現有的對象添加新的功能,同時又不改變其結構,至關於對現有的對象進行了一個包裝。
使用場景不少,好比之前寫jQ項目,能夠本身快速動態拓展jQ上面的方法,或者vue的自定義指令,主要是但願經過繼承的方式擴展老舊功能。
答: ①代碼解耦、必定要按模塊劃分而不是按功能劃分
各個模塊的生命週期(初始化、銷燬)統一由框架進行管理:經過提供通用類Disposable,統一管理相關資源的註冊和銷燬。
模塊間不直接引入和調用,而是經過聲明依賴的方式,從框架中獲取相應的服務並使用。
不直接使用全局事件進行通訊,而是經過訂閱具體服務的方式來處理:經過使用一樣的方式this._register()註冊事件和訂閱事件,將事件相關資源的處理統一掛載到dispose()方法中
②各個部分各個模塊開發職責的仔細拆分 ③代碼開發儘快組件化、提升可複用性,避免業務邏輯過分耦合臃腫,最終難以拓展
Ioc是指依賴注入,簡單理解就是藉助於"第三方"實現具備依賴關係的對象之間的解耦。通常使用代理模式。
答:聲明式、命令式、函數式
答:面向切面編程是面向對象中的一種方式而已。在代碼執行過程當中,動態嵌入其餘代碼,叫作面向切面編程。
答: 函數式編程是面向數學的抽象,關心數據(代數結構)之間的映射關係。函數式編程將計算描述爲一種表達式求值。
響應式編程是一種基於數據流和變化傳遞的聲明式的編程範式。
函數響應式編程是一種混合體,響應式編程思想爲體, 函數式編程思想爲用。
答:
<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;
}
複製代碼
答: 能夠用元素的scrollHeight屬性和clientHeight屬性來判斷, 當scrollHeight大於clientHeight的時候,元素就是能夠垂直滾動的;若是檢測水平滾動的話,能夠用scrollWidth和clientWidth。
答: 左側寬度自動增加,右側寬度自動增加而且不可溢出省略。當左側文字長度超出的時候,左側文字溢出省略。 在 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>
複製代碼
答: 沙箱設計的目的是爲了讓不可信的代碼運行在必定的環境中,從而限制這些代碼訪問隔離區以外的資源。
答: 表單中當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"/>
複製代碼
答: hash
路由模式的實現主要是基於下面幾個特性:
history
路由模式的實現主要基於存在下面幾個特性:
答: 能夠,也能夠進行splice()操做。
const聲明建立一個值的只讀引用。但這並不意味着它所持有的值是不可變的,只是變量標識符不能從新分配。例如,在引用內容是對象的狀況下,這意味着能夠改變對象的內容(例如,其參數)。
答:
可配置性決定是否能夠使用delete刪除屬性,以及是否能夠修改屬性描述符的特性,默認值爲true
可枚舉性決定屬性是否出如今對象的屬性枚舉中,好比是否能夠經過for-in循環返回該屬性,默認值爲true
可寫性決定是否能夠修改屬性的值,默認值爲true
屬性值包含這個屬性的數據值,讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。默認值爲undefined
在讀取屬性時調用的函數。默認值爲undefined
在寫入屬性時調用的函數。默認值爲undefined
答: 我只用過console.clear()、console.log()、console.info()、console.warn()、console.error()、console.time()、console.timeEnd()。其餘的不知道,我也不經常使用
答: 首先callback不是異步API,它是早年JS異步編程實現的一種手段。
Promise是社區爲了解決回調地獄的問題在ES6版本提出的一種解決方案;
Generator也是一種異步編程解決方案,它最大的特色就是能夠交出函數的執行權,Generator 函數能夠看出是異步任務的容器,須要暫停的地方,都用 yield 語法來標註;
Async/await是 ES7 中提出的新的異步解決方案,async 是 Generator 函數的語法糖,async/await 的優勢是代碼清晰
(不像使用 Promise 的時候須要寫不少 then 的方法鏈)。async/await 不只僅是 JS 的異步編程的一種方式,其可讀性也接近於同步代碼,讓人更容易理解。
答: 首先它的用法是 Object.defineProperty(object, propertyname, descriptor)
它內部的descriptor參數以下:
屬性的值,默認爲 undefined。
該屬性是否可寫,若是設置成 false,則任何對該屬性改寫的操做都無效(但不會報錯),對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。
若是爲false,則任未嘗試刪除目標屬性或修改屬性如下特性(writable, configurable, enumerable)的行爲將被無效化,對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。 。
是否能在for-in循環中遍歷出來或在Object.keys中列舉出來。對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。
一旦目標對象訪問該屬性,就會調用這個方法,並返回結果。默認爲 undefined。
一旦目標對象設置該屬性,就會調用這個方法。默認爲 undefined。
答: Proxy的優點以下
Object.defineProperty 的優點以下
Object.defineProperty 不足在於:
答: 使用場景太多了,業務上也每天用,略。
答: 首先,不必定非要用TS,大型業務產品、多人協做寫大堆的業務代碼不適合TS。
優點: 1.爲JavaScript、IDE和實踐(如靜態檢查)提供了高效的開發工具。(主要) 2.其餘的好比強大的類型系統,泛型支持、模塊支持等等(次要)
答: const 和 readonly 的區別:
枚舉和常量枚舉的區別: 常量枚舉經過在枚舉上使用 const 修飾符來定義,常量枚舉不一樣於常規的枚舉,他們會在編譯階段被刪除。常量枚舉成員在使用的地方會被內聯進來,之因此能夠這麼作是由於,常量枚舉不容許包含計算成員;如上例所示,在運行時是沒有 Size 變量的,所以常量枚舉會帶來一個對性能的提高。
接口和類型別名的區別:
答: Any就是任意類型,能夠將 TypeScript 進化成強大的 AnyScript。
答: any
顧名思義就是任意類型。 never
表示永不存在的值的類型。 unknown
表示未知類型,即寫代碼的時候還不清楚會獲得怎樣的數據類型,它能被賦值爲任何類型,但不能被賦值給除了 any 和 unknown 以外的其餘類型,同時,不容許執行 unknown 類型變量的方法(any 能夠)。 void
表示無任何類型,正好與 any 相反,沒有類型,若是是函數則應沒有返回值或者返回 undefined
答: 能夠,interface 可以描述 JavaScript 對象的任何形式,包括函數。
interface 也能夠被 class 類 implements,這裏至關於聲明瞭一個 interface 包含了各類屬性,須要 class 去實現,注意給類自己聲明類型,其實就是給構造器進行類型聲明,不能添加其餘屬性。
答: 能夠
答: this 沒法在未聲明的狀況下使用,在編寫函數是須要在函數裏首位聲明this
答: 聯合類型表示取值能夠爲多種類型中的一種,當 TypeScript 不肯定一個聯合類型的變量究竟是哪一個類型的時候,咱們只能訪問此聯合類型的全部類型裏共有的屬性或方法(交集)。
答: TypeScript 類型聲明很是靈活,這也意味着一千個莎士比亞就能寫出一千個哈姆雷特。在團隊協做中,爲了更好的可維護性, 咱們應該儘量地踐行如下3條原則:
第一,類型定義使 getSmallPet變得侷限。從代碼邏輯看,它的做用是返回一個不下蛋的動物,返回的類型指向的是Fish或Bird。但我若是隻想在一羣鳥中挑出一個不下蛋的鳥呢?經過調用這個方法,我只能獲得一個 多是Fish、或者是Bird的神奇生物。
第二,代碼重複、難以擴展。好比,我想再增長一個烏龜,我必須找到全部相似 Fish | Bird 的地方,而後把它修改成 Fish | Bird | Turtle
第三,類型簽名沒法提供邏輯相關性。咱們再審視一下類型簽名,徹底沒法看出這裏爲何是 Fish | Bird 而不是其餘動物,它們兩個到底和邏輯有什麼關係纔可以被放在這裏
答: 用 mapped type,用完以後不能加額外的屬性,用類型並運算解決。
type Props = {
[key in Link]: U;
} & { type: string;}
複製代碼
答: ?:表示該屬性或參數爲可選項 !:表示強制解析(告訴typescript編譯器,這裏必定有值),變量後使用 !:表示類型推斷排除null、undefined ?? 若是??運算符左側的表達式求值爲undefined或null,則返回其右側的值;不然,返回其左側的值
答:
Exclude<T, U> -- 從T中剔除能夠賦值給U的類型。
Extract<T, U> -- 提取T中能夠賦值給U的類型。
NonNullable<T> -- 從T中剔除null和undefined。
ReturnType<T> -- 獲取函數返回值類型。
InstanceType<T> -- 獲取構造函數類型的實例類型。
複製代碼
答: Typescrit的模塊機制與es6的模塊基本相似,也提供了轉換爲amd,es6,umd,commonjs,system的轉換。typescript的按需加載,也叫動態加載,編譯器會檢測是否每一個模塊都會在生成的JavaScript中用到。 若是一個模塊標識符只在類型註解部分使用,而且徹底沒有在表達式中使用時,就不會生成require
這個模塊的代碼。 省略掉沒有用到的引用對性能提高是頗有益的,並同時提供了選擇性加載模塊的能力。這種模式的核心是import id = require("...")
語句可讓咱們訪問模塊導出的類型。 模塊加載器會被動態調用(經過require
)。
模塊加載的最佳實踐
用戶應該更容易地使用你模塊導出的內容。 嵌套層次過多會變得難以處理,所以仔細考慮一下如何組織你的代碼。
模塊中使用命名空間是沒必要要的,在模塊中導出的東西確定不能重名,而導入時使用者確定會爲其命名或者直接使用,也不存在重名,使用命名空間是多餘的。
三、若是僅導出單個 class
或 function
,使用 export default。
如剛纔所說,default是比較好的實踐。
四、若是要導出多個對象,把它們放在頂層裏導出
五、導入時明確地列出導入的名字
六、導入大量模塊時使用命名空間
七、使用從新導出進行擴展
你可能常常須要去擴展一個模塊的功能。 JS裏經常使用的一個模式是JQuery那樣去擴展原對象。 如咱們以前提到的,模塊不會像全局命名空間對象那樣去合併。 推薦的方案是不要去改變原來的對象,而是導出一個新的實體來提供新的功能。
答: TypeScript裏的類型兼容性是基於結構子類型的。 結構類型是一種只使用其成員來描述類型的方式。 它正好與名)類型造成對比。TypeScript的結構性子類型是根據JavaScript代碼的典型寫法來設計的。 由於JavaScript裏普遍地使用匿名對象,例如函數表達式和對象字面量,因此使用結構類型系統來描述這些類型比使用名義類型系統更好。
協變 (Covariant) :協變表示Comp<T>
類型兼容和T
的一致。
逆變 (Contravariant) :逆變表示Comp<T>
類型兼容和T
相反。
雙向協變 (Covariant) :雙向協變表示Comp<T>
類型雙向兼容。
不變 (Bivariant) :不變表示Comp<T>
雙向都不兼容。
答: 展開操做符正與解構相反。 它容許你將一個數組展開爲另外一個數組,或將一個對象展開爲另外一個對象。對象的展開比數組的展開要複雜的多。 像數組展開同樣,它是從左至右進行處理,但結果仍爲對象。 這就意味着出如今展開對象後面的屬性會覆蓋前面的屬性。對象展開還有其它一些意想不到的限制。 首先,它僅包含對象 自身的可枚舉屬性。 大致上是說當你展開一個對象實例時,你會丟失其方法:
答: 有,叫類做用域,類變量 也能夠稱爲 字段。類變量 聲明在一個類裏頭,但在類的方法外面,能夠經過類的實例化對象來訪問。靜態變量 靜態的類變量,靜態的變量能夠經過類名直接訪問
答: 同名interface接口會自動合併,interface同名的class也會自動聚合。 但type不能自動聚合,由於type聲明不能重名。
答: 能夠選擇安裝其npm包的typescript版本,npm install @types/包名 --save
,通常都是這樣命名。 若是是本身寫的js庫 能夠單獨編寫.d.ts文件
答:
{
"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'
}]
}
複製代碼
答: 經過tsconfig.json中的paths項來配置
答:
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中建立的一些數據等等
答: 看狀況,若是是服務端渲染會拿不到數據。
componentWillMount方法的調用在constructor以後,在render以前,在這方法裏的代碼調用setState方法不會觸發重渲染,因此它通常不會用來做加載數據之用,它也不多被使用到。
通常的從後臺(服務器)獲取的數據,都會與組件上要用的數據加載有關,因此都在componentDidMount方法裏面做。雖然與組件上的數據無關的加載,也能夠在constructor裏做,但constructor是做組件state初紿化工做,並非設計來做加載數據這工做的,因此全部有反作用的代碼都會集中在componentDidMount方法裏。
答: Hook代碼可讀性更強,本來同一塊功能的代碼邏輯被拆分在了不一樣的生命週期函數中,容易使開發者不利於維護和迭代,經過 React Hooks 能夠將功能代碼聚合,方便閱讀維護;
組件樹層級變淺,在本來的代碼中,咱們常用 HOC/render/Props 等方式來複用組件的狀態,加強功能等,無疑增長了組件樹層數及渲染,而在 React Hooks 中,這些功能均可以經過強大的自定義的 Hooks 來實。
hooks組件實際上是下降了react開發的使用難度的,讓新手能夠在不使用class組件的狀況下依然能夠進行項目開發。
答:
高階組件實際上就是把一個組件當參數傳入,再返回一個新的組件出來。業務過分封裝的高階組件,可能會致使組件層次嵌套變深。
而自定義 Hook 能夠不用使用高階組件依然能夠進行功能複用。
答: 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(返回新值)和舊值進行比較
答: 當內部data發生改變,state發生改變(經過調用this.setState()) 以及父組件傳過來的props發生改變時,會致使組件從新渲染。
react生命週期中有這樣一個鉤子,叫shouldComponentUpdate函數,是重渲染時render()函數調用前被調用的函數,兩個參數 nextProps和nextState ,分別表示下一個props和state的值。當函數返回false時,阻止接下來的render()函數的調用,阻止組件重渲染,返回true時,組件照常渲染。 先後不改變state的值的setState和無數據交換的父組件的重渲染都會致使組件的重渲染,但咱們能夠經過shouldComponentUpdate來阻止這兩種狀況,shouldComponentUpdate並非完美的,只能阻止扁平的對象,這時候能夠考慮Immutable.js
(Immutable.js 的基本原則是對於不變的對象返回相同的引用,而對於變化的對象,返回新的引用)或者PureRenderMixin
插件。
答: 不傳參數、空數組、有一個或者多個值得數組、返回一個函數。
useEffect的第二個參數可用於定義其依賴的全部變量。若是其中一個變量發生變化,則useEffect會再次運行。若是包含變量的數組爲空,則在更新組件時useEffect不會再執行,由於它不會監放任何變量的變動。
答: useEffect的第二個參數可用於定義其依賴的全部變量。若是其中一個變量發生變化,則useEffect會再次運行。若是包含變量的數組爲空,則在更新組件時useEffect不會再執行,由於它不會監放任何變量的變動。
答:
首先閉包是由函數以及建立該函數的詞法環境組合而成。這個詞法環境包含了該閉包建立時所能訪問的全部局部變量。劃重點是閉包建立時的變量值,閉包建立以後即便這些變量值改變了也不會影響到閉包內保存的這個變量。
而useEffect、useMemo、useCallback都是自帶閉包的。每一次組件的渲染,它們都會捕獲當前組件函數上下文中的狀態(state, props),因此每一次這三種hooks的執行,反映的也都是當前的狀態,你沒法使用它們來捕獲上一次的狀態。
對 Hook 過期閉包的解決辦法:
注意依賴項爲空和不傳依賴項是兩個概念,前者是傳了依賴項但它是一個空數組,後者是直接不傳這個參數。前者只有依賴項改變時纔會執行函數,後者只要組件數據改變了就執行。
以函數的形式更新state,同 react 的 setState 同樣,useState Hook 也能夠經過函數的形式來修改 state,而且使用當前的 state 值做爲函數參數。
經過 useRef 生成的對象來綁定 state,這樣更新 state 的時候就能夠不用依賴於該 state,而是直接在該綁定對象上的基礎上更新便可。
useReducer 能夠達到和使用函數形式更新的 useState 同樣的效果,也是在更新時在當前的 state 基礎上進行操做。
答: 一個函數組件,在react執行渲染時該函數都會被調用,因此函數內的useState在每次都會被調用。useState在不一樣階段,其對應的實現不同,在onMount階段:初始化state;在onUpdate階段:更新state。useState返回的是一個數組,數組的第二項是一個函數,該函數每次被調用後,都會觸發react的更新。
答:
使用 shouldComponentUpdate 規避冗餘的更新邏輯
PureComponent + Immutable.js
React.memo 與 useMemo
答: 指令本質上就是一個 JavaScript 對象,對象上掛着一些鉤子函數,不管是官方提供的指令,仍是自定義指令,一個指令從第一次被綁定到元素上到最終與被綁定的元素解綁,它會通過如下幾種狀態:
了每一個狀態的鉤子函數,這樣咱們就可讓指令在不一樣狀態下作不一樣的事情。當虛擬DOM渲染更新的時候會觸發create、update、destory這三個鉤子函數,從而就會執行updateDirectives函數來處理指令的相關邏輯,執行指令函數,讓指令生效。
答:
答:
支持 npm 方式和 cdn 方式,並支持按需引入、支持多語言、文檔詳盡、組件豐富。(不瞭解)
答:
漸進式表明的含義是:沒有多作職責以外的事。
你能夠使用jsx開發,你也能夠寫template;你能夠使用vue全家桶,你也能夠把它作爲某個業務的輕量視圖,隨你,不強求不主張。
答:
父向子傳遞數據是經過 props,子向父是經過 events( parent / attrs/$listeners
Bus;Vuex
Bus;Vuex;provide / inject API、 listeners
答:
使用watch而且搭配deep:true 就能夠實現對對象的深度監聽
答:
MVC 是一種使用 MVC(Model View Controller 模型-視圖-控制器)設計建立 Web 應用程序的模式。
MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。
MVVM 本質上就是MVC 的改進版。MVVM 就是將其中的View 的狀態和行爲抽象化,讓咱們將視圖 UI 和業務邏輯分開。
答: MVVM,特色是採用雙向綁定(data-binding): View的 變更,自動反映在View Model,反之亦然。這樣開發者就不用處理接收事件和View更新的工做,框架已經幫你作好了。
答:
插件系統是給vue項目提供可選功能的npm包,如:Babel/TypeScript 轉譯、ESLint 集成、unit和 e2e測試 等
答:
對比vue-cli2,cli3 最主要的就是生成的項目中,進行webpack配置的文件沒有了。cli3的腳手架封裝了webpack絕大部分配置,使得生成的項目更加清晰,可是在開發中免不了會有本身的個性需求,來添加一些本身的項目配置,此時只需在項目的根目錄下新建一個vue.config.js文件便可。而webpack中是經過 resolve.alias 來實現此功能的。在vue.config.js中修改webpack的配置,能夠經過configureWebpack方法。
答:
答:
答:
ESLint 和 TSLint 都是 Javascript 的語法檢查器,一般使用 ESLint 或 TSLint 用於解決團隊開發上不一樣代碼風格所帶來的一系列不和諧的問題。
ESLint 支持幾種格式的配置文件:
package.json:在 package.json 裏建立一個 eslintConfig 屬性,在那裏定義你的配置。
TSLint 是配合 Typescript 來使用的。TSLint 執行規則的方式存在一些框架問題,從而影響性能,而修復這些問題會破壞現有的規則。ESLint 的性能更好,而且社區用戶一般擁有 ESLint 的規則配置(好比 React 和 Vue 的配置),而不會擁有 TSLint 的規則配置。
答:
Node是基於Chrome V8引擎開發的能使JavaScript在服務器端運行的運行時環境,TS最終編譯成JS,而後生成字節碼->機器碼。 Node.js支持TS語法,有什麼疑問嗎,畢竟最終都是編譯成JS?
答:
編譯選項,在這裏找到與生成相應的 .d.ts 文件和 聲明文件 相關的選項,其中包括:
配置完 tsconfig.json 文件後,再次執行 npm run build 會在項目根目錄下生成 types 文件夾,該文件夾主要存放自動生成的 TypeScript 聲明文件。
答:
在使用babel-preset-typescript能夠使 JavaScript 與 TypeScript 並存,且在編譯過程是同一階段進行的。透過 Babel preset 配置,能夠更容易的實現咱們所需的JS項目平滑轉移到 TS項目過程。
可是,這對Babel有必定的要求,而不一樣版本的Babel對於 monorepo 存在相容性問題,特別是要從 subrepo 引用 module 時,會致使 Babel 的配置沒法正確取得。
答:
在webpack中 Loader 就是負責完成項目中各類各樣資源模塊的加載,從而實現總體項目的模塊化,而 Plugin 則是用來解決項目中除了資源模塊打包之外的其餘自動化工做,對比 Loader 只是在模塊的加載環節工做,而插件的做用範圍幾乎能夠觸及 Webpack 工做的每個環節。
答:
// 見試卷背面
複製代碼
答:
// 見試卷背面
複製代碼
答:
通常狀況下,npm包的根目錄時node_modules,能夠使用package.json的directories屬性裏的directories.lib,更改 Npm 包的根目錄。
答:
一般人們在使用打包工具的 babel 插件編譯代碼時都會屏蔽掉 node_modules 目錄下的文件。由於按照約定你們發佈到 npm 的模塊代碼都是基於 ES5 規範的,所以配置 babel 插件屏蔽 node_modules 目錄能夠極大的提升編譯速度。但用戶若是使用了咱們發佈的基於 ES6 規範的包就必須配置複雜的屏蔽規則以便把咱們的包加入編譯的白名單。
若是用戶是在 NodeJS 環境使用咱們的包,那麼極有可能連打包這一步驟都沒有。若是用戶的 NodeJS 環境又恰巧不支持 ES6 模塊規範,那麼就會致使代碼報錯。
基於以上兩個緣由,pkg.module 字段要指向的應該是一個基於 ES6 模塊規範的使用ES5語法書寫的模塊。基於 ES6 模塊規範是爲了用戶在使用咱們的包時能夠享受 Tree Shaking 帶來的好處;使用 ES5 語法書寫是爲了用戶在配置 babel 插件時能夠放心的屏蔽 node_modules 目錄。至關於在一個包內同時發佈了兩種模塊規範的版本。
當打包工具遇到咱們的模塊時:
答:
peerDependencies的目的是提示宿主環境去安裝知足插件peerDependencies所指定依賴的包,而後在插件import或者require所依賴的包的時候,永遠都是引用宿主環境統一安裝的npm包,最終解決插件與所依賴包不一致的問題。
答:
答:
npm run changelog
自動生成的版本日誌信息
答:
子模塊是進行開發和需求進行對接將需求文檔做爲子模塊項目,嵌入開發人員的項目中。子模塊的使用既能夠減小需求或設計人員的git操做,又能夠及時的將doc文檔發佈到項目的目錄文件下,並且不會對開發人員的項目產生任何影響。
答:
git rebase -i <commit id> 列出 commit 列表
答:
git log --pretty=oneline
git reset --soft <commit>
答:
在 .gitignore 文件中添加 ignore 條目, 如: .DS_Store 提交 .gitignore 文件: git commit -a -m "添加ignore規則"
答:
用Commitizen,Commitizen 是一個撰寫符合上面 Commit Message 標準的一款工具。 在push操做時檢查commit的信息,使用正則檢查是否匹配(好比使用angular的git規範),不符合的不容許Push。
答:
commit格式以下:
<type>: <subject>
<BLANK LINE>
<body>
type - 提交 commit 的類型
複製代碼
feat: 新功能
fix: 修復問題
docs: 修改文檔
style: 修改代碼格式(不影響邏輯功能,好比格式化、補充分號等等)
refactor: 重構代碼(fix bug或增長新功能不屬於此範圍)
perf: 提高頁面性能
test: 增長/修改測試用例
chore: 修改工具相關(包括但不限於文檔、代碼生成等, 好比修改了README,webpack配置文件等等)
deps: 升級依賴
subject - 用一句話清楚的描述此次提交作了什麼
body - 補充subject,適當增長緣由、目的等相關因素,可選。
答:
當你提交一個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
複製代碼
答:
Git Hooks是定製化的腳本程序,因此它實現的功能與相應的git動做相關,以下幾個簡單例子:
答:
客戶端鉤子由諸如提交和合並這樣的操做所調用, 而服務器端鉤子做用於諸如接收被推送的提交這樣的聯網操做。
答:
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等,好比發郵件,通知持續構建服務器等。
答:
pre-commit是客戶端hooks之一,也是接下來要介紹的鉤子。pre-commit在git add提交以後,而後執行git commit時執行,腳本執行沒報錯就繼續提交,反之就駁回提交的操做。 這個鉤子中能夠實現:對將要提交的代碼進行檢查、優化代碼格式、或者對提交的圖片進行壓縮等等任務。
Git 每次提交代碼,都要寫 Commit message(提交說明),不然就不容許提交。
答:
代碼提交以前會經過 husky 配合 git hook 進行提交信息校驗,一旦提交信息不符合 Angular 規範,則提交會失敗。
答:
藉助Commitizen,使用 git cz 代替 git commit 進行復合 Angular 規範的 Commit Message 信息提交,規範團隊的git規範。代碼提交以前會經過 husky 配合 git hook 進行提交信息校驗,一旦提交信息不符合 團隊的git規範,正則匹配失敗,則提交會失敗。
答:
能夠。鉤子都被存儲在 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 的狀況下檢查以前文件是否有衝突。
答:
在使用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 //不容許使用++ --運算符
}
複製代碼
答:
答:
這倆解決的不是一個問題,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"]
}
複製代碼
答:
假設你的默認格式化程序是prettier,那麼稍做改變便可,相信你已經知道怎樣操做,再也不贅述。 這樣,你先使用prettier格式化了代碼,再使用eslint去糾正了不符合eslint規則的部分,就實現了二者衝突的解決。
答:
Ctrl+s / command+s 時自動修復代碼的格式錯誤,自動修復的規則是讀取項目根目錄的Eslint規則。
答:
sourceMap就是一個信息文件,裏面儲存着打包前的位置信息。也就是說,轉換後的代碼的每個位置,所對應的轉換前的位置。有了它,出錯的時候,瀏覽器控制檯將直接顯示原始代碼出錯的位置,而不是轉換後的代碼,點擊出錯信息將直接跳轉到原始代碼位置。方便定位和解決問題。
答:
從nodejs8開始,node去掉了_debugger , 內部集成了inspect , 以往使用node-inspect實現的在線調試再也不可用.node8開始要用新方法了。
node --inspect-brk=0.0.0.0:8080 index.js
複製代碼
chrome://inspect
,在彈出的界面中輸入ip:port便可調試。答:
Grunt、Gulp、Webpack、vite、Rollup
答:
VS Code提供了兩種設置方式:
用戶設置: 這種方式進行的設置,會應用於該用戶打開的全部工程;
工做空間設置:工做空間是指使用VS Code打開的某個文件夾,在該文件夾下會建立一個名爲.vscode的隱藏文件夾,裏面包含着僅適用於當前目錄的VS Code的設置,工做空間的設置會覆蓋用戶的設置。
"用戶設置"會應用於用戶打開的全部工程;
"工做區設置"僅適用於當前目錄的VS Code的設置。
答:
固然不是。
答:
功能測試,性能測試,界面測試
答:
Selenium、cypress、Appium 、Requests、Jmeter、Mitmproxy
答:
端到端測試;
cypress 、Selenium 、puppeteer、nightwatch
答:
// 見試卷背面
複製代碼
答:
CDN的工做原理就是將您源站的資源緩存到位於全球各地的CDN節點上,用戶請求資源時,就近返回節點上緩存的資源,而不須要每一個用戶的請求都回您的源站獲取,避免網絡擁塞、緩解源站壓力,保證用戶訪問資源的速度和體驗。
答:
是基於TCP的,websocket的協議是在TCP/IP協議簇的應用層,和http在同一層。
答:
答: 略(內容太多)
簡單回答一個不全的答案,剩下須要深刻的等面試官一個一個問:
答:
代理其本質上能夠理解爲中介。當A和B不方便進行交互時,每每會引入一箇中間角色C,那麼C即是中介,即是代理。
正向代理服務器一般位於客戶端和服務器之間,相似一個跳板機,經過代理服務器能夠訪問到目標服務器。
正向代理時,一般,客戶端發送對目標服務器的請求,代理服務器在中間將請求轉發給目標服務器,並將結果返回給客戶端。
反向代理與正向代理剛好相反,代理服務位於服務器端。
對客戶端來講,反向代理服務器就好像是目標服務器。反向代理服務器接收客戶端發來的請求,而後將其分發到內網的服務器,並將內網服務器返回的結果返回給客戶端。
整個過程客戶端並不會感知到反向代理後面的服務,也不須要客戶端作任何設置,只須要把反向代理服務器當成真正的服務器就行。
答:
能夠。 HTTP 協議中的 Cookie 包括 Web Cookie 和瀏覽器 Cookie,它是服務器發送到 Web 瀏覽器的一小塊數據。服務器發送到瀏覽器的 Cookie,瀏覽器會進行存儲,並與下一個請求一塊兒發送到服務器。一般,它用於判斷兩個請求是否來自於同一個瀏覽器,例如用戶保持登陸狀態。
答:
客戶端請求服務端,服務端會爲此次請求開闢一塊內存空間,這個對象即是 Session 對象,存儲結構爲 ConcurrentHashMap。Session 彌補了 HTTP 無狀態特性,服務器能夠利用 Session 存儲客戶端在同一個會話期間的一些操做記錄。
HTTP 協議中的 Cookie 包括 Web Cookie 和瀏覽器 Cookie,它是服務器發送到 Web 瀏覽器的一小塊數據。服務器發送到瀏覽器的 Cookie,瀏覽器會進行存儲,並與下一個請求一塊兒發送到服務器。一般,它用於判斷兩個請求是否來自於同一個瀏覽器,例如用戶保持登陸狀態。
服務器端session,若是你不指定session的存儲時間,在你打開的瀏覽器中存儲的值,是能夠在新打開的框口內獲得的,關閉後就自動消失(消失的實際上是session_id,由於session的機制是依賴於cookie的(還能夠依賴其餘的)。
答:
在服務器端設置cookie的時候設置 http-only, 這樣就能夠防止用戶經過JS獲取cookie。對cookie的讀寫或發送通常有以下字段進行設置:
設置Cookie,能夠防止攻擊者拿到正經常使用戶的Cookie冒充身份非法調用網站接口。
答:
在用戶第一次登陸成功的時候,後端會返回一個 Token,這個值Token 主要的做用就是用於識別用戶的身份。至關於帳號密碼。正常狀況下,前端給後端發送請求的時候,後端都須要先判斷用戶的身份,來返回相應的數據給用戶。獲取到Token後,你須要把 Token 存在 Cookie中。接着向服務器發送請求時,你從 Cookie 中取出 Token,在請求頭中攜帶上 Token 。Token過時時間設置足夠長,只要token沒過時,這段時間用戶都是免登陸。
安全問題:其餘人使用本機,實現免登陸,沒法在每次使用應用時驗證用戶的身份。提供了便捷,失去了安全校驗。
對用戶登陸的密碼進行加密,密碼MD5化,不使用明文傳輸。
答:
經常使用的內容編碼方式:
答:
文件的斷點續傳,
前端工做
後端工做
答:
代理是中間人,使用代理的主機發出的IP報文的目的IP是代理的,可是會在應用層裏明確告訴代理,本身真實需求是什麼。 網關即Gateway,它是鏈接基於不一樣通訊協議的網絡的設備,使文件能夠在這些網絡之間傳輸。
答:
由於 HTTPS 保證了傳輸安全,防止傳輸過程被監聽、防止數據被竊取,能夠確認網站的真實性(具體細節二面再說)。不過須要注意的是,即使使用 HTTPS 仍可能會被抓包,由於HTTPS 只防止用戶在不知情的狀況下通訊被監聽,若是用戶主動授信,是能夠構建「中間人」網絡,代理軟件能夠對傳輸內容進行解密。
答:
傳統的對稱式加密須要通信雙方都保存同一份密鑰,經過這份密鑰進行加密和解密。因此非對稱加密也稱爲單密鑰加密。 在非對稱加密中,加密和解密使用的是不一樣的密鑰。非對稱加密中的密鑰分爲公鑰和私鑰。公鑰顧名思義就是公開的,任何人均可以經過公鑰進行信息加密,可是隻有用戶私鑰的人才能完成信息解密。非對稱加密帶來了一個好處,避免了對稱式加密須要傳輸和保存同一份密鑰的痛苦。
非對稱加密必定比對稱加密機密性更高嗎? 不必定, 由於機密性高低是根據祕鑰長度而變化的。並且非對稱加密最大的問題,就是性能較差,沒法應用於長期的通訊。
答:
HTTP不具有必要的安全功能,與最初的設計相比,現今的Web網站應用的HTTP協議的使用方式已發生了翻天覆地的變化。幾乎現今全部的Web網站都會使用會話(session)管理、加密處理等安全性方面的功能,而HTTP協議內並不具有這些功能。
從總體上看,HTTP就是一個通用的單純協議機制。所以它具有較多優點,可是在安全性方面則呈劣勢。 就拿遠程登陸時會用到的SSH協議來講,SSH具有協議級別的認證及會話管理等功能,HTTP協議則沒有。另外在架設SSH服務方面,任何人均可以輕易地建立安全等級高的服務,而HTTP即便已架設好服務器,但若想提供服務器基礎上的Web應用,不少狀況下都須要從新開發。
所以,開發者須要自行設計並開發認證及會話管理功能來知足Web應用的安全。而自行設計就意味着會出現各類形形色色的實現。結果,安全等級並不完備,可仍在運做的Web應用背後卻隱藏着各類容易被攻擊者濫用的安全漏洞的Bug。
答:
首先,最小粒度 / 代價的渲染的表單項or組件應該具有什麼樣的特性:
而這個表單項or組件實現起來主要分爲三部分:
爲了能減小使用ref,同時又能操做表單數據(取值、修改值、手動校驗等),我將用於存儲數據的FormStore,從Form組件中分離出來,經過new FormStore()建立並手動傳入Form組件。
符合以上標準,就能夠認爲這個表單作到了最小粒度/最小代價的渲染。
對於有大量表單的頁面,能夠使用Lighthouse做爲衡量工具,來排查和優化頁面。
答:
使用 shouldComponentUpdate 規避冗餘的更新邏輯
PureComponent + Immutable.js
React.memo 與 useMemo
使用 useMemo,咱們能夠對函數組件的執行邏輯進行更加細粒度的管控(尤爲是定向規避掉一些高開銷的計算),同時也彌補了 React.memo 沒法感知函數內部狀態的遺憾,這對咱們總體的性能提高是大有裨益的。
答:
Vue-cli@3.0 採用了一套基於插件的架構,它將部分核心功能收斂至 CLI 內部,同時對開發者暴露可拓展的 API 以供開發者對 CLI 的功能進行靈活的拓展和配置。
整個插件系統當中包含2個重要的組成部分:
@vue/cli 提供 vue cli 命令,負責偏好設置,生成模板、安裝插件依賴的工做,例如 vue create <projectName>
、 vue add <pluginName>
@vue/cli-service 做爲 @vue/cli 整個插件系統當中的內部核心插件,提供了 webpack 配置更新,本地開發構建服務
前者主要完成了對於插件的依賴管理,項目模板的拓展等,後者主要是提供了在運行時本地開發構建的服務,同時後者也做爲 @vue/cli 整個插件系統當中的內部核心插件而存在。在插件系統內部也對核心功能進行了插件化的拆解,例如 @vue/cli-service 內置的基礎 webpack 配置,npm script 命令等。
@vue/cli-service 插件系統當中幾個核心的模塊:
答:
Webpack 插件機制的目的是爲了加強 Webpack 在項目自動化構建方面的能力。在webpack中 Loader 就是負責完成項目中各類各樣資源模塊的加載,從而實現總體項目的模塊化,而 Plugin 則是用來解決項目中除了資源模塊打包之外的其餘自動化工做,對比 Loader 只是在模塊的加載環節工做,而插件的做用範圍幾乎能夠觸及 Webpack 工做的每個環節。
Webpack 的插件機制就是咱們在軟件開發中最多見的鉤子機制。鉤子機制也特別容易理解,它有點相似於 Web 中的事件。在 Webpack 整個工做過程會有不少環節,爲了便於插件的擴展,Webpack 幾乎在每個環節都埋下了一個鉤子。這樣咱們在開發插件的時候,經過往這些不一樣節點上掛載不一樣的任務,就能夠輕鬆擴展 Webpack 的能力。
答:
\r\n 表示回車並換行 (則會將打印紙張上移一行,且下一個打字位置將回到該行的最左側) \n 表示換行
答:
/dev/null:表示 的是一個黑洞,一般用於丟棄不須要的數據輸出, 或者用於輸入流的空文件
curl -Iwww.baidu.com 2>/dev/null | head -l 錯誤信息定位到黑洞
cat /dev/null > /home/omc/h.txt
答:
alias dev="npm run dev"
複製代碼
答:
答:
默認不區分,想要區分,就在重裝系統or單獨分區的時候,選擇 MAC OS 擴展(區分大小寫,日誌格式)
答:
SHELL_FOLDER=$(dirname $(readlink -f "$0"))
複製代碼
答:
客戶端請求服務端,服務端會爲此次請求開闢一塊內存空間,這個對象即是 Session 對象,存儲結構爲 ConcurrentHashMap。Session 彌補了 HTTP 無狀態特性,服務器能夠利用 Session 存儲客戶端在同一個會話期間的一些操做記錄。
HTTP 協議中的 Cookie 包括 Web Cookie 和瀏覽器 Cookie,它是服務器發送到 Web 瀏覽器的一小塊數據。服務器發送到瀏覽器的 Cookie,瀏覽器會進行存儲,並與下一個請求一塊兒發送到服務器。一般,它用於判斷兩個請求是否來自於同一個瀏覽器,例如用戶保持登陸狀態。
服務器端session,若是你不指定session的存儲時間,在你打開的瀏覽器中存儲的值,是能夠在新打開的框口內獲得的,關閉後就自動消失(消失的實際上是session_id,由於session的機制是依賴於cookie的(還能夠依賴其餘的)。
答:
題目太大,篇幅有限,略。
答:
因爲一個用戶的異常訪問或者數據異常,加上沒有作好異常處理和安全保護,直接致使了整個 Node.js 服務重啓了,從而中斷了全部人的請求,用戶體驗很是差。 ①因爲 Node.js 使用的是 JavaScript,而JavaScript 是一個弱類型語言,所以在現網常常會引起一些由代碼邏輯的異常致使的進程異常退出。 ②其次在 Node.js 中也常常會由於內存的使用不當,致使內存泄漏,當在 64 位系統中達到 1.4 G(32 位系統 0.7 G)時,Node.js 就會異常崩潰。 ③再而因爲Node.js 的 I/O 較多也較爲頻繁,當啓用較多 I/O 句柄,可是沒有及時釋放,一樣會引起進程問題。
關於 JSON.parse 不少時候咱們都比較天然地將其餘接口或者第三方的數據拿來解析,可是這裏每每會忽略其非 JSON 字符串的問題,在這裏須要進行try catch 異常判斷。
當前 Node.js 的 Promise 應用愈來愈普遍了,所以對於 Promise 的 catch 也應該多進行重視,對於每一個 Promise 都應該要處理其異常 catch 邏輯,否則系統會提示 warning 信息。 還有一些常見的長鏈接的服務,好比 Socket、Redis、Memcache 等等,咱們須要在鏈接異常時進行處理,若是沒有處理一樣會致使異常,好比 Socket 提供了 Socket.on(‘error’) 的監聽。
設置最大臨時緩存數,超出則不使用緩存;設置最大緩存句柄數,超出則不使用緩存;定時清理當前的臨時緩存和句柄緩存。
通常狀況下不建議使用全局變量,全局變量必需要有必定的上限和清理規則才能保證服務的安全。
要注意一個點,有些模塊咱們使用單例的模式,就是在每次 require 後都返回這個對象,這種狀況也比較容易引起內存泄漏的問題。由於單例模式會引起每一個用戶訪問的數據的疊加。
通常打開文件句柄後,咱們都應該主動關閉,若是未主動關閉,就會致使文件句柄愈來愈多,從而引起句柄泄漏問題。
答:
QraphQL是對後端REST API向業務層的聚合與裁剪,REST更關注對業務細粒度的拆分與重用。
其實就是增長了一箇中間層對前端的請求和響應作預處理和後處理,前端的工做少了,後端的工做也沒多,卻加入了中端的依賴,好處是避免前端和後端的屢次遠距離的交互。 而graphql存在一個很難控制的問題就是查詢複雜度。在開發過程當中須要把控好解析粒度,而就目前主流關係型數據庫,restful api依舊是最好的選擇。graphql準確的說在查詢圖結構數據時更有優點,這也是其名稱的主意。
答:
在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的功能。
答:
SSR:
SPA:
答:
暫時沒遇到慢的狀況,沒法回答,放棄此題。
答:
CICD,持續集成和持續交付,各個部門合做同一個項目時,各類管理的倉庫發生變動,就會自動對代碼進行測試和構建,等Pipline跑完以後,反饋運行結果,自動打出最新的master包。
經常使用的CICD工具備Jenkins 、Travis 等等。
答:
題目太大,篇幅有限,略。
答:
不知道這道題是想問我如何把線上已有的資源從新蒐集成本地的素材中心,仍是單純的把 顯示資源換成本地的。 我對這道題目的理解是: 好比一個頁面內有一個el-table,5000頁,每頁20個商品,你也不知道每一個商品的連接,每一個商品後面的URL尚未規律,此時對於開發者而言,你所知道的也就是頁面的節點、接口的API這兩個關鍵信息了。想要把這20*5000個商品的商品詳情圖從新整理本地素材中心,那就手寫一個爬蟲,一次性爬完,把全部的資源路徑拿下來。或者寫一個chrome插件一鍵收集。
若是隻是單純的想把本應用的生產環境靜態資源換成本地的,直接找到 應用的靜態資源連接,找到它的baseUrl,換成本地的dev環境下的baseUrl就能夠了。通常項目的靜態資源和服務端服務都是分開發的,二者獨立,分開推送。而且每次發版,靜態資源都有本身所屬的版本號。
答:
// 見試卷背面
複製代碼
答:
// 見試卷背面
複製代碼
答:
參考vue-cli,至少知足如下幾點:
答:
至少知足4個條件
答:
寫代碼以前就要寫好文檔,沒有文檔的組件庫=白寫。文檔不只僅是給使用者看的,同時也是維護者給本身產品的一種宏觀把控。 文檔第一要實時、簡潔,易於上手;第二要作好版本控制,不管是Vue組件庫,仍是小程序組件庫,不少時候都要考慮大環境的限制(好比Vue的api變更、小程序基礎庫的變更),要實時更新和兼容,遇到沒法兼容的版本隔閡,要特別說明,適當的地方throw waring;第三,要支持按需加載和通用性和高拓展性,我曾經也只是一個組件庫api調用大師,如今也在維護本身的組件庫,我深知UI庫最大的問題不是樣式不全,而是別人以爲你的很差用,爲了用你一個toast而要下載整個組件庫,得不償失,按需加載很重要,支持拓展更重要,方便其餘的開發者在你的組件庫上打補丁。
答:
這個不算是問題,設計和整理API文檔,能體現維護者對本身產品的態度,在開發之時就要開始整理,而不是等所有開發完。在寫API文檔的同時還能夠幫助你從旁觀者的角度審覈你的產品功能。具體API文檔的實現方案,能夠使用vuepress、rap2等等之類的。
答: 單點登陸是指在同一賬號平臺下的多個應用系統中,用戶只需登陸一次,就可訪問全部相互信任的應用系統。好比你在網頁中登陸了百度雲盤,隨後你再去貼吧發帖 是不須要二次登陸的。
單點登陸的本質就是在多個應用系統中共享登陸狀態。若是用戶的登陸狀態是記錄在 Session 中的,要實現共享登陸狀態,就要先共享 Session,好比能夠將 Session 序列化到 Redis 中,讓多個應用系統共享同一個 Redis,直接讀取 Redis 來獲取 Session。
由於不一樣的應用系統有着不一樣的域名,儘管 Session 共享了,可是一個企業不一樣應用的域名不一樣,依然可能出現跨站or跨域。
前端方面的實現方式:
答:
方式有2類,
1、經過i18等插件,在項目代碼中定義語言環境,經過通用的語言翻譯函數,提供的功能就是靜態和動態文案的翻譯。主要思路就是經過構建工具去完成樣式, 圖片替換, class屬性等的替換工做,在業務代碼中不會出現過多的因國際化而多出的變量名,同時使用一個通用的翻譯函數去完成靜態文案及動態文案的翻譯工做,而不用使用不一樣框架提供的相應的國際化插件。
2、在項目中不使用插件,而是封裝一套通用的翻譯函數,在後臺服務那邊定義語言環境,經過API服務調用不一樣的主題和語言環境,返回不一樣的字段和頁面內容。(項目代碼中無中文,全是用翻譯函數包裹後的變量)
答:
監控是作法,埋點是工具,數據分析是目的。這三者一條道上的不一樣角色。
監控什麼,通常前端項目所要監控的數據主要分爲5種:
系統的生命週期數據,可用於觀察頁面性能狀況、總體訪問狀況等。
HTTP 測速數據,可用於觀察外部服務調用狀況 、頁面性能優化等。
系統異常數據,可用於觀察系統穩定性、系統異常問題。
用戶行爲數據,可用於觀察頁面穩定性、總體訪問狀況等。
用戶日誌,用於進行用戶反饋的問題排查。
前端常見的埋點方案有三種:代碼埋點、可視化埋點、無痕埋點。 無論使用哪一種埋點方式,咱們都須要對數據進行標準化處理。因爲最終的數據須要落盤到服務端並進行計算和監控,所以咱們須要將採集的數據,按照與服務端約定好的協議格式來進行轉換。
爲了快速發現並定位問題,咱們須要將這些埋點的數據、運行的日誌上報發送到服務端,服務端再進行轉換、存儲、計算和監控。通常採用new一個gif的形式上報,同時爲了不數據的上報過於頻繁、增長服務端的壓力,咱們能夠在本地進行數據的整合,好比經過隊列或數組的方式進行維護,而後選擇如下方式/時機進行上報。
插一句話,有些場景咱們須要知道用戶設備崩潰or異常的數據狀況,但每每這種狀況下埋點沒有觸發,或者該觸發的時候用戶退出應用了,因此這裏建議把須要埋點上報的數據離線上傳,就是把埋點上報隊列,放在緩存裏,慢慢吐泡泡,同時不跟主業務流程中的API服務競爭資源。
數據上報完成後,通常來講須要搭建可視化的管理端,來對這些數據進行直觀的監控。在平常監控中,咱們還會經過對監控數據、配置告警閾值等方式,結合郵件、機器人等方式推送到相關的人員,來及時發現並解決問題。好比若是5分鐘的異常數量大於閥值,就釘釘or短信報警,讓相關同事及時修復功能異常。若是想要作得更多,咱們甚至能夠結合其餘系統來進行協做,好比關聯 BUG 管理系統、自動生成 BUG 單,將 BUG 單綁定到對應的版本分支上,經過提交對應的修復分支、進行測試驗證後,自動地扭轉 BUG 單狀態,等等。
答:
我沒有當過Leader,如下純從隊友角度回答。
事前:進行合理的分工排期,對項目風險進行把控,學會自測,不要把全部的風險都拋給測試同窗處理。
過後:對發佈上線的產品及時監控,觀察是否運行正常,是否符合預期。遇到上線後帶了bug,或者上線版本異常,此時首要目標時恢復業務,及時回滾,不要嘗試用新版本覆蓋。
覆盤:及時發現本身的問題並改進,避免掉進同一個坑;讓團隊成員和管理者知道本身在作什麼;整理沉澱和分享項目經驗,不奢求讓整個團隊都獲得成長,至少本身在這個產品開發週期內有所收穫。
答:
通常的管理後臺應用,基本就是Vue+Element,或者React+antd這樣的組件,而且業務仍是toB,to內部使用的這種,通常都是SPA,在性能方面,也不是那麼講究, 代碼角度的優化:
工程角度的優化:
騷操做禁區
:
答:
答:
答:
// 見試卷背面
複製代碼
答:
阿里的imgCook、雲鳳蝶、京東的通天塔、滿幫的碼良、徐小夕的H5 Doring等等,無一例外,至少都知足一下幾種能力:
答:
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,
}
複製代碼
答: 模板引擎的做用是將模板文件轉換成另外一種模板格式的工具,引擎內部流程是把模板文件裏的內容當成字符串傳入到模板引擎中,而後模板引擎根據必定語法對該字符串進行解析處理,而後返回一個函數,以後咱們在執行函數時把數據傳輸進去,便可拿到根據模板和數據獲得的新字符串。
模板 ----> 輸入到模板引擎 ----> 生成函數 ----> 把數據當成參數,執行該函數 ----> 輸出結果。
立刻開始實現一個知足 變量定義、插值、條件判斷、數組遍歷 功能的建議模板引擎。
"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 = {
"<": "<",
">": ">",
"&": "&",
" ": " ",
'"': """,
"'": "'",
"\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__;
}
複製代碼
答:
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;
},
};
複製代碼
答:
// 見試卷背面
複製代碼
考試大綱
以下
基礎知識主要包含如下幾個方面:
基礎:計算機原理、編譯原理、數據結構、算法、設計模式、編程範式等基本知識瞭解
語法: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、模板引擎等了解和使用
得分:_____