怎么调试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 分钟前
前端框架对比和选择?
javascript·前端框架·vue·react·angular
Redstone Monstrosity10 分钟前
字节二面
前端·面试
东方翱翔17 分钟前
CSS的三种基本选择器
前端·css
Fan_web39 分钟前
JavaScript高级——闭包应用-自定义js模块
开发语言·前端·javascript·css·html
yanglamei19621 小时前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
千穹凌帝1 小时前
SpinalHDL之结构(二)
开发语言·前端·fpga开发
冯宝宝^1 小时前
基于mongodb+flask(Python)+vue的实验室器材管理系统
vue.js·python·flask
dot.Net安全矩阵1 小时前
.NET内网实战:通过命令行解密Web.config
前端·学习·安全·web安全·矩阵·.net
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
Hellc0071 小时前
MacOS升级ruby版本
前端·macos·ruby