平時(shí)我們寫(xiě)代碼的時(shí)候都是直接使用 ES6 或者更高級(jí)的語(yǔ)法,導(dǎo)入導(dǎo)出模塊也是使用 import/export 語(yǔ)法,但是這樣也會(huì)帶來(lái)一些問(wèn)題。" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

所在位置: 首頁(yè) > 營(yíng)銷(xiāo)資訊 > 網(wǎng)站運(yùn)營(yíng) > Webpack(2) - 如何把多個(gè)模塊打包成一個(gè)文件

Webpack(2) - 如何把多個(gè)模塊打包成一個(gè)文件

時(shí)間:2023-06-10 08:45:02 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)

時(shí)間:2023-06-10 08:45:02 來(lái)源:網(wǎng)站運(yùn)營(yíng)

Webpack(2) - 如何把多個(gè)模塊打包成一個(gè)文件:Webpack(1)- Babel 是如何轉(zhuǎn)譯我們的代碼的

平時(shí)我們寫(xiě)代碼的時(shí)候都是直接使用 ES6 或者更高級(jí)的語(yǔ)法,導(dǎo)入導(dǎo)出模塊也是使用 import/export 語(yǔ)法,但是這樣也會(huì)帶來(lái)一些問(wèn)題。

1.非現(xiàn)代瀏覽器(IE 全系等等)并不支持 import/export,而現(xiàn)代瀏覽器只需使用

<script type="module">2.但是使用 import/export 會(huì)帶來(lái)頁(yè)面請(qǐng)求過(guò)多的問(wèn)題,一個(gè)文件會(huì)產(chǎn)生一個(gè)請(qǐng)求。

首先解決請(qǐng)求過(guò)多的問(wèn)題,一個(gè)項(xiàng)目一般有一個(gè)入口文件,可以通過(guò)這個(gè)文件找到所有的依賴(lài)文件,并進(jìn)行打包整合,先來(lái)嘗試收集文件間的依賴(lài)關(guān)系:

假如 project_1 目錄下有如下三個(gè)文件

index.js

import a from './a.js'import b from './b.js'console.log(a.getB())console.log(b.getA())a.js

import b from './b.js'const a = { value:'a', getB:()=> b.value + ' from a.js'}export default ab.js

import a from './a.js'const b = { value: 'b', getA: ()=> a.value + ' from b.js'}可以看到 index 依賴(lài) a 和 b,a 和 b 又相互依賴(lài)。

// bundler.jsconst {readFileSync} = require('fs')const {resolve} = require('path')// 使用 HashMap 存儲(chǔ)依賴(lài)關(guān)系const depRelation = {}function collectCodeAndDeps(filepath){ // 使用文件的項(xiàng)目路徑做 key,如 index.js。getProjectPath 內(nèi)部實(shí)現(xiàn)略 const key = getProjectPath(filepath) // 防止循環(huán)引用時(shí)會(huì)造成的無(wú)限調(diào)用 if(Object.keys(depRelation).includes(key)){ return } // 獲取文件內(nèi)容,并存放 const code = readFileSync(filepath).toString() depRelation[key] = {deps:[], code} // 將代碼轉(zhuǎn)換 AST,方便后續(xù)操作 const ast = parse(code,{sourceType:'module'}) traverse(ast,{ enter:path => { if(path.node.type === 'ImportDeclaration'){ // 獲取依賴(lài)文件的絕對(duì)路徑 const depAbsolutePath = resolve(dirname(filepath), path.node.source.value) // 獲取在項(xiàng)目中的路徑并保存 const depProjectPath = getProjectPath(depAbsolutePath) depRelation[key].deps.push(depProjectPath) // 繼續(xù)查找依賴(lài) collectCodeAndDeps(depAbsolutePath) } } }) }depRelation 是一個(gè)對(duì)象,保存了文件的依賴(lài)關(guān)系。

現(xiàn)在 code 中的代碼還是未轉(zhuǎn)譯過(guò)的格式,很簡(jiǎn)單,只需要 babel 幫我們轉(zhuǎn)譯一下:

// bundler.jsimport babel from '@babel/core'... // 獲取文件內(nèi)容,并存放const code = readFileSync(filepath).toString()const {code: es5Code} = babel.transform(code,{ presets: ['@babel/preset-env']})depRelation[key] = {deps:[], code: es5Code}...現(xiàn)在依賴(lài)也收集好了,代碼也轉(zhuǎn)譯好了,只剩下如何將代碼都寫(xiě)入到一個(gè)文件并執(zhí)行了,這時(shí)我們先來(lái)看看轉(zhuǎn)譯后的代碼,看看如何執(zhí)行:

