文件路径:VUE3.0源码/script/build.js整个脚本从入口函数run()开始,大概经历了以下过程:1.根据是否为正式的release版本,为true时需要清除构建缓存,避免枚举值过时;2、分析命令行参数是否指定了需要编译的模块信息,如果没有则编译所有模块;3.最后的构建过程就是执行rollup打包4.另外还有一个buildtypes的过程。将ts源码转成js后,会生成一堆*.d.ts,还可以生成api报告生成文档描述模型(xxx.api.json)等5.形式化prd环境也会检查不同压缩方式下的*.global.prod.js文件的大小。具体源码如下://nodejs模块:fs-extra是fs模块的扩展,提供了更方便的API,继承了fs模块的APIconstfs=require('fs-extra')constpath=require('path')/**nodejs模块:提供文件路径相关的api*/constchalk=require('chalk')/**nodejs模块:作用是修改控制台中字符串的样式*/constexeca=require('execa')/*详细解释见调用处*/const{gzipSync}=require('zlib')/*nodejs模块:使用Gzip压缩数据块*/const{compress}=require('brotli')/*google开源brotli压缩算法*/const{targets:allTargets,fuzzyMatchTarget}=require('./utils')/***minimist轻量级命令行参数解析引擎*process.argv.slice(2)对应执行命令参数位置*(即第三个起始位,相当于下面exa中的“-x3-y4-n5-abc--beep=boopfoobarbaz”mple)例子如下:*nodeexample/parse.js-x3-y4-n5-abc--beep=boopfoobarbaz*args结果为:{*_:['foo','bar','baz'],*x:3,*y:4,*n:5,*a:true,*b:true,*c:true,*beep:'boop'*}*/constargs=require('minimist')(process.argv.slice(2))//待编译模块数组consttargets=args._//console.log('@@@@@@@@@@@@@@@@目标:',targets,args);//获取'esm-bundler,cjs'等命令行参数中输入的formats参数,多个值可以用逗号分隔constformats=args.formats||args.fconstdevOnly=args.devOnly||args.dconstprodOnly=!devOnly&&(args.prodOnly||args.p)constsourceMap=args.sourcemap||args.s//正式发布版本,为true时需要清空构建缓存,避免枚举值过时constisRelease=args.release//TSAPI解析:输出*.d.ts,*.api.mdapireport,*.api.json文档描述模型,Markdown文档constbuildTypes=args.t||参数类型||isRelease//如果设置了该参数,则返回targets中所有匹配成功的结果;false时,只返回第一个匹配成功的结果constbuildAllMatching=args.all||args.a/***execa*第一个参数:string类型,类似于在cmd中运行脚本时输入的命令*第二个参数:string[]第一个参数绑定的命令的相关属性信息**gitrev-parseHEAD-->获取最新的commitid(例如:0996f0ac76188c324831f19089bdd87b9c364cb6)*commitvalue是commitid的前7位*commit=*.slice(0,7)='0996f0a'**/constcommit=execa.sync('git',['rev-parse','HEAD']).stdout.slice(0,7)run()asyncfunctionrun(){if(isRelease){//删除构建缓存发布版本以避免过时denumvaluesawaitfs.remove(path.resolve(__dirname,'../node_modules/.rts2_cache'))}//如果命令行没有指定模块,编译所有模块if(!targets.length){awaitbuildAll(allTargets)checkAllSizes(allTargets)}else{//在命令行编译指定模块awaitbuildAll(fuzzyMatchTarget(targets,buildAllMatching))checkAllSizes(fuzzyMatchTarget(targets,buildAllMatching))}}asyncfunctionbuildAll(targets){forconsttargetoftargets){awaitbuild(target)}}asyncfunctionbuild(target){constpkgDir=path.resolve(`packages/${target}`)constpkg=require(`${pkgDir}/package.json`)//只为发布构建发布包if(isRelease&&pkg.private){//不打包私有模块return}//如果构建特定格式,不删除dist。//定义模块打包类型,需要清除历史打包数据|(devOnly?'开发':'生产')/***execa*第一个参数:string类型,类似于在cmd中运行脚本时键入的命令*第二个参数:string[]第一个参数绑定的命令的相关属性信息*第三个参数:execa.Options*rollup-wc--environmentTARGET:[target],FORMATS:umd**-wc:-w和-c的组合,-c使用配置文件rollup.config.js打包js,-w观察thesource文件改变并自动重新打包**--environment:设置传递给文件的环境变量,可以通过JS文件中的process.env读取*这里设置:process.env.COMMIT,process.env。TARGET等几个变量**/awaitexeca('rollup',['-c','--environment',[`COMMIT:${commit}`,`NODE_ENV:${env}`,`TARGET:${target}`,formats?`FORMATS:${formats}`:``,buildTypes?`TYPES:true`:``,prodOnly?`PROD_ONLY:true`:``,sourceMap?`SOURCE_MAP:true`:``].filter(Boolean)/***filter(Boolean)移除所有“false"类型的元素,Boolean是一个函数*consta=[1,2,"b",0,{},"",NaN,3,undefined,null,5];*a.filter(布尔值);//[1,2,"b",{},3,5]*/.join(',')],/***stdio选项用于配置父进程和子进程之间建立的管道*默认情况下,子进程的stdin、stdout、stderr重定向到对应的subprocess.stdin、subprocess.stdout、subprocess的流.stderr属于ChildProcess对象*这相当于options.stdio=['inherit','inherit','inherit']*/{stdio:'inherit'})if(buildTypes&&pkg.types){console.log()console.log(chalk.bold(chalk.yellow(`Rollinguptypedefinitionsfor${target}...`)))/***buildtypes*@microsoft/api-extractor的一般工作流程是如下:*1.tsc将ts源码转换为js后,会生成一堆*.d.ts*2.APIExtractor读取这些d.ts可以生成API报告*2.1.打包删除乱七八糟的d.ts*2.3生成文档描述模型(xxx.api.json)可以通过微软提供的api-documenter进一步转化为Markdown文档。*/const{Extractor,ExtractorConfig}=require('@microsoft/api-extractor')//读取extractor配置文件路径(api-extractor.json与package.json同级)constextractorConfigPath=path.resolve(pkgDir,`api-extractor.json`)//加载api-extractor.json配置文件并返回一个'ExtractorConfig'对象constextractorConfig=ExtractorConfig.loadFileAndPrepare(extractorConfigPath)//使用准备好的'ExtractorConfig'对象调用API提取器。constextractorResult=Extractor.invoke(extractorConfig,{//表示API提取器作为本地构建的一部分运行,例如在开发人员的计算机上。默认值:falselocalBuild:true,//如果为true,API提取器将输出包含{@linkExtractorLogLevel.Verbose}详细消息的输出。showVerboseMessages:true})//提取成功if(extractorResult.succeeded){//concatadditionald.tstorolled-updtsconsttypesDir=path.resolve(pkgDir,'types')//是否是当前根目录编译后的模块是有一个types目录(eg.packages\vue\types)if(awaitfs.exists(typesDir)){/***package.json中的pkg.types:types属性(eg:dist/vue.d.ts)*dtsPath:例如。(\packages\vue\dist\vue.d.ts)*/constdtsPath=path.resolve(pkgDir,pkg.types)//读取packages/vue/dist/vue.d.tsconstexisting=awaitfs.readFile(dtsPath,'utf-8')/***读取typesDir(eg.packages\vue\types)目录下的文件*Return:typeFiles=['vue.d.ts']*/consttypeFiles=awaitfs.readdir(typesDir)//遍历读取typeFiles=['vue.d.ts']文件信息,返回toAdd列表consttoAdd=awaitPromise.all(typeFiles.map(file=>{returnfs.readFile(path.解析(typesDir,文件),'utf-8')}))//将typesDir(eg.packages\vue\types)目录下的所有*.d.ts文件信息汇总到一个文件中//dtsPath:eg.(\packages\vue\dist\vue.d.ts)awaitfs.writeFile(dtsPath,existing+'\n'+toAdd.join('\n'))}console.log(chalk.bold(chalk.green(`APIExtractor成功完成。`)))}else{console.error(`APIExtractor完成并出现${extractorResult.errorCount}错误`+`和${extractorResult.warningCount}警告`)process.exitCode=1}awaitfs.remove(`${pkgDir}/dist/packages`)}}functioncheckAllSizes(targets){if(devOnly){return}console.log()for(consttargetoftargets){checkSize(target)}console.log()}functioncheckSize(target){constpkgDir=path.resolve(`packages/${target}`)checkFileSize(`${pkgDir}/dist/${target}.global.prod.js`)}/***检查*.global.prod.js文件的大小不同的压缩方法*vue.global.prod.jsmin:100.07kb/gzip:38.20kb/brotli:34.33kb*min:elementsize*gzip:使用nodejs模块-gzip压缩数据块*brotli:google开源的brotli压缩算法*/functioncheckFileSize(filePath){if(!fs.existsSync(filePath)){return}constfile=fs.readFileSync(filePath)constminSize=(file.length/1024).toFixed(2)+'kb'constgzipped=gzipSync(file)constgzippedSize=(gzipped.length/1024).toFixed(2)+'kb'constcompressed=compress(文件)constcompressedSize=(compressed.length/1024).toFixed(2)+'kb'console.log(`${chalk.gray(chalk.bold(path.basename(filePath)))}min:${minSize}/gzip:${gzippedSize}/brotli:${compressedSize}`)}如果你对“前端源码”情有独钟,可以关注微信前端源码解析公众号,内容不断更新中!目前VUE3.0源码正在解析中,欢迎加入!~