時(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)譯我們的代碼的<script type="module">
2.但是使用 import/export 會(huì)帶來(lái)頁(yè)面請(qǐng)求過(guò)多的問(wèn)題,一個(gè)文件會(huì)產(chǎn)生一個(gè)請(qǐng)求。import a from './a.js'import b from './b.js'console.log(a.getB())console.log(b.getA())
a.jsimport b from './b.js'const a = { value:'a', getB:()=> b.value + ' from a.js'}export default a
b.jsimport 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)系。// 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ī)范命名的,只需要遵守即可。// 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>關(guān)鍵詞:打包,文件
客戶(hù)&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
客戶(hù)&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
微信公眾號(hào)
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。