深入學習webpack 4.x核心用法及其源碼(五)

自己編寫一個 Loader

目錄結構:

在這裏插入圖片描述

我在 loaders 目錄下寫了一個 replaceLoader.js 的文件,代碼如下:

module.exports = function(source) {
    return source.replace('world', '你好')
}

其實 Loader 就是一個函數,但是不可以使用 箭頭函數 ,爲什麼?因爲箭頭函數會改變我們的指向

src 目錄中我就自由一個 index.js ,代碼如下:

console.log('hello world');

webpack.config.js 中使用這個非常簡單的 Loader ,代碼如下:

const path = require('path');
module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js',
    },
    module: {
        rules: [
            {
                test: /\.js/,
                use: [path.join(__dirname,'./loaders/replaceLoader.js')]
            }
        ]
    },
    output: {
        path: path.join(__dirname,'dist'),
        filename: '[name].js',
    }
}

打包過後就會將你 world 替換成 你好

如果你想要傳遞參數,可以這樣:

module.exports = {
    module: {
        rules: [
            {
                test: /\.js/,
                use: [
                    {
                        loader: path.join(__dirname,'./loaders/replaceLoader.js'),
                        options: {
                            name: 'zhangSan'
                        }
                    }
                ]
            }
        ]
    },
}

通過 this.query 來獲取 options 中的數據,此外你還可以通過 loader-utils 中的 getOptions來獲取數據

想了解更多可以參看:https://www.webpackjs.com/api/loaders/

自定義一個插件

目錄結構:

在這裏插入圖片描述

使用過 plugin 應該都瞭解,它是一個 構造函數,代碼如下:

coptyRight-webpack-plugin.js

module.exports = class CopyrightWebpackPlugin {
    apply(compiler){
        compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin',(compilation, callback) => {
            compilation.assets['copyright.txt'] = {
                source: function() {
                    return 'hello world'
                },
                size: function() {
                    return 11;
                }
            };
            callback();
        })
    }
}

這個有什麼作用呢?

就是在打包生成資源到 output 目錄之前,多生成一個名爲 copyright.txt 的文件

其中 compiler 中保存的是所有關於插件的信息,hooks 表示 生命週期函數(鉤子) , emit 就表示的是 生成資源到 output 目錄之前tapAsync 表示 異步compilation 表示 當前打包的所有信息assets 表示 當前 output 中的所有文件信息

想了解更多:https://www.webpackjs.com/api/plugins/

模擬 webpack 打包方式

提前安裝:@babel/core@babel/traverse@babel/parser'@babel/preset-env'

目錄結構:

在這裏插入圖片描述

bunder.js

const fs = require('fs');
const path = require('path');
const paser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const babel = require('@babel/core');

const  moduleAnalyser = (filename) => {
    const content = fs.readFileSync(filename, 'utf-8');
    const ast = (paser.parse(content,{
        sourceType: 'module'
    }));
    const dependencies = {}
    traverse(ast, {
        ImportDeclaration({ node }) {
            const dirname = path.dirname(filename);
            const newFile = path.join(dirname, node.source.value);
            dependencies[node.source.value] = newFile;
        }
    })
    const { code } = babel.transformFromAst(ast, null, {
        presets: ['@babel/preset-env']
    })
    return {
        filename,
        dependencies,
        code,
    }
}

const makeDependenciesGraph = (entry) => {
    const entryModule = moduleAnalyser(entry);
    const graphArray = [entryModule]

    for(let i = 0; i < graphArray.length; i++) {
        const item = graphArray[i];
        const {dependencies} = item;
        if(dependencies) {
            for(let j in dependencies) {
                graphArray.push(moduleAnalyser(dependencies[j]))
            }
        }
    }
    const graph = {};
    graphArray.forEach(item => {
        graph[item.filename] = {
            dependencies: item.dependencies,
            code: item.code,
        }
    })
    return graph;
}


const generateCode = (entry) => {
    const graph = JSON.stringify(makeDependenciesGraph(entry));
    return `
        (function(graph) {
            function require(module) {
                function localRequire(relativePath) {
                    return require(graph[module].dependencies[relativePath])
                }
                var exports = {};
                (function(require,exports , code) {
                    eval(code)
                })(localRequire,exports ,graph[module].code);
                return exports;
            };
            require('${entry}')
        })(${graph})
    `
}


const code = generateCode('./src/index.js');
console.log(code);

src 下的文件都是隨便寫的。

好了,雖然我以前學過點,但還是不得其解,現在回頭仔細梳理一下也是好的,這個系列也是邊學邊寫的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章