国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

webpack究竟做了什么(一)

neuSnail / 1144人閱讀

摘要:今時(shí)今日,做前端不用個(gè)好像都被時(shí)代拋棄了一樣,每天開(kāi)發(fā)的時(shí)候,該上線了,反正執(zhí)行個(gè)命令刷刷地就打包好了,你根本無(wú)需知道執(zhí)行命令之后整個(gè)過(guò)程究竟干了什么。終于有一天,我忍不住要搞清楚究竟做了什么。

今時(shí)今日,做前端不用個(gè)webpack好像都被時(shí)代拋棄了一樣,每天開(kāi)發(fā)的時(shí)候npm run dev,該上線了npm run build,反正執(zhí)行個(gè)命令刷刷地就打包好了,你根本無(wú)需知道執(zhí)行命令之后整個(gè)過(guò)程究竟干了什么。webpack就像個(gè)黑盒,你得小心翼翼遵循它的配置行事,配好了就萬(wàn)幸。這使得我很長(zhǎng)一段時(shí)間以來(lái),都對(duì)webpack畢恭畢敬,能跑起來(lái)的代碼就是最好的代碼,千萬(wàn)別亂動(dòng)配置。
終于有一天,我忍不住要搞清楚webpack究竟做了什么。

我們?yōu)槭裁葱枰獁ebpack

去搞清楚webpack做了什么之前,我覺(jué)得首先要思考一下我們?yōu)槭裁葱枰獁ebpack,它究竟解決了什么痛點(diǎn)。想想我們?nèi)粘0岽u的場(chǎng)景:
1.開(kāi)發(fā)的時(shí)候需要一個(gè)開(kāi)發(fā)環(huán)境,要是我們修改一下代碼保存之后瀏覽器就自動(dòng)展現(xiàn)最新的代碼那就好了(熱更新服務(wù))
2.本地寫(xiě)代碼的時(shí)候,要是調(diào)后端的接口不跨域就好了(代理服務(wù))
3.為了跟上時(shí)代,要是能用上什么ES678N等等新東西就好了(翻譯服務(wù))
4.項(xiàng)目要上線了,要是能一鍵壓縮代碼啊圖片什么的就好了(壓縮打包服務(wù))
5.我們平時(shí)的靜態(tài)資源都是放到CDN上的,要是能自動(dòng)幫我把這些搞好的靜態(tài)資源懟到CDN去就好了(自動(dòng)上傳服務(wù))
巴拉巴拉等等服務(wù),那么多你需要的服務(wù),如果你打一個(gè)響指,這些服務(wù)都有條不紊地執(zhí)行好,豈不是美滋滋!所以我們需要webpack幫我們?nèi)フ夏敲炊喾?wù),而node的出現(xiàn),賦予了我們?nèi)ゲ僮飨到y(tǒng)的能力,這才有了我們今天的幸福(kubi)生活(manong)。
所以我覺(jué)得要根據(jù)自己的需求來(lái)使用webpack,知道自己需要什么樣的服務(wù),webpack能不能提供這樣的服務(wù),如果可以,那么這個(gè)服務(wù)應(yīng)該在構(gòu)建中的哪個(gè)環(huán)節(jié)被處理。

如果與輸入相關(guān)的需求,找entry(比如多頁(yè)面就有多個(gè)入口)

如果與輸出相關(guān)的需求,找output(比如你需要定義輸出文件的路徑、名字等等)

如果與模塊尋址相關(guān)的需求,找resolve(比如定義別名alias)

如果與轉(zhuǎn)譯相關(guān)的需求,找loader(比如處理sass處理es678N)

如果與構(gòu)建流程相關(guān)的需求,找plugin(比如我需要在打包完成后,將打包好的文件復(fù)制到某個(gè)目錄,然后提交到git上)

抽絲剝繭之后,去理解這些的流程,你就能從webpack那一坨坨的配置中,定位到你需求被webpack處理的位置,最后加上相應(yīng)的配置即可。

webpack打包出來(lái)的什么

webpack搞了很多東西,但最終產(chǎn)出的無(wú)非就是經(jīng)過(guò)重重服務(wù)處理過(guò)的代碼,那么這些代碼是怎樣的呢?
首先我們先來(lái)看看入口文件index.js:

console.log("index")
const one = require("./module/one.js")
const two = require("./module/two.js")
one()
two()

嗯,很簡(jiǎn)單,沒(méi)什么特別,引入了兩個(gè)模塊,最后執(zhí)行了它們一下。其中one.js和two.js的代碼也很簡(jiǎn)單,就是導(dǎo)出了個(gè)函數(shù):

// one.js
module.exports = function () {
  console.log("one")
}
// two.js
module.exports = function () {
  console.log("two")
}

好了,就是這么簡(jiǎn)單的代碼,放到webpack打包出來(lái)的是什么呢?

