Vue3源码工具函数解读

193 阅读4分钟

Vue3中有一个shared模块,里面存放的是一些比较实用的工具函数,难度较小,初学者也完全可以看懂

准备工作

首先,我们需要先将 vue-next clone到本地,进入到 vue-next/package/shared/src/index.ts,这里就是工具函数的位置,方便起见,我将顺序阅读所有工具。

1. babelParserDefaultPlugins 默认插件

export const babelParserDefaultPlugins = [
  'bigInt',
  'optionalChaining',
  'nullishCoalescingOperator'
]

这个是babel解析相关的几个默认插件

2. EMPTY_OBJ 空对象

export const EMPTY_OBJ = __DEV__
  ? Object.freeze({})
  : {}

EMPTY_OBJ.name = 'Lqf'
console.log(EMPTY_OBJ.name) // undefined

这是一个适用于空对象判断的工具,开发环境采用的是 freeze方法,可冻结最外层的对象

3. EMPTY_ARR 空数组

export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []

EMPTY_ARR.length = 2
console.log(EMPTY_ARR.length) // 1

和上一条类型,适用于空数组的判断

4. NOOP 空函数

export const NOOP = () => {}

适用于空函数的判断,在源码初始化时经常会设置函数的默认值是空函数

5. NO 返回false的函数

export const NO = () => false

永远返回false的函数

6. isOn 事件判断

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

vue3的事件与自定义事件都以 on 为开头字母,该工具主要用以判断事件

7. isModelListener 判断listener

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

判断字符串是不是以 onUpdate: 开头,主要用以判断 listener 监听

8. extend 合并

export const extend = Object.assign

const user1 = { name: 'xiaoming' }
const user2 = extend(user1, { name: 'xiaohong', age: 18 })
console.log(user1 === user2) // true

用以合并对象

9. remove 删除数组的一项

export const remove = (arr, el) => {
  const i = arr.indexOf(el)
  if (i > -1) {
    arr.splice(i, 1)
  }
}

传入数组和索引,删除该索引的内容

10. hasOwn 是否为本身属性

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

查看对象是否拥有该属性

11. isArray 是否为数组

export const isArray = Array.isArray

isArray([1, 2]) // true
const fArr = { __proto__: Array.prototype, length: 0 }
isArray(fArr) // false
fArr instance Array // true

12. 判断多种类型

// 是否为Map
export const isMap = (val) => toTypeString(val) === '[object Map]'
// 是否为Set
export const isSet = (val) => toTypeString(val) === '[object Set]'
// 是否为Date
export const isDate = (val )  => val instanceof Date
// 是否为function
export const isFunction = (val) => typeof val === 'function'
// 是否为string
export const isString = (val) => typeof val === 'string'
// 是否为Symbol
export const isSymbol = (val) => typeof val === 'symbol'
// 是否为Object
export const isObject = (val) => val !== null && typeof val === 'object'
// 是否为Promise
export const isPromise = (val) => isObject(val) && isFunction(val.then) && isFunction(val.catch)
// 是否为纯粹的对象,例如 isObject([]) === true isPlainObject === false
export const isPlainObject = (val) => toTypeString(val) === '[object Object]'

13. 对象转字符串

// 对象转字符串方法
export const objectToString = Object.prototype.toString
// 对象转字符串,格式为 "[object, xxxxxx]"
export const toTypeString = (value)=> objectToString.call(value)
// 对象转字符串截取后几位内容
export const toRawType = (value) => return toTypeString(value).slice(8, -1)

14. 是否为字符串数字

export const isIntegerKey = (key) =>
  isString(key) &&
  key !== 'NaN' &&
  key[0] !== '-' &&
  '' + parseInt(key, 10) === key
  
isIntegerKey('hello') // false
isIntegerKey('123') // true

15. makeMap 字符串转Map格式,用于后续查询

export function makeMap(
  str,
  expectsLowerCase // 是否为小写
): (key: string) => boolean {
  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]
}

16. isReservedProp 是否为保留Prop

export const isReservedProp = makeMap(
  ',key,ref,' +
  'onVnodeBeforeMount,onVnodeMounted,' +
  'onVnodeBeforeUpdate,onVnodeUpdated,' +
  'onVnodeBeforeUnmount,onVnodeUnmounted'
)

// 最后的格式类似为为{ key: true, ref: true, onVnodeBeforeMount: true, ... }

17. cacheStringFunction 缓存

const cacheStringFunction = (fn) => {
  const cache = Object.create(null)
  return ((str) => {
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  })
}

该方法缓存的是函数

18. 正则与标准化

const camelizeRE = /-(\w)/g
// 将连字形式转化为驼峰形式 on-click => onClick
export const camelize = cacheStringFunction((str) => {
  // _: 匹配内容  c: 括号内容
  return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
})

const hyphenateRE = /\B([A-Z])/g
// 将驼峰转化为连字符 onClick => on-click
export const hyphenate = cacheStringFunction((str) =>
  // $1: 第一个括号的内容
  str.replace(hyphenateRE, '-$1').toLowerCase()
)

// 字符串第一个字母大写 click => Click
export const capitalize = cacheStringFunction(
  (str) => str.charAt(0).toUpperCase() + str.slice(1)
)

// 将字符串变化成Vue3事件的形态 click => onClick
export const toHandlerKey = cacheStringFunction((str) =>
  str ? `on${capitalize(str)}` : ``
)

19. onChanged 是否改变

// Object.is 与 === 区别为两点 1. NaN等于NaN 2. -0不等于+0 
export const hasChanged = (value, oldValue) => !Object.is(value, oldValue)

20. invokeArrayFns 执行数组的所有函数

export const invokeArrayFns = (fns, arg) => {
  for (let i = 0; i < fns.length; i++) {
    fns[i](arg)
  }
}

21. def 设置属性内容

export const def = (obj, key, value) => {
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: false,
    value
  })
}

22. toNumber 转化为数字

export const toNumber = (val) => {
  const n = parseFloat(val)
  return isNaN(n) ? val : n
}

toNumber('123') // 123
toNumber('asd') // 'asd'
toNumber('123asd') // ''asd

23. 获取全局变量

// self > window > global(node) > {}(可能是微信小程序环境)
let _globalThis
export const getGlobalThis = () => {
  return (
    // 缓存
    _globalThis ||
    (_globalThis =
      typeof globalThis !== 'undefined'
        ? globalThis
        : typeof self !== 'undefined'
          ? self
          : typeof window !== 'undefined'
            ? window
            : typeof global !== 'undefined'
              ? global
              : {})
  )
}

以上就是我对 Vue3 工具函数的简略理解和描述