platforms/web/util 目录下的工具方法全解
index.js 文件
query
- 源码如下:
/**
* Query an element selector if it's not an element already.
*/
export function query (el: string | Element): Element {
if (typeof el === 'string') {
const selected = document.querySelector(el)
if (!selected) {
process.env.NODE_ENV !== 'production' && warn(
'Cannot find element: ' + el
)
return document.createElement('div')
}
return selected
} else {
return el
}
}
描述:查询元素
参数:
{String | Element} el
css选择符或者DOM元素
返回值:
{Element} el
DOM 元素源码分析:
如果参数是字符串,那么将该字符串作为 css
选择符并使用 document.querySelector()
函数查询元素,如果查找到该元素则返回该元素,否则在非生产环境下会打印警告信息并返回一个新创建的 div
。
如果参数不是一个字符串,则原封不动地返回参数。
attrs.js 文件
isReservedAttr
- 源码如下:
// these are reserved for web because they are directly compiled away
// during template compilation
export const isReservedAttr = makeMap('style,class')
- 描述:
isReservedAttr
函数是通过makeMap
生成的,用来检测一个属性是否是保留属性(web平台的保留属性),由源码可知,保留属性有两个:style
和class
。
mustUseProp
- 源码如下:
// attributes that should be using props for binding
const acceptValue = makeMap('input,textarea,option,select,progress')
export const mustUseProp = (tag: string, type: ?string, attr: string): boolean => {
return (
(attr === 'value' && acceptValue(tag)) && type !== 'button' ||
(attr === 'selected' && tag === 'option') ||
(attr === 'checked' && tag === 'input') ||
(attr === 'muted' && tag === 'video')
)
}
- 描述:用来检测一个属性在标签中是否要使用元素对象原生的
prop
进行绑定,注意:这里的prop
指的是元素对象的属性,而非Vue
中的props
概念。
举个例子,如下:
const el = document.createElement('div')
el.innerHTML // 这里的 el.innerHTML 属性就是元素对象的属性
参数:
{String} tag
标签名{String} type
标签的type
属性,多用于如<input type="button"/>
{String} attr
属性名
返回值:如果给定的属性
attr
在标签tag
中要使用元素对象原生的prop
进行绑定,那么就返回true
,否则返回false
。源码分析:
首先定义一个函数 acceptValue
,这是一个使用 makeMap
生成的函数,用来检测标签是否是以下标签之一:input,textarea,option,select,progress
。
mustUseProp
的函数体就是一个由多个判断组成的语句:
return (
// `input,textarea,option,select,progress` 这些标签的 value 属性都应该使用元素对象的原生的 prop 绑定(除了 type === 'button' 之外)
(attr === 'value' && acceptValue(tag)) && type !== 'button' ||
// option 标签的 selected 属性应该使用元素对象的原生的 prop 绑定
(attr === 'selected' && tag === 'option') ||
// input 标签的 checked 属性应该使用元素对象的原生的 prop 绑定
(attr === 'checked' && tag === 'input') ||
// video 标签的 muted 属性应该使用元素对象的原生的 prop 绑定
(attr === 'muted' && tag === 'video')
)
总结为:属于以下几种情况之一的,应该使用元素对象的原生 prop
绑定:
input,textarea,option,select,progress
这些标签的value
属性都应该使用元素对象的原生的prop
绑定(除了type === 'button'
之外)option
标签的selected
属性应该使用元素对象的原生的prop
绑定input
标签的checked
属性应该使用元素对象的原生的prop
绑定video
标签的muted
属性应该使用元素对象的原生的prop
绑定
class.js 文件
compat.js 文件
compat.js
文件的全部代码如下:
import { inBrowser } from 'core/util/index'
// check whether current browser encodes a char inside attribute values
let div
function getShouldDecode (href: boolean): boolean {
div = div || document.createElement('div')
div.innerHTML = href ? `<a href="\n"/>` : `<div a="\n"/>`
return div.innerHTML.indexOf(' ') > 0
}
// #3663: IE encodes newlines inside attribute values while other browsers don't
export const shouldDecodeNewlines = inBrowser ? getShouldDecode(false) : false
// #6828: chrome encodes content in a[href]
export const shouldDecodeNewlinesForHref = inBrowser ? getShouldDecode(true) : false
该文件主要导出两个变量,分别是 shouldDecodeNewlines
和 shouldDecodeNewlinesForHref
,这两个变量都是布尔值,那么这两个变量是干嘛的呢?我们看一个例子就知道了,假设我们有如下 DOM
:
<div id="link-box">
<!-- 注意 href 属性值,链接后面加了一个换行 -->
<a href="http://hcysun.me
">aaaa</a>
<!-- 注意 href 属性值,链接后面加了一个Tab -->
<a href="http://hcysun.me ">bbbb</a>
</div>
上面的 DOM
看上去貌似没有什么奇特的地方,关键点在于 <a>
标签的 href
属性,我们在第一个 <a>
标签的 href
属性值后面添加了换行符,在第二个 <a>
标签的 href
属性值后面添加了制表符。那么这么做会有什么影响呢?执行下面的代码就显而易见了:
console.log(document.getElementById('link-box').innerHTML)
上面的代码中我们打印了 id
为 link-box
的 innerHTML
,如下图:
注意,只有在 chrome
浏览器下才能获得如上效果,可以发现,在获取的内容中换行符和制表符分别被转换成了 

