最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • webpack4流程分析3

    正文概述 掘金(dream_99517)   2021-07-10   412

    1. NormalModule

    class NormalModule extends Module {
      constructor({ type, request, rawRequest, loaders, resource, parser, generator }) {
        // 这些参数都是如何来的? 到NormalModuleFactory中查看
        this.type = type
        this.request = request // 经过loader处理过的 loaders.map()
        this.rawRequest = rawRequest
        this.loaders = loaders // loader
        this.resource = resource // code
        this.parser = parser // js解析器
        this.generator = generator // 模版生成
      }
      // 调用build方法开始构建
      build(options, compilation, resolver, fs, callback) {
        return this.doBuild(options, compilation, resolver, fs, (err) => {
          // webpack中使用acorn 我们可以使用babel来模拟
          // 关注主流程 parser 过程比较复杂先跳过不分析
          const result = this.parser.parse(
            this._ast || this._source.source(),
            {},
            cb
          );
          callback();
        });
      }
      doBuild() {
        // 处理loader 这里就是loader执行的时机 将code经过loader处理之后在给parse解析
        // runLoaders的流程也相对独立 跳过
        runLoaders({}, (err, result) => {
          return callback();
        });
      }
    }
    

    2. NormalModuleFactory

    class NormalModuleFactory {
      constructor(context, resolverFactory, options) {
        this.resolverFactory = resolverFactory;
        this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
          let resolver = this.hooks.resolver.call(null);
          resolver(result, (err, data) => {
            // 我们要清楚NormalModule中的参数就要分析resolver这个hook
            let createdModule = new NormalModule(result);
          });
        });
        // 这一步找文件和loader的绝对路径 使用的是增强的node中require找文件规则
        this.hooks.resolver.tap("NormalModuleFactory", () => (data, callback) => {
          // 参数就这里返回的 主要是返回loader和文件 module的绝对路径和一些其他的构建信息
          // 获取解析器 loader的解析器
          const loaderResolver = this.getResolver("loader");
          // 文件和模块
          const normalResolver = this.getResolver("normal", data.resolveOptions);
          // inline loader的解析
          // loader排序 在runLoaders中按照顺序执行post inline normal pre
          callback(null, {
            // 返回数据给NormalModule
            context,
            request: loaders.map(),
            dependencies: data.dependencies,
            userRequest,
            rawRequest: request,
            loaders,
            resoutce,
            parser: this.getParser("javascript/auto"),
            generator: this.getGenerator("javascript/auto"),
          });
        });
      }
      // create方法创建模块
      create() {
        // 执行factory 这部分的逻辑之前分析过了
        const factory = this.hooks.factory.call(null);
        factory(result, (err, module) => {
          callback(null, module);
        });
      }
      // resolverFactory是compiler中
      getResolver(type, resolveOptions) {
        return this.resolverFactory.get(type, resolveOptions || {});
      }
      getParser() {
        return this.createParser(type, parserOptions);
      }
      getGenerator(type, generatorOptions) {
        return this.createGenerator(type, generatorOptions);
      }
      
      createParser(type, parserOptions) {
        // 针对不同的模块创建不同的解析器 这些钩子是哪里的
        const parser = this.hooks.createParser.for(type).call(parserOptions);
        this.hooks.parser.for(type).call(parser, parserOptions);
        return parser;
      }
      
      createGenerator(type, generatorOptions) {
        const generator = this.hooks.createGenerator
          .for(type)
          .call(generatorOptions);
        this.hooks.generator.for(type).call(generator, generatorOptions);
        return generator;
      }
    }
    

    3.ResolverFactory

    // 在compiler中
    this.resolverFactory = new ResolverFactory();
    
    class ResolverFactory extends Tapable {
       // get方法获取不同的loader处理
      get(type, resolveOptions) {
        const newResolver = this._create(type, resolveOptions);
        return newResolver;
      }
      _create(type, resolveOptions) {
        // type对应 webpackOptionsApply 中 tap 的类型 normal loader
        resolveOptions = this.hooks.resolveOptions.for(type).call(resolveOptions);
        // 创建解析器 enhanced-resolve 增强的路径解析包 
        // todo 不分析这里
        const resolver = Factory.createResolver(resolveOptions);
        return resolver;
      }
    }
    

    4.WebpackOptionsApply

    // 在执行webpack的时候会执行这个文件 之前我们 EntryOptionPlugin()就是这里挂载的 还有很多其他的
    
    // js的解析器和模板生成的api都是JavascriptModulesPlugin中挂载的
    new JavascriptModulesPlugin().apply(compiler); // js模块
    // new JsonModulesPlugin().apply(compiler); // json模块
    
    // moduleIds
    // chunkIds
    
    // 为Factory.createResolver提供默认的参数配置  ResolverFactory中触发钩子
    // compiler.resolverFactory.hooks.resolveOptions.for("normal").tap();
    // compiler.resolverFactory.hooks.resolveOptions.for("loader").tap();
    
    

    5. JavascriptModulesPlugin

    class JavascriptModulesPlugin {
      apply(compiler) {
        compiler.hooks.compilation.tap(
          "JavascriptModulesPlugin",
          (compilation, { normalModuleFactory }) => {
            normalModuleFactory.hooks.createParser
              .for("javascript/auto")
              .tap("JavascriptModulesPlugin", (options) => {
                // webpack中的Parser使用的acorn 这部分逻辑比较复杂 先跳过
                return new Parser(options, "auto");
              });
    
            normalModuleFactory.hooks.createGenerator
              .for("javascript/auto")
              .tap("JavascriptModulesPlugin", () => {
                return new JavascriptGenerator();
              });
          }
        );
      }
    }
    

    6.Compilation

    class Compilation extends Tapable {
      _addModuleChain(context, dependency, onModule, callback) {
        moduleFactory.create({}, (err, module) => {
          // 如果有依赖项就要处理模块的依赖项
          const afterBuild = () => {
            // 处理模块的依赖 那么我们如何找到模块的依赖
            // 得到ast语法树遍历找到依赖收集
            this.processModuleDependencies(module, (err) => callback(null, module));
          };
          // 构建模块 调用module的build方法 就是 module.build(
          this.buildModule(module, false, null, null, (err) => {
            afterBuild();
          });
        });
      }
      // webpack非常恶心的地方就是各种回调 搞死人
      buildModule(module, optional, origin, dependencies, thisCallback) {
        module.build({}, (err) => {
          return thisCallback();
        });
      }
      // 模块build之后会处理依赖 这些依赖是哪里来的? 生成ast之后分析 require和import得到
      processModuleDependencies(module, callback) {
        module.dependencies.forEach((item, callback) => {
          // 拿到对应的工厂
          const factory = item.factory;
          // 工厂创建模块 然后build 递归处理
          factory.crete({}, (err, dependentModule) => {
            this.buildModule();
          });
        });
      }
    }
    

    7.总结

    在我们执行module.build的时候
        => runLoaders 会先处理loader
        => 然后会使用解析器生成ast做分析
        
    我们本次主要是分析 module 中的一些参数
    1. loader和文件的怎么找到的 我们使用了enhanced-resolve路径解析包找到资源的绝对路径
    2. js parse哪来的? JavascriptModulesPlugin中会tap对应的钩子 而执行webpack函数的时候会apply()
    3. 在 ModuleFactory 的 resolver 中我们会返回创建 module 的参数(最要是找路径和构建需要的一些参数)
    4. 接下来我们就要分析parse的过程 生成ast 找到依赖项 递归处理
    
    // 简述模块的打包流程
    1. 找到模块和loader的绝对路径  normalFacroty 中的 resolver 流程
    2. 读取内容经过loader的处理 得到 js 模块 Module中的 runLoaders过程
    3. 将js通过parse得到ast语法树
    4. 分析ast中的依赖项(require, import) 分析模块的依赖 
    5. 递归编译
    

    起源地下载网 » webpack4流程分析3

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元