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

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

相关推荐
bysking18 分钟前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓34 分钟前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_41137 分钟前
无网络安装ionic和运行
前端·npm
理想不理想v39 分钟前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云1 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205871 小时前
web端手机录音
前端
齐 飞1 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
神仙别闹1 小时前
基于tensorflow和flask的本地图片库web图片搜索引擎
前端·flask·tensorflow
aPurpleBerry2 小时前
JS常用数组方法 reduce filter find forEach
javascript
GIS程序媛—椰子2 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js