怎么调试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

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

相关推荐
码客前端3 分钟前
理解 Flex 布局中的 flex:1 与 min-width: 0 问题
前端·css·css3
Komorebi゛3 分钟前
【CSS】圆锥渐变流光效果边框样式实现
前端·css
工藤学编程16 分钟前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保16 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫17 分钟前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
博主花神18 分钟前
【React】扩展知识点
javascript·react.js·ecmascript
内存不泄露23 分钟前
基于Spring Boot和Vue 3的智能心理健康咨询平台设计与实现
vue.js·spring boot·后端
欧阳天风25 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder28 分钟前
箭头函数和 this 绑定
linux·前端·javascript·typescript
郑州光合科技余经理29 分钟前
架构解析:同城本地生活服务o2o平台海外版
大数据·开发语言·前端·人工智能·架构·php·生活