学堂 学堂 学堂公众号手机端

webpack把react编译成weapp

lewis 1年前 (2024-04-26) 阅读数 23 #技术
引言

webpack@5.69.0
之前使用taro@3.5.5创建一个taro-react项目之后build了一个weapp项目

最后一步就是启用webpackreact代码编译成weapp


webpack入口

package.json中可以找到入口文件
这个文件的作用就是引入webpack核心函数(lib/webpack.js)以及工具文件,抛出整理好之后的webpack核心函数

"main": "lib/index.js",

最终输出:

// lib/index.js
// mergeExports是处理fn的,最后输出的就是结果处理的fn
module.exports = mergeExports(fn, {
 // 很多webpack内置插件
 get webpack() {
     return require("./webpack"); // 核心文件
 },
 ......
})

mergeExports

// lib/index.js
// 第一个参数是对象(函数继承于Fuction,Fuction继承于Object)
const mergeExports = (obj, exports) => {
 // 克隆exports对象,传入这个对象里有很多的文件的引入
 const descriptors = Object.getOwnPropertyDescriptors(exports);
 // 遍历这个对象
 for (const name of Object.keys(descriptors)) {
     const descriptor = descriptors[name];
     if (descriptor.get) {
         const fn = descriptor.get;
         // 遍历出的属性一个个添加getter到传入的webpack函数
         Object.defineProperty(obj, name, {
             configurable: false,
             enumerable: true,
             /**
               * memoize就是执行了传入的fn返回一个返回执行之后的结果的一个函数
               * memoize(fn)等于
               * function(){
               *   return fn();
               * } 
               */
             get: memoize(fn)
         });
     } else if (typeof descriptor.value === "object") {
         Object.defineProperty(obj, name, {
             configurable: false,
             enumerable: true,
             writable: false,
             value: mergeExports({}, descriptor.value) // 递归
         });
     } else {
         throw new Error(
             "Exposed values must be either a getter or an nested object"
         );
     }
 }
 // 返回了一个冻结之后的对象,这个对象是个函数,函数里面有很多添加进去的属性方法
 return /** @type {A & B} */ (Object.freeze(obj)); 
};

打印descriptor可以得到如下

{
  get: [Function: get webpack],
  set: undefined,
  enumerable: true,
  configurable: true
}
...
{
  value: {
 ModuleDependency: [Getter],
 ConstDependency: [Getter],
 NullDependency: [Getter]
  },
  writable: true,
  enumerable: true,
  configurable: true
}
...

打印最终返回的

