Vue 3 和 React 18 源码片段 1——shared 最近在整合框架时,又一次涉及到了 Vue/React 相关源码,也包括tarjs、raxjs、uni-app等跨端 DSL,优秀的框架通常都会注重性能和代码执行细节,因此借此文/系列开始进行整理。
代码来源
一、工具函数部分shared Vue:packages/shared 类型判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 export const objectToString = Object .prototype .toString export const toTypeString = (value : unknown ): string => objectToString.call (value) export const isArray = Array .isArray export const isMap = (val : unknown ): val is Map <any , any > => toTypeString (val) === '[object Map]' export const isSet = (val : unknown ): val is Set <any > => toTypeString (val) === '[object Set]' export const isDate = (val : unknown ): val is Date => val instanceof Date export const isFunction = (val : unknown ): val is Function => typeof val === 'function' export const isString = (val : unknown ): val is string => typeof val === 'string' export const isSymbol = (val : unknown ): val is symbol => typeof val === 'symbol' export const isObject = (val : unknown ): val is Record <any , any > => val !== null && typeof val === 'object' export const isPromise = <T = any >(val : unknown ): val is Promise <T> => { return isObject (val) && isFunction (val.then ) && isFunction (val.catch ) } export const isPlainObject = (val : unknown ): val is object => toTypeString (val) === '[object Object]'
数据信息判断 正整数字符串的判断:
1 2 3 4 5 6 export const isIntegerKey = (key : unknown ) => isString (key) && key !== 'NaN' && key[0 ] !== '-' && '' + parseInt (key, 10 ) === key
判断属性:
1 2 3 4 5 6 const hasOwnProperty = Object .prototype .hasOwnProperty export const hasOwn = ( val : object , key : string | symbol ): key is keyof typeof val => hasOwnProperty.call (val, key)
数据处理 对象拓展/合并属性:
1 2 export const extend = Object .assign
删除数组元素:
1 2 3 4 5 6 export const remove = <T>(arr : T[], el : T ) => { const i = arr.indexOf (el) if (i > -1 ) { arr.splice (i, 1 ) } }
字符串转数字:
1 2 3 4 5 export const toNumber = (val : any ): any => { const n = parseFloat (val) return isNaN (n) ? val : n }
新旧值比较:
1 2 3 4 export const hasChanged = (value : any , oldValue : any ): boolean => value !== oldValue && (value === value || oldValue === oldValue)
处理HTML转义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 const escapeRE = /["'&<>]/ function escapeHtml (string ) { const str = '' + string const match = escapeRE.exec (str) 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 = '"' break case 38 : escaped = '&' break case 39 : escaped = ''' break case 60 : escaped = '<' break case 62 : escaped = '>' 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 } const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g function escapeHtmlComment (src ) { return src.replace (commentStripRE, '' ) }
数据比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import { isArray, isDate, isObject } from './' function looseCompareArrays (a : any [], b : any [] ) { 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]) } return equal } export function looseEqual (a : any , b : any ): boolean { 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) if (aValidType || bValidType) { 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 } } } return String (a) === String (b) } export function looseIndexOf (arr : any [], val : any ): number { return arr.findIndex (item => looseEqual (item, val)) }
React:packages/shared React 的 shared 模块更为松散。
类型判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 declare function isArray (a: mixed ): boolean %checks (Array .isArray (a)); const isArrayImpl = Array .isArray ;function isArray (a: mixed ): boolean { return isArrayImpl (a); } export default isArray;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const MAYBE_ITERATOR_SYMBOL = Symbol .iterator ;const FAUX_ITERATOR_SYMBOL = '@@iterator' ;export function getIteratorFn (maybeIterable: ?any ): ?() => ?Iterator <*> { if (maybeIterable === null || typeof maybeIterable !== 'object' ) { return null ; } const maybeIterator = (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL ]) || maybeIterable[FAUX_ITERATOR_SYMBOL ]; if (typeof maybeIterator === 'function' ) { return maybeIterator; } return null ; }
数据信息判断 属性判断:
1 2 3 4 5 6 7 const hasOwnProperty = Object .prototype .hasOwnProperty ;export default hasOwnProperty;
环境信息判断 是否为DOM环境:
1 2 3 4 5 6 7 8 9 export const canUseDOM : boolean = !!( typeof window !== 'undefined' && typeof window .document !== 'undefined' && typeof window .document .createElement !== 'undefined' );
数据处理 对象拓展/合并:
1 2 3 4 5 6 7 8 const assign = Object .assign ;export default assign;
数据比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function is (x: any, y: any ) { return ( (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) ); } const objectIs : (x: any, y: any ) => boolean = typeof Object .is === 'function' ? Object .is : is; export default objectIs;
浅比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import is from './objectIs' ;import hasOwnProperty from './hasOwnProperty' ;function shallowEqual (objA: mixed, objB: mixed ): boolean { if (is (objA, objB)) { return true ; } if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false ; } const keysA = Object .keys (objA); const keysB = Object .keys (objB); if (keysA.length !== keysB.length ) { return false ; } for (let i = 0 ; i < keysA.length ; i++) { const currentKey = keysA[i]; if ( !hasOwnProperty.call (objB, currentKey) || !is (objA[currentKey], objB[currentKey]) ) { return false ; } } return true ; } export default shallowEqual;
二、标识处理
Vue:Patch flag Vue3的静态标记是diff性能优化的一个手段、主要是在与上次 virtual DOM 比较时,只对比带有Patch Flag的节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 export const enum PatchFlags { TEXT = 1 , CLASS = 1 << 1 , STYLE = 1 << 2 , PROPS = 1 << 3 , FULL_PROPS = 1 << 4 , HYDRATE_EVENTS = 1 << 5 , }
React:Symbol Flag React16起,源码中的大多判断处理就依赖与Symbol常量
1 2 3 4 5 6 7 8 9 export const REACT_ELEMENT_TYPE = Symbol .for ('react.element' );export const REACT_PORTAL_TYPE = Symbol .for ('react.portal' );export const REACT_FRAGMENT_TYPE = Symbol .for ('react.fragment' );export const REACT_STRICT_MODE_TYPE = Symbol .for ('react.strict_mode' );export const REACT_PROFILER_TYPE = Symbol .for ('react.profiler' );export const REACT_PROVIDER_TYPE = Symbol .for ('react.provider' );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 export type ReactProvider <T> = { $$typeof : Symbol | number, type : ReactProviderType <T>, key : null | string, ref : null , props : { value : T, children?: ReactNodeList , ... }, ... };
从 shared 包中就不难发现,shared承担高频工具函数、标识和通用处理,也是整个框架中至关重要的代码。
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com