(二)vue3-shared(vue3工具函数)

3,153 阅读6分钟

image.png 图里还差几个style、class。props转换的函数

参考

github.com/lxchuan12/v…

juejin.cn/post/699497…

www.yuque.com/ruochuan12/…

www.yuque.com/ruochuan12/…

准备

找到shared模块。

shared: Internal utilities shared across multiple packages (especially environment-agnostic utils used by both runtime and compiler packages).

且 可以匹配构建

The build script builds all public packages (packages without private: true in their package.json).

Packages to build can be specified with fuzzy matching:

# build runtime-core only
yarn build runtime-core

# build all packages matching "runtime"
yarn build runtime --all

所以,要执行的命令为

git clone https://github.com/vuejs/vue-next.git
cd vue-next
yarn build shared

---效果
$ yarn build shared
yarn run v1.22.10
$ node scripts/build.js shared

C:\Users\82454\Desktop\vue3\vue-next\packages\shared\src\index.ts 鈫?packages\shared\dist\shared.esm-bundler.js...
created packages\shared\dist\shared.esm-bundler.js in 3.6s

C:\Users\82454\Desktop\vue3\vue-next\packages\shared\src\index.ts 鈫?packages\shared\dist\shared.cjs.js...
created packages\shared\dist\shared.cjs.js in 2s

C:\Users\82454\Desktop\vue3\vue-next\packages\shared\src\index.ts 鈫?packages\shared\dist\shared.cjs.prod.js...
created packages\shared\dist\shared.cjs.prod.js in 1.8s


Done in 8.55s.

可以发现虽然打包出的package里有很多模块,但只有shareddist文件夹

接下来看 vue-next/packages/shared/dist/shared.esm-bundler.js

工具函数

  • 导出的函数有
export { EMPTY_ARR, EMPTY_OBJ, NO, NOOP, PatchFlagNames, babelParserDefaultPlugins, camelize, capitalize, def, escapeHtml, escapeHtmlComment, extend, generateCodeFrame, getGlobalThis, hasChanged, hasOwn, hyphenate$1 as hyphenate, invokeArrayFns, isArray$1 as isArray, isBooleanAttr, isDate$1 as isDate, isFunction, isGloballyWhitelisted, isHTMLTag, isIntegerKey, isKnownHtmlAttr, isKnownSvgAttr, isMap$1 as isMap, isModelListener, isNoUnitNumericStyleProp, isObject$1 as isObject, isOn, isPlainObject$1 as isPlainObject, isPromise, isReservedProp, isSSRSafeAttrName, isSVGTag, isSet$1 as isSet, isSpecialBooleanAttr, isString$1 as isString, isSymbol, isVoidTag, looseEqual, looseIndexOf, makeMap, normalizeClass, normalizeProps, normalizeStyle, objectToString$1 as objectToString, parseStringStyle, propsToAttrMap, remove, slotFlagsText, stringifyStyle, toDisplayString, toHandlerKey, toNumber, toRawType, toTypeString$1 as toTypeString };

EMPTY_ARR 空数组

Object.freeze

//如果是开发环境,返回一个不可修改的数组,否则返回一个空数组
const EMPTY_ARR = (process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];

EMPTY_OBJ 空对象

同上

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

NO 永远返回false的函数

const NO = () => false;

NOOP 返回空对象的函数

const NOOP = () => { };

// 很多库的源码中都有这样的定义函数,比如 jQuery、underscore、lodash 等
// 使用场景:1. 方便判断, 2. 方便压缩
// 1. 比如:(判断环境用的么?目前认知不够)
const instance = {
    render: NOOP
};

// 条件
const dev = true;
if(dev){
    instance.render = function(){
        console.log('render');
    }
}

// 可以用作判断。
if(instance.render === NOOP){
 console.log('i');
}
// 2. 再比如:
// 方便压缩代码
// 如果是 function(){} ,不方便压缩代码

PatchFlagNames 是否diff的标志?

/**
 * 不是很懂,应该是和diff判断有关的,2的指数,用 与或 来判断是否相应的diff吧应该
 * dev only flag -> name mapping
 */
const PatchFlagNames = {
    [1 /* TEXT */]: `TEXT`,
    [2 /* CLASS */]: `CLASS`,
    [4 /* STYLE */]: `STYLE`,
    [8 /* PROPS */]: `PROPS`,
    [16 /* FULL_PROPS */]: `FULL_PROPS`,
    [32 /* HYDRATE_EVENTS */]: `HYDRATE_EVENTS`,
    [64 /* STABLE_FRAGMENT */]: `STABLE_FRAGMENT`,
    [128 /* KEYED_FRAGMENT */]: `KEYED_FRAGMENT`,
    [256 /* UNKEYED_FRAGMENT */]: `UNKEYED_FRAGMENT`,
    [512 /* NEED_PATCH */]: `NEED_PATCH`,
    [1024 /* DYNAMIC_SLOTS */]: `DYNAMIC_SLOTS`,
    [2048 /* DEV_ROOT_FRAGMENT */]: `DEV_ROOT_FRAGMENT`,
    [-1 /* HOISTED */]: `HOISTED`,
    [-2 /* BAIL */]: `BAIL`
};

babelParserDefaultPlugins babel 解析默认插件

/** 啃不动
 * List of @babel/parser plugins that are used for template expression
 * transforms and SFC script transforms. By default we enable proposals slated
 * for ES2020. This will need to be updated as the spec moves forward.
 * Full list at https://babeljs.io/docs/en/next/babel-parser#plugins
 */
const babelParserDefaultPlugins = [
    'bigInt',
    'optionalChaining',
    'nullishCoalescingOperator'
];

makeMap 检查key是否存在

//isHTMLTag('html')==>true
/**
 * Make a map and return a function for checking if a key
 * is in that map.
 * IMPORTANT: all calls of this function must be prefixed with
 * \/\*#\_\_PURE\_\_\*\/
 * So that rollup can tree-shake them if necessary.
 */
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];
}
const HTML_TAGS = 'html,body,base,head,link,meta,style,title,address,article,aside,footer,' +
    'header,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,' +
    'figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,' +
    'data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,' +
    'time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,' +
    'canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,' +
    'th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,' +
    'option,output,progress,select,textarea,details,dialog,menu,' +
    'summary,template,blockquote,iframe,tfoot';
const isHTMLTag = /*#__PURE__*/ makeMap(HTML_TAGS);

camelize、hyphenate  转驼峰-缓存

replace

