疑惑
查了很多资料,对指令名的介绍都是一笔带过,只说是驼峰形式. 但是在实际使用时,经常遇到定义的指令名与指令标签对应不上的情况. 对指令名就感到非常疑惑. 定义时指令名是一种形式,使用时又是一种形式,两者怎么关联对应的?
分析源码
找不到资料,自己查查angular源码,一探究竟.
首先在angular.js文件,找到解析指令名的代码
switch(nodeType) { case 1: /* Element */ // use the node name://此处是解析标签形式的指令 addDirective(directives, directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective); // iterate over the attributes for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { var attrStartName = false; var attrEndName = false; attr = nAttrs[j]; if (!msie || msie >= 8 || attr.specified) { name = attr.name; value = trim(attr.value); // support ngAttr attribute binding ngAttrName = directiveNormalize(name); if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { name = snake_case(ngAttrName.substr(6), '-'); } var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); if (ngAttrName === directiveNName + 'Start') { attrStartName = name; attrEndName = name.substr(0, name.length - 5) + 'end'; name = name.substr(0, name.length - 6); } //此处是解析属性形式的指令名 nName = directiveNormalize(name.toLowerCase()); attrsMap[nName] = name; if (isNgAttr || !attrs.hasOwnProperty(nName)) { attrs[nName] = value; if (getBooleanAttrName(node, nName)) { attrs[nName] = true; // presence means true } } addAttrInterpolateDirective(node, directives, value, nName); addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, attrEndName); } }
上面一处是解析标签形式的指令名,调用了directiveNormalize(nodeName_(node).toLowerCase())
另一处是解析属性形式的指令名,调用了directiveNormalize(name.toLowerCase())
两处都使用了toLowerCase()
所以解析指令的第一步:将指令名转为小写.
继续看directiveNormalize()函数
var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;/** * Converts all accepted directives format into proper directive name. * All of these will become 'myDirective': * my:Directive * my-directive * x-my-directive * data-my:directive * * Also there is special case for Moz prefix starting with upper case letter. * @param name Name to normalize */function directiveNormalize(name) { return camelCase(name.replace(PREFIX_REGEXP, ''));}
它使用用了name.replace(PREFIX_REGEXP, ''),它的作用是去掉x和data开始的前缀
所以解析指令的第二步: 去掉以x-和data-开头的前缀(例如 x- x: x_ data- data: data_ 忽略大小写)
再继续看camelCase()函数
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;var MOZ_HACK_REGEXP = /^moz([A-Z])/;var jqLiteMinErr = minErr('jqLite');/** * Converts snake_case to camelCase. * Also there is special case for Moz prefix starting with upper case letter. * @param name Name to normalize */function camelCase(name) { return name. replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { return offset ? letter.toUpperCase() : letter; }). replace(MOZ_HACK_REGEXP, 'Moz$1');}
使用了两个replace替换字符,第二个replace不用管,重点看第一个replace,它是什么作用?
把它拿出来看看
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;function camelCase(name) { return name. replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { console.log("_:" + _) console.log("separator:" + separator) console.log("letter:" + letter) return offset ? letter.toUpperCase() : letter; }).}console.log("----my-directive")console.log(camelCase(my-directive));console.log("----mydirective")console.log(camelCase(my-directive));console.log("----mydirectiveworld")console.log(camelCase(my-directive));/* 输出内容----my-directive_:-dseparator:-dletter:dmyDirective----mydirectivemydirective----mydirectiveworldmydirectiveworld*/
可以看到第一个replace作用是生成驼峰指令名
所以解析指令的第三步:根据分割符(: - _)标记,将字符转换为驼峰形式
关于replace函数的说明,可以看看这篇文章
http://yongqing.is-programmer.com/posts/56305.html
更多指令名与指令对应的示例
以分割符"-"为例mymenu --> mymenu 正确mymenu --> myMenu 错误mymenu --> my-Menu 错误myMenu --> my-Menu 正确myMenu --> myMenu 错误myMenu --> mymenu 错误MyMenu --> x-MyMenu 正确MyMenu --> MyMenu 错误MyMenu --> mymenu 错误myProductsMenu --> my-Products-Menu 正确myProductsMenu --> myProductsMenu 错误myProductsMenu --> my-ProductsMenu 错误
总结
指令的匹配过程
将指令名转换为小写.
去掉以x-和data-开头的前缀(例如 x- x: x_ data- data: data_ 忽略大小写)
根据分割符(: - _),转换为驼峰形式
其实只要注意一点: 匹配过程以定义时的指令名为准,分割符是用来标识驼峰的(angular没有能力识别单词,分割符的一个作用是标识驼峰).