<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          22個(gè)Vue 源碼中的工具函數(shù)

          共 7738字,需瀏覽 16分鐘

           ·

          2022-07-13 13:19

          來源 | https://segmentfault.com/a/1190000042073070


          前言

          在 vue 源碼中,封裝了很多工具函數(shù),學(xué)習(xí)這些函數(shù),一方面學(xué)習(xí)大佬們的實(shí)現(xiàn)方式,另一方面是溫習(xí)基礎(chǔ)知識(shí),希望大家在日常工作中,簡(jiǎn)單的函數(shù)也可以自己封裝,提高編碼能力。
          本次涉及的工具函數(shù) 1-16 在 Vue3 的源碼中,路徑是 core/packages/shared/src/index.ts
          17-22 在 Vue2 的源碼中,路徑是 vue/src/shared/util.ts

          1、 EMPTY_OBJ 空對(duì)象

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

          注意:
          Object.freeze 只能淺凍結(jié),如果屬性是對(duì)象,對(duì)屬性的屬性的修改就無法凍結(jié)了

          const obj = {    name: '張三',    info: {        a: 1,        b: 2    }};Object.freeze(obj);obj.name = '李四';console.log(obj); // { name: '張三', info: { a: 1, b: 2 } }obj.info.a = 66;console.log(obj); // { name: '張三', info: { a: 66, b: 2 } }

          源碼中的使用:


          可以看出基本都是作為初始化或者兜底使用,由此產(chǎn)生疑問:
          • 使用的地方有的是 options,有的是 props,不同地方用同一個(gè)對(duì)象,不會(huì)有問題么?
            首先,很多初始化操作,后續(xù)都會(huì)重新賦值,EMPTY_OBJ 只是作為占位使用。其次,因?yàn)?nbsp;Object.freeze 的原因,無法修改 EMPTY_OBJ,所以任何引用這個(gè)對(duì)象的地方,都不會(huì)受到影響。
          • 為什么判斷是 __DEV__(process.env.NODE_ENV !== 'production') 的時(shí)候才使用 Object.freeze?
            Object.freeze 更多的是 Vue 源碼開發(fā)者在調(diào)試時(shí)使用,可以通過報(bào)錯(cuò),防止對(duì)空對(duì)象操作,更快發(fā)現(xiàn)源碼問題。也因此,開發(fā)環(huán)境最終會(huì)避免了對(duì) EMPTY_OBJ 的賦值操作,所以在生產(chǎn)環(huán)境使用 Object.freeze 意義不大。

          2、EMPTY_ARR 空數(shù)組

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

          3、 NOOP 空函數(shù)

          const NOOP = () => {}
          依舊作為兜底和占位使用:

          4、 NO 永遠(yuǎn)返回 false 的函數(shù)

          const NO = () => false
          源碼中的使用:

          5、isOn 判斷字符串是不是 on 開頭,并且 on 后首字母不是小寫字母

          const onRE = /^on[^a-z]/;const isOn = (key) => onRE.test(key);// 示例isOn('onChange'); // trueisOn('onchange'); // falseisOn('on3change'); // true

          6、類型判斷

          const isArray = Array.isArray
          const isFunction = (val) => typeof val === 'function'const isString = (val) => typeof val === 'string'const isSymbol = (val) => typeof val === 'symbol'const isObject = (val) => val !== null && typeof val === 'object'const toTypeString = (value) => Object.prototype.toString.call(value)const isMap = (val) => toTypeString(val) === '[object Map]'const isSet = (val) => toTypeString(val) === '[object Set]'const isDate = (val) => toTypeString(val) === '[object Date]'const isPlainObject = (val) => Object.prototype.toString.call(val) === '[object Object]'// isPlainObject 判斷是不是普通對(duì)象(排除正則、數(shù)組、日期、new Boolean、new Number、new String 這些特殊的對(duì)象)isObject([]) // trueisPlainObject([]) // falseconst isPromise = (val) => { return isObject(val) && isFunction(val.then) && isFunction(val.catch)}

          7、 toRawType 提取數(shù)據(jù)原始類型

          const toRawType = (value) => {  return Object.prototype.toString.call(value).slice(8, -1)}// 示例toRawType('');  'String'toRawType([]);  'Array'
          源碼中的使用:


          8、isIntegerKey 判斷是不是數(shù)字型的字符串

          const isIntegerKey = (key) => isString(key) &&    key !== 'NaN' &&    key[0] !== '-' &&    '' + parseInt(key, 10) === key;  // 例子:isIntegerKey('a'); // falseisIntegerKey('0'); // trueisIntegerKey('011'); // falseisIntegerKey('11'); // trueisIntegerKey('-11'); // falseisIntegerKey(11); // falseisIntegerKey('NaN'); // false

          9、makeMap 將字符串分隔成 map,區(qū)分大小寫,返回一個(gè)函數(shù)來判斷 map 中是否含有某個(gè) 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];}

          10、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');    // ['', 'key', 'ref', 'ref_for', 'ref_key', 'onVnodeBeforeMount', 'onVnodeMounted', 'onVnodeBeforeUpdate', 'onVnodeUpdated', 'onVnodeBeforeUnmount', 'onVnodeUnmounted']
          // 示例isReservedProp('key'); // trueisReservedProp('onVnodeBeforeMount'); // trueisReservedProp(''); // trueisReservedProp(' '); // false
          如果有 /*#__PURE__*/ 這個(gè)標(biāo)志,說明他是純函數(shù),如果沒有調(diào)用它,打包工具會(huì)直接通 tree-shaking 把它刪除,減少代碼體積。

          11、 isBuiltInDirective 是否是內(nèi)置指令

          const isBuiltInDirective = /*#__PURE__*/ makeMap(  'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo')

          12、 cacheStringFunction 將函數(shù)變?yōu)榭删彺娼Y(jié)果的函數(shù)

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

          13、 camelize & hyphenate 連字符與駝峰互轉(zhuǎn)

          const camelizeRE = /-(\w)/g;const camelize = cacheStringFunction((str) => {    return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));});// 清爽版const camelize = str => str.replace(camelizeRE, (_, c) => {    return c ? c.toUpperCase() : '';});// 舉例:on-click-a => onClickAcamelize('on-click-a');const hyphenateRE = /\B([A-Z])/g;const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase());// 清爽版const hyphenate = str => str.replace(hyphenateRE, '-$1').toLowerCase();// 仿照 camelize 寫法const hyphenate = str => str.replace(hyphenateRE, (_, c) => {    return c ? `-${c.toLowerCase()}` : '';});// 舉例:onClickA => on-click-ahyphenate('onClickA');

          14、 hasChanged 判斷是不是有變化

          const hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue);// 示例hasChanged(1, 1); // falsehasChanged(1, 2); // truehasChanged(+0, -0); // falsehasChanged(NaN, NaN); // false// 場(chǎng)景:watch 監(jiān)測(cè)值是不是變化了// 擴(kuò)展 Object.is & ===Object.is(+0, -0); // false           Object.is(NaN, NaN); // true+0 === -0 // trueNaN === NaN // false

          15、invokeArrayFns 執(zhí)行數(shù)組里的函數(shù)

          const invokeArrayFns = (fns, arg) => {    for (let i = 0; i < fns.length; i++) {        fns[i](arg);    }};// 示例const arr = [    function(val){        console.log(val + '張三');    },    function(val){        console.log(val + '李四');    },    function(val){        console.log(val + '王五');    },]invokeArrayFns(arr, '我是:');
          源碼中的使用:

          16、 toNumber 轉(zhuǎn)數(shù)字

          const toNumber = (val) => {    const n = parseFloat(val);    return isNaN(n) ? val : n;};
          toNumber('111'); // 111toNumber('a111'); // 'a111'toNumber('11a11'); // '11'toNumber(NaN); // NaN// isNaN vs Number.isNaN// isNaN 判斷是不是數(shù)字 is Not a Number// Number.isNaN 判斷是不是 NaNisNaN(NaN); // trueisNaN('a'); // trueNumber.isNaN(NaN); // trueNumber.isNaN('a'); // false// Number.isNaN 的 polyfillif (!Number.isNaN) { Number.isNaN = function (n) { // 方法一 return (window.isNaN(n) && typeof n === 'number'); // 方法二 利用只有 NaN 不跟自己相等的特性 return n !== n; };}

          17、isPrimitive 是否為原始數(shù)據(jù)

          function isPrimitive(value) {  return (    typeof value === 'string' ||    typeof value === 'number' ||    typeof value === 'symbol' ||    typeof value === 'boolean'  )}

          18、 isValidArrayIndex 是否為有效的數(shù)組下標(biāo),整數(shù)并且不是無窮大

          function isValidArrayIndex(val) {  const n = parseFloat(String(val))  return n >= 0 && Math.floor(n) === n && isFinite(val)}// isFinite 如果參數(shù)是 NaN,正無窮大或者負(fù)無窮大,會(huì)返回 false,其他返回 true

          19、bind 能兼容的bind函數(shù)

          function polyfillBind(fn, ctx) {  function boundFn(a) {    const l = arguments.length    return l      ? l > 1        ? fn.apply(ctx, arguments)        : fn.call(ctx, a)      : fn.call(ctx)  }
          boundFn._length = fn.length return boundFn}function nativeBind(fn, ctx) { return fn.bind(ctx)}const bind = Function.prototype.bind ? nativeBind : polyfillBind

          20、 toArray 類數(shù)組轉(zhuǎn)化為數(shù)組

          function toArray(list, start) {  start = start || 0  let i = list.length - start  const ret = new Array(i)  while (i--) {    ret[i] = list[i + start]  }  return ret}

          21、 once 只執(zhí)行一次

          function once(fn) {  let called = false  return function () {    if (!called) {      called = true      fn.apply(this, arguments)    }  }}

          22、 isNative 是否為原生系統(tǒng)函數(shù)

          function isNative(Ctor) {  return typeof Ctor === 'function' && /native code/.test(Ctor.toString())}


          學(xué)習(xí)更多技能

          請(qǐng)點(diǎn)擊下方公眾號(hào)


          瀏覽 88
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  熟女视频一区二区三区国产 | H无码里番肉片在线播放 | 色逼逼逼逼 | 亚洲爽爽 | 99热亚洲精品 |