怎么调试vue核心源码和写reactivity核心的代码 - 源码系列12

先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.htmldebugger就可以调试源码了!

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

运行下,可以啦!

这样再继续写refwatchcomputed等等,就很容易调试了。

可以把原来的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

不配置也无所谓,哈哈哈,还是那句,要端着(* ̄︶ ̄)~

相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼3 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax