今年十月份换了工作,经过众多面试官的折磨,各种原理拷打,裸辞了三月终于找到一份工作,痛下决心,作为一名菜鸟,开始了我的阅读vue源码之路。
首先,vue3源码地址:https://github.com/vuejs/core
npm i pnpm
pnpm i
找到顶层的package.json文件,找到命令
执行了scripts/dev.js文件,测试:
断点分析:
获取prod默认表明开发环境,__dirname为当前文件路径
开始遍历targets数组,其中relative生成相对路径,resolve生成绝对路径,
outfile = "E:\\学习\\vue3\\core\\packages\\vue\\dist\\vue.global.js"
relativeOutfile="packages\\vue\\dist\\vue.global.js"
指向vue.global.js
...
esbuild构建
入口文件entryPoints:xxxx\core\packages\vue\src\index.ts
DEV: prod ? false
: true
,环境变量为开发环境
文件更改时,执行此行,在终端打印
文件全部注释:
// @ts-check
// Using esbuild for faster dev builds.
// We are still using Rollup for production builds because it generates
// smaller files and provides better tree-shaking.
import esbuild from 'esbuild'
import fs from 'node:fs'
import { dirname, relative, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import { parseArgs } from 'node:util'
import { polyfillNode } from 'esbuild-plugin-polyfill-node'
const require = createRequire(import.meta.url)
// 当前文件夹绝对路径
const __dirname = dirname(fileURLToPath(import.meta.url))
// format:global,prod:false,inline:false
// positionals: ['vue']
// parseArgs: 解析命令行参数
const {
values: { format: rawFormat, prod, inline: inlineDeps },
positionals,
} = parseArgs({
allowPositionals: true,
options: {
format: {
type: 'string',
short: 'f',
default: 'global',
},
prod: {
type: 'boolean',
short: 'p',
default: false,
},
inline: {
type: 'boolean',
short: 'i',
default: false,
},
},
})
const format = rawFormat || 'global'
// ['vue']
const targets = positionals.length ? positionals : ['vue']
// resolve output iife:立即执行函数表达式, cjs:CommonJS, esm:ESM
const outputFormat = format.startsWith('global')
? 'iife'
: format === 'cjs'
? 'cjs'
: 'esm'
// global
const postfix = format.endsWith('-runtime')
? `runtime.${format.replace(/-runtime$/, '')}`
: format
const privatePackages = fs.readdirSync('packages-private')
for (const target of targets) {
const pkgBase = privatePackages.includes(target)
? `packages-private`
: `packages`
// pkgBasePath: '../packages/vue'
const pkgBasePath = `../${pkgBase}/${target}`
const pkg = require(`${pkgBasePath}/package.json`)
// packages\vue\dist\vue.global.js
const outfile = resolve(
__dirname,
`${pkgBasePath}/dist/${
target === 'vue-compat' ? `vue` : target
}.${postfix}.${prod ? `prod.` : ``}js`,
)
const relativeOutfile = relative(process.cwd(), outfile)
// resolve externals
// TODO this logic is largely duplicated from rollup.config.js
/** @type {string[]} */
let external = []
if (!inlineDeps) {
// cjs & esm-bundler: external all deps
if (format === 'cjs' || format.includes('esm-bundler')) {
external = [
...external,
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
// for @vue/compiler-sfc / server-renderer
'path',
'url',
'stream',
]
}
if (target === 'compiler-sfc') {
const consolidatePkgPath = require.resolve(
'@vue/consolidate/package.json',
{
paths: [resolve(__dirname, `../packages/${target}/`)],
},
)
const consolidateDeps = Object.keys(
require(consolidatePkgPath).devDependencies,
)
external = [
...external,
...consolidateDeps,
'fs',
'vm',
'crypto',
'react-dom/server',
'teacup/lib/express',
'arc-templates/dist/es5',
'then-pug',
'then-jade',
]
}
}
/** @type {Array<import('esbuild').Plugin>} */
const plugins = [
{
name: 'log-rebuild',
setup(build) {
build.onEnd(() => {
// 'packages\\vue\\dist\\vue.global.js'
console.log(`built: ${relativeOutfile}`)
})
},
},
]
if (format !== 'cjs' && pkg.buildOptions?.enableNonBrowserBranches) {
plugins.push(polyfillNode())
}
// 打包入口文件\core\packages\vue\src\index.ts
esbuild
.context({
entryPoints: [resolve(__dirname, `${pkgBasePath}/src/index.ts`)],
outfile, // 'xxx\\core\\packages\\vue\\dist\\vue.global.js'
bundle: true,
external, // []
sourcemap: true,
format: outputFormat, // iife
globalName: pkg.buildOptions?.name, // Vue
platform: format === 'cjs' ? 'node' : 'browser', // browser
plugins,
// define 提供了一种用常量表达式替换全局标识符的方法。 它可以在不改变代码本身的情况下改变某些构建之间代码的行为
define: {
__COMMIT__: `"dev"`,
__VERSION__: `"${pkg.version}"`,
__DEV__: prod ? `false` : `true`,
__TEST__: `false`,
__BROWSER__: String(
format !== 'cjs' && !pkg.buildOptions?.enableNonBrowserBranches,
), // true
__GLOBAL__: String(format === 'global'), // true
__ESM_BUNDLER__: String(format.includes('esm-bundler')), // false
__ESM_BROWSER__: String(format.includes('esm-browser')), // false
__CJS__: String(format === 'cjs'), // false
__SSR__: String(format !== 'global'), // false
__COMPAT__: String(target === 'vue-compat'), // false
__FEATURE_SUSPENSE__: `true`,
__FEATURE_OPTIONS_API__: `true`,
__FEATURE_PROD_DEVTOOLS__: `false`,
__FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__: `true`,
},
})
.then(ctx => ctx.watch()) // ctx.watch() 监听文件变化
}
core\packages\vue\src\index.ts
打开packages\vue\examples\transition\modal.html断点分析index.ts文件;
compileToFunction:将template转换成成render函数