# vue3.x 源码摸索

# vnode 到真实 DOM

整个过程的思维导图(超级详细包含代码)

在线查看-processon 思维导图 (opens new window)

# vue3.x 中的工具函数

# EMPTY_OBJ 空对象

const EMPTY_OBJ = process.env.NODE_ENV !== 'production' ? Object.freeze({}) : {}
1

开发环境需要报错信息,冻结对象

生产环境不需要报错信息,不冻结对象

# EMPTY_ARR 空数组

const EMPTY_ARR = process.env.NODE_ENV !== 'production' ? Object.freeze([]) : []
1

# NOOP 空函数

const NOOP = () => {}
1
  1. 方便判断
  2. 方便压缩

# NO 永远返回 false 的函数

const NO = () => false
1

方便压缩

# 判断字符串是不是 on 开头,并且 on 后首字母是大写字母

const onRE = /^on[^a-z]/
const isOn = (key) => onRE.test(key)
1
2

# 判断是否是 isModelListener 监听器

判断是不是'onUpdate:'开头既可

const isModelListener = (key) => key.startsWith('onUpdate:')
1

# extend 继承合并

const extend = Object.assign
1

# remove 移除数组的一项

const remove = (arr, el) => {
  const i = arr.indexOf(el)
  if (i > -1) {
    arr.splice(i, 1)
  }
}
1
2
3
4
5
6

splice 其实是一个很耗性能的方法。删除数组中的一项,其他元素都要移动位置。

引申:axios InterceptorManager 拦截器源码 中,拦截器用数组存储的。

但实际移除拦截器时,只是把拦截器置为 null 。而不是用 splice 移除。

最后执行时为 null 的不执行,同样效果。

axios 拦截器这个场景下,不得不说为性能做到了很好的考虑。

# hasOwn 是不是自己本身所拥有的属性

const hasOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (val, key) => hasOwnProperty.call(val, key)
1
2

# isArray 判断数组

const isArray = Array.isArray
1

# isMap 判断是不是 Map 对象

const isMap = (val) => toTypeString(val) === '[object Map]'
1

# isSet 判断是不是 Set 对象

const isSet = (val) => toTypeString(val) === '[object Set]'
1

# isDate 判断是不是 Date 对象

const isDate = (val) => val instanceof Date
1

# isFunction 判断是不是函数

const isFunction = (val) => typeof val === 'function'
1

# isString 判断是不是字符串

const isString = (val) => typeof val === 'string'
1

# isSymbol 判断是不是 Symbol

const isSymbol = (val) => typeof val === 'symbol'
1

# isObject 判断是不是对象

const isObject = (val) => val !== null && typeof val === 'object'
1

# isPromise 判断是不是 Promise

const isPromise = (val) =>
  isObject(val) && isFunction(val.then) && isFunction(val.catch)
1
2

# objectToString 对象转字符串

const objectToString = Object.prototype.toString
1

# toTypeString 对象转字符串

const toTypeString = (val) => objectToString.call(val)
1

# toRawType 对象转字符串,截取后几位

const toRawType = (val) => toTypeString(val).slice(8, -1)
1

# isPlainObject 判断是不是纯粹的对象

const isPlainObject = (val) => toTypeString(val) === '[object Object]'
1

# isIntegerKey 判断是不是数字型的字符串 key 值

const isIntegerKey = (val) =>
  isString(key) &&
  key !== 'NaN' &&
  key[0] !== '-' &&
  key === '' + parseInt(key, 10)
1
2
3
4
5

# makeMap && isReservedProp

判断一个属性是否为保留属性

function makeMap(str, expectsLowerCase) {
  const map = Object.create(null)
  const list = str.split(',')
  for (let i = 0; i < list.length; i++) {
    map[list[i]] = true
  }
  return expectsLowerCase
    ? (val) => !!map[val.toLowerCase()]
    : (val) => !!map[val]
}
1
2
3
4
5
6
7
8
9
10

# cacheStringFunction 缓存

const cacheStringFunction = (fn) => {
  const cache = Object.create(null)
  return (str) => {
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  }
}
1
2
3
4
5
6
7

# 连字符 - 转驼峰 on-click => onClick

const camelizeRE = /-(\w)/g
const camelize = cacheStringFunction((str) =>
  str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
)
1
2
3
4

# 驼峰 转 连字符 - onClick => on-click

const hyphenateRE = /\B([A-Z])/g
const hyphenate = cacheStringFunction((str) =>
  str.replace(hyphenateRE, '-$1').toLowerCase()
)
1
2
3
4

# 首字母转大写

const capitalize = cacheStringFunction(
  (str) => str.charAt(0).toUpperCase() + str.slice(1)
)
1
2
3

# click => onClick

const toHandleKey = cacheStringFunction((str) =>
  str ? `on${capitalize(str)}` : ''
)
1
2
3

# hasChanged 判断是不是有变化

const hasChanged = (val, oldVal) => !Object.is(val, oldVal)
1

# invokeArrayFns 执行数组里的函数

const invokeArrayFns = (fns, arg) => {
  for (let i = 0; i < fns.length; i++) {
    fns[i](arg)
  }
}
1
2
3
4
5

# def 定义对象属性

const def = (obj, key, value) => {
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: false,
    value
  })
}
1
2
3
4
5
6
7

数据描述符(其中属性为:enumerable,configurable,value,writable)与存取描述符(其中属性为 enumerable,configurable,set(),get())之间是有互斥关系的。在定义了 set()和 get()之后,描述符会认为存取操作已被 定义了,其中再定义 value 和 writable 会引起错误。

# toNumber 转数字

const toNumer = (val) => {
  const n = parseFloat(val)
  return isNaN(n) ? val : n
}
1
2
3
4

# getGlobalThis 全局对象

let _globalThis
const getGlobalThis = () => {
  return (
    _globalThis ||
    (_globalThis =
      typeof globalThis !== 'undefined'
        ? globalThis
        : typeof self !== 'undefined'
        ? self
        : typeof window !== 'undefined'
        ? window
        : typeof global !== 'undefined'
        ? global
        : {})
  )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 参考

初学者也能看懂的 Vue3 源码中那些实用的基础工具函数 (opens new window)

据说 99% 的人不知道 vue-devtools 还能直接打开对应组件文件?本文原理揭秘 (opens new window)

最近更新: 4 小时前