经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » HTML/CSS » HTML5 » 查看文章
Vue.js 源码分析(十五) 指令篇 v-bind指令详解
来源:cnblogs  作者:大沙漠  时间:2019/7/2 8:54:49  对本文有异议

指令是Vue.js模板中最常用的一项功能,它带有前缀v-,比如上面说的v-if、v-html、v-pre等。指令的主要职责就是当其表达式的值改变时,相应的将某些行为应用到DOM上,先介绍v-bind指令

v-bind用于动态地绑定一个或多个特性,或一个组件 prop 到表达式。

例如:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <div id="app"><a v-bind:href="href">链接</a></div>
  10. <script>
  11. Vue.config.productionTip=false;
  12. Vue.config.devtools=false;
  13. var app = new Vue({
  14. el:'#app',
  15. data:{href:"http://www.baidu.com"}
  16. })
  17. </script>
  18. </body>
  19. </html>

渲染为:

当我们点击整个超链接时将跳转到http://www.baidu.com,如果在控制台输入app.href="http://www.taobao.com"时:

点击按钮后就跳转到淘宝了

 

源码分析


 以上面的例子为例,Vue内部将DOM解析成AST对象的时候会执行parse()函数,该函数解析到a节点时会执行到processElement()函数,该函数先将key、ref、插槽、class和style解析完后就会执行processAttrs()函数,如下:

  1. function processAttrs (el) { //第9526行 对剩余的属性进行分析
  2. var list = el.attrsList;
  3. var i, l, name, rawName, value, modifiers, isProp;
  4. for (i = 0, l = list.length; i < l; i++) { //遍历每个属性
  5. name = rawName = list[i].name;
  6. value = list[i].value;
  7. if (dirRE.test(name)) { //如果该属性以v-、@或:开头,表示这是Vue内部指令
  8. // mark element as dynamic
  9. el.hasBindings = true;
  10. // modifiers
  11. modifiers = parseModifiers(name); //获取修饰符,比如:{native: true,prevent: true}
  12. if (modifiers) {
  13. name = name.replace(modifierRE, '');
  14. }
  15. if (bindRE.test(name)) { // v-bind //bindRD等于/^:|^v-bind:/ ,即该属性是v-bind指令时 例如:<a :href="url">你好</a>
  16. name = name.replace(bindRE, ''); //去掉指令特性,获取特性名,比如 href
  17. value = parseFilters(value); //对一些表达式做解析,例如{a|func1|func2}
  18. isProp = false; //是否绑定到DOM对象上
  19. if (modifiers) {
  20. if (modifiers.prop) { //如果有修饰符
  21. isProp = true;
  22. name = camelize(name);
  23. if (name === 'innerHtml') { name = 'innerHTML'; }
  24. }
  25. if (modifiers.camel) {
  26. name = camelize(name);
  27. }
  28. if (modifiers.sync) {
  29. addHandler(
  30. el,
  31. ("update:" + (camelize(name))),
  32. genAssignmentCode(value, "$event")
  33. );
  34. }
  35. }
  36. if (isProp || (
  37. !el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) //如果isProp为true
  38. )) { //则调用addProp()
  39. addProp(el, name, value);
  40. } else {
  41. addAttr(el, name, value); //否则调用addAttr()
  42. }
  43. } else if (onRE.test(name)) { // v-on //onRE等于/^@|^v-on:/,即该属性是v-on指令时
  44. name = name.replace(onRE, '');
  45. addHandler(el, name, value, modifiers, false, warn$2);
  46. } else { // normal directives //普通指令
  47. name = name.replace(dirRE, '');
  48. // parse arg
  49. var argMatch = name.match(argRE);
  50. var arg = argMatch && argMatch[1];
  51. if (arg) {
  52. name = name.slice(0, -(arg.length + 1));
  53. }
  54. addDirective(el, name, rawName, value, arg, modifiers);
  55. if ("development" !== 'production' && name === 'model') {
  56. checkForAliasModel(el, value);
  57. }
  58. }
  59. } else {
  60. /**/
  61. }
  62. }
  63. }

addAttr()函数用于在AST对象上新增一个attrs属性,如下:

  1. function addAttr (el, name, value) { //第6550行
  2. (el.attrs || (el.attrs = [])).push({ name: name, value: value }); //将{name: name,value: value}保存到el.attrs里面
  3. el.plain = false; //修正el.plain为false
  4. }

例子里执行到这里时对应的AST对象为:

执行generate()函数获取data$2时会判断是否有attrs属性,如果有则将属性保存到attrs上,例子里的实例渲染后render函数等于:

  1. if (el.attrs) {     //第10306行
  2. data += "attrs:{" + (genProps(el.attrs)) + "},";
  3. }

 genProps用于拼凑对应的值,如下:

  1. function genProps (props) { //第10537行 拼凑AST对象的属性或DOM属性用的
  2. var res = '';
  3. for (var i = 0; i < props.length; i++) { //遍历prps
  4. var prop = props[i]; //对应的值
  5. /* istanbul ignore if */
  6. {
  7. res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ","; //拼凑字符串
  8. }
  9. }
  10. return res.slice(0, -1)
  11. }