const cacheStringFunction$1 = (fn) => {
    const cache = Object.create(null);
    return ((str) => {
        const hit = cache[str];
        return hit || (cache[str] = fn(str));
    });
};
const camelizeRE = /-(\w)/g;//匹配 -加数字、大小写字母和下划线 如-a -B
const camelize = cacheStringFunction$1((str) => {
    //'aa-a-a'.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));返回 "aaAA"
    return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
});
//'aaAaaA'.replace(/\B([A-Z])/g, '-$1').toLowerCase() "aa-aaa-a"
const hyphenateRE$1 = /\B([A-Z])/g;
const hyphenate$1 = cacheStringFunction$1((str) => str.replace(hyphenateRE$1, '-$1').toLowerCase());
用法应该是这样:
camelize('aa-a') 得到 'aaA',第二次转化相同字符串可以不用正则替换而从缓存读取
camelize为return里的函数
{
  const cache = Object.create(null);
    return ((str) => {
        const hit = cache[str];
        return hit || (cache[str] = str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')););
    });
}

第一次执行camelize('aa-a')时,cache['aa-a']为空,给cache['aa-a']赋值'aaA'后返回'aaA'
第二次执行,cache['aa-a']有值,直接返回

capitalize 首字母大写-缓存

const capitalize = cacheStringFunction$1((str) => str.charAt(0).toUpperCase() + str.slice(1));
//'aaAAAA'.charAt(0).toUpperCase() + 'aaAAAA'.slice(1)  "AaAAAA"

两个函数用了同一个对象名cache缓存,但是函数重复构建,cache不会覆盖

toHandlerKey

const toHandlerKey = cacheStringFunction$1((str) =>
  str ? `on${capitalize(str)}` : ``
);

def 定义对象属性

JavaScript 对象所有API解析

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

escapeHtml 转义html符号

正则.exec(字符串)=》
	若有匹配到=》['字符串',index:匹配到的序列号]
	若无=》null
// 将字符串中的"'&<>转成相应的编码
const escapeRE = /["'&<>]/;
function escapeHtml(string) {
    const str = '' + string;
    const match = escapeRE.exec(str);
    //var str = "Visit W3School"; 
    //var patt = new RegExp("W3School","g");
    //console.log(patt.exec(str))
    //["W3School", index: 6, input: "Visit W3School", groups: undefined]
    if (!match) {
        return str;
    }
    let html = '';
    let escaped;
    let index;
    let lastIndex = 0;
    for (index = match.index; index < str.length; index++) {
        switch (str.charCodeAt(index)) {
            case 34: // "
                escaped = '&quot;';
                break;
            case 38: // &
                escaped = '&amp;';
                break;
            case 39: // '
                escaped = '&#39;';
                break;
            case 60: // <
                escaped = '&lt;';
                break;
            case 62: // >
                escaped = '&gt;';
                break;
            default:
                continue;
        }
        if (lastIndex !== index) {
            html += str.substring(lastIndex, index);
        }
        lastIndex = index + 1;
        html += escaped;
    }
    return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}

escapeHtmlComment  注释符号转义

//'<!-- xxxx -->'.replace(/^-?>|<!--|-->|--!>|<!-$/g,'') 输出 " xxxx "
// https://www.w3.org/TR/html52/syntax.html#comments
const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g;
function escapeHtmlComment(src) {
    return src.replace(commentStripRE, '');
}

extend 继承/合并

const extend = Object.assign;
// 例子:
const data = { name: '若川' };
const data2 = entend(data, { mp: '若川视野', name: '是若川啊' });
console.log(data); // { name: "是若川啊", mp: "若川视野" }
console.log(data2); // { name: "是若川啊", mp: "若川视野" }
console.log(data === data2); // true
//数组和数组
 extend([3,4,5], [1,2])=>[1,2,5]
//数组合到对象 [1,2]=>{0:1,1:2}
extend({test1:'test1',result1:'result1'}, [1,2,3]);
//{ '0': 1, '1': 2, '2': 3, test1: 'test1', result1: 'result1' }
//对象合到数组里 
[ 1, 2, 3, test2: 'test2', result2: 'result2' ]

generateCodeFrame

放弃,驾驭不住

function generateCodeFrame(source, start = 0, end = source.length) {
    // Split the content into individual lines but capture the newline sequence
    // that separated each line. This is important because the actual sequence is
    // needed to properly take into account the full line length for offset
    // comparison
    let lines = source.split(/(\r?\n)/);
    // Separate the lines and newline sequences into separate arrays for easier referencing
    const newlineSequences = lines.filter((_, idx) => idx % 2 === 1);
    lines = lines.filter((_, idx) => idx % 2 === 0);
    let count = 0;
    const res = [];
    for (let i = 0; i < lines.length; i++) {
        count +=
            lines[i].length +
                ((newlineSequences[i] && newlineSequences[i].length) || 0);
        if (count >= start) {
            for (let j = i - range; j <= i + range || end > count; j++) {
                if (j < 0 || j >= lines.length)
                    continue;
                const line = j + 1;
                res.push(`${line}${' '.repeat(Math.max(3 - String(line).length, 0))}|  ${lines[j]}`);
                const lineLength = lines[j].length;
                const newLineSeqLength = (newlineSequences[j] && newlineSequences[j].length) || 0;
                if (j === i) {
                    // push underline
                    const pad = start - (count - (lineLength + newLineSeqLength));
                    const length = Math.max(1, end > count ? lineLength - pad : end - start);
                    res.push(`   |  ` + ' '.repeat(pad) + '^'.repeat(length));
                }
                else if (j > i) {
                    if (end > count) {
                        const length = Math.max(Math.min(end - count, lineLength), 1);
                        res.push(`   |  ` + '^'.repeat(length));
                    }
                    count += lineLength + newLineSeqLength;
                }
            }
            break;
        }
    }
    return res.join('\n');
}

getGlobalThis 全局对象

像作用域链,也像原型链

const getGlobalThis = () => {
    return (_globalThis ||
        (_globalThis =
            typeof globalThis !== 'undefined'
                ? globalThis
                : typeof self !== 'undefined'
                    ? self
                    : typeof window !== 'undefined'
                        ? window
                        : typeof global !== 'undefined'
                            ? global
                            : {}));
};

hasChanged 变化

Object.is

//检测值的变化 和===的不同之处只有两个:一是+0不等于-0,二是 NaN 等于自身。
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
// -0===0 true 但1/-0为负无穷,1/0为正无穷,可以通过这个判断是否为正负零
if (!Object.is) {
  Object.is = function(x, y) {
    // SameValue algorithm
    if (x === y) { // Steps 1-5, 7-10
      // Steps 6.b-6.e: +0 != -0
      return x !== 0 || 1 / x === 1 / y;
    } else {
      // Step 6.a: NaN == NaN
      return x !== x && y !== y;
    }
  };
}

hasOwn 检测是否属性是否拥有

const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
// .call 则是函数里 this 显示指定以为第一个参数,并执行函数。
//Object.prototype.hasOwnProperty.call(x,y)


hasOwn({__proto__: { a: 1 }}, 'a') // false
hasOwn({ a: undefined }, 'a') // true
hasOwn({}, 'a') // false
hasOwn({}, 'hasOwnProperty') // false
hasOwn({}, 'toString') // false
// 是自己的本身拥有的属性,不是通过原型链向上查找的。

invokeArrayFns 执行数组里的函数

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

isArray 判断数组

const isArray = Array.isArray;

isArray([]); // true
const fakeArr = { __proto__: Array.prototype, length: 0 };
isArray(fakeArr); // false
fakeArr instanceof Array; // true
// 所以 instanceof 这种情况 不准确

isDate 是否Data对象

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

isFunction

const isFunction = (val) => typeof val === 'function';
// 判断数组有多种方法,但这个是比较常用也相对兼容性好的。

isIntegerKey key是否为数字

const isString$1 = (val) => typeof val === 'string';
const isIntegerKey = (key) => isString$1(key) &&
    key !== 'NaN' &&
    key[0] !== '-' &&
    '' + parseInt(key, 10) === key;
// 例子:
isInegerKey('a'); // false
isInegerKey('0'); // true
isInegerKey('011'); // false
isInegerKey('11'); // true
// 其中 parseInt 第二个参数是进制。
// 字符串能用数组取值的形式取值。
//  还有一个 charAt 函数,但不常用 
'abc'.charAt(0) // 'a'
// charAt 与数组形式不同的是 取不到值会返回空字符串'',数组形式取值取不到则是 undefined

isMap 判断是不是 Map 对象

const objectToString$1 = Object.prototype.toString;
const toTypeString$1 = (value) => objectToString$1.call(value);
const isMap = (val) => toTypeString(val) === '[object Map]';
//Object.prototype.toString.call(new Map())
// 例子:
const map = new Map();
isMap(map); // true

isObject 是否对象

const isObject$1 = (val) => val !== null && typeof val === 'object';
// 例子:
isObject(null); // false
isObject({name: '若川'}); // true
// 判断不为 null 的原因是 typeof null 其实 是 object

isPlainObject 是否纯粹对象

const isPlainObject$1 = (val) => toTypeString$1(val) === '[object Object]';
//纯粹不纯粹有啥区别??

isOn 判断是否on+非小写字母

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

// 例子:
isOn('onChange'); // true
isOn('onchange'); // false
isOn('on3change'); // true

isPromise

//判断是否是对象,是否有then和catch方法
const isPromise = (val) => {
    return isObject$1(val) && isFunction(val.then) && isFunction(val.catch);
};
// 判断是不是Promise对象
const p1 = new Promise(function(resolve, reject){
  resolve('若川');
});
isPromise(p1); // true

isSSRSafeAttrName 是否安全属性词

//isSSRSafeAttrName('/t') 
//VM2655:9 unsafe attribute name: /t
//false
const unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/;
const attrValidationCache = {};
function isSSRSafeAttrName(name) {
    if (attrValidationCache.hasOwnProperty(name)) {
        return attrValidationCache[name];
    }
    const isUnsafe = unsafeAttrCharRE.test(name);
    if (isUnsafe) {
        console.error(`unsafe attribute name: ${name}`);
    }
    return (attrValidationCache[name] = !isUnsafe);
}

isSet

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

isString

const isString = (val) => typeof val === 'string';
//toTypeString$1(val) === '[object String]'; 用这个应该也行

isSymbol

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

looseEqual 判断对象是否相同

不像Object.is要引用地址相同,属性和值相同即可

function looseCompareArrays(a, b) {
    if (a.length !== b.length)
        return false;
    let equal = true;
    for (let i = 0; equal && i < a.length; i++) {
        equal = looseEqual(a[i], b[i]);
        //这里高能,如果是我会if(!equal)return false,他这样写特别有逼格耶
    }
    return equal;
}

function looseEqual(a, b) {
    //引用地址相同,值也没啥好比的了
    if (a === b)
        return true;
    //若都是日期对象,比时间戳
    let aValidType = isDate(a);
    let bValidType = isDate(b);
    if (aValidType || bValidType) {
        return aValidType && bValidType ? a.getTime() === b.getTime() : false;
    }
    //若都是数组,比长度,再比值
    aValidType = isArray(a);
    bValidType = isArray(b);
    if (aValidType || bValidType) {
        return aValidType && bValidType ? looseCompareArrays(a, b) : false;
    }
    aValidType = isObject(a);
    bValidType = isObject(b);
    //若都是对象,比key数量,比key,比key值,key值比对跟深拷贝类似
    if (aValidType || bValidType) {
        /* istanbul ignore if: this if will probably never be called */
        if (!aValidType || !bValidType) {
            return false;
        }
        const aKeysCount = Object.keys(a).length;
        const bKeysCount = Object.keys(b).length;
        if (aKeysCount !== bKeysCount) {
            return false;
        }
        for (const key in a) {
            const aHasKey = a.hasOwnProperty(key);
            const bHasKey = b.hasOwnProperty(key);
            if ((aHasKey && !bHasKey) ||
                (!aHasKey && bHasKey) ||
                !looseEqual(a[key], b[key])) {
                return false;
            }
        }
    }
    //比字符串,String(对象):[object Object]
    return String(a) === String(b);
}

looseIndexOf 在数组找和参数值相同的序列号

function looseIndexOf(arr, val) {
    return arr.findIndex(item => looseEqual(item, val));
}

normalizeClass

//console.log(normalizeClass(["a c ", "b"])); ==>a c b
function normalizeClass(value) {
    let res = '';
    if (isString(value)) {
        res = value;
    }
    else if (isArray(value)) {
        // 类似数组 join(' ')
        for (let i = 0; i < value.length; i++) {
            const normalized = normalizeClass(value[i]);
            if (normalized) {
                res += normalized + ' ';
            }
        }
    }
    else if (isObject(value)) {
        for (const name in value) {
            if (value[name]) {
                res += name + ' ';
            }
        }
    }
    return res.trim();
}
normalizeClass 用于将标签属性 class 转化为标准的 classconst str = 'a b c'
const arr = [
  {
    a: true,
    b: false,
    c: true,
  },
]
const obj = {
  a: true,
  b: false,
  c: true,
}
console.log(normalizeClass(str)) // a b c
console.log(normalizeClass(arr)) // a c
console.log(normalizeClass(obj)) // a c

normalizeStyle

const listDelimiterRE = /;(?![^(]*\))/g;
const propertyDelimiterRE = /:(.+)/;
function parseStringStyle(cssText) {
    const ret = {};
    cssText.split(listDelimiterRE).forEach(item => {
        if (item) {
            const tmp = item.split(propertyDelimiterRE);
            tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
        }
    });
    return ret;
}
function normalizeStyle(value) {
    if (isArray(value)) {
        const res = {};
        for (let i = 0; i < value.length; i++) {
            const item = value[i];
            const normalized = isString(item)
                ? parseStringStyle(item)
                : normalizeStyle(item);
            if (normalized) {
                for (const key in normalized) {
                    res[key] = normalized[key];
                }
            }
        }
        return res;
    }
    else if (isString(value)) {
        return value;
    }
    else if (isObject(value)) {
        return value;
    }
}
normalizeStyle 用于将标签的内联 style 里的属性转化为标准的 style 属性。
const str = 'margin:10px;'
const arr = [
  {
    margin: '10px',
    borderWidth: '10px',
  },
]
const obj = {
  margin: '10px',
  borderWidth: '10px',
}
console.log(normalizeStyle(str)) // margin:10px;
console.log(normalizeStyle(arr)) // { margin: '10px', borderWidth: '10px' }
console.log(normalizeStyle(obj)) // { margin: '10px', borderWidth: '10px' }

normalizeProps

function normalizeProps(props) {
    if (!props)
        return null;
    let { class: klass, style } = props;
    if (klass && !isString(klass)) {
        props.class = normalizeClass(klass);
    }
    if (style) {
        props.style = normalizeStyle(style);
    }
    return props;
}
normalizeProps 用于 props 上的 class 和 style 属性转化。
const props = {
  class: [{ a: true, b: false, c: true }],
  style: [{ margin: '10px', borderWidth: '10px' }],
}
console.log(normalizeProps(props)) // { class: 'a c', style: { margin: '10px', borderWidth: '10px' } }

stringifyStyle

const hyphenateRE = /\B([A-Z])/g;
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase());
const isNoUnitNumericStyleProp = /*#__PURE__*/ makeMap(`animation-iteration-count,border-image-outset,border-image-slice,` +
    `border-image-width,box-flex,box-flex-group,box-ordinal-group,column-count,` +
    `columns,flex,flex-grow,flex-positive,flex-shrink,flex-negative,flex-order,` +
    `grid-row,grid-row-end,grid-row-span,grid-row-start,grid-column,` +
    `grid-column-end,grid-column-span,grid-column-start,font-weight,line-clamp,` +
    `line-height,opacity,order,orphans,tab-size,widows,z-index,zoom,` +
    // SVG
    `fill-opacity,flood-opacity,stop-opacity,stroke-dasharray,stroke-dashoffset,` +
    `stroke-miterlimit,stroke-opacity,stroke-width`);
function stringifyStyle(styles) {
    let ret = '';
    if (!styles || isString(styles)) {
        return ret;
    }
    for (const key in styles) {
        const value = styles[key];
        const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key);
        if (isString(value) ||
            (typeof value === 'number' && isNoUnitNumericStyleProp(normalizedKey))) {
            // only render valid values
            ret += `${normalizedKey}:${value};`;
        }
    }
    return ret;
}
stringifyStyle 用于将对象格式的样式转化为对象格式并且将样式属性转化。
const str = 'margin:10px;'
const obj = {
  margin: '10px',
  borderWidth: '10px',
}
console.log(stringifyStyle(str)) // ''
console.log(stringifyStyle(obj)) // margin:10px;border-width:10px;