和 	
。实际上,这算是浏览器的怪癖行为。在 IE
中,不仅仅是 a
标签的 href
属性值,任何属性值都存在这个问题。这就会影响 Vue
的编译器在对模板进行编译后的结果,导致莫名奇妙的问题,为了避免这些问题 Vue
需要知道什么时候要做兼容工作,这就是 shouldDecodeNewlines
和 shouldDecodeNewlinesForHref
这两个变量的作用。
下面我们看一下具体实现,首先定义了一个函数 getShouldDecode
:
let div
function getShouldDecode (href: boolean): boolean {
div = div || document.createElement('div')
div.innerHTML = href ? `<a href="\n"/>` : `<div a="\n"/>`
return div.innerHTML.indexOf(' ') > 0
}
该函数的作用是判断当前浏览器是否会对属性值中所包含的换行符进行编码,如果是则返回真,否则返回假。其实现原理分三步:
- 1、创建一个
div
- 2、设置这个
div
的innerHTML
为<a href="\n"/>
或者<div a="\n"/>
- 3、获取该
div
的innerHTML
并检测换行符是否被编码
getShouldDecode
接收一个布尔值参数 href
,如果该参数为 true
意味着要监测的是 a
标签的 href
属性,否则检测任意属性。
有了上面的函数再实现 shouldDecodeNewlines
和 shouldDecodeNewlinesForHref
这两个变量就容易多了:
export const shouldDecodeNewlines = inBrowser ? getShouldDecode(false) : false
export const shouldDecodeNewlinesForHref = inBrowser ? getShouldDecode(true) : false
最终如果 shouldDecodeNewlines
为 true
,意味着 Vue
在编译模板的时候,要对属性值中的换行符或制表符做兼容处理。而 shouldDecodeNewlinesForHref
为 true
意味着 Vue
在编译模板的时候,要对 a
标签的 href
属性值中的换行符或制表符做兼容处理。当然,一切都是以在浏览器中为前提的,因为上面的代码中存在一个 inBrowser
的判断。
最后再啰嗦一句,为什么只在浏览器中才需要判断是否需要做此兼容处理呢?那是因为,只有完整版(包括编译器)的 Vue
才会遇到这个问题,因为只有完整版的 Vue
才会在浏览器中对模板进行编译,才有可能在获取模板的时候使用 innerHTML
获取模板内容。
element.js 文件
isHTMLTag
- 源码如下:
export const isHTMLTag = makeMap(
'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'
)
描述:检查是否是HTML标签
源码分析
isHTMLTag
是一个使用 makeMap
生成的函数,可以在 shared/util.js 文件工具方法全解 中查看 makeMap
方法。
isSVG
- 源码如下:
// this map is intentionally selective, only covering SVG elements that may
// contain child elements.
export const isSVG = makeMap(
'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',
true
)
描述:检查是否是SVG标签
源码分析
isSVG
是一个使用 makeMap
生成的函数,可以在 shared/util.js 文件工具方法全解 中查看 makeMap
方法。
isPreTag
- 源码如下:
export const isPreTag = (tag: ?string): boolean => tag === 'pre'
描述:检查给定的标签是否是
pre
标签源码分析
通过 tag === 'pre'
进行判断。
isReservedTag
- 源码如下:
export const isReservedTag = (tag: string): ?boolean => {
return isHTMLTag(tag) || isSVG(tag)
}
描述:检查给定的标签是否是保留的标签
源码分析
通过如下代码:
isHTMLTag(tag) || isSVG(tag)
判断一个标签是否是保留标签,我们可以知道,如果一个标签是 html
标签,或者是 svg
标签,那么这个标签即是保留标签。
getTagNamespace
- 源码如下:
export function getTagNamespace (tag: string): ?string {
if (isSVG(tag)) {
return 'svg'
}
// basic support for MathML
// note it doesn't support other MathML elements being component roots
if (tag === 'math') {
return 'math'
}
}
描述:获取元素(标签)的命名空间
参数:
{String} tag
标签名
返回值:
{String | undefined}
如果一个标签满足isSVG(tag)
,则返回'svg'
,如果标签为math
则返回'math'
,其他情况返回undefined
。