例子执行到这里渲染的render函数等于:

  1. with(this) {
        return _c('div', {
            attrs: {
                "id": "app"
            }
        },
        [_c('a', {
            attrs: {
                "href": href
            }
        },
        [_v("链接")])])
    }

这样当该函数执行的时候就会触发Vue实例的href属性,此时就会将渲染watcher作为href属性的订阅者了,当href修改时就会触发渲染watcher的重新渲染了。

最后当a标签整个DOM元素生成之后会触发attrs模块的create事件去设置href特性,如下:

  1. function updateAttrs (oldVnode, vnode) { //第6294行 更新attrs
  2. var opts = vnode.componentOptions; //获取vnode.componentOptions(组件才有)
  3. if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {
  4. return
  5. }
  6. if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { //如果在oldVnode和vnode上都没有定义attrs属性
  7. return //则直接返回,不做处理
  8. }
  9. var key, cur, old;
  10. var elm = vnode.elm;
  11. var oldAttrs = oldVnode.data.attrs || {};
  12. var attrs = vnode.data.attrs || {}; //新VNode的attrs属性
  13. // clone observed objects, as the user probably wants to mutate it
  14. if (isDef(attrs.__ob__)) {
  15. attrs = vnode.data.attrs = extend({}, attrs);
  16. }
  17. for (key in attrs) { //遍历新VNode的每个attrs
  18. cur = attrs[key];
  19. old = oldAttrs[key];
  20. if (old !== cur) {
  21. setAttr(elm, key, cur); //则调用setAttr设置属性
  22. }
  23. }
  24. // #4391: in IE9, setting type can reset value for input[type=radio]
  25. // #6666: IE/Edge forces progress value down to 1 before setting a max
  26. /* istanbul ignore if */
  27. if ((isIE || isEdge) && attrs.value !== oldAttrs.value) { //IE9的特殊情况
  28. setAttr(elm, 'value', attrs.value);
  29. }
  30. for (key in oldAttrs) {
  31. if (isUndef(attrs[key])) {
  32. if (isXlink(key)) {
  33. elm.removeAttributeNS(xlinkNS, getXlinkProp(key));
  34. } else if (!isEnumeratedAttr(key)) {
  35. elm.removeAttribute(key);
  36. }
  37. }
  38. }
  39. }
  40. function setAttr (el, key, value) { //设置el元素的key属性为value
  41. if (el.tagName.indexOf('-') > -1) { //如果el的标签名里含有-
  42. baseSetAttr(el, key, value);
  43. } else if (isBooleanAttr(key)) { //如果key是布尔类型的变量(比如:disabled、selected)
  44. // set attribute for blank value
  45. // e.g. <option disabled>Select one</option>
  46. if (isFalsyAttrValue(value)) {
  47. el.removeAttribute(key);
  48. } else {
  49. // technically allowfullscreen is a boolean attribute for <iframe>,
  50. // but Flash expects a value of "true" when used on <embed> tag
  51. value = key === 'allowfullscreen' && el.tagName === 'EMBED'
  52. ? 'true'
  53. : key;
  54. el.setAttribute(key, value);
  55. }
  56. } else if (isEnumeratedAttr(key)) { //如果key是这三个之一:contenteditable,draggable,spellcheck
  57. el.setAttribute(key, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true');
  58. } else if (isXlink(key)) {
  59. if (isFalsyAttrValue(value)) {
  60. el.removeAttributeNS(xlinkNS, getXlinkProp(key));
  61. } else {
  62. el.setAttributeNS(xlinkNS, key, value);
  63. }
  64. } else { //不满足上述的情况就直接调用baseSetAttr设置属性
  65. baseSetAttr(el, key, value);
  66. }
  67. }
  68. function baseSetAttr (el, key, value) { //设置el的key属性为value
  69. if (isFalsyAttrValue(value)) { //如果value是null或false
  70. el.removeAttribute(key); //则删除属性
  71. } else {
  72. // #7138: IE10 & 11 fires input event when setting placeholder on
  73. // <textarea>... block the first input event and remove the blocker
  74. // immediately.
  75. /* istanbul ignore if */
  76. if (
  77. isIE && !isIE9 &&
  78. el.tagName === 'TEXTAREA' &&
  79. key === 'placeholder' && !el.__ieph
  80. ) { 特殊情况
  81. var blocker = function (e) {
  82. e.stopImmediatePropagation();
  83. el.removeEventListener('input', blocker);
  84. };
  85. el.addEventListener('input', blocker);
  86. // $flow-disable-line
  87. el.__ieph = true; /* IE placeholder patched */
  88. }
  89. el.setAttribute(key, value); //直接调用原生DOMAPI setAttribute设置属性
  90. }
  91. }

原文链接:http://www.cnblogs.com/greatdesert/p/11104024.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号