# 瀏覽器與新技術
點擊關注本[公眾號](http://www.hmoore.net/book/dsh225/javascript_vue_css/edit#_118)獲取文檔最新更新,并可以領取配套于本指南的《**前端面試手冊**》以及**最標準的簡歷模板**.
[TOC]
本章關于瀏覽器原理部分的內容主要來源于[瀏覽器工作原理](http://taligarsiel.com/Projects/howbrowserswork1.htm),這是一篇很長的文章,可以算上一本小書了,有精力的非常建議閱讀。
## 常見的瀏覽器內核有哪些?
| 瀏覽器/RunTime | 內核(渲染引擎) | JavaScript 引擎 |
| --- | --- | --- |
| Chrome | Blink(28~)
Webkit(Chrome 27) | V8 |
| FireFox | Gecko | SpiderMonkey |
| Safari | Webkit | JavaScriptCore |
| Edge | EdgeHTML | Chakra(for JavaScript) |
| IE | Trident | Chakra(for JScript) |
| PhantomJS | Webkit | JavaScriptCore |
| Node.js | \- | V8 |
## 瀏覽器的主要組成部分是什么?
1. **用戶界面**?- 包括地址欄、前進/后退按鈕、書簽菜單等。除了瀏覽器主窗口顯示的您請求的頁面外,其他顯示的各個部分都屬于用戶界面。
2. **瀏覽器引擎**?- 在用戶界面和呈現引擎之間傳送指令。
3. **呈現引擎**?- 負責顯示請求的內容。如果請求的內容是 HTML,它就負責解析 HTML 和 CSS 內容,并將解析后的內容顯示在屏幕上。
4. **網絡**?- 用于網絡調用,比如 HTTP 請求。其接口與平臺無關,并為所有平臺提供底層實現。
5. **用戶界面后端**?- 用于繪制基本的窗口小部件,比如組合框和窗口。其公開了與平臺無關的通用接口,而在底層使用操作系統的用戶界面方法。
6. **JavaScript 解釋器**。用于解析和執行 JavaScript 代碼。
7. **數據存儲**。這是持久層。瀏覽器需要在硬盤上保存各種數據,例如 Cookie。新的 HTML 規范 (HTML5) 定義了“網絡數據庫”,這是一個完整(但是輕便)的瀏覽器內數據庫。

> 圖:瀏覽器的主要組件。
值得注意的是,和大多數瀏覽器不同,Chrome 瀏覽器的每個標簽頁都分別對應一個呈現引擎實例。每個標簽頁都是一個獨立的進程。
## 瀏覽器內核、渲染引擎和js引擎的關系
[瀏覽器內核](http://www.966266.com/jishu/49.html#nh)又可以分成兩部分:[渲染引擎](http://www.966266.com/jishu/49.html)(layout engineer或者RenderingEngine)和[JS引擎](http://www.966266.com/jishu/49.html#js)。
**渲染引擎功能作用**
???????渲染引擎,負責對網頁語法的解釋(如HTML、[JavaScript](http://www.966266.com/jishu/49.html#js))并渲染網頁。所以,通常所謂的瀏覽器內核也就是瀏覽器所采用的渲染引擎,渲染引擎決定了瀏覽器如何顯示網頁的內容以及頁面的格式信息。不同的瀏覽器內核對網頁編寫語法的解釋也有不同,因此同一網頁在不同的內核的瀏覽器里的渲染(顯示)效果也可能不同,這也是網頁編寫者需要在不同內核的瀏覽器中[**測試**](http://lib.csdn.net/base/softwaretest)網頁顯示效果的原因。
當前主流渲染引擎內核:
firefox使用gecko引擎
IE使用Trident引擎
2015年微軟推出自己新的瀏覽器,原名叫斯巴達,后改名edge,使用edge引擎
opera最早使用Presto引擎,后來棄用
chrome\\safari\\opera使用webkit引擎
13年chrome和opera開始使用Blink引擎
**JS引擎功能作用**
??????最開始渲染引擎和[**js**](http://lib.csdn.net/base/javascript)引擎并沒有區分的很明確,后來JS引擎越來越獨立,內核就傾向于只指渲染引擎。[**JavaScript**](http://lib.csdn.net/base/javascript)最初由網景公司的Brendan Eich設計,是一種動態、弱類型、基于原型的語言,內置支持類。以它為基礎,制定了ECMAScript標準。[**javascript**](http://lib.csdn.net/base/javascript)在瀏覽器的實現中還必須含有DOM和BOM。Web瀏覽器一般使用公共?API來創建主機對象來負責將DOM對象反射進JavaScript。JS引擎負責對JavaScript進行解釋、編譯和執行,以使網頁達到一些動態的效果。
主要的網頁瀏覽器JavaScript引擎:
1)?[Mozilla](http://firefox.966266.com/)
* Rhino,由Mozilla基金會管理,開放源代碼,完全以Java編寫。
* SpiderMonkey,第一款JavaScript引擎,由BrendanEich在NetscapeCommunications時編寫,用于[Mozilla?](http://firefox.966266.com/)[Firefox](http://firefox.966266.com/)1.0~3.0版本。
* TraceMonkey,基于實時編譯的引擎,其中部份代碼取自Tamarin引擎,用于?Mozilla Firefox 3.5~3.6版本。
* J?gerMonkey,(J?gerMonkey,也有人拼寫成[JagerMonkey](http://www.966266.com/jishu/jagermonkey.html))德文J?ger原意為獵人,結合追蹤和組合碼技術大幅提高效能,部分技術借鑿了[V8](http://www.966266.com/jishu/v8.html)、JavaScriptCore、[WebKit](http://www.966266.com/jishu/53.html),用于[Mozilla Firefox 4.0](http://firefox.966266.com/)以上版本。
2)[Google](http://chrome.966266.com/)
·????????[V8](http://www.966266.com/jishu/v8.html),開放源代碼,由Google丹麥開發,是[Google Chrome](http://chrome.966266.com/)的一部分。
3)[微軟](http://ie.966266.com/)
* [Chakra](http://www.966266.com/jishu/chakra.html),中文譯名為查克拉,用于[Internet Explorer 9](http://ie.966266.com/soft-down/ie9-win7-32.html)。
* ?[JScript?](http://www.966266.com/jishu/jscript.html)是由微軟公司開發的活動腳本語言,是微軟對ECMAScript規范的實現.[IE](http://ie.966266.com/)?3.0-[IE8.0](http://ie.966266.com/soft-down/ie8-xp.html)使用的JS引擎
4)其它
* ?KJS,KDE的ECMAScript/JavaScript引擎,最初由Harri Porten開發,用于KDE項目的?Konqueror網頁瀏覽器中。
* Narcissus,開放源代碼,由BrendanEich編寫(他也參與編寫了第一個SpiderMonkey)。
* Tamarin,由AdobeLabs編寫,Flash Player?9所使用的引擎。
* Nitro(原名SquirrelFish),為[Safari](http://safari.966266.com/)?4編寫。
* ?Carakan,由[Opera軟件公司](http://opera.966266.com/)編寫,自[Opera](http://opera.966266.com/)10.50版本開始使用。
## 瀏覽器是如何渲染UI的?
1. 瀏覽器獲取HTML文件,然后對文件進行解析,形成DOM Tree
2. 與此同時,進行CSS解析,生成Style Rules
3. 接著將DOM Tree與Style Rules合成為 Render Tree
4. 接著進入布局(Layout)階段,也就是為每個節點分配一個應出現在屏幕上的確切坐標
5. 隨后調用GPU進行繪制(Paint),遍歷Render Tree的節點,并將元素呈現出來

## 瀏覽器如何解析css選擇器?
瀏覽器會『從右往左』解析CSS選擇器。
我們知道DOM Tree與Style Rules合成為 Render Tree,實際上是需要將*Style Rules*附著到DOM Tree上,因此需要根據選擇器提供的信息對DOM Tree進行遍歷,才能將樣式附著到對應的DOM元素上。
以下這段css為例
~~~
.mod-nav h3 span {font-size: 16px;}
~~~
我們對應的DOM Tree 如下

若從左向右的匹配,過程是:
1. 從 .mod-nav 開始,遍歷子節點 header 和子節點 div
2. 然后各自向子節點遍歷。在右側 div 的分支中
3. 最后遍歷到葉子節點 a ,發現不符合規則,需要回溯到 ul 節點,再遍歷下一個 li-a,一顆DOM樹的節點動不動上千,這種效率很低。
如果從右至左的匹配:
1. 先找到所有的最右節點 span,對于每一個 span,向上尋找節點 h3
2. 由 h3再向上尋找 class=mod-nav 的節點
3. 最后找到根元素 html 則結束這個分支的遍歷。
后者匹配性能更好,是因為從右向左的匹配在第一步就篩選掉了大量的不符合條件的最右節點(葉子節點);而從左向右的匹配規則的性能都浪費在了失敗的查找上面。
## DOM Tree是如何構建的?
1. 轉碼: 瀏覽器將接收到的二進制數據按照指定編碼格式轉化為HTML字符串
2. 生成Tokens: 之后開始parser,瀏覽器會將HTML字符串解析成Tokens
3. 構建Nodes: 對Node添加特定的屬性,通過指針確定 Node 的父、子、兄弟關系和所屬 treeScope
4. 生成DOM Tree: 通過node包含的指針確定的關系構建出DOM Tree

## 瀏覽器重繪與(回流/重排)的區別?
* 重排: 部分渲染樹(或者整個渲染樹)需要重新分析并且節點尺寸需要重新計算,表現為重新生成布局,重新排列元素
* 重繪: 由于節點的幾何屬性發生改變或者由于樣式發生改變,例如改變元素背景色時,屏幕上的部分內容需要更新,表現為某些元素的外觀被改變
單單改變元素的外觀,肯定不會引起網頁重新生成布局,但當瀏覽器完成重排之后,將會重新繪制受到此次重排影響的部分
重排和重繪代價是高昂的,它們會破壞用戶體驗,并且讓UI展示非常遲緩,而相比之下重排的性能影響更大,在兩者無法避免的情況下,一般我們寧可選擇代價更小的重繪。
『重繪』不一定會出現『重排』,『重排』必然會出現『重繪』。
## 如何觸發重排和重繪?
任何改變用來構建渲染樹的信息都會導致一次重排或重繪:
* 添加、刪除、更新DOM節點
* 通過display: none隱藏一個DOM節點-觸發重排和重繪
* 通過visibility: hidden隱藏一個DOM節點-只觸發重繪,因為沒有幾何變化
* 移動或者給頁面中的DOM節點添加動畫
* 添加一個樣式表,調整樣式屬性
* 用戶行為,例如調整窗口大小,改變字號,或者滾動。
## 如何避免重繪或者重排?
### 集中改變樣式
我們往往通過改變class的方式來集中改變樣式
~~~
// 判斷是否是黑色系樣式
const theme = isDark ? 'dark' : 'light'
// 根據判斷來設置不同的class
ele.setAttribute('className', theme)
~~~
### 使用DocumentFragment
我們可以通過createDocumentFragment創建一個游離于DOM樹之外的節點,然后在此節點上批量操作,最后插入DOM樹中,因此只觸發一次重排
~~~
var fragment = document.createDocumentFragment();
for (let i = 0;i<10;i++){
let node = document.createElement("p");
node.innerHTML = i;
fragment.appendChild(node);
}
document.body.appendChild(fragment);
~~~
### 提升為合成層
將元素提升為合成層有以下優點:
* 合成層的位圖,會交由 GPU 合成,比 CPU 處理要快
* 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
* 對于 transform 和 opacity 效果,不會觸發 layout 和 paint
提升合成層的最好方式是使用 CSS 的 will-change 屬性:
~~~
#target {
will-change: transform;
}
~~~
### CSS優化
1\. 使用 transform 代替 定位使用 (top)
2\. 使用 visibility 替換 display:none,前者只會引起重繪,后者會發生回流(布局改變)
3\. 避免使用 table 布局方式(可能很小的一個改動會造成整個table的重新布局)
4\. 盡可能在DOM樹的最末端改變class(回流是不可避免的,但可以減少其影響。盡可能在DOM樹的最末端改變class,可以限制了回流的范圍,使其影響盡可能少的節點)
5\. 避免設置多層內聯樣式,CSS 選擇符從右往左匹配查找,避免節點層級過多(例:div>span>em)
6\. 將動畫效果應用到position屬性為absolute或fixed的元素上(避免影響其他元素的布局,這樣只是一個重繪,而不是回流,同時,控制動畫速度可以選擇 requestAnimationFrame)
7\. 避免使用CSS表達式,可能會引發回流
8\. 將頻繁重繪或者回流的節點設置為圖層,圖層能夠阻止該節點的渲染行為影響別的節點,例如will-change、video、iframe、canvas等標簽,瀏覽器會自動將該節點變為圖層
9\. 啟用CSS3 硬件加速(GPU加速),使用css3硬件加速,可以讓transform、opacity、filters這些動畫不會引起回流重繪 。但是對于動畫的其它屬性,比如background-color這些,還是會引起回流重繪的,不過它還是可以提升這些動畫的性能
### JS優化
1\. 避免頻繁操作樣式,(最好一次性重寫style屬性,或者將樣式列表定義為class并一次性更改class屬性)
2\. 避免頻繁操作DOM(創建一個documentFragment,在它上面應用所有DOM操作,最后再把它添加到文檔中)
3\. 避免頻繁讀取會引發回流/重繪的屬性,如果確實需要多次使用,就用一個變量緩存起來
> 關于合成層的詳解請移步[無線性能優化:Composite](https://fed.taobao.org/blog/2016/04/26/performance-composite/)
## 前端如何實現即時通訊?
### 短輪詢
短輪詢的原理很簡單,每隔一段時間客戶端就發出一個請求,去獲取服務器最新的數據,一定程度上模擬實現了即時通訊。
* 優點:兼容性強,實現非常簡單
* 缺點:延遲性高,非常消耗請求資源,影響性能
### comet
comet有兩種主要實現手段,一種是基于 AJAX 的長輪詢(long-polling)方式,另一種是基于 Iframe 及 htmlfile 的流(streaming)方式,通常被叫做長連接。
> 具體兩種手段的操作方法請移步[Comet技術詳解:基于HTTP長連接的Web端實時通信技術](http://www.52im.net/thread-334-1-1.html)
長輪詢優缺點:
* 優點:兼容性好,資源浪費較小
* 缺點:服務器hold連接會消耗資源,返回數據順序無保證,難于管理維護
長連接優缺點:
* 優點:兼容性好,消息即時到達,不發無用請求
* 缺點:服務器維護長連接消耗資源
### SSE
> 使用指南請看[Server-Sent Events 教程](https://www.ruanyifeng.com/blog/2017/05/server-sent_events.html)
SSE(Server-Sent Event,服務端推送事件)是一種允許服務端向客戶端推送新數據的HTML5技術。
* 優點:基于HTTP而生,因此不需要太多改造就能使用,使用方便,而websocket非常復雜,必須借助成熟的庫或框架
* 缺點:基于文本傳輸效率沒有websocket高,不是嚴格的雙向通信,客戶端向服務端發送請求無法復用之前的連接,需要重新發出獨立的請求

### Websocket
> 使用指南請看[WebSocket 教程](http://www.ruanyifeng.com/blog/2017/05/websocket.html)
Websocket是一個全新的、獨立的協議,基于TCP協議,與http協議兼容、卻不會融入http協議,僅僅作為html5的一部分,其作用就是在服務器和客戶端之間建立實時的雙向通信。
* 優點:真正意義上的實時雙向通信,性能好,低延遲
* 缺點:獨立與http的協議,因此需要額外的項目改造,使用復雜度高,必須引入成熟的庫,無法兼容低版本瀏覽器

### Web Worker
> 后面性能優化部分會用到,先做了解
Web Worker 的作用,就是為 JavaScript 創造多線程環境,允許主線程創建 Worker 線程,將一些任務分配給后者運行
> [Web Worker教程](http://www.ruanyifeng.com/blog/2018/07/web-worker.html)
### Service workers
> 后面性能優化部分會用到,先做了解
Service workers 本質上充當Web應用程序與瀏覽器之間的代理服務器,也可以在網絡可用時作為瀏覽器和網絡間的代理,創建有效的離線體驗。
> [Service workers教程](https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API)
## 什么是瀏覽器同源策略?
同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用于隔離潛在惡意文件的重要安全機制。
同源是指"協議+域名+端口"三者相同,即便兩個不同的域名指向同一個ip地址,也非同源。
下表給出了相對http://store.company.com/dir/page.html同源檢測的示例:

瀏覽器中的大部分內容都是受同源策略限制的,但是以下三個標簽可以不受限制:
* `<img src=XXX>`
* `<link href=XXX>`
* `<script src=XXX>`
## 如何實現跨域?
跨域是個比較古老的命題了,歷史上跨域的實現手段有很多,我們現在主要介紹三種比較主流的跨域方案,其余的方案我們就不深入討論了,因為使用場景很少,也沒必要記這么多奇技淫巧。
### 最經典的跨域方案jsonp
jsonp本質上是一個Hack,它利用`<script>`標簽不受同源策略限制的特性進行跨域操作。
jsonp優點:
* 實現簡單
* 兼容性非常好
jsonp的缺點:
* 只支持get請求(因為`<script>`標簽只能get)
* 有安全性問題,容易遭受xss攻擊
* 需要服務端配合jsonp進行一定程度的改造
jsonp的實現:
~~~
function JSONP({
url,
params,
callbackKey,
callback
}) {
// 在參數里制定 callback 的名字
params = params || {}
params[callbackKey] = 'jsonpCallback'
// 預留 callback
window.jsonpCallback = callback
// 拼接參數字符串
const paramKeys = Object.keys(params)
const paramString = paramKeys
.map(key => `${key}=${params[key]}`)
.join('&')
// 插入 DOM 元素
const script = document.createElement('script')
script.setAttribute('src', `${url}?${paramString}`)
document.body.appendChild(script)
}
JSONP({
url: 'http://s.weibo.com/ajax/jsonp/suggestion',
params: {
key: 'test',
},
callbackKey: '_cb',
callback(result) {
console.log(result.data)
}
})
~~~
### 最流行的跨域方案cors
cors是目前主流的跨域解決方案,跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被準許訪問來自不同源服務器上的指定的資源。當一個資源從與該資源本身所在的服務器不同的域、協議或端口請求一個資源時,資源會發起一個跨域 HTTP 請求。
如果你用express,可以這樣在后端設置
~~~
//CORS middleware
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://example.com');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
//...
app.configure(function() {
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: 'cool beans' }));
app.use(express.methodOverride());
app.use(allowCrossDomain);
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
~~~
在生產環境中建議用成熟的開源中間件解決問題。
#### 如何讓CORS攜帶Cookie
```
Access-Control-Allow-Origin: <origin> | * // 授權的訪問源
Access-Control-Max-Age: <delta-seconds> // 預檢授權的有效期,單位:秒
Access-Control-Allow-Credentials: true | false // 是否允許攜帶
Cookie Access-Control-Allow-Methods: <method>[, <method>]* // 允許的請求動詞
Access-Control-Allow-Headers: <field-name>[, <field-name>]* // 額外允許攜帶的請求頭
Access-Control-Expose-Headers: <field-name>[, <field-name>]* // 額外允許訪問的響應頭
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
```
https://www.cnblogs.com/OpenCoder/p/9915214.html
### 最方便的跨域方案Nginx
nginx是一款極其強大的web服務器,其優點就是輕量級、啟動快、高并發。
現在的新項目中nginx幾乎是首選,我們用node或者java開發的服務通常都需要經過nginx的反向代理。

反向代理的原理很簡單,即所有客戶端的請求都必須先經過nginx的處理,nginx作為代理服務器再講請求轉發給node或者java服務,這樣就規避了同源策略。
~~~
#進程, 可更具cpu數量調整
worker_processes 1;
events {
#連接數
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#連接超時時間,服務器會在這個時間過后關閉連接。
keepalive_timeout 10;
# gizp壓縮
gzip on;
# 直接請求nginx也是會報跨域錯誤的這里設置允許跨域
# 如果代理地址已經允許跨域則不需要這些, 否則報錯(雖然這樣nginx跨域就沒意義了)
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
# srever模塊配置是http模塊中的一個子模塊,用來定義一個虛擬訪問主機
server {
listen 80;
server_name localhost;
# 根路徑指到index.html
location / {
root html;
index index.html index.htm;
}
# localhost/api 的請求會被轉發到192.168.0.103:8080
location /api {
rewrite ^/b/(.*)$ /$1 break; # 去除本地接口/api前綴, 否則會出現404
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.0.103:8080; # 轉發地址
}
# 重定向錯誤頁面到/50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
~~~
### 其它跨域方案
1. HTML5 XMLHttpRequest 有一個API,postMessage()方法允許來自不同源的腳本采用異步方式進行有限的通信,可以實現跨文本檔、多窗口、跨域消息傳遞。
2. WebSocket 是一種雙向通信協議,在建立連接之后,WebSocket 的 server 與 client 都能主動向對方發送或接收數據,連接建立好了之后 client 與 server 之間的雙向通信就與 HTTP 無關了,因此可以跨域。
3. window.name + iframe:window.name屬性值在不同的頁面(甚至不同域名)加載后依舊存在,并且可以支持非常長的 name 值,我們可以利用這個特點進行跨域。
4. location.hash + iframe:a.html欲與c.html跨域相互通信,通過中間頁b.html來實現。 三個頁面,不同域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通信。
5. document.domain + iframe: 該方式只能用于二級域名相同的情況下,比如 a.test.com 和 b.test.com 適用于該方式,我們只需要給頁面添加 document.domain ='test.com' 表示二級域名都相同就可以實現跨域,兩個頁面都通過js強制設置document.domain為基礎主域,就實現了同域。
> 其余方案來源于[九種跨域方式](https://juejin.im/post/5c23993de51d457b8c1f4ee1#heading-19)
* * *
參考文章:
* [為什么 CSS 選擇器解析的時候是從右往左?](https://segmentfault.com/q/1010000000713509)
* * *
## 公眾號
想要實時關注筆者最新的文章和最新的文檔更新請關注公眾號**程序員面試官**,后續的文章會優先在公眾號更新.
**簡歷模板**:關注公眾號回復「模板」獲取
《**前端面試手冊**》:配套于本指南的突擊手冊,關注公眾號回復「fed」獲取

- 前言
- 指南使用手冊
- 為什么會有這個項目
- 面試技巧
- 面試官到底想看什么樣的簡歷?
- 面試回答問題的技巧
- 如何通過HR面
- 推薦
- 書籍/課程推薦
- 前端基礎
- HTML基礎
- CSS基礎
- JavaScript基礎
- 瀏覽器與新技術
- DOM
- 前端基礎筆試
- HTTP筆試部分
- JavaScript筆試部分
- 前端原理詳解
- JavaScript的『預解釋』與『變量提升』
- Event Loop詳解
- 實現不可變數據
- JavaScript內存管理
- 實現深克隆
- 如何實現一個Event
- JavaScript的運行機制
- 計算機基礎
- HTTP協議
- TCP面試題
- 進程與線程
- 數據結構與算法
- 算法面試題
- 字符串類面試題
- 前端框架
- 關于前端框架的面試須知
- Vue面試題
- React面試題
- 框架原理詳解
- 虛擬DOM原理
- Proxy比defineproperty優劣對比?
- setState到底是異步的還是同步的?
- 前端路由的實現
- redux原理全解
- React Fiber 架構解析
- React組件復用指南
- React-hooks 抽象組件
- 框架實戰技巧
- 如何搭建一個組件庫的開發環境
- 組件設計原則
- 實現輪播圖組件
- 性能優化
- 前端性能優化-加載篇
- 前端性能優化-執行篇
- 工程化
- webpack面試題
- 前端工程化
- Vite
- 安全
- 前端安全面試題
- npm
- 工程化原理
- 如何寫一個babel
- Webpack HMR 原理解析
- webpack插件編寫
- webpack 插件化設計
- Webpack 模塊機制
- webpack loader實現
- 如何開發Babel插件
- git
- 比較
- 查看遠程倉庫地址
- git flow
- 比較分支的不同并保存壓縮文件
- Tag
- 回退
- 前端項目經驗
- 確定用戶是否在當前頁面
- 前端下載文件
- 只能在微信中訪問
- 打開新頁面-被瀏覽器攔截
- textarea高度隨內容變化 vue版
- 去掉ios原始播放大按鈕
- nginx在MAC上的安裝、啟動、重啟和關閉
- 解析latex格式的數學公式
- 正則-格式化a鏈接
- 封裝的JQ插件庫
- 打包問題總結
- NPM UI插件
- 帶你入門前端工程
- webWorker+indexedDB性能優化
- 多個相鄰元素切換效果出現邊框重疊問題的解決方法
- 監聽前端storage變化