持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
**这是源码共读的第2期,链接:juejin.cn/post/708499…
前言
前面写了vue2和axios中使用的工具函数,学习这些通用的工具函数,可以让我们在日常开发中实际使用,而且可以锻炼自己抽离通用函数的能力,怎样组织自己的工具函数库。本篇文章来学习下vue3中的一些工具函数
有很多函数在vue2中已经学习过,本篇文章就介绍一些不曾学过,或有新体会的函数。
函数学习
noop 空函数
const NOOP = () => {}
为什么要定义一个空函数呢?作用有两个:1、方便判断。2、方便压缩。
isIntegerKey 判断是不是数字型的字符串key值
const isIntegerKey = (key) => isString(key) &&
key !== 'NaN' &&
key[0] !== '-' &&
'' + parseInt(key, 10) === key;
// 用于这种类型
const obj = {
1: 'a',
2: 'b',
3: 'c'
};
// 事例
isIntegerKey('a'); // false
isIntegerKey('0'); // true
isIntegerKey('011'); // false
isIntegerKey('NaN'); // false
- 判断的key是以10进制来进行计算的
makeMap 检查是否存在key值
传入一个以逗号分隔的字符串,生成一个map对象,并返回一个函数检测传入的key值是否在这个map中。第二个参数是否将key值小写进行判断。
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];
}
以这个函数为基础,产出具体的判断函数。
isGloballyWhitelisted、isSpecialBooleanAttr、isNoUnitNumericStyleProp、isKnownHtmlAttr、isHTMLTag、isReservedProp等等。
这里以isReservedProp,来看下使用
isReservedProp 判断是否是保留属性
const isReservedProp = /*#__PURE__*/ makeMap(
// the leading comma is intentional so empty string "" is also included
',key,ref,ref_for,ref_key,' +
'onVnodeBeforeMount,onVnodeMounted,' +
'onVnodeBeforeUpdate,onVnodeUpdated,' +
'onVnodeBeforeUnmount,onVnodeUnmounted');
这个判断方案值得学习,可能在业务中这种判断的情况不多,但不妨碍我们去学习理解如何更好的组织代码,如何去抽象我们的逻辑。
cacheStringFunction 缓存处理结果
const cacheStringFunction = (fn) => {
const cache = Object.create(null);
return ((str) => {
const hit = cache[str];
return hit || (cache[str] = fn(str));
});
};
- 这个处理也是很实用的,我们很多不变数据需要进行二次处理,利用闭包将结果缓存,减少冗余处理。
正则处理字符串
camelize 连字符转小驼峰
const camelizeRE = /-(\w)/g;
/**
* @private
*/
const camelize = cacheStringFunction((str) => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
});
// camelize('on-click'); // onClick
-(\w),\w是0-9a-zA-Z_ 数字、大小写字母和下划线组成- 这里利用cacheStringFunction缓存处理结果,相同入参下,第二次可以直接获取缓存的数据,而不用再次处理。
- 正则替换。
replace的回调函数中的参数_就是我们匹配到的结果,这里_是'_c'。而第二个参数c就是小括号捕获到的内容也就是'c'。将匹配的结果替换为捕获的值的大写,就将连字符转为小驼峰了。
hyphenateRE 驼峰转连字符
const hyphenateRE = /\B([A-Z])/g;
/**
* @private
*/
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase());
// hyphenate('onClick'); // on-click
- \B 非单词边界。这个正在
/\B([A-Z])/g就是匹配所有的 非单词边界+大写字母。 - 看上面的字符串
'onClick','nC'之间包含一个非单词边界,即匹配到的内容是:(无界)C; str.replace(hyphenateRE, '-$1')就是把匹配到的内容替换为前面带连字符的内容,也就是上面的(无界)C替换成'-C';- 这时拿到的内容就是
'on-Click',然后利用toLowerCase方法将所有字母都小写,最终得到'on-click';
什么是单词边界和非单词边界。
\b 单词边界
匹配一个单词的边界,也就是单词和空格的位置。不同则为界,界左和界右肯定不是相同类型的
- 边界只匹配位置,不匹配字符,也就是只是个边界。可以看上图粉色竖线标记的地方,
- 如上面的单词This左右各一个边界。
\B 非单词边界
匹配非单词边界,也就是不是单词和空格的位置
- 看上图,也就是粉色竖线标记的位置。
- 看This这个单词,匹配到3处非单词边界,都是字母挨着字母,字母和字母是同类,同类无界,但匹配非界就匹配到了。
capitalize 首字母大写
const capitalize = cacheStringFunction((str) => str.charAt(0).toUpperCase() + str.slice(1));
capitalize('click'); // Click
利用charAt方法获取字母的第一个字符转为大小,然后利用字符串的slice方法,将之后的字符截取拼接成最后的字符串。
toHandlerKey 处理事件key
const toHandlerKey = cacheStringFunction((str) => str ? `on${capitalize(str)}` : ``);
// toHandlerKey('click'); // onClick
hasChanged
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
这里用到了Object.is这个方法。它与严格比较(===)的行为基本一致。不同之处一个是+0和-0不相等,另一个就是NaN跟NaN相等。
Object.is(NaN, NaN); // true
NaN === NaN; // false
Object.is(+0, -0); // false
+0 === -0; // true
invokeArrayFns 顺序调用数组里的方法
const invokeArrayFns = (fns, arg) => {
for (let i = 0; i < fns.length; i++) {
fns[i](arg);
}
};
这里没有看源码中的使用,感觉是一种多函数的调度用法,通过数组遍历顺序执行。
收获
- 正则相关。了解到单词边界和非单词边界的正则含义及应用。
- Object.is() 方法的学习。
invokeArrayFns这个方法让我想到在业务中可以处理一些调度方案,一系列的函数,如何组织它们的执行,就可以利用数组的方式将其组织起来。