「如何從零到一實現一個玩具瀏覽器🌏」

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家好,我是速凍魚🐟,一條水系前端💦,喜歡花裏胡哨💐,持續沙雕🌲歡迎小夥伴們加我","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"微信","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"sudongyuer","attrs":{}}],"attrs":{}},{"type":"text","text":"拉你進羣關注我的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"公衆號","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"前端速凍魚","attrs":{}}],"attrs":{}},{"type":"text","text":"一起進步,期待與大家共同成長🥂","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"閱讀本文 📖","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1.您將瞭解到什麼是有限狀態機","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2.您將瞭解到瀏覽器渲染基本流程與原理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"3.您將和我一起完成一個玩具瀏覽器的編寫","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"本文倉庫地址:","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/sudongyuer/toy-browser","title":"","type":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"toy-browser","attrs":{}}]}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"前言 🌵","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近在學習瀏覽器渲染原理,光知道理論還不行🌝,我們得動手實踐才能更深入的瞭解瀏覽器渲染後背的點點滴滴💧,下面分享給大家","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"前置知識 💻","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.什麼是有限狀態機 ⭐","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有限狀態機(Finite-state machine)是一個非常有用的模型,可以模擬世界上大部分事物。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每一個狀態都是一個機器","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在每一個機器裏,我們可以做計算、存儲、輸出......","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有的這些機器接受的輸入是一致的","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"狀態機的每一個機器本身沒有狀態,如果我們用函數來表示的話,它應該是純函數(無副作用)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每一個機器知道下一個狀態","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個機器都有確定的下一個狀態(Moore)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個機器根據輸入決定下一個狀態(Mealy)","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1f07d59764bd0ed7f0d1f06bfefa0c1f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單說,它有三個特徵:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"狀態總數(state)是有限的。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"任一時刻,只處在一種狀態之中。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"某種條件下,會從一種狀態轉變(transition)到另一種狀態。","attrs":{}}]}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉例來說,網頁上有一個菜單元素。鼠標懸停的時候,菜單顯示;鼠標移開的時候,菜單隱藏。如果使用有限狀態機描述,就是這個菜單隻有兩種狀態(顯示和隱藏),鼠標會引發狀態轉變。現在還不太瞭解沒關係,後邊我們看代碼就好理解多了,這裏對狀態機不做過多描述。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"感興趣可以看看阮一峯老師的文章","attrs":{}},{"type":"link","attrs":{"href":"http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html","title":"","type":null},"content":[{"type":"text","text":"JavaScript與有限狀態機","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"極客時間","attrs":{}}],"attrs":{}},{"type":"text","text":"Winter大佬的重學前端也有相關內容","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.我們使用有限狀態機來解決什麼問題 🌟","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有限狀態機的寫法,邏輯清晰,表達力強,有利於封裝事件。一個對象的狀態越多、發生的事件越多,就越適合採用有限狀態機的寫法。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"比如使用有限狀態機處理字符串","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"在一個字符串中,如何使用狀態機找到字符“abcdef”","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"function findStr(str) {\n let state= start;\n for (const c of str) {\n state=state(c)\n }\n return state===end\n}\n\nfunction start(c) {\n if (c === 'a') {\n return findA\n } else return start\n}\n\nfunction end(c) {\n return end\n}\n\nfunction findA(c) {\n if (c === 'b') {\n return findB\n } else return start(c)\n}\n\nfunction findA2(c) {\n if (c === 'b') {\n return findB2\n } else return start(c)\n}\nfunction findA3(c) {\n if (c === 'b') {\n return findB3\n } else return start(c)\n}\n\n\nfunction findB(c) {\n if (c === 'a') {\n return findA2\n } else return start(c)\n\n}\n\nfunction findB2(c) {\n if (c === 'a') {\n return findA3\n } else return start(c)\n\n}\n\nfunction findB3(c) {\n if (c === 'x') {\n return end\n } else return start(c)\n\n}\n\n\n\nconsole.log(findStr('aaabxababx'))\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"後邊我們實戰中也將會使用狀態機來對html文本進行解析構建我們的抽象語法樹(AST)","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.瀏覽器渲染的大致流程 💫","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5d/5d576609041af2d735f716bc0ee4f3e8.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發送HTTP請求獲取HTML","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對獲取到的HMTL進行解析成一顆光禿禿的DOM樹","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對獲取到的CSS進行計算,將計算出來的值添加到DOM樹上,形成一棵帶有CSS樣式的渲染樹","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了渲染樹,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關係,從而去計算出每個節點在屏幕中的位置,大小","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了每個dom的位置大小信息後,瀏覽器就可以將各個節點繪製到屏幕上了","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e6/e628139fdfc6b87d701bdac74b956534.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"下面不說廢話直接開搞^_^","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"實現流程 🌊","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"tips☀️:以下代碼有點長,不想查看細節的小夥伴可以直接看後邊","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"總結","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":",也可以到","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/sudongyuer/toy-browser","title":"","type":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"toy-browser","attrs":{}}]},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"查看","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"源碼","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"這裏我不會很詳細的去介紹代碼的每一步實現,重要的想讓大家對整個渲染流程有個全面的認識🍎","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1.用node模擬我們的服務端 🐻","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接收請求,響應我們的HTML就是我們的node服務要做的事情,就這麼簡單^_^","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"server.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"const http = require(\"http\");\n\nhttp\n .createServer((req, res) => {\n let body = [];\n req\n .on(\"error\", (err) => {\n console.error(err);\n })\n .on(\"data\", (chunk) => {\n console.log(\"chunk\", chunk);\n body.push(chunk);\n })\n .on(\"end\", () => {\n body = Buffer.concat(body).toString();\n console.log(\"body\", body);\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(\n`\n\n \n\n\n
\n
\n
\n
\n\n`\n );\n });\n })\n .listen(\"8088\");\nconsole.log(\"server started\");\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2.客戶端編寫 🐼","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在客戶端我們會發送","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"http請求","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"拿到html","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對html進行解析","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"拿到dom樹","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"計算css","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"形成渲染樹","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"layout","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"形成有位置的dom樹","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"render","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Bitmap","attrs":{}}],"attrs":{}},{"type":"text","text":"->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"瀏覽器展示我們的畫面","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4c/4c41f3bf1e125fbd497cbf4480e1cf82.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"先從整體看看我們client需要做什麼,看不懂沒關係,我會分開解釋每一個流程在代碼中的具體實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"client.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1fa6265ca573346bb6aebba6f2f15e7d.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 發送http請求獲取html","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bb/bb41002f36073e52d669e38567def1cf.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tips⭐:以下代碼只展示了核心調用部分,想要看全部實現的小夥伴可以查看我的源碼","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/sudongyuer/toy-browser","title":"","type":null},"content":[{"type":"text","text":"toy-browser","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/54/54be01e3fd141e8cfcbe878f9242f3da.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 對獲取到的html文本進行詞法分析獲取token","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/41/4129d08c06547a7e9e73ec17feabb607.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"敲黑板","attrs":{}}],"attrs":{}},{"type":"text","text":"👨‍🏫","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"這裏就要開始用到我們上文提到的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"有限狀態機","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對html進行解析了哦","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tips⭐:以下代碼只展示了核心調用部分,想要看全部實現的小夥伴可以查看我的源碼","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/sudongyuer/toy-browser","title":"","type":null},"content":[{"type":"text","text":"toy-browser","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對html的每一個字符使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"有限狀態機","attrs":{}}],"attrs":{}},{"type":"text","text":"進行詞法分析,形成","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"(","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"token","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"指有效部分,這裏可以理解爲一個htm標籤,eg:
、就算是一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"token","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":")","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6e/6e56d2c4b7e77183ba20ffc76f1e8c1f.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/85/85924d1c13b7cbbb0464661e1e297c14.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於狀態太多,這裏只例舉了部分狀態,我們要通過這個狀態機對html的每個字符進行詞法分析得到token好進行後邊的語法分析","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4c/4cf33a48b9bd7c07a9dbce79d8e252aa.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.3 對獲取到的token進行語法分析構建dom樹","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4f/4f2cfb51f41b14d516ddc6c20a65a026.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們拿到每一個詞法分析過後的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":",根據每個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":"的屬性執行不同的邏輯來構建我們的語法樹🌳(其實我們css計算也就會在最初生成dom樹的時候進行)","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"使用棧這個數據結構來維護我們的dom樹🌴,根據","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"token","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"type","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"來對","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Node","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"進行入棧和出棧的操作,最後遍歷完每個token,對每個token進行邏輯處理後,棧頂只剩下我們的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"document","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象,這個","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"document","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象就是我們dom樹的對象表現形式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":")","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a5/a520a9dd102eadeb8ec30658160b3c21.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"let stack = [{ type: \"document\", children: [] }]; //doms樹解析用的棧\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"根據每個token不同的type,執行不同的邏輯,添加","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Node","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"到我們的dom樹🌴上","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e1/e168d3997d60d37f64247c92cc446d80.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"當遇到type爲style的token時,使用一個數組","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"rules[]","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"來維護這個樣式規則","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"let rules=[];\n\n/**\n * 添加樣式規則的方法\n * @param text\n */\nfunction addCSSRules(text){\n //調用css這個現成的庫對css樣式文本進行詞法語法分析獲取css的Ast\n var ast=css.parse(text);\n // console.log(JSON.stringify(ast,null,4));\n rules.push(...ast.stylesheet.rules);\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.4 對dom樹進行css計算並獲取渲染樹","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/afdaa96695e28567fd781ade28f9400a.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實這一步我們是在獲取到token的時候就會進行css計算,爲了方便理解,所以單獨劃分一步。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"可以看到這裏我們拿到token後,進行語法分析的時候就會進行css計算","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/be/be621b56c2db4f7c02fd17359ecf9fe3.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dom樹","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"的每個","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"元素節點","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"進行","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"css計算","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":",計算完成後,每個","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"元素節點","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象上就會維護一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"computedStyle","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"屬性,這樣我們的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dom樹","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"就變成了一顆帶有","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"css樣式","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"渲染樹","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"了🎄","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ee/ee7a5b0b755ce283b6cb3533245ab7fd.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/75/75afe9d9a06f5e9869ebc8120c0a53ec.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.5 對渲染樹的每個元素進行位置的計算","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏根據瀏覽的排版規則來對我們設置的屬性進行位置的計算,這裏我們只實現了flex這個排版的算法,因爲它比較容易實現,能力又不是太差,這裏只是爲了感受排版的過程🍃。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瀏覽器排版規則包括🌻:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一代就是 正常流 —— 包含了 position, display,flow;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二代就是 flex —— 這個就比較接近人的自然思維;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三代就是 grid —— 是一種更強大的排版模式;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第四代可能是 Houdini —— 是一組底層 API,它們公開了 CSS 引擎的各個部分,從而使開發人員能夠通過加入瀏覽器渲染引擎的樣式和佈局過程來擴展 CSS。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"同樣是在","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"parser.js","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"解析語法樹的時候調用","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"layout","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"函數對我們的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dom","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"元素進行位置計算","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"parser.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9d/9da3d6ada2cfa3351aa8b1176ddc8b6a.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"解析","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dom","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"元素上的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"computedStyle","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"屬性然後根據我們的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"flex排版規則","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"計算出","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dom","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"元素的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"位置","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"大小","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"獲得一顆帶有位置的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dom樹","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"🎄","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"layout.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/70/70f545d388393444147de3ad7f791f75.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.6 萬事俱備,只欠東風!有了一棵帶樣式、帶位置的dom樹,我們就可以進行最後一步渲染啦","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經歷千辛萬苦的dom樹解析,我們終於擁有一棵帶樣式、帶位置的dom樹,這裏我們使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"images","attrs":{}}],"attrs":{}},{"type":"text","text":"這個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"開源庫","attrs":{}}],"attrs":{}},{"type":"text","text":"來","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"模擬","attrs":{}}],"attrs":{}},{"type":"text","text":"我們的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"瀏覽器渲染","attrs":{}}],"attrs":{}},{"type":"text","text":",最終會在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"src目錄","attrs":{}}],"attrs":{}},{"type":"text","text":"生成一張我們渲染過後的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"圖片","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"使用npm或者yarn安裝","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"images開源庫","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"🍉,這個庫可以幫助我們生成圖片,在client.js中調用","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"render函數","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"進行我們的渲染過程,最終生成","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"圖片","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"🖼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"client.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6e/6eeb86b4ed7160f94acc0dc3b0546c17.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"render函數","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"中,我們遍歷元素的屬性,獲取","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"寬高","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"背景顏色","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":",調用","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"images庫","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"提供的","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"API","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"完成渲染邏輯","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"render.js","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e0/e000a4f5944d3fe767f8ba279e757a4e.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.7 最後展示我們渲染過後生成的圖片🖼","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"viewport.jpg","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/92/923f76741bd50758f78d2dc388abb09d.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"總結 🍁","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"終於終於終於","attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"歷經千辛萬苦","attrs":{}},{"type":"text","text":"🌈,我們終於從客戶端發送","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"http請求","attrs":{}}],"attrs":{}},{"type":"text","text":"到服務端","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"響應請求","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"解析響應報文","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"獲取html文本","attrs":{}}],"attrs":{}},{"type":"text","text":",通過","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"詞法語法","attrs":{}}],"attrs":{}},{"type":"text","text":"解析","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"html","attrs":{}}],"attrs":{}},{"type":"text","text":"獲取","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dom樹","attrs":{}}],"attrs":{}},{"type":"text","text":"🎄,在解析","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"html","attrs":{}}],"attrs":{}},{"type":"text","text":"過程中進行了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"css屬性計算","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"樣式匹配","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"位置計算","attrs":{}}],"attrs":{}},{"type":"text","text":"最終獲取到了一棵帶有","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"樣式","attrs":{}}],"attrs":{}},{"type":"text","text":",有","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"位置","attrs":{}}],"attrs":{}},{"type":"text","text":"的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dom樹","attrs":{}}],"attrs":{}},{"type":"text","text":"🎄,最後完成","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"渲染","attrs":{}}],"attrs":{}},{"type":"text","text":"的完整","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"過程","attrs":{}}],"attrs":{}},{"type":"text","text":",不知道小夥伴們是不是感覺收穫滿滿🍉,也對整個瀏覽器渲染流程有了一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"完整的認識","attrs":{}}],"attrs":{}},{"type":"text","text":",如果你還是有很多疑問❓,您可以到","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/sudongyuer/toy-browser","title":"","type":null},"content":[{"type":"text","text":"toy-browser","attrs":{}}]},{"type":"text","text":"下載這個項目,在本地跑一下,自己感受一下整個過程🤓,我相信效果可能會更好~","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8a/8a4f96d4245a1656e3aa62fb4e3e3bea.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"toy-browser源代碼倉庫地址:","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/sudongyuer/toy-browser","title":"","type":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"toy-browser","attrs":{}}]},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"👣","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"參考文獻 📚","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://developer.mozilla.org/zh-CN/docs/Web/Performance/How_browsers_work","title":"","type":null},"content":[{"type":"text","text":"MDN渲染頁面:瀏覽器的工作原理","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html","title":"","type":null},"content":[{"type":"text","text":"JavaScript與有限狀態機","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://developer.mozilla.org/zh-CN/docs/Web/Performance/How_browsers_work","title":"","type":null},"content":[{"type":"text","text":"渲染樹構建、佈局及繪製","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction","title":"","type":null},"content":[{"type":"text","text":"瀏覽器的渲染過程","attrs":{}}]}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"結束語 🌞","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/89/896a9a42d0b43b674a09ceb0492b07de.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼我的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"第三篇文章","attrs":{}}],"attrs":{}},{"type":"text","text":"就結束了,文章的目的其實很簡單,就是對日常工作的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"總結和輸出","attrs":{}}],"attrs":{}},{"type":"text","text":",輸出一些覺得對大家有用的東西,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"菜不菜不重要,但是熱愛","attrs":{}},{"type":"text","text":"🔥,希望大家能夠喜歡我的文章,我真的很用心在寫,也希望通過文章認識更多志同道合的朋友,如果你也喜歡","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"折騰","attrs":{}}],"attrs":{}},{"type":"text","text":",歡迎加我","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"好友","attrs":{}}],"attrs":{}},{"type":"text","text":",一起","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"沙雕","attrs":{}}],"attrs":{}},{"type":"text","text":",一起","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"進步","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"github🤖:","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/sudongyuer/","title":"","type":null},"content":[{"type":"text","text":"sudongyu","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"個人博客👨‍💻:","attrs":{}},{"type":"link","attrs":{"href":"https://sudongyuer.github.io/","title":"","type":null},"content":[{"type":"text","text":"速凍魚blog","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"vx👦:sudongyuer","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"寫在最後","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"夥伴們,如果喜歡我的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"口水話","attrs":{}}],"attrs":{}},{"type":"text","text":"給🐟🐟點一個贊👍或者關注➕都是對我最大的支持。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章