[Function: f] {
  webpack: [Getter],
  validate: [Getter],
  validateSchema: [Getter],
  version: [Getter],
  cli: [Getter],
  AutomaticPrefetchPlugin: [Getter],
  AsyncDependenciesBlock: [Getter],
  BannerPlugin: [Getter],
  Cache: [Getter],
  Chunk: [Getter],
  ChunkGraph: [Getter],
  CleanPlugin: [Getter],
  Compilation: [Getter],
  Compiler: [Getter],
  ConcatenationScope: [Getter],
  ContextExclusionPlugin: [Getter],
  ContextReplacementPlugin: [Getter],
  DefinePlugin: [Getter],
  DelegatedPlugin: [Getter],
  Dependency: [Getter],
  DllPlugin: [Getter],
  DllReferencePlugin: [Getter],
  DynamicEntryPlugin: [Getter],
  EntryOptionPlugin: [Getter],
  EntryPlugin: [Getter],
  EnvironmentPlugin: [Getter],
  EvalDevToolModulePlugin: [Getter],
  EvalSourceMapDevToolPlugin: [Getter],
  ExternalModule: [Getter],
  ExternalsPlugin: [Getter],
  Generator: [Getter],
  HotUpdateChunk: [Getter],
  HotModuleReplacementPlugin: [Getter],
  IgnorePlugin: [Getter],
  JavascriptModulesPlugin: [Getter],
  LibManifestPlugin: [Getter],
  LibraryTemplatePlugin: [Getter],
  LoaderOptionsPlugin: [Getter],
  LoaderTargetPlugin: [Getter],
  Module: [Getter],
  ModuleFilenameHelpers: [Getter],
  ModuleGraph: [Getter],
  ModuleGraphConnection: [Getter],
  NoEmitOnErrorsPlugin: [Getter],
  NormalModule: [Getter],
  NormalModuleReplacementPlugin: [Getter],
  MultiCompiler: [Getter],
  Parser: [Getter],
  PrefetchPlugin: [Getter],
  ProgressPlugin: [Getter],
  ProvidePlugin: [Getter],
  RuntimeGlobals: [Getter],
  RuntimeModule: [Getter],
  SingleEntryPlugin: [Getter],
  SourceMapDevToolPlugin: [Getter],
  Stats: [Getter],
  Template: [Getter],
  UsageState: [Getter],
  WatchIgnorePlugin: [Getter],
  WebpackError: [Getter],
  WebpackOptionsApply: [Getter],
  WebpackOptionsDefaulter: [Getter],
  WebpackOptionsValidationError: [Getter],
  ValidationError: [Getter],
  cache: { MemoryCachePlugin: [Getter] },
  config: {
 getNormalizedWebpackOptions: [Getter],
 applyWebpackOptionsDefaults: [Getter]
  },
  dependencies: {
 ModuleDependency: [Getter],
 ConstDependency: [Getter],
 NullDependency: [Getter]
  },
  ids: {
 ChunkModuleIdRangePlugin: [Getter],
 NaturalModuleIdsPlugin: [Getter],
 OccurrenceModuleIdsPlugin: [Getter],
 NamedModuleIdsPlugin: [Getter],
 DeterministicChunkIdsPlugin: [Getter],
 DeterministicModuleIdsPlugin: [Getter],
 NamedChunkIdsPlugin: [Getter],
 OccurrenceChunkIdsPlugin: [Getter],
 HashedModuleIdsPlugin: [Getter]
  },
  javascript: {
 EnableChunkLoadingPlugin: [Getter],
 JavascriptModulesPlugin: [Getter],
 JavascriptParser: [Getter]
  },
  optimize: {
 AggressiveMergingPlugin: [Getter],
 AggressiveSplittingPlugin: [Getter],
 InnerGraph: [Getter],
 LimitChunkCountPlugin: [Getter],
 MinChunkSizePlugin: [Getter],
 ModuleConcatenationPlugin: [Getter],
 RealContentHashPlugin: [Getter],
 RuntimeChunkPlugin: [Getter],
 SideEffectsFlagPlugin: [Getter],
 SplitChunksPlugin: [Getter]
  },
  runtime: {
 GetChunkFilenameRuntimeModule: [Getter],
 LoadScriptRuntimeModule: [Getter]
  },
  prefetch: { ChunkPrefetchPreloadPlugin: [Getter] },
  web: {
 FetchCompileAsyncWasmPlugin: [Getter],
 FetchCompileWasmPlugin: [Getter],
 JsonpChunkLoadingRuntimeModule: [Getter],
 JsonpTemplatePlugin: [Getter]
  },
  webworker: { WebWorkerTemplatePlugin: [Getter] },
  node: {
 NodeEnvironmentPlugin: [Getter],
 NodeSourcePlugin: [Getter],
 NodeTargetPlugin: [Getter],
 NodeTemplatePlugin: [Getter],
 ReadFileCompileWasmPlugin: [Getter]
  },
  electron: { ElectronTargetPlugin: [Getter] },
  wasm: { AsyncWebAssemblyModulesPlugin: [Getter] },
  library: { AbstractLibraryPlugin: [Getter], EnableLibraryPlugin: [Getter] },
  container: {
 ContainerPlugin: [Getter],
 ContainerReferencePlugin: [Getter],
 ModuleFederationPlugin: [Getter],
 scope: [Getter]
  },
  sharing: {
 ConsumeSharedPlugin: [Getter],
 ProvideSharedPlugin: [Getter],
 SharePlugin: [Getter],
 scope: [Getter]
  },
  debug: { ProfilingPlugin: [Getter] },
  util: {
 createHash: [Getter],
 comparators: [Getter],
 runtime: [Getter],
 serialization: [Getter],
 cleverMerge: [Getter],
 LazySet: [Getter]
  },
  sources: [Getter],
  experiments: {
 schemes: { HttpUriPlugin: [Getter] },
 ids: { SyncModuleIdsPlugin: [Getter] }
  }
}

fn:

// lib/index.js
// fn懒加载出来的webpack核心函数
const fn = lazyFunction(() => require("./webpack"));

lazyFunction:这里我们可以知道webpack核心函数(lib/webpack)是怎么接受参数的