propsToAttrMap

const propsToAttrMap = {
    acceptCharset: 'accept-charset',
    className: 'class',
    htmlFor: 'for',
    httpEquiv: 'http-equiv'
};

remove 数组删除某值

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

toDisplayString 展示相应的字符串形式

JSON.stringify

//toDisplayString({a:2,b:3})  
//{
//  "a": 2,
//  "b": 3
//}

const objectToString = Object.prototype.toString;
const replacer = (_key, val) => {
    // can't use isRef here since @vue/shared has no deps
    if (val && val.__v_isRef) {
        return replacer(_key, val.value);
    }
    else if (isMap(val)) {
        return {
            [`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val]) => {
                entries[`${key} =>`] = val;
                return entries;
            }, {})
        };
    }
    else if (isSet(val)) {
        return {
            [`Set(${val.size})`]: [...val.values()]
        };
    }
    else if (isObject(val) && !isArray(val) && !isPlainObject(val)) {
        return String(val);
    }
    return val;
};
const toDisplayString = (val) => {
    return val == null
        ? ''
        : isArray(val) || (isObject(val) && val.toString === objectToString)
            ? JSON.stringify(val, replacer, 2)
            : String(val);
};
console.log(
  toDisplayString(
    new Map([
      ["a", 2],
      ["b", 3],
    ])
  )
);
==>
{
  "Map(2)": {
    "a =>": 2,
    "b =>": 3
  }
}

toNumber 转数字

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

toNumber('111'); // 111
toNumber('a111'); // 'a111'
parseFloat('a111'); // NaN
isNaN(NaN); // true

其实 isNaN 本意是判断是不是 NaN 值,但是不准确的。 比如:isNaN('a')true。 所以 ES6 有了 Number.isNaN 这个判断方法,为了弥补这一个API

Number.isNaN('a')  // false
Number.isNaN(NaN); // true

toRawType 类似typeof

const toRawType = (value) => {
  // extract "RawType" from strings like "[object RawType]"
  return toTypeString$1(value).slice(8, -1);
};
//toRawType('');  'String'

小结

  • 缓存那里有点东西
  • 正则果然写工具函数还是必备的东西呢
  • 川哥还是yyds
  • replaceObject.freeze