/******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};
/******/
/******/     // The require function
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // Check if module is in cache
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }
/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };
/******/
/******/         // Execute the module function
/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/         // Flag the module as loaded
/******/         module.l = true;
/******/
/******/         // Return the exports of the module
/******/         return module.exports;
/******/     }
/******/
/******/
/******/     // expose the modules object (__webpack_modules__)
/******/     __webpack_require__.m = modules;
/******/
/******/     // expose the module cache
/******/     __webpack_require__.c = installedModules;
/******/
/******/     // define getter function for harmony exports
/******/     __webpack_require__.d = function(exports, name, getter) {
/******/         if(!__webpack_require__.o(exports, name)) {
/******/             Object.defineProperty(exports, name, {
/******/                 configurable: false,
/******/                 enumerable: true,
/******/                 get: getter
/******/             });
/******/         }
/******/     };
/******/
/******/     // getDefaultExport function for compatibility with non-harmony modules
/******/     __webpack_require__.n = function(module) {
/******/         var getter = module && module.__esModule ?
/******/             function getDefault() { return module["default"]; } :
/******/             function getModuleExports() { return module; };
/******/         __webpack_require__.d(getter, "a", getter);
/******/         return getter;
/******/     };
/******/
/******/     // Object.prototype.hasOwnProperty.call
/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/     // __webpack_public_path__
/******/     __webpack_require__.p = "";
/******/
/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

console.log("index")
const one = __webpack_require__(1)
const two = __webpack_require__(2)
one()
two()


/***/ }),
/* 1 */
/***/ (function(module, exports) {

module.exports = function () {
  console.log("one")
}

/***/ }),
/* 2 */
/***/ (function(module, exports) {

module.exports = function () {
  console.log("two")
}

/***/ })
/******/ ]);

真是不忍直視……我寫(xiě)得這么簡(jiǎn)潔優(yōu)雅的代碼,經(jīng)過(guò)webpack的處理后如此不堪入目!但為了搞清楚這坨東西究竟做了什么,我不得不忍丑去將它簡(jiǎn)化了一下。

簡(jiǎn)化webpack打包出來(lái)的代碼

其實(shí)進(jìn)過(guò)簡(jiǎn)化后就可以看到,這些代碼意圖十分明顯,也是我們十分熟悉的套路。

(function (modules) {
    const require = function (moduleId) {
      const module = {}
      module.exports = null
      modules[moduleId].call(module, module, require)
      return module.exports
    }
    require(0)
})([
function (module, require) {
    console.log("index")
    const one = require(1)
    const two = require(2)
    one()
    two()
},
function (module, require) {
    module.exports = function () {
        console.log("one")
    }
},
function (module, require) {
    module.exports = function () {
        console.log("two")
    }
}])

這樣看可能會(huì)直觀一點(diǎn):

你會(huì)看到這不就是我們掛在嘴邊的自執(zhí)行函數(shù)嗎?然后參數(shù)是一個(gè)數(shù)組,這個(gè)數(shù)組就是我們的模塊,當(dāng)require(0)的時(shí)候就會(huì)執(zhí)行這個(gè)數(shù)組索引為0的代碼,以此類推而達(dá)到模塊化的效果。這里有個(gè)關(guān)鍵點(diǎn),就是我們明明寫(xiě)的時(shí)候是require("./module/one.js"),怎么最后出來(lái)可以變成require(1)呢?

讓我們自己來(lái)擼一個(gè)

沒(méi)有什么比自己擼一個(gè)理解得更透徹了。我們根據(jù)上面的最終打包的結(jié)果來(lái)捋一捋要做一些什么事情。
1.觀察一下,我們需要一個(gè)自執(zhí)行函數(shù),這里面需要控制的是這個(gè)自執(zhí)行函數(shù)的傳參,就是那個(gè)數(shù)組
2.這個(gè)數(shù)組是毋容置疑是根據(jù)依賴關(guān)系來(lái)形成的
3.我們要找到所有的require然后將require的路徑替換成對(duì)應(yīng)數(shù)組的索引
4.將這個(gè)處理好的文件輸出出來(lái)
ok,上代碼:

const fs = require("fs")
const path = require("path")
const esprima = require("esprima")
const estraverse = require("estraverse")
// 定義上下文 即所有的尋址都按照這個(gè)基準(zhǔn)進(jìn)行
const context = path.resolve(__dirname, "../")
// 處理路徑
const pathResolve = (data) => path.resolve(context, data)
// 定義全局?jǐn)?shù)據(jù)格式
const dataInfo = {
    // 入口文件源碼
    source: "",
    // 分析入口文件源碼得出的依賴信息
    requireInfo: null,
    // 根據(jù)依賴信息得出的各個(gè)模塊
    modules: null
}
/**
 * 讀取文件
 * @param {String} path 
 */
