入门源码学习之vue3工具函数

201 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

前言

随着尤大官宣Vue3  2022  2  7  成为新的默认版本,学习vue3或者升级vue3已经成为老生常谈的话题。
正好最近公司有不少vue2项目要升级vue3,趁这次学习源码活动加深一下vue3工具函数的理解。
纸上得来终觉浅,绝知此事要躬行。下面探讨一下vue3源码工具函数,一起学以致用吧~

学习目标

  1. 学习如何调试vue3源码
  2. 理解vue3源码shared模块中的实用工具函数
  3. 学习源码中优秀代码和思想,尝试学以致用,投入到自己的项目中

前期准备

  1. 源码地址
  2. 阅读指南:README.md或贡献指南
  • 从贡献指南得知,运行环境要求如下
// 环境准备
node.js 版本16以上
pnpm 版本7以上
版本检测:node -v 
pnpm -v
  1. 下载源码
git clone https://github.com/vuejs/vue-next.git 
cd vue-next
// pnpm下载、淘宝镜像设置
npm i pnpm -g
pnpm config set registry https://registry.npmmirror.com
// 安装依赖
pnpm install
// 打包
pnpm build
  1. 工具函数路径/packages/shared/src/index.ts

image.png

源码调试

1.生成sourcemap调试代码,根据贡献指南的描述,可以在package.json中配置-s或--sourcemap来生成sourcemap,但是会导致打包速度慢,因此我们配置开发时生成sourcemap, 在package.json的script标签加入以下命令:

"dev:sourcemap":"node scripts/dev.js -s"
  • 生成文件位置:packages\vue\dist\vue.global.js.map

7b406969270e0ac34dbca2d60285070.jpg 2.安装live server插件创建本地服务

image.png

工具函数

1.EMPTY_OBJ 空对象
  • 源码
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
  ? Object.freeze({})
  : {}
  • 源码理解:非生产环境返回冻结的空对象,否则返回空对象
  • 拓展知识点:Object.freeze方法,冻结一个对象,使得该对象不能被修改,添加新属性,删除已有属性
 const obj = {
       a:1,
       b:2,
       c:{}
   }
   Object.freeze(obj)
   obj.add = 3
   obj.c.add = 3
   console.log(obj)
  • 用以上方法调试一下object.freeze方法,结果如下,可以看出Object.freeze是浅冻结

image.png

2.EMPTY_ARR
  • 源码
export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []
// 测试代码如下
const arr = []
Object.freeze(arr)
arr.push('a')
arr.concat([obj])
  • 源码理解:非生产环境返回冻结的空数组,否则直接返回空数组
  • 尝试用push给冻结的数组添加新元素,报错如下

image.png

  • concat操作虽然不报错,但也没变化

image.png

3.NOOP空函数
  • 源码
export const NOOP = () => {}
  • 源码理解:返回一个空函数,便于压缩代码跟函数判断
  • 测试代码
const NOOP = () => {}
   const noopObj = {
       fun: NOOP
   }
   noopObj.add = function(){
      console.log('add')
   }
   if(noopObj.add===NOOP){
       console.log('pass')
   }
4.NO函数一直返回false
  • 源码
export const NO = () => false
  • 源码理解 一直返回false,用处是压缩代码并一直返回false
5.isOn 判断是否以on开头
  • 源码
const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)
  • 源码理解:利用正则判断传入值是否以小写字母on开头,其中^在首部代表以什么开头,[^a-z]表示小写字母
  • 测试代码
const onRE = /^on[^a-z]/
const isOn = (key) => onRE.test(key)
const isOnChange = isOn('onChange')?isOn('change'):'true'
  • 调试结果

image.png

6. isModelListener函数
  • 源码
export const isModelListener = (key: string) => key.startsWith('onUpdate:')
  • 源码理解: 判断传入值是否以onUpdate:开头,可以用来判断事件监听器
  • 测试代码
const isModelListener = (key) => key.startsWith('onUpdate:')
const isOnChange = isModelListener('onUpdate')?isOn('ONchange'):'true'
  • 测试结果 image.png
7.extend合并
  • 源码
export const extend = Object.assign
  • 源码理解:利用Object.assign合并对象,Object.assign的作用是将所有可枚举属性的值从一个或多个源对象(sources)分配到目标对象(target),并返回目标对象,不能转换null或者undefined
  • 测试代码
const extend = Object.assign
const test = extend({},{a:1})
const obj2 = extend('','a')
const obj1 = extend(null,'a')
  • 调试结果

image.png

8.remove 移除项
  • 源码
export const remove = <T>(arr: T[], el: T) => {
  const i = arr.indexOf(el)
  if (i > -1) {
    arr.splice(i, 1)
  }
}
  • 源码理解:移除数组中的指定值,该方法会改变源数组
  • 测试代码
const testArr = ['a',1,'b','c']
remove(testArr,1)
remove(testArr,'d')
  • 测试结果

image.png

9.hasOwn判断自身属性
  • 源码
const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
  val: object,
  key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)
  • 源码理解:利用Object.prototype.hasOwnProperty判断自身是否有某个属性
  • 测试代码
 const hasOwnProperty = Object.prototype.hasOwnProperty
 const hasOwn = (val,key) => hasOwnProperty.call(val, key)
 const testOwn = hasOwn(obj,'d')?? 'null' // false
10.isArray 判断是否是数组
  • 源码
export const isArray = Array.isArray
  • 源码理解:利用Array.isArray判断是否是数组
  • 测试代码
const isArray = Array.isArray
const isArr = isArray([])
const isArr1 = isArray('')
  • 测试结果

image.png

总结

下载了vue3源码,并尝试分析了其中10个shared包的工具函数,学会了动手调试代码并尝试生成map调试代码以及可以借鉴源码压缩代码的书写方式,在项目开发中也可以适当抽取相应的工具函数,由于时间原因只分析了部分源码,但是掌握分析源码的方法比分析数量多更重要,希望能坚持源码学习,有不对的地方欢迎赐教喔~

参考文献

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