// lib/index.js
const lazyFunction = factory => {
 // 和mergeExports一样返回一个返回执行了factory后的函数
 const fac = memoize(factory); // fac函数一个函数
 const f = (
     // 这里args就是我们传入的webpack配置参数
     (...args) => {
        /** 
          * fac() 等于 factory()
          * fac()(...args) 等于 factory()(...args)
          * factory 等于 () => require("./webpack")
          * require("./webpack")(...args)
          */
         return fac()(...args); 
     }
 );
 return (f);// 最后返回f这个函数,在mergeExports里getter添加之后作为入口抛出,在前端工程化项目中webpack启动器里传入webpack配置参数执行
};
webpack.js

lib/webpack.js
抛出编译器(有callback则执行编译器编译)

webpack 方法 主方法,抛出编译器compiler(有callback则执行编译器编译)
options是一个webpack配置对象或者配置对象数组,回调函数callback,有callback就可以在这里直接编译,没有则需要外部调用compiler.run()
在《build一个weapp》的packages/taro-webpack5-runner/src/index.mini.ts中 const compiler = webpack(webpackConfig) 没有传callback只传了webpack配置,正好在webpack启动器里调用了run、watch、close方法

// lib/webpack.js
const webpack = (options, callback) => {
 // create返回一个对象对象里有编译器、是否监听、监听参数
 const create = () => {
     // options作为数组然后用webpackOptionsSchemaCheck校验参数
     if (!asArray(options).every(webpackOptionsSchemaCheck)) { // false
        ...
     }
     let compiler; // MultiCompiler多个编译器或Compiler单个编译器
     let watch = false; // 是否热更新监听,只要有一个webpack配置是热更新就监听
     let watchOptions; // 热更新监听的参数可以是对象也可以是个对象数组
     if (Array.isArray(options)) {
         compiler = createMultiCompiler(options,options);
         watch = options.some(options => options.watch);
         watchOptions = options.map(options => options.watchOptions || {});
     } else {
         const webpackOptions = options;
         compiler = createCompiler(webpackOptions);
         watch = webpackOptions.watch;
         watchOptions = webpackOptions.watchOptions || {};
     }
     return { compiler, watch, watchOptions };
 };
 if (callback) {
     try {
         const { compiler, watch, watchOptions } = create();
         if (watch) {
             // 开始监听
             compiler.watch(watchOptions, callback);
         } else {
             // 开始编译
             compiler.run((err, stats) => {
                 compiler.close(err2 => {
                     callback(err || err2, stats);
                 });
             });
         }
         // 抛出编译器
         return compiler;
     } catch (err) {
         process.nextTick(() => callback(err));
         return null;
     }
 } else {
     const { compiler, watch } = create();
     if (watch) {
         util.deprecate(
             () => {},
             "A 'callback' argument needs to be provided to the 'webpack(options, callback)' function when the 'watch' option is set. There is no way to handle the 'watch' option without a callback.",
             "DEP_WEBPACK_WATCH_WITHOUT_CALLBACK"
         )();
     }
     // 抛出编译器
     return compiler;
 }
};

createCompiler创建一个编译器

// 参数就是webpack配置参数对象
const createCompiler = rawOptions => {
 // 把webpack配置参数标准化
 const options = getNormalizedWebpackOptions(rawOptions);
 // 基础参数的初始化,这里只给日志输出格式和options.context(项目地址路径)进行了赋值
 applyWebpackOptionsBaseDefaults(options);
 // 实例化Compiler编译器
 const compiler = new Compiler(options.context, options);
 // 拓展compiler,注册beforeRun钩子,添加对文件的缓存、监听、输入输出
 new NodeEnvironmentPlugin({
     infrastructureLogging: options.infrastructureLogging
 }).apply(compiler);
 // 注册plugin插件this指向编译器compiler,重新注册一遍插件
 if (Array.isArray(options.plugins)) {
     for (const plugin of options.plugins) {
         if (typeof plugin === "function") {
             plugin.call(compiler, compiler);
         } else {
             plugin.apply(compiler);
         }
     }
 }
 // 基础参数的初始化
 applyWebpackOptionsDefaults(options);
 // 执行environment钩子和afterEnvironment钩子
 compiler.hooks.environment.call();
 compiler.hooks.afterEnvironment.call();
 // 注册内置插件,lib/index.js中的第二个参数
 new WebpackOptionsApply().process(options, compiler);
 // 执行initialize钩子
 compiler.hooks.initialize.call();
 return compiler;
};
版权声明

本文仅代表作者观点,不代表博信信息网立场。

热门