Vue组件命名的一些问题

原文:聊聊 Vue 组件命名那些事

组件注册

我们以一个最简单的例子来研究 Vue 组件的注册过程:

Vue.component('MyComponent', {
  template: '<div>hello, world</div>'
})

通过跟踪代码的执行过程,发现对组件的名称有两处检查。

  1. 检查名称是否与 HTML 元素或者 Vue 保留标签重名,不区分大小写。可以发现,只检查了常用的 HTML 元素,还有很多元素没有检查,例如buttonmain
if (type === 'component' && (commonTagRE.test(id) || reservedTagRE.test(id))) {
  warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + id);
}
// var commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i;
// var reservedTagRE = /^(slot|partial|component)$/i;
  1. 检查组件名称是否以字母开头,后面跟字母、数值或下划线
if (!/^[a-zA-Z][\w-]*$/.test(name)) {
        warn('Invalid component name: "' + name + '". Component names ' + 'can only contain alphanumeric characaters and the hyphen.');
      }

基于以上两点,可以总结出组件的命名规则为:组件名以字母开头,后面跟字母、数值或下划线,并且不与 HTML 元素或 Vue 保留标签重名。
然而我们注意到,在上面的检查中,不符合规则的组件名称是 warn 而不是 error,意味着检查并不是强制的。实际上,Vue 组件注册的名称是没有限制的。你可以用任何 JavaScript 能够表示的字符串,不管是数字、特殊符号、甚至汉字,都可以成功注册。

模板解析

虽然 Vue 组件没有命名限制,但是我们终究是要在模板中引用的,不合理的组件名可能会导致我们无法引用它。
为了弄清楚 Vue 是如何将模板中的标签对应到自定义组件的,我们以一段简单的代码说明:

       new Vue({
         el: '#app',
         template: '<my-component></my-component>'
       })

总体来说,模板解析分为两个过程:
首先,Vue 会将 template 中的内容插到 DOM 中,以方便解析标签。由于 HTML 标签不区分大小写,所以在生成的标签名都会转换为小写。例如,当你的 template<MyComponent></MyComponent> 时,插入 DOM 后会被转换为 <mycomponent></mycomponent>
然后,通过标签名寻找对应的自定义组件。匹配的优先顺序从高到低为:原标签名、camelCase化的标签名、PascalCase化的标签名。例如 <my-component>会依次匹配 my-component、myComponent、MyComponent。camelCase 和 PascalCase 的代码如下:

       var camelizeRE = /-(\w)/g;
       
       function camelize(str) {
         return str.replace(camelizeRE, toUpper);
       }
       
       function toUpper(_, c) {
         return c ? c.toUpperCase() : '';
       }
       
       function pascalize(str) {
         var camelCase = camelize(str);
         return camelCase.charAt(0).toUpperCase() + camelCase.slice(1)
       }

对于一个 Vue 新手,经常对以下示例代码不能正常运行感到非常疑惑:

       Vue.component('MyComponent', {
         template: '<div>hello, world</div>'
       })
       
       new Vue({
         el: '#app',
         template: '<MyComponent></MyComponent>'
       })

如果我们按照模板解析的过程推理,就很好解释了。模板 <MyComponent></MyComponent> 插入到 DOM 后会变成<mycomponent></mycomponent>。标签 mycomponent 匹配的组件依次为 mycomponent(原标签名)、mycomponent(camelCase形式)、Mycomponent(PascalCase形式),并没有匹配到注册的组件名 MyComponent,所以会报找不到组件 <mycomponent> 的警告。

命名限制