const readFile = (path) => {
    return new Promise((resolve, reject) => {
        fs.readFile(path, function (err, data) {
            if (err) {
                console.log(err)
                reject(err)
                return
            }
            resolve(data)
        })
    })
}
/**
 * 分析入口源碼
 */
const getRequireInfo = () => {
    // 各個(gè)依賴的id 從1開(kāi)始是因?yàn)?是入口文件
    let id = 1
    const ret = []
    // 使用esprima將入口源碼解析成ast
    const ast = esprima.parse(dataInfo.source, {range: true})
    // 使用estraverse遍歷ast
    estraverse.traverse(ast, {
        enter (node) {
            // 篩選出require節(jié)點(diǎn)
            if (node.type === "CallExpression" && node.callee.name === "require" && node.callee.type === "Identifier") {
                // require路徑,如require("./index.js"),則requirePath = "./index.js"
                const requirePath = node.arguments[0]
                // 將require路徑轉(zhuǎn)為絕對(duì)路徑
                const requirePathValue = pathResolve(requirePath.value)
                // 如require("./index.js")中"./index.js"在源碼的位置
                const requirePathRange = requirePath.range
                ret.push({requirePathValue, requirePathRange, id})
                id++
            } 
        }
    })
    return ret
}
/**
 * 模塊模板
 * @param {String} content 
 */
const moduleTemplate = (content) => `function (module, require) {
${content}
},`
/**
 * 獲取模塊信息
 */
const getModules = async () => {
    const requireInfo = dataInfo.requireInfo
    const modules = []
    for (let i = 0, len = requireInfo.length; i < len; i++) {
        const file = await readFile(requireInfo[i].requirePathValue)
        const content = moduleTemplate(file.toString())
        modules.push(content)
    }
    return modules
}
/**
 * 將入口文件如require("./module/one.js")等對(duì)應(yīng)成require(1)模塊id
 */
const replace = () => {
    const requireInfo = dataInfo.requireInfo
    // 需要倒序處理,因?yàn)楸热绲谝粋€(gè)require("./module/one.js")中的路徑是在源碼字符串42-59這個(gè)區(qū)間
    // 而第二個(gè)require("./module/two.js")中的路徑是在源碼字符串82-99這個(gè)區(qū)間,那么如果先替換位置較前的代碼
    // 則此時(shí)源碼字符串已經(jīng)少了一截(從"./module/one.js"變成1),那第二個(gè)require的位置就不對(duì)了
    const sortRequireInfo = requireInfo.sort((item1, item2) => item1.requirePathRange[0] < item2.requirePathRange[0])
    sortRequireInfo.forEach(({requirePathRange, id}) => {
        const start = requirePathRange[0]
        const end = requirePathRange[1]
        const headerS = dataInfo.source.substr(0, start)
        const endS = dataInfo.source.substr(end)
        dataInfo.source = `${headerS}${id}${endS}`
    })
}
/**
 * 輸出打包好的文件
 */
const output = async () => {
    const data = await readFile(pathResolve("./template/indexTemplate.js"))
    const indexModule = moduleTemplate(dataInfo.source)
    const allModules = [indexModule, ...dataInfo.modules].join("")
    const result = `${data.toString()}([
${allModules}
])`
    fs.writeFile(pathResolve("./build/output.js"), result, function (err) {
        if (err) {
            throw err;
        }
    })
}
const main = async () => {
    // 讀取入口文件
    const data = await readFile(pathResolve("./index.js"))
    dataInfo.source = data.toString()
    // 獲取依賴信息
    dataInfo.requireInfo = getRequireInfo()
    // 獲取模塊信息
    dataInfo.modules = await getModules()                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
    // 將入口文件如require("./module/one.js")等對(duì)應(yīng)成require(1)模塊id
    replace()
    // 輸出打包好的文件
    output()
    console.log(JSON.stringify(dataInfo))
}
main()
 

這里的關(guān)鍵是將入口源碼轉(zhuǎn)成ast從而分析出require的路徑在源碼字符串中所在的位置,我們這里用到了esprima去將源碼轉(zhuǎn)成ast,然后用estraverse去遍歷ast從而篩選出我們感興趣的節(jié)點(diǎn),這時(shí)我們就可以對(duì)轉(zhuǎn)化成ast的代碼為所欲為了,babel就是這樣的原理為我們轉(zhuǎn)化代碼的。

最后

到這里我們可以知道,除去其他雜七雜八的服務(wù),webpack本質(zhì)上就是一個(gè)將我們平時(shí)寫(xiě)的模塊化代碼轉(zhuǎn)成現(xiàn)在瀏覽器可以直接執(zhí)行的代碼。當(dāng)然上面的代碼是非常簡(jiǎn)陋的,我們沒(méi)有去遞歸處理依賴,沒(méi)有去處理require的尋址(比如require("vue")是怎樣找到vue在哪里的)等等的細(xì)節(jié)處理,只為還原一個(gè)最簡(jiǎn)單易懂的結(jié)構(gòu)。上面的源碼可以在這里找到。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/96859.html

