先clone核心代码
shell
# --depth=1只下载最新版本
git clone git@github.com:vuejs/core.git --depth=1
cd core
pnpm install
简单看下package.json
的脚本
json
"scripts": {
"dev": "node scripts/dev.js",
"build": "node scripts/build.js",
}
生成dist
看dev.js
默认是打包vue
的包。
我们这边先打下reactivity
,运行node scripts/dev.js reactivity -f=esm
额外说下,format有三种
- iife ----- 自执行函数cjs是commonjs,esm是es6模块,xx.global.js
- cjs ----- commonjs(require module.exports),xx.cjs.js
- esm ----- es6模块(import export),xx.esm.js
dist
里建个index.html
,debugger
就可以调试源码了!
html
<script type="module">
import {reactive,effect} from './reactivity.esm.js';
debugger
const obj = reactive({name:'hm',age:18})
effect(()=>{
console.log(obj.age)
})
obj.age = 19
obj.age = 20
</script>
以上控制台输出18 19 20
。
分析上面代码的逻辑
创建我们自己的reactivity
复制reactivity
文件夹,修改文件夹名为reactivty-hm
,删掉src里所有内容。 根据上面分析的逻辑,开始撸码index.ts
ts
// @ts-nocheck
export function reactive(target:any){
// proxy的get track
return new Proxy(target,{
get(target, key, receiver) {
track(target,key)
return target[key]
},
// proxy的set trigger
set(target, key, value, receiver) {
target[key] = value
trigger(target,key)
return true
}
})
}
// 全局变量,targetMap 记录 {obj: { name: [effect1] },obj2:... }
const targetMap = new WeakMap()
// effect运行的时候,将自己赋值给activeEffect,这样方便运行函数获取属性的时候,通过这个变量收集effect
let activeEffect = null
// track 说白了 收集effect1 {obj: { name: [effect1] },obj2:... }
function track(target:any,key:string){
let depsMap = targetMap.get(target)
if(!depsMap){
targetMap.set(target,(depsMap = new Map()))
}
let dep = depsMap.get(key)
if(!dep){
depsMap.set(key,(dep = new Set()))
}
const shouldTrack = !dep.has(activeEffect);
// 这句是核心,上面就是判断各种有没有
shouldTrack && dep.add(activeEffect);
console.log(targetMap)
}
// trigger 让effect1执行 {obj: { name: [effect1] },obj2:... }
function trigger(target,key){
const depsMap = targetMap.get(target)
if(!depsMap) return
const dep = depsMap.get(key)
if(!dep) return
console.log(dep)
dep.forEach(effect => effect.run())
}
export function effect(fn){
const _effect = new ReactiveEffect(fn)
_effect.run()
return _effect
}
// ReactiveEffect创建effect示例的,核心方法就是effect
class ReactiveEffect{
constructor(public fn){
this._effect = this
}
run(){
activeEffect = this
return this.fn()
}
}
运行我们自己的reactivity
运行node scripts/dev.js reactivity -f=esm
哟西,生成啦!
把刚刚的index.html
复制进这里的dist
,路径改成./reactivity-hm.esm.js
运行下,可以啦!
这样再继续写ref
,watch
,computed
等等,就很容易调试了。
可以把原来的reactivity.esm.js
和/reactivity.esm.js.map
复制过来,这样看下自己写的和源码是否表现一致。
看下源码的dev.js
稍微简化些,本质就是根据命令行参数,看下是哪个package
和打包方式,然后输出dist在相关包下
js
import esbuild from 'esbuild'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import minimist from 'minimist'
// 参数,拿到包名和打包方式
const args = minimist(process.argv.slice(2))
const target = args._[0] || 'reactivity'
const format = args.f || 'global'
const require = createRequire(import.meta.url) // import.meta.url当前文件路径
const pkg = require(`../packages/${target}/package.json`) // pkg就是当前文件夹下的package.json的对象
const outputFormat = format.startsWith('global')
? 'iife'
: format === 'cjs'
? 'cjs'
: 'esm'
const postfix = format.endsWith('-runtime')
? `runtime.${format.replace(/-runtime$/, '')}`
: format
const __dirname = dirname(fileURLToPath(import.meta.url)) // __dirname当前文件的文件夹路径
const outfile = resolve(
__dirname,
`../packages/${target}/dist/${
target === 'vue-compat' ? `vue` : target
}.${postfix}.js`
)
esbuild
.context({
entryPoints: [resolve(__dirname, `../packages/${target}/src/index.ts`)],
outfile,
bundle: true,
sourcemap: true,
format: outputFormat,
globalName: pkg.buildOptions?.name || target,
platform: format === 'cjs' ? 'node' : 'browser',
// define是将代码里这些key替换成value
define: {
__VERSION__: `"${pkg.version}"`,
__DEV__: `true`,
}
})
.then(ctx => ctx.watch())
如果你想自己搭一套esbuild生成代码的话
建个目录
shell
mkdir esbuild-demo
cd esbuild-demo
# 换成npm也行,我这端着呢,哈哈哈
pnpm init
pnpm i -D esbuild
# 可以这么写,import { resolve } from 'node:path'
pnpm i -D @types/node
# 可以解析参数
pnpm i -D minimist
mkdir scripts
# 复制dev.js进去
mkdir packages
# 复制reactivity-hm进去
配置下package.json
的脚本就行node scripts/dev.js reactivity -f=esm
不配置也无所谓,哈哈哈,还是那句,要端着(* ̄︶ ̄)~