通过分析组件注册和模板解析的过程,发现 Vue 组件命名限制并没有我们想象得多。大家可以尝试一下各种命名,我试过 <a_=-*%按钮></a_=-*%按钮> 都可正常运行。
但是,并不意味着完全没有限制。由于在模板需要插入到 DOM 中,所以模板中的标签名必须能够被 DOM 正确地解析。主要有三种情况:一是完全不合法的标签名,例如 </>;二是与 HTML 元素重名会产生不确定的行为,例如使用 input 做组件名不会解析到自定义组件,使用 button 在 Chrome 上正常但在 IE 上不正常;三是与 Vue 保留的 slot、partial、component 重名,因为会优先以本身的意义解析,从而产生非预期的结果。
上述命名限制存在的根本原因,在于模板解析的过程依赖了 DOM。能不能对模板解析过程改进一下,使其不依赖于 DOM 呢?实际上,这正是 Vue 2.0 的主要改进,将模板解析过程使用 Virtual DOM 实现,使得组件命名更加灵活。

Vue 2.0 组件命名机制

组件注册

Vue 2.0 的组件注册过程与 Vue 1.0 基本相同,只是 HTML 标签和 Vue 保留标签范围有些不同:

    // 区分大小写
    var 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,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'
    );
    
    // 不区分大小写
    var isSVG = makeMap(
      'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font,' +
      'font-face,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
      'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
      true
    );
    
    var isReservedTag = function (tag) {
      return isHTMLTag(tag) || isSVG(tag)
    };
    
    // 区分大小写
    var isBuiltInTag = makeMap('slot,component', true);

虽然 HTML 元素重名警告的标签数大大增加了,但重要的是重名区分大小写,所以我们可以愉快 地使用 Input、Select、Option 等而不用担心重名。这个功劳属于 Vue 2.0 引入的 Virtual DOM。

模板解析

前面提到,Vue 2.0 相对于 1.0 的最大改进就是引入了 Virtual DOM,使模板的解析不依赖于 DOM。

使用 Virtual DOM 解析模板时,不必像 DOM 方式那样将模板中的标签名转成小写,而是原汁原味地保留原始标签名。然后,使用原始的标签名进行匹配组件。例如,<MyComponent></MyComponent> 不会转为为小写形式,直接以 MyComponent 为基础开始匹配。当然,匹配的规则与 1.0 是一样的,即依次匹配:原标签名、camelCase化的标签名、PascalCase化的标签名。

之前在 1.0 不能正常运行的示例代码,在 2.0 中可以正常运行了:

Vue.component('MyComponent', {
  template: '<div>hello, world</div>'
})

new Vue({
  el: '#app',
  template: '<MyComponent></MyComponent>'
})

在 Vue 1.0 和 2.0 中还有一种定义组件模板的方式,即使用 DOM 元素。在这种情况下,解析模板时仍然会将标签转为小写形式。所以下面的代码,在 1.0 和 2.0 均不能正常运行。

// index.html
<div id="app">
  <MyComponent></MyComponent>
</div>

// main.js
Vue.component('MyComponent', {
  template: '<div>hello, world</div>'
})

new Vue({
  el: '#app'
})

命名限制

Vue 2.0 中组件的命名限制与 1.0 的最大区别在于区分了大小写。总结一下就是:一是不使用非法的标签字符;二是不与 HTML 元素(区分大小写)或 SVG 元素(不区分大小写)重名;三是不使用 Vue 保留的 slot 和 component(区分大小写)。

除了以上三条,由于 Vue 2.0 内置了 KeepAlive、Transition、TransitionGroup 三个组件,所以尽量避免与这三个组件重名。但从另一方面讲,你也可以故意重名来实现一些特殊的功能。例如,keep-alive 的匹配顺序为 keep-alive、keepAlive、KeepAlive,所以我们可以注册一个 keep-alive 组件来拦截 KeepAlive 匹配。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 2,207评论 0 6
  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 5,048评论 0 29
  • 此文基于官方文档,里面部分例子有改动,加上了一些自己的理解 什么是组件? 组件(Component)是 Vue.j...
    陆志均阅读 3,825评论 5 14
  • 目录 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 UI组件 element★13489 ...
    余生社会阅读 19,729评论 7 233
  • 最近,日更想写点什么,就是想写那种非生活流水账的一些小感悟,但是想半天都写不出来,但是如果写生活,那倒是可以行云流...
    火然开朗阅读 228评论 0 1