相關(guān)文章

  • 深入理解 Webpack 打包分塊(下)

    摘要:例如允許我們?cè)诖虬鼤r(shí)將腳本分塊利用瀏覽器緩存我們能夠有的放矢的加載資源。文章的內(nèi)容大體分為兩個(gè)方面,一方面在思路制定模塊分離的策略,另一方面從技術(shù)上對(duì)方案進(jìn)行落地。我之前提到測(cè)試之下是什么樣具體的場(chǎng)景并不重要。前言 隨著前端代碼需要處理的業(yè)務(wù)越來(lái)越繁重,我們不得不面臨的一個(gè)問(wèn)題是前端的代碼體積也變得越來(lái)越龐大。這造成無(wú)論是在調(diào)式還是在上線時(shí)都需要花長(zhǎng)時(shí)間等待編譯完成,并且用戶也不得不花額外的...

    pingan8787 評(píng)論0 收藏0
  • 深入NUXT,看看條命令行的背后到底發(fā)生了什么

    摘要:一介紹隨著社區(qū)的框架的發(fā)布,社區(qū)也終于誕生了屬于自己的前后端同構(gòu)框架。本文主要研究的運(yùn)行原理,分析它從接收一條指令,到完成指令背后所發(fā)生的一系列事情。最后,通過(guò)來(lái)檢查輸出的是否存在問(wèn)題,然后發(fā)出通知,表明可用。 showImg(https://segmentfault.com/img/bVIc9l?w=536&h=136); 一、介紹 Nuxt.js - Universal Vue.j...

    opengps 評(píng)論0 收藏0
  • 如何從零入門(mén)React?實(shí)戰(zhàn)做個(gè)FM應(yīng)用吧

    摘要:面試造航母,工作擰螺絲,新公司面試技術(shù)官要求會(huì)技術(shù)棧。然而公司項(xiàng)目暫時(shí)并沒(méi)有用到,不過(guò)為了提升實(shí)戰(zhàn)經(jīng)驗(yàn),還是在業(yè)余時(shí)間搗騰出一個(gè),以下是項(xiàng)目介紹。前段為了學(xué)習(xí)小程序的開(kāi)發(fā),做了個(gè)小程序名叫口袋吉他,這也是個(gè)人興趣驅(qū)使的開(kāi)發(fā)想法。 面試造航母,工作擰螺絲,新公司面試技術(shù)官要求會(huì)react技術(shù)棧。 問(wèn):有使用過(guò)React么?答:沒(méi),只使用過(guò)Vue。又問(wèn):給你一星期能上手開(kāi)發(fā)么?答:可以(一...

    codecook 評(píng)論0 收藏0
  • 當(dāng)我們調(diào)用yield,它究竟做了什么

    摘要:顯然,要理解,首先要了解迭代器,接著了解什么是生成器。生成器上述代碼中,就是一個(gè)迭代器,循環(huán)部分就是迭代過(guò)程。迭代器和生成器的執(zhí)行效率因?yàn)樯善鬟叺吷桑哉加脙?nèi)存極少,執(zhí)行效率也更高。 顯然,要理解yield,首先要了解迭代器(iterator),接著了解什么是生成器(generator)。 迭代器 通俗的講,迭代器就是可以逐個(gè)訪問(wèn)的容器,而逐個(gè)逐步訪問(wèn)的過(guò)程成為迭代。 ite...

    Jochen 評(píng)論0 收藏0
  • 輸入 url 到頁(yè)面展現(xiàn)的短短幾秒內(nèi)瀏覽器究竟做了什么

    摘要:在上述過(guò)程再細(xì)化為瀏覽器搜索自己的緩存。至此,瀏覽器已經(jīng)得到了域名對(duì)應(yīng)的地址。具體過(guò)程如下在中這一過(guò)程如下首先是字節(jié)流,經(jīng)過(guò)解碼之后是字符流,然后通過(guò)詞法分析器會(huì)被解釋成詞語(yǔ),之后經(jīng)過(guò)語(yǔ)法分析器構(gòu)建成節(jié)點(diǎn),最后這些節(jié)點(diǎn)被組建成一棵樹(shù)。 面試的時(shí)候,我們經(jīng)常會(huì)被問(wèn)從在瀏覽器地址欄中輸入 url 到頁(yè)面展現(xiàn)的短短幾秒內(nèi)瀏覽器究竟做了什么?那么瀏覽器到底做了啥? 瀏覽器的多進(jìn)程架構(gòu)一個(gè)好的程...

    dackel 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<