Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【webpack进阶系列】手撸一个mini-webpack(一) : 分析收集依赖? #93

Open
amandakelake opened this issue Dec 30, 2019 · 0 comments

Comments

@amandakelake
Copy link
Owner

amandakelake commented Dec 30, 2019

一、入口单文件依赖分析

// bundler.js
const fs = require('fs');
const path = require('path');
// 把JS代码转为AST(抽象语法树,可以简单google一下概念,先不用太深入)
const parser = require('@babel/parser');
// 帮助我们解析AST的内容,最直接的就是通过 ImportDeclaration 点位找到文件的依赖入口
const traverse = require('@babel/traverse').default;
// babel.transformFromAst(AST, code, options) 可以帮助我们把AST转换成能ES5代码
const babel = require('@babel/core');

// 入口文件模块分析
const moduleAnalyse = filename => {
    const content = fs.readFileSync(filename, 'utf-8');
    // 得到抽象语法树
    const ast = parser.parse(content, {
        sourceType: 'module',
    });
    // 找到该文件的依赖
    const dependencies = {};
    traverse(ast, {
        ImportDeclaration({ node }) {
            const dirname = path.dirname(filename);
            // node.source.value就是获取到的模块路径名,带有相对于当前文件的路径
            // 比如import sleep from './utils/sleep.js'里面的'./utils/sleep.js'
            dependencies[node.source.value] = `./${path.join(dirname, node.source.value)}`;
        },
    });
    // babel翻译AST为浏览器可以识别的代码
    const { code } = babel.transformFromAst(ast, null, {
        presets: ['@babel/preset-env'],
    });
    return {
        filename,
        dependencies,
        code,
    };
};

const moduleInfo = moduleAnalyse('./src/index.js');
console.log('moduleInfo', moduleInfo);

通过分析入口文件src/index.js

import _ from 'lodash';
import sleep from './utils/sleep';
import content from './utils/lgc.lgc';

console.log('content', content);
console.log('time1', Date.now());
sleep().then(() => {
    console.log('time2', Date.now());
});

得到的结果如图,有文件目录、依赖文件的路径、当前文件编译后能在浏览器跑起来的代码
Snipaste_2020-01-06_20-36-57

那么,试想一发,只要把依赖文件也如法炮制处理一发,得到所有能在浏览器上跑的代码,这些代码合并起来,是不是就完成了项目的所有代码打包呢?

二、递归分析得到项目依赖图

结合上面的单文件依赖分析方法,我们稍加变通,施以递归大法就可以得到所有模块的依赖

const makeDependenciesGraph = entry => {
    // 先拿到入口文件的模块分析对象
    const entryModule = moduleAnalyse(entry);
    // 将通过递归遍历,把所有的模块依赖收集到这里
    const graphArray = [entryModule];
    for (let i = 0; i < graphArray.length; i++) {
        const item = graphArray[i];
        const { dependencies } = item;
        // 判断dependencies对象是否为空,即item是否还有依赖
        if (Object.keys(dependencies).length > 0) {
            for (let j in dependencies) {
                // 把得到的子依赖添加进graphArray,长度发生变化,for循环继续,形成了递归
                graphArray.push(moduleAnalyse(dependencies[j]));
            }
        }
    }
    // 数组转换为对象  方便后续操作
    const graph = {};
    graphArray.forEach(item => {
        graph[item.filename] = {
            dependencies: item.dependencies,
            code: item.code,
        };
    });

    return graph;
};

const graphInfo = makeDependenciesGraph('./src/index.js');
console.log('graphInfo', graphInfo);

理论上,以上代码应该能拿到所有的依赖,让我们node bundler.js跑起来看看
首先一个报错
Snipaste_2020-01-06_21-30-23

这个好理解,我们还没对node_modules里面的依赖路径做处理,那就先把import _ from 'lodash'这一行删了,再跑看看,又一个报错
Snipaste_2020-01-06_21-30-52

噢,类似的,sleep文件没有加后缀名,无法读取,这些都是webpack的默认配置里面帮忙做了的事情,加上后缀import sleep from './utils/sleep.js';, 再跑
Snipaste_2020-01-06_21-36-39

完美,拿到了所有的JS依赖的ES5代码(理论上可以直接在浏览器上跑的代码),只要我们再按照一定的规则,把这些代码合并成一份,那么,webpack雏形是不是就出来了?

当然,webpack做了很多很多的事情
比如上面两个报错,依赖路径分析、文件后缀解析
我们现在只是分析了JS文件,还有CSS、图片、字体这些静态资源的分析,如果上了TS、框架,还需要各种loader帮助处理成JS语法【webpack进阶系列】写个loader ,然后在打包过程中还需要各种各样的plugin帮忙做一下代码压缩、性能优化等等的事情【webpack进阶系列】写个plugin

理解了webpack这个寻找依赖图的核心原理,剩下的事情只能说:师傅带进门,修行在个人

下一篇:【webpack进阶系列(二)】手撸一个mini-webpack(2) : 打包依赖代码

@amandakelake amandakelake changed the title 【webpack进阶系列】写一段 Bundler 代码 【webpack进阶系列】简易Bundler Jan 6, 2020
@amandakelake amandakelake changed the title 【webpack进阶系列】简易Bundler 【webpack进阶系列】简易Bundler -> 生成依赖图 Jan 6, 2020
@amandakelake amandakelake changed the title 【webpack进阶系列】简易Bundler -> 生成依赖图 【webpack进阶系列】webpack如何分析收集依赖? Jan 20, 2020
@amandakelake amandakelake changed the title 【webpack进阶系列】webpack如何分析收集依赖? 【webpack进阶系列】手撸一个mini-webpack(一) : 分析收集依赖? Jan 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant