当前位置: 首页 > news >正文

React 18.2.0 源码打包

一、React源码地址

GitHub:React

二、参考文章

sourcemap实战-生成react源码sourcemap
Rollup中文文档
JavaScript Source Map 详解
全网最优雅的 React 源码调试方式

三、打包操作
  1. 安装依赖
// 全局安装yarn
npm i -g yarn
// 源码项目目录下执行yarn安装依赖
yarn
  1. 修改后的build.js 文件
'use strict';const rollup = require('rollup');
const babel = require('rollup-plugin-babel');
const commonjs = require('rollup-plugin-commonjs');
const prettier = require('rollup-plugin-prettier');
const replace = require('rollup-plugin-replace');
const stripBanner = require('rollup-plugin-strip-banner');
const chalk = require('chalk');
const resolve = require('rollup-plugin-node-resolve');
const fs = require('fs');
const argv = require('minimist')(process.argv.slice(2));
const Modules = require('./modules');
const Bundles = require('./bundles');
const Stats = require('./stats');
const sizes = require('./plugins/sizes-plugin');
const useForks = require('./plugins/use-forks-plugin');
const Packaging = require('./packaging');
const {asyncRimRaf} = require('./utils');
const codeFrame = require('babel-code-frame');
const Wrappers = require('./wrappers');// 默认以实验模式进行构建。如果通过环境变量设置了发布渠道,则检查其是否为 "experimental"。
const __EXPERIMENTAL__ = true
// Errors in promises should be fatal.
let loggedErrors = new Set();
process.on('unhandledRejection', err => {if (loggedErrors.has(err)) {// No need to print it twice.process.exit(1);}throw err;
});const { UMD_DEV } = Bundles.bundleTypes;const {getFilename} = Bundles;// 处理路由参数,拆分出需要打包的文件的文件名
function parseRequestedNames(names, toCase) {let result = [];for (let i = 0; i < names.length; i++) {let splitNames = names[i].split(',');for (let j = 0; j < splitNames.length; j++) {let name = splitNames[j].trim();if (!name) {continue;}if (toCase === 'uppercase') {name = name.toUpperCase();} else if (toCase === 'lowercase') {name = name.toLowerCase();}result.push(name);}}return result;
}const requestedBundleTypes = argv.type? parseRequestedNames([argv.type], 'uppercase'): [];// 解析脚本输入的文件名
const requestedBundleNames = parseRequestedNames(argv._, 'lowercase');// 用于处理非 ES2015 代码,如 Flow 类型、类属性、对象扩展运算符等)转换为兼容旧环境的代码,同时避免引入额外运行时依赖,为 Closure Compiler 的优化做准备。
// Non-ES2015 stuff applied before closure compiler.
const babelPlugins = [// These plugins filter out non-ES2015.// 移除Flow的类型定义'@babel/plugin-transform-flow-strip-types',// 转换类属性(class properties),将类中的属性定义转换为对象字面量形式,支持使用更简洁的语法定义类属性。['@babel/plugin-proposal-class-properties', {loose: true}],// 兼容在函数末尾加逗号的写法'syntax-trailing-function-commas',// These use loose mode which avoids embedding a runtime.// TODO: Remove object spread from the source. Prefer Object.assign instead.['@babel/plugin-proposal-object-rest-spread',{loose: true, useBuiltIns: true},],['@babel/plugin-transform-template-literals', {loose: true}],// TODO: Remove for...of from the source. It requires a runtime to be embedded.'@babel/plugin-transform-for-of',// TODO: Remove array spread from the source. Prefer .apply instead.['@babel/plugin-transform-spread', {loose: true, useBuiltIns: true}],'@babel/plugin-transform-parameters',// TODO: Remove array destructuring from the source. Requires runtime.['@babel/plugin-transform-destructuring', {loose: true, useBuiltIns: true}],// Transform Object spread to shared/assignrequire('../babel/transform-object-assign'),
];// Babel 插件数组 ,其核心作用是定义一组用于将现代 JavaScript 代码(ES6+)转换为 ES5 兼容语法的插件集合,确保构建产物能在不支持新特性的旧版浏览器中正常运行。
const babelToES5Plugins = [// These plugins transform DEV mode. Closure compiler deals with these in PROD.'@babel/plugin-transform-literals','@babel/plugin-transform-arrow-functions','@babel/plugin-transform-block-scoped-functions','@babel/plugin-transform-shorthand-properties','@babel/plugin-transform-computed-properties',['@babel/plugin-transform-block-scoping', {throwIfClosureRequired: true}],
];// 获取babel的配置
function getBabelConfig(updateBabelOptions,bundleType,packageName,externals,isDevelopment,bundle
) {const canAccessReactObject =packageName === 'react' || externals.indexOf('react') !== -1;let options = {exclude: '/**/node_modules/**',babelrc: false,configFile: false,presets: [],plugins: [...babelPlugins],};if (isDevelopment) {options.plugins.push(...babelToES5Plugins,// Turn console.error/warn() into a custom wrapper[require('../babel/transform-replace-console-calls'),{shouldError: !canAccessReactObject,},]);}if (updateBabelOptions) {options = updateBabelOptions(options);}return options;
}// 获取rollup的配置
function getRollupOutputOptions(outputPath,format,globals,globalName,bundleType
) {return {file: outputPath,format,globals,freeze: true,interop: false,name: globalName,sourcemap: true,esModule: false,};
}// 禁止fbjs的导入
function forbidFBJSImports() {return {name: 'forbidFBJSImports',resolveId(importee, importer) {if (/^fbjs\//.test(importee)) {throw new Error(`Don't import ${importee} (found in ${importer}). ` +`Use the utilities in packages/shared/ instead.`);}},};
}function getPlugins(entry,externals,updateBabelOptions,filename,packageName,bundleType,globalName,moduleType,pureExternalModules,bundle
) {const forks = Modules.getForks(bundleType, entry, moduleType, bundle);const isUMDBundle =bundleType === UMD_DEV ||bundleType === UMD_PROD ||bundleType === UMD_PROFILING;return [// Shim any modules that need forking in this environment.useForks(forks),// Ensure we don't try to bundle any fbjs modules.forbidFBJSImports(),// Use Node resolution mechanism.resolve({skip: externals,}),// Remove license headers from individual modulesstripBanner({exclude: 'node_modules/**/*',}),// Compile to ES2015.babel(getBabelConfig(updateBabelOptions,bundleType,packageName,externals,true,bundle)),// Remove 'use strict' from individual source files.// {//   transform(source) {//     return source.replace(/['"]use strict["']/g, '');//   }, // },// Turn __DEV__ and process.env checks into constants.replace({__DEV__: 'true',__PROFILE__: 'true',__UMD__: isUMDBundle ? 'true' : 'false','process.env.NODE_ENV':"'production'",__EXPERIMENTAL__,// Enable forked reconciler.// NOTE: I did not put much thought into how to configure this.__VARIANT__: bundle.enableNewReconciler === true,}),// The CommonJS plugin *only* exists to pull "art" into "react-art".// I'm going to port "art" to ES modules to avoid this problem.// Please don't enable this for anything else!isUMDBundle && entry === 'react-art' && commonjs(),// Add the whitespace back if necessary.// License and haste headers, top-level `if` blocks.// {//   renderChunk(source) {//     return Wrappers.wrapBundle(//       source,//       bundleType,//       globalName,//       filename,//       moduleType,//       bundle.wrapWithModuleBoundaries//     );//   },// },// Record bundle size.sizes({getSize: (size, gzip) => {const currentSizes = Stats.currentBuildResults.bundleSizes;const recordIndex = currentSizes.findIndex(record =>record.filename === filename && record.bundleType === bundleType);const index = recordIndex !== -1 ? recordIndex : currentSizes.length;currentSizes[index] = {filename,bundleType,packageName,size,gzip,};},}),].filter(Boolean);
}// 1,将所有的打包格式和所有打包的bundle进行叉乘,得到所有的组合方式
// 2. 遍历所有的组合方式进行打包
// 3. 打包前过滤掉 组合中bundleType和bundle不匹配的
// 4. 
// 是否跳过构建
function shouldSkipBundle(bundle, bundleType) {// 判断bundle支持的类型中是否包含需要打包成的产物的类型const shouldSkipBundleType = bundle.bundleTypes.indexOf(bundleType) === -1;if (shouldSkipBundleType) {return true;}// 判断脚本调用参数是否满足:bundleType 中包含脚本入参传入的格式,也就是该bundle支持所需要打包的格式if (requestedBundleTypes.length > 0) {const isAskingForDifferentType = requestedBundleTypes.every(requestedType => bundleType.indexOf(requestedType) === -1);if (isAskingForDifferentType) {return true;}}// 输入的文件名if (requestedBundleNames.length > 0) {// If the name ends with `something/index` we only match if the// entry ends in something. Such as `react-dom/index` only matches// `react-dom` but not `react-dom/server`. Everything else is fuzzy// search.// bundle的入口文件const entryLowerCase = bundle.entry.toLowerCase() + '/index.js';// const isAskingForDifferentNames = requestedBundleNames.every(requestedName => {const matchEntry = entryLowerCase.indexOf(requestedName) !== -1;if (!bundle.name) {return !matchEntry;}const matchName =bundle.name.toLowerCase().indexOf(requestedName) !== -1;return !matchEntry && !matchName;});if (isAskingForDifferentNames) {return true;}}return false;
}function resolveEntryFork(resolvedEntry) {// Pick which entry point fork to use:// .modern.fb.js// .classic.fb.js// .fb.js// .stable.js// .experimental.js// .jsconst resolvedForkedEntry = resolvedEntry.replace('.js','.experimental.js');if (fs.existsSync(resolvedForkedEntry)) {return resolvedForkedEntry;}// Just use the plain .js one.return resolvedEntry;
}// 创建bundle.js 执行创建逻辑
async function createBundle(bundle, bundleType) {// 判断类型是否匹配if (shouldSkipBundle(bundle, bundleType)) {return;}const filename = getFilename(bundle, bundleType);const logKey =chalk.white.bold(filename) + chalk.dim(` (${bundleType.toLowerCase()})`);const format = 'umd';const packageName = Packaging.getPackageName(bundle.entry);let resolvedEntry = resolveEntryFork(require.resolve(bundle.entry));const shouldBundleDependencies =bundleType === UMD_DEV ||bundleType === UMD_PROD ||bundleType === UMD_PROFILING;const peerGlobals = Modules.getPeerGlobals(bundle.externals, bundleType);let externals = Object.keys(peerGlobals);if (!shouldBundleDependencies) {const deps = Modules.getDependencies(bundleType, bundle.entry);externals = externals.concat(deps);}const importSideEffects = Modules.getImportSideEffects();const pureExternalModules = Object.keys(importSideEffects).filter(module => !importSideEffects[module]);const rollupConfig = {input: resolvedEntry,treeshake: {pureExternalModules,},external(id) {const containsThisModule = pkg => id === pkg || id.startsWith(pkg + '/');const isProvidedByDependency = externals.some(containsThisModule);if (!shouldBundleDependencies && isProvidedByDependency) {if (id.indexOf('/src/') !== -1) {throw Error('You are trying to import ' +id +' but ' +externals.find(containsThisModule) +' is one of npm dependencies, ' +'so it will not contain that source file. You probably want ' +'to create a new bundle entry point for it instead.');}return true;}return !!peerGlobals[id];},onwarn: handleRollupWarning,plugins: getPlugins(bundle.entry,externals,bundle.babel,filename,packageName,bundleType,bundle.global,bundle.moduleType,pureExternalModules,bundle),output: {externalLiveBindings: false,freeze: false,interop: false,esModule: false,},};const mainOutputPath = Packaging.getBundleOutputPath(bundleType,filename,packageName);const rollupOutputOptions = getRollupOutputOptions(mainOutputPath,format,peerGlobals,bundle.global,bundleType);console.log(`${chalk.bgYellow.black(' BUILDING ')} ${logKey}`);try {// rollup api 打包const result = await rollup.rollup(rollupConfig);await result.write(rollupOutputOptions);} catch (error) {console.log(`${chalk.bgRed.black(' OH NOES! ')} ${logKey}\n`);handleRollupError(error);throw error;}console.log(`${chalk.bgGreen.black(' COMPLETE ')} ${logKey}\n`);
}function handleRollupWarning(warning) {if (warning.code === 'UNUSED_EXTERNAL_IMPORT') {const match = warning.message.match(/external module '([^']+)'/);if (!match || typeof match[1] !== 'string') {throw new Error('Could not parse a Rollup warning. ' + 'Fix this method.');}const importSideEffects = Modules.getImportSideEffects();const externalModule = match[1];if (typeof importSideEffects[externalModule] !== 'boolean') {throw new Error('An external module "' +externalModule +'" is used in a DEV-only code path ' +'but we do not know if it is safe to omit an unused require() to it in production. ' +'Please add it to the `importSideEffects` list in `scripts/rollup/modules.js`.');}// Don't warn. We will remove side effectless require() in a later pass.return;}if (warning.code === 'CIRCULAR_DEPENDENCY') {// Ignored} else if (typeof warning.code === 'string') {// This is a warning coming from Rollup itself.// These tend to be important (e.g. clashes in namespaced exports)// so we'll fail the build on any of them.console.error();console.error(warning.message || warning);console.error();process.exit(1);} else {// The warning is from one of the plugins.// Maybe it's not important, so just print it.console.warn(warning.message || warning);}
}function handleRollupError(error) {loggedErrors.add(error);if (!error.code) {console.error(error);return;}console.error(`\x1b[31m-- ${error.code}${error.plugin ? ` (${error.plugin})` : ''} --`);console.error(error.stack);if (error.loc && error.loc.file) {const {file, line, column} = error.loc;// This looks like an error from Rollup, e.g. missing export.// We'll use the accurate line numbers provided by Rollup but// use Babel code frame because it looks nicer.const rawLines = fs.readFileSync(file, 'utf-8');// column + 1 is required due to rollup counting column start position from 0// whereas babel-code-frame counts from 1const frame = codeFrame(rawLines, line, column + 1, {highlightCode: true,});console.error(frame);} else if (error.codeFrame) {// This looks like an error from a plugin (e.g. Babel).// In this case we'll resort to displaying the provided code frame// because we can't be sure the reported location is accurate.console.error(error.codeFrame);}
}async function buildEverything() {if (!argv['unsafe-partial']) {await asyncRimRaf('build');}let bundles = [];// eslint-disable-next-line no-for-of-loops/no-for-of-loopsfor (const bundle of Bundles.bundles) {bundles.push([bundle, UMD_DEV],);}// 创建bundle.js// eslint-disable-next-line no-for-of-loops/no-for-of-loopsfor (const [bundle, bundleType] of bundles) {await createBundle(bundle, bundleType);}await Packaging.copyAllShims();await Packaging.prepareNpmPackages();console.log(Stats.printResults());Stats.saveResults();
}buildEverything();
  1. 运行打包脚本
yarn build react/index,react-dom/index --type=UMD_DEV

执行之后在控制台能看到打包结果:请添加图片描述
打包产物在项目根目录下build文件夹:
在这里插入图片描述
4. 在react项目中使用打包的产物

  1. 获取创建的react项目的webpack配置:
    npm run eject
    
  2. 在config/webpack.config.js里面修改配置项: 在这里插入图片描述
  3. 将打包的产物react.development.js react.development.js.map react-dom.development.js react-dom.development.js.map复制到react项目的public目录下
  4. 运行react项目
    yarn start
    
http://www.lryc.cn/news/573627.html

相关文章:

  • C++智能指针的知识!
  • 无人机表演越来越火,C端市场大爆发
  • Java基础八股文 - 面试者心理历程与标准答案
  • 微处理器原理与应用篇---常见基础知识(7)
  • 反无人机系统:技术利刃如何守护低空安全?
  • 啥是 SaaS
  • C# .NET多线程异步记录日声,队列LOG
  • docker镜像封装与发布微服务学习
  • NotePad++ 怎么没有找到插件管理?
  • Python打卡DAY34
  • 【科研绘图系列】R语言绘制论文组合图形(multiple plots)
  • Redis快的原因
  • 【单调栈】-----【小A的柱状图】
  • 大零售生态下开源链动2+1模式、AI智能名片与S2B2C商城小程序的协同创新研究
  • 如何用AI开发完整的小程序<7>—让AI微调UI排版
  • Spring AI 项目实战(十):Spring Boot + AI + DeepSeek 构建智能合同分析技术实践(附完整源码)
  • opencv 之双目立体标定算法核心实现
  • C#控制Button单击事件指定时间间隔触发
  • 计算鱼眼相机的内参矩阵和畸变系数方法
  • 风险矩阵与灰色综合评价
  • AMAT P5000 CVDFDT CVDMAINT Precision 5000 Mark 操作 电气原理 PCB图 电路图等
  • git 如何忽略某个文件夹文件
  • NW896NW859美光固态闪存NW893NX764
  • 激活函数为何能增强神经网络的非线性表达能力?
  • 【node】Mac m1 安装nvm 和node
  • WEB3合约开发以太坊中货币单位科普
  • 【数据结构与算法】数据结构核心概念系统梳理
  • go excel解析库xuri/excelize中的SAX
  • 【人工智能基础】初识神经网络
  • 2.jupyter切换使用conda虚拟环境的最佳方法