webpack打包原理分析和實現(二)

上一篇,通過@babel/parser將index中的es6代碼解析成ast(抽象語法樹),接下來,我們可以根據Body裏面的分析結果,遍歷出所有的引入模塊,但是比較麻煩,這裏推薦babel的一個模塊@babel/traverse,幫我們處理。

分析單個模塊依賴

npm i @babel/traverse --save
爲了獲得模塊(文件)的依賴,通過ast可以看到依賴的路徑存放在Node下面的resource的value裏,我們需要提取ast中type爲ImportDeclaration的Node,traverse中提供提取它的同名函數,回調參數爲node的對象,我們把依賴的模塊路徑同意存放在dependencies 裏,key爲入口文件路徑,值爲項目裏的全路徑

moduleAnalyser(entryFile) {
        //! 分析入口模塊的內容
        const content = fs.readFileSync(entryFile, 'utf-8')
        console.log(content)

        //!分析出哪些是依賴?以及依賴的路徑
        const ast = parser.parse(content, {
            sourceType: 'module'
        })
        const dependencies = {}
        traverse(ast, {
            //提取哪個字段就用哪個函數
            ImportDeclaration({node}) {
                console.log(node.source.value)
                //路徑拼接
                const newPathName = "./" + path.join(path.dirname(entryFile), node.source.value)
                dependencies[node.source.value] = newPathName
                console.log(dependencies)
            }
        })
        //console.log(ast.program.body)
      
    }

打印結果爲

./expo.js
./src\expo.js
{ './expo.js': './src\\expo.js' }

目前爲止,通過moduleAnalyser這個方法,拿到了依賴的項目路徑,存在dependencies對象中,供後續使用。

接下來,把代碼處理成瀏覽器可運行的代碼,需要藉助@babel/core,和babel/preset-env,把ast語法樹轉換成合適的代碼

npm i @babel/core @babel/preset-env --save

webpack中添加導入和代碼

const babel = require('@babel/core')
const {code} = babel.transformFromAst(ast, null, {
            presets: ['@babel/preset-env']
        })

code爲處理後的代碼
導出所有分析出的信息

return {
            entryFile,
            dependencies,//如果沒有值,說明沒有依賴
            code
        }

moduleAnalyser 完整代碼:

 moduleAnalyser(entryFile) {
        //! 分析入口模塊的內容
        const content = fs.readFileSync(entryFile, 'utf-8')
        console.log(content)

        //!分析出哪些是依賴?以及依賴的路徑
        const ast = parser.parse(content, {
            sourceType: 'module'
        })
        const dependencies = {}
        traverse(ast, {
            //提取哪個字段就用哪個函數
            ImportDeclaration({node}) {
                console.log(node.source.value)
                // path.dirname(entryFile)
                // console.log(path.dirname(entryFile))
                //路徑拼接
                const newPathName = "./" + path.join(path.dirname(entryFile), node.source.value)
                console.log(newPathName)
                dependencies[node.source.value] = newPathName
                console.log(dependencies)
            }
        })
        console.log(ast.program.body)
        //! 處理內容,轉換ast
        const {code} = babel.transformFromAst(ast, null, {
            presets: ['@babel/preset-env']
        })
        console.log('------------------------'+code)
        return {
            entryFile,
            dependencies,//如果沒有值,說明沒有依賴
            code
        }
    }

分析所有模塊依賴

  • 根據webpack入口配置的options,獲取入口模塊,加入modules
  • 遍歷modules,判斷是否有依賴,如果有,就拿到依賴的路徑,通過moduleAnalyser,分析出依賴模塊,加入到modules
  • 分析最開始得到main.js,打包出的代碼結構其實是一個對象,因此將modules轉成對象結構
run() {//入口函數
        const entryModule= this.moduleAnalyser(this.entry)
        console.log(entryModule)

        //!處理其他模塊,做一個信息彙總
        this.modules.push(entryModule);
        for (let i = 0; i < this.modules.length; i++) {
            const item = this.modules[i]
            const {dependencies} = item
            if (dependencies) {
                for (let j in dependencies) {
                    this.modules.push(this.moduleAnalyser(dependencies[j]))//分析每個子模塊的依賴

                }
            }
        }
        console.log(this.modules)
        //! 數組處理成對象
        const obj = {}
        this.modules.forEach((item) => {
            obj[item.entryFile] = {
                dependencies: item.dependencies,
                code: item.code
            }
        })
        console.log(obj)//已完成分析入口依賴
        // this.file(obj)
    }

到這裏,我們已經將依賴的各個模塊轉成了瀏覽器需要的信息,但是還是對象,需要進一步輸出爲代碼文件,將在下一篇實現

如果你熱愛編程,或者遇到了什麼問題,歡迎加入羣663077768,一起學習交流

發佈了57 篇原創文章 · 獲贊 47 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章