refactor(core): use makeMap for faster string match checks (#282)

This commit is contained in:
Ayush Goyal 2019-10-15 21:41:08 +05:30 committed by Evan You
parent eb28d45933
commit 58fffcb987
6 changed files with 53 additions and 202 deletions

View File

@ -23,9 +23,9 @@ import {
parseJS,
walkJS
} from '../utils'
import { globalsWhitelist } from '@vue/shared'
import { isGloballyWhitelisted, makeMap } from '@vue/shared'
const literalsWhitelist = new Set([`true`, `false`, `null`, `this`])
const isLiteralWhitelisted = makeMap('true,false,null,this')
export const transformExpression: NodeTransform = (node, context) => {
if (node.type === NodeTypes.INTERPOLATION) {
@ -87,8 +87,8 @@ export function processExpression(
if (
!asParams &&
!context.identifiers[rawExp] &&
!globalsWhitelist.has(rawExp) &&
!literalsWhitelist.has(rawExp)
!isGloballyWhitelisted(rawExp) &&
!isLiteralWhitelisted(rawExp)
) {
node.content = `_ctx.${rawExp}`
} else if (!context.identifiers[rawExp]) {
@ -261,7 +261,7 @@ function shouldPrefix(identifier: Identifier, parent: Node) {
// not in an Array destructure pattern
!(parent.type === 'ArrayPattern') &&
// skip whitelisted globals
!globalsWhitelist.has(identifier.name) &&
!isGloballyWhitelisted(identifier.name) &&
// special case for webpack compilation
identifier.name !== `require` &&
// is a special keyword but parsed as identifier

View File

@ -1,7 +1,7 @@
import { ComponentInternalInstance, Data } from './component'
import { nextTick } from './scheduler'
import { instanceWatch } from './apiWatch'
import { EMPTY_OBJ, hasOwn, globalsWhitelist } from '@vue/shared'
import { EMPTY_OBJ, hasOwn, isGloballyWhitelisted } from '@vue/shared'
import { ExtractComputedReturns } from './apiOptions'
import { UnwrapRef, ReactiveEffect } from '@vue/reactivity'
import { warn } from './warning'
@ -106,6 +106,6 @@ if (__RUNTIME_COMPILE__) {
// this trap is only called in browser-compiled render functions that use
// `with (this) {}`
PublicInstanceProxyHandlers.has = (_: any, key: string): boolean => {
return key[0] !== '_' && !globalsWhitelist.has(key)
return key[0] !== '_' && !isGloballyWhitelisted(key)
}
}

View File

@ -1,176 +1,28 @@
const HTMLTagSet = new Set([
'html',
'body',
'base',
'head',
'link',
'meta',
'style',
'title',
'address',
'article',
'aside',
'footer',
'header',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'hgroup',
'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',
'rtc',
'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',
'menuitem',
'summary',
'content',
'element',
'shadow',
'template',
'blockquote',
'iframe',
'tfoot'
])
import { makeMap } from './makeMap'
const HTML_TAGS =
'html,body,base,head,link,meta,style,title,address,article,aside,footer,' +
'header,h1,h2,h3,h4,h5,h6,hgroup,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,rtc,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,menuitem,' +
'summary,content,element,shadow,template,blockquote,iframe,tfoot'
/**
* this list is intentionally selective, only covering SVG elements that may
* contain child elements.
*/
const SVGTagSet = new Set([
'svg',
'animate',
'circle',
'clippath',
'cursor',
'defs',
'desc',
'ellipse',
'filter',
'font-face',
'foreignObject',
'g',
'glyph',
'image',
'line',
'marker',
'mask',
'missing-glyph',
'path',
'pattern',
'polygon',
'polyline',
'rect',
'switch',
'symbol',
'text',
'textpath',
'tspan',
'use',
'view'
])
const SVG_TAGS =
'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +
'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view'
const VoidTagSet = new Set([
'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'link',
'meta',
'param',
'source',
'track',
'wbr'
])
const VOID_TAGS =
'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr'
export const isVoidTag = (tag: string) => VoidTagSet.has(tag)
export const isHTMLTag = (tag: string) => HTMLTagSet.has(tag)
export const isSVGTag = (tag: string) => SVGTagSet.has(tag)
export const isHTMLTag = makeMap(HTML_TAGS)
export const isSVGTag = makeMap(SVG_TAGS)
export const isVoidTag = makeMap(VOID_TAGS)

View File

@ -1,25 +1,8 @@
export const globalsWhitelist = new Set([
'Infinity',
'undefined',
'NaN',
'isFinite',
'isNaN',
'parseFloat',
'parseInt',
'decodeURI',
'decodeURIComponent',
'encodeURI',
'encodeURIComponent',
'Math',
'Number',
'Date',
'Array',
'Object',
'Boolean',
'String',
'RegExp',
'Map',
'Set',
'JSON',
'Intl'
])
import { makeMap } from './makeMap'
const GLOBALS_WHITE_LISTED =
'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' +
'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
'Object,Boolean,String,RegExp,Map,Set,JSON,Intl'
export const isGloballyWhitelisted = makeMap(GLOBALS_WHITE_LISTED)

View File

@ -1,6 +1,7 @@
export * from './patchFlags'
export * from './element'
export { globalsWhitelist } from './globalsWhitelist'
export { isGloballyWhitelisted } from './globalsWhitelist'
export { makeMap } from './makeMap'
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
? Object.freeze({})

View File

@ -0,0 +1,15 @@
/**
* Make a map and return a function for checking if a key
* is in that map.
*/
export function makeMap(
str: string,
expectsLowerCase?: boolean
): (key: string) => boolean {
const map: Record<string, boolean> = Object.create(null)
const list: Array<string> = str.split(',')
for (let i = 0; i < list.length; i++) {
map[list[i]] = true
}
return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val]
}