深入解析Vue 组件命名那些事


Posted in Javascript onJuly 18, 2017

There are only two things in Computer Sciences: cache invalidation and naming things.

—— Phil Karlton

诚如上述所言,编程中变量命名确实令人很头疼。我们模糊地知道,Vue 组件的名称最好不要和原生 HTML 标签相同。为了避免重名,通常会在组件名称前面加上一个前缀,如 el-button、el-input、el-date-picker。这通常不会有什么问题,但有时候你的模板中混杂了原生 HTML 标签和组件标签,要想区分它们并不是很容易。

当我看到 Ant.design 的 React 组件是下面这样的时候,我感觉到一种自由的味道。首先,组件名可以使用原生 HTML 标签名,意味着再也不用较劲脑汁去规避原生 HTML 标签了。另外,这些组件都使用了首字母大写标签名,使它们很容易地与原生小写的 HTML 标签区分。

ReactDOM.render(
 <div>
  <Button type="primary">Primary</Button>
  <Input placeholder="Basic usage" />
  <Select defaultValue=".com" style={{ width: 70 }}>
   <Option value=".com">.com</Option>
   <Option value=".jp">.jp</Option>
   <Option value=".cn">.cn</Option>
   <Option value=".org">.org</Option>
  </Select>
 </div>,
 mountNode
);

受 Ant.design 的启发,我思考 Vue 组件命名能不能达到同样的效果呢?要找到答案,必须摸清楚 Vue 组件命名到底有什么限制。下面将分别从 Vue 1.0 和 Vue 2.0 来谈谈组件命名的机制:

Vue 1.0 组件命名机制

组件注册

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

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

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

检查名称是否与 HTML 元素或者 Vue 保留标签重名,不区分大小写。可以发现,只检查了常用的 HTML 元素,还有很多元素没有检查,例如 button、main。

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;

检查组件名称是否以字母开头,后面跟字母、数值或下划线。

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 匹配。

总结

到这里,我们可以知道 Vue 2.0 完全可以像 React 那样使用 PascalCase 形式的组件标签。对于 Vue 1.0,想以 PascalCase 形态写模板,尽量以全小写或者仅首字母大写形式注册组件,例如 <InputNumber> 组件,可以注册为 inputnumber 或者 Inputnumber。但是,如果你想在 1.0 中使用 Input、Select 这类与 HTML 元素重名的标签名,基本上是无解的,所以是时候尝试下 Vue 2.0 了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery使用hide方法隐藏页面上指定元素的方法
Mar 30 Javascript
Jquery数字上下滚动动态切换插件
Aug 08 Javascript
jQuery基于扩展实现的倒计时效果
May 14 Javascript
Bootstrap 实现查询的完美方法
Oct 26 Javascript
常用原生js自定义函数总结
Nov 20 Javascript
jquery实现手机端单店铺购物车结算删除功能
Feb 22 Javascript
Vue响应式添加、修改数组和对象的值
Mar 20 Javascript
JavaScript 中的 this 简单规则
Sep 19 Javascript
微信小程序之批量上传并压缩图片的实例代码
Jul 05 Javascript
vee-validate vue 2.0自定义表单验证的实例
Aug 28 Javascript
基于JavaScript canvas绘制贝塞尔曲线
Dec 25 Javascript
vue 数字翻牌器动态加载数据
Apr 20 Vue.js
js实现本地图片文件拖拽效果
Jul 18 #Javascript
详解使用angularjs的ng-options时如何设置默认值(初始值)
Jul 18 #Javascript
js实现移动端导航点击自动滑动效果
Jul 18 #Javascript
JS实现点击Radio动态更新table数据
Jul 18 #Javascript
Angularjs的启动过程分析
Jul 18 #Javascript
关于javascript sort()排序你可能忽略的一点理解
Jul 18 #Javascript
jQuery扇形定时器插件pietimer使用方法详解
Jul 18 #jQuery
You might like
投票管理程序
2006/10/09 PHP
PHP利用APC模块实现大文件上传进度条的方法
2015/10/29 PHP
Yii 2.0在Grid中格式化时间方法示例
2017/06/06 PHP
php 多个变量指向同一个引用($b = &amp;$a)用法分析
2019/11/13 PHP
关于JS中的闭包浅谈
2013/08/23 Javascript
详解js私有作用域中创建特权方法
2016/01/25 Javascript
javascript正则表达式总结
2016/02/29 Javascript
jquery插件bootstrapValidator表单验证详解
2016/12/15 Javascript
Nuxt.js踩坑总结分享
2018/01/18 Javascript
JS对象与json字符串相互转换实现方法示例
2018/06/14 Javascript
详解Vue组件之间通信的七种方式
2019/04/14 Javascript
深入学习JavaScript 高阶函数
2019/06/11 Javascript
基于JS正则表达式实现模板数据动态渲染(实现思路详解)
2020/03/07 Javascript
vue+canvas实现移动端手写签名
2020/05/21 Javascript
[04:04]DOTA2亚洲邀请赛比赛场馆&酒店全攻略
2017/03/23 DOTA
简单介绍Python中的JSON使用
2015/04/28 Python
用Python逐行分析文件方法
2019/01/28 Python
浅谈python标准库--functools.partial
2019/03/13 Python
python3 实现爬取TOP500的音乐信息并存储到mongoDB数据库中
2019/08/24 Python
Python sklearn中的.fit与.predict的用法说明
2020/06/28 Python
使用Keras构造简单的CNN网络实例
2020/06/29 Python
python代码实现图书管理系统
2020/11/30 Python
稀有和绝版书籍:Biblio.com
2017/02/02 全球购物
机电工程学生自荐信范文
2013/12/07 职场文书
大学竞选班长演讲稿
2014/04/24 职场文书
连带责任保证书
2014/04/29 职场文书
事业单位考核材料
2014/05/21 职场文书
高效课堂标语
2014/06/26 职场文书
计划生育证明书写要求
2014/09/17 职场文书
标准离婚协议书范文下载
2014/11/30 职场文书
体育教师个人工作总结
2015/02/09 职场文书
2016优秀大学生个人事迹材料范文
2016/03/01 职场文书
诚信高考倡议书
2019/06/24 职场文书
2019交通安全宣传标语集锦!
2019/06/28 职场文书
详解CocosCreator项目结构机制
2021/04/14 Javascript
CSS3实现360度循环旋转功能
2022/02/12 HTML / CSS