// a.js"use strict";Object.defineProperty(exports, "__esModule", {value: true}); // 設(shè)置 esModuleexports["default"] = void 0; var _b = _interopRequireDefault(require("./b.js")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj };}var a = { value: 'a', getB: function getB() { return _b["default"].value + ' from a.js'; }};var _default = a; exports["default"] = _default;其中 require 和 exports 都不知道從哪來(lái)的,這是我們?cè)趫?zhí)行這段代碼時(shí)需要實(shí)現(xiàn)的,至于為什么叫這兩個(gè)名字,是根據(jù) CommonJS 規(guī)范命名的,只需要遵守即可。

為了讓這段代碼執(zhí)行,需要將這段代碼用函數(shù)包住,然后調(diào)用執(zhí)行,模板字符串可以幫助我們實(shí)現(xiàn),這樣寫(xiě)入文件后就是可執(zhí)行的代碼:

// bundler.jsimport { writeFileSync } from 'fs'...function generateCode(){ let code = '' code += 'var depRelation = [' + depRelation.map(item => { const { key, deps, code } = item return `{ key: ${JSON.stringify(key)}, deps: ${JSON.stringify(deps)}, code: function(require, module, exports){ ${code} } }` }).join(',') + '];/n' return code}// 將生成的代碼寫(xiě)入文件writeFileSync('dist.js', generateCode())這里還有一個(gè)細(xì)節(jié),depRelation 變量的類(lèi)型由對(duì)象改為了數(shù)組,因?yàn)閷?duì)象是無(wú)序的,無(wú)法確定入口文件時(shí)哪個(gè),數(shù)組則將入口文件放在第一個(gè):

// bundler.js...const depRelation = []function collectCodeAndDeps(filepath){ ... // depRelation[key] = {deps:[], code: es5Code} const item = { key, deps: [], code: es5Code } depRelation.push(item) ... travers(ast, { enter:path=>{ ... // depRelation[key].deps.push(depProjectPath) item.deps.push(depProjectPath) ... } }) ...}接下來(lái)調(diào)用執(zhí)行的函數(shù):

// 用 HashMap 將引入過(guò)的模塊保存var modules = {};// 執(zhí)行入口文件execute(depRelation[0].key);function execute(key) { // 判斷是否已存在 if (modules[key]) { return modules[key]; } var item = depRelation.find((i) => i.key === key); if (!item) { throw new Error(`${key} is not found`); } var pathTokey = (path) => { var dirname = key.substring(0, key.lastIndexOf("/") + 1); var projectPath = (dirname + path) .replace(//.///g, "") .replace(//////, "/"); return projectPath; }; // 實(shí)現(xiàn) require var require = (path) => { return execute(pathTokey(path)); }; // 初始化 exports modules[key] = { __esModule: true, }; var module = { export: modules[key] }; // 兼容操作 item.code(require, module, module.export); // 執(zhí)行代碼 // 返回最終模塊值 return modules[key];}執(zhí)行路線(xiàn)是,先 execute('index.js') => 發(fā)現(xiàn) require('./a.js') => execute('a.js') => 發(fā)現(xiàn) require('./b.js') => execute('b.js'),最后還是用模板字符串,一起寫(xiě)到文件中:

function generateCode(){ let code = '' code += 'var depRelation = [' + depRelation.map(item => { const { key, deps, code } = item return `{ key: ${JSON.stringify(key)}, deps: ${JSON.stringify(deps)}, code: function(require, module, exports){ ${code} } }` }).join(',') + '];/n' code += 'var modules = {};/n' code += `execute(depRelation[0].key)/n` code += ` function execute(key) { if (modules[key]) { return modules[key] } var item = depRelation.find(i => i.key === key) if (!item) { throw new Error(/`/${item} is not found/`) } var pathToKey = (path) => { var dirname = key.substring(0, key.lastIndexOf('/') + 1) var projectPath = (dirname + path).replace(////.//////g, '').replace(////////////, '/') return projectPath } var require = (path) => { return execute(pathToKey(path)) } modules[key] = { __esModule: true } var module = { exports: modules[key] } item.code(require, module, module.exports) return modules[key] } ` return code}最后使用 node 命令行執(zhí)行 dist.js,成功打?。?br>
最終達(dá)到了我們要的效果:

  1. 多個(gè)文件打包成一個(gè)文件,減少請(qǐng)求量
  2. 兼容舊版模擬器
目前 bundler.js 的缺點(diǎn):

  1. 無(wú)法打包 CommonJS 文件
  2. 無(wú)法打包 CSS 文件
  3. 無(wú)法設(shè)置入口文件名和出口文件名(目前為固定的 index.js 和 dist.js)



完。

關(guān)鍵詞:打包,文件

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