摘要:當發(fā)現(xiàn)該路徑為文件夾時則,則依次查找如下文件字段擴展名文件解析文件可以定位之后,則是解析定位下來的文件了,本文用的是,文檔如規(guī)范文檔解析文件返回一個語法樹。對語法樹進行遍歷對遇到為且其為為的節(jié)點將該節(jié)點的以及下標包裝成對象儲存起來。
前言
自動化打包工具webpack,相信很多人和我一樣嘗試著研究下它,但是繁雜的功能以及高度抽象的代碼實在是很難理解,所以筆者只能通過github的webpack的第一次提交進行分析,實現(xiàn),并將實現(xiàn)的一些心得分享一下。
功能分析對于node端來講,有commonjs來規(guī)范模塊的標識,定義,引用。而瀏覽器端由于缺乏原生對象支持就需要通過自我實現(xiàn)來模擬commonjs規(guī)范。
webpack是通過一個IIFE立即調(diào)用函數(shù)表達式去實現(xiàn)這個規(guī)范的。簡要的去注釋,去除內(nèi)部運行的代碼,其格式如下:
(function(module){})([function(){},function(){}]);
簡單點說就是各個模塊代碼以數(shù)組的形式傳遞給運行函數(shù),在進行存儲。詳細分析可以參考簡要分析webpack打包后代碼
所以實現(xiàn)以上的功能需求點如下:
文件路徑分析與定位resolve
文件編譯&解析,分析出依賴文件parse
生成需要打包的文件樹depTree
將依賴文件寫入輸出文件內(nèi)writeChunk
文件分析與定位本功能和node的require類似,故有參考node require源碼
文件分析,將文件為兩種類型
以 "./","../","/" 標識符開頭的路徑文件模塊
該類文件會通過path.join 轉(zhuǎn)化為真實的路徑而定位。
自定義的文件模塊
這類相對比較麻煩,他在當前目錄下面的node_modules,查找文件,未找到則一路向上查找,最終查找到或者拋出異常。如:
[ "/Users/zhujian/Documents/workspace/webpack/simple-webpack/node_modules", "/Users/zhujian/Documents/workspace/webpack/node_modules", "/Users/zhujian/Documents/workspace/node_modules", "/Users/zhujian/Documents/node_modules", "/Users/zhujian/node_modules", "/Users/node_modules", "/node_modules" ]
文件定位
對于部分文件并沒有帶擴展名,此時有默認的擴展名依次以.js,.jsx為后綴依次補充。當然我們可以用傳入extensions,修改默認的擴展名。
{extensions:["js","jsx","jpg"]}
當發(fā)現(xiàn)該路徑為文件夾時則,則依次查找如下文件
package.json(main字段)
index+(擴展名)
文件解析
文件可以定位之后,則是解析定位下來的文件了,本文用的是exprima,文檔如parser規(guī)范文檔
esprima解析文件,返回一個語法樹。
對語法樹進行遍歷,對遇到type 為CallExpression,且其callee為name為require的節(jié)點,將該節(jié)點的value,以及下標包裝成對象儲存起來。
比如
const b = require("./b");
解析后
.... "init": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "require", "range": [ 10, 17 ] }, "arguments": [ { "type": "Literal", "value": "./b", "raw": ""./b"", "range": [ 18, 23 ] } ], "range": [ 10, 24 ] }, ....
我們要做的就是提取value "./b",以及該字符串在文件所處的位置range。
文件樹生成主要是從入口文件開始,將所有依賴的js,以及其內(nèi)容,分配的id組成一個可操作的扁平化的對象和存儲著name與id對應(yīng)的map對象。
實現(xiàn)手法上也是遞歸resolve函數(shù),獲取到各個文件的依賴,文件,id的信息,最后得到depTree對象
舉個例子:
{ modules: { "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/a.js": { filename: "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/a.js", id: 0, requires: [Array], rangeRequires: [Array], source: "const b = require("./b"); const c = require("c"); const {e, f, g} = require("./m"); }, "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/b.js": { filename: "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/b.js", id: 1, requires: [], rangeRequires: [], source: "const b = "b"; module.exports = b; " }, "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/c.js": { filename: "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/c.js", id: 2, requires: [], rangeRequires: [], source: "const c = "c"; module.exports = c; " }, "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/m.js": { filename: "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/m.js", id: 3, requires: [], rangeRequires: [], source: "http:// const core = require("./core"); const a = 1; }, nextModuleId: 4, mapNameToId: { "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/a.js": 0, "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/b.js": 1, "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/c.js": 2, "/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/m.js": 3 } }文件寫入
寫入主函數(shù),替換入口的執(zhí)行函數(shù)。這塊會用到之前的path和id關(guān)系的map對象,通過路口文件的絕對路徑,找出入口文件的mainId,并進行替換。
寫入?yún)?shù)數(shù)組。遍歷文件樹,將文件節(jié)點的source內(nèi)容替換掉
大致如下:
require("module") 替換為__webpack_require__(0)
這個地方要考慮的點是
如果用replace替換的話,會影響source帶部分關(guān)鍵字的內(nèi)容,不可取。
用索引替換的字符串的話,一旦第一個替換成功,整個字符串長度發(fā)生變化,原先的索引下標就失效了。
官方實現(xiàn)
const result = [source]; replaces.forEach(replace => { const {from, value, end} = replace; const source = result.shift(); result.unshift(source.substr(0, from), value, source.substr(end)) });代碼實現(xiàn)
本人的簡易版webpack實現(xiàn)simple-webpack
(完)
參考資料webpack早期源碼
require源碼解讀
node require源碼
parser規(guī)范文檔
簡要分析webpack打包后代碼
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107753.html
摘要:模仿實現(xiàn)簡單的打包工具是一款前端項目構(gòu)建工具,隨著現(xiàn)在前端生態(tài)的發(fā)展,已經(jīng)成為前端開發(fā)人員必備的技能之一,很多開發(fā)人員開始使用和的時候,都會使用默認的單頁應(yīng)該創(chuàng)建指令來創(chuàng)建一個工程化項目,實際上,這些工程化的項目都是基于來搭建的當我們熟悉使 模仿webpack實現(xiàn)簡單的打包工具 webpack是一款前端項目構(gòu)建工具,隨著現(xiàn)在前端生態(tài)的發(fā)展,webpack已經(jīng)成為前端開發(fā)人員必備的技能之...
摘要:模仿實現(xiàn)簡單的打包工具是一款前端項目構(gòu)建工具,隨著現(xiàn)在前端生態(tài)的發(fā)展,已經(jīng)成為前端開發(fā)人員必備的技能之一,很多開發(fā)人員開始使用和的時候,都會使用默認的單頁應(yīng)該創(chuàng)建指令來創(chuàng)建一個工程化項目,實際上,這些工程化的項目都是基于來搭建的當我們熟悉使 模仿webpack實現(xiàn)簡單的打包工具 webpack是一款前端項目構(gòu)建工具,隨著現(xiàn)在前端生態(tài)的發(fā)展,webpack已經(jīng)成為前端開發(fā)人員必備的技能之...
摘要:模仿實現(xiàn)簡單的打包工具是一款前端項目構(gòu)建工具,隨著現(xiàn)在前端生態(tài)的發(fā)展,已經(jīng)成為前端開發(fā)人員必備的技能之一,很多開發(fā)人員開始使用和的時候,都會使用默認的單頁應(yīng)該創(chuàng)建指令來創(chuàng)建一個工程化項目,實際上,這些工程化的項目都是基于來搭建的當我們熟悉使 模仿webpack實現(xiàn)簡單的打包工具 webpack是一款前端項目構(gòu)建工具,隨著現(xiàn)在前端生態(tài)的發(fā)展,webpack已經(jīng)成為前端開發(fā)人員必備的技能之...
摘要:先看下官方文檔中對模塊的描述在模塊化編程中,開發(fā)者將程序分解成離散功能塊,并稱之為模塊。每個模塊具有比完整程序更小的接觸面,使得校驗調(diào)試測試輕而易舉。 先看下webpack官方文檔中對模塊的描述: 在模塊化編程中,開發(fā)者將程序分解成離散功能塊(discrete chunks of functionality),并稱之為模塊。每個模塊具有比完整程序更小的接觸面,使得校驗、調(diào)試、測試輕而易...
閱讀 3000·2021-11-23 09:51
閱讀 2820·2021-11-11 16:55
閱讀 2935·2021-10-14 09:43
閱讀 1404·2021-09-23 11:22
閱讀 1045·2019-08-30 11:04
閱讀 1674·2019-08-29 11:10
閱讀 970·2019-08-27 10:56
閱讀 3125·2019-08-26 12:01