一.webpack打包基本原理
1.以入口文件單個(gè)模塊為例
1.1獲取模塊內(nèi)容
使用node.js的核心模塊fs
// bundle.js
const fs = require('fs')
const getModuleInfo = file => {
? ? const body = fs.readFileSync(file, 'utf-8')
? ? console.log(body)
}
getModuleInfo('./src/index.js')
讀出來的是字符串,可以用正則表達(dá)式提取import、export的內(nèi)容以及路徑名。
1.2分析模塊內(nèi)容
安裝@babel/parser,把js文件的代碼內(nèi)容轉(zhuǎn)換成js對(duì)象的形式,抽象語法樹
1.3對(duì)模塊內(nèi)容做處理
遍歷抽象語法樹,將相對(duì)路徑轉(zhuǎn)為絕對(duì)路徑;將ES6轉(zhuǎn)化ES5
2.遞歸獲取所有模塊的信息
從入口模塊開始,對(duì)每一個(gè)模塊以及模塊依賴的模塊都調(diào)用getModuleInfo 進(jìn)行分析,最終返回包含所有模塊信息的對(duì)象
根據(jù)模塊分析數(shù)據(jù),生產(chǎn)瀏覽器運(yùn)行的代碼
手寫loader
loader本質(zhì)上是一個(gè)函數(shù),會(huì)在我們加載一些文件時(shí)執(zhí)行。
1.創(chuàng)建syncLoader.js
這個(gè)函數(shù)必須返回一個(gè)buffer或string
對(duì)于loader的編寫,一定不要使用箭頭函數(shù),那樣會(huì)改變this的指向。
// syncLoader.js
const loaderUtils = require('loader-utils')
module.exports = function (source) {
? ? const options = loaderUtils.getOptions(this)
? ? console.log(options)
? ? source += options.message
? ? // 可以傳遞更詳細(xì)的信息
? ? this.callback(null, source)
}
2.配置webpack
const path = require('path')
module.exports = {
? ? mode: 'development',
? ? entry: {
? ? ? ? main: './src/index.js'
? ? },
? ? output: {
? ? ? ? path: path.resolve(__dirname, 'dist'),
? ? ? ? filename: '[name].js'
? ? },
? ? resolveLoader: {
? ? ? ? // loader路徑查找順序從左往右
? ? ? ? modules: ['node_modules', './']
? ? },
? ? module: {
? ? ? ? rules: [
? ? ? ? ? ? {
? ? ? ? ? ? ? ? test: /\.js$/,
? ? ? ? ? ? ? ? use: {
? ? ? ? ? ? ? ? ? loader: 'syncLoader',
? ? ? ? ? ? ? ? ? options: {
? ? ? ? ? ? ? ? ? ? message: '1111'
? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ]
? ? }
}
異步loader
const loaderUtils = require('loader-utils')
module.exports = function (source) {
? ? const options = loaderUtils.getOptions(this)
? ? const asyncfunc = this.async()
? ? setTimeout(() => {
? ? ? ? source += '走上人生顛覆'
? ? ? ? asyncfunc(null, res)
? ? }, 200)
}