带你理解vue中的v-bind

目录
  • 一、v-bind关键源码分析
  • 1、v-bind化的属性统一存储在哪里:attrsMap与attrsList
    • 2、解析HTML,解析出属性集合attrs,在start回调中返回
    • 3、在start回调中创建ASTElement,createASTElement(... ,attrs, ...)
    • 4、创建后ASTElement会生成attrsList和attrsMap
    • 5、attrs的数据类型定义
    • 6、绑定属性获取函数
  • 二、如何获取v-bind的值
    • 1、v-bind:key源码分析
    • 2、v-bind:title源码分析
    • 3、v-bind:class源码分析
    • 4、、v-bind:style源码分析
    • 5、v-bind:text-content.prop源码分析
    • 6、v-bind的修饰符.camel .sync源码分析

一、v-bind关键源码分析

1、v-bind化的属性统一存储在哪里:attrsMap与attrsList

<p v-bind:title="vBindTitle"></p>

假设为p标签v-bind化了title属性,我们来分析title属性在vue中是如何被处理的。

vue在拿到这个html标签之后,处理title属性,会做以下几步:

  • 解析HTML,解析出属性集合attrs,在start回调中返回
  • 在start回调中创建ASTElement,createASTElement(... ,attrs, ...)
  • 创建后ASTElement会生成attrsListattrsMap

至于创建之后是如何处理v-bind:title这种普通的属性值的,可以在下文的v-bind:src源码分析中一探究竟。

2、解析HTML,解析出属性集合attrs,在start回调中返回


  function handleStartTag (match) {
    ...
    const l = match.attrs.length
    const attrs = new Array(l)
    for (let i = 0; i < l; i++) {
      const args = match.attrs[i]
      ...
      attrs[i] = {
        name: args[1],
        value: decodeAttr(value, shouldDecodeNewlines)
      }
    }
   ...
    if (options.start) {
      // 在这里上传到start函数
      options.start(tagName, attrs, unary, match.start, match.end)
    }
  }

3、在start回调中创建ASTElement,createASTElement(... ,attrs, ...)

// 解析HMTL
parseHTML(template, {
    ...
    start(tag, attrs, unary, start, end) {
        let element: ASTElement = createASTElement(tag, attrs, currentParent) // 注意此处的attrs
    }
})

4、创建后ASTElement会生成attrsList和attrsMap

// 创建AST元素
export function createASTElement (
  tag: string,
  attrs: Array<ASTAttr>, // 属性对象数组
  parent: ASTElement | void // 父元素也是ASTElement
): ASTElement { // 返回的也是ASTElement
  return {
    type: 1,
    tag,
    attrsList: attrs,
    attrsMap: makeAttrsMap(attrs),
    rawAttrsMap: {},
    parent,
    children: []
  }
}

5、attrs的数据类型定义

// 声明一个ASTAttr 属性抽象语法树对象 数据类型
declare type ASTAttr = {
  name: string; // 属性名
  value: any; // 属性值
  dynamic?: boolean; // 是否是动态属性
  start?: number;
  end?: number
};

6、绑定属性获取函数

 绑定属性获取函数getBindingAttr 和 属性操作函数 getAndRemoveAttr

getBindingAttr及其子函数getAndRemoveAttr在处理特定场景下的v-bind十分有用,也就是”v-bind如何处理不同的绑定属性“章节很有用。 这里将其列举出来供下文v-bind:key源码分析;v-bind:src源码分析;v-bind:class源码分析;v-bind:style源码分析;v-bind:dataset.prop源码分析源码分析参照。

export function getBindingAttr (
  el: ASTElement,
  name: string,
  getStatic?: boolean
): ?string {
  const dynamicValue =
    getAndRemoveAttr(el, ':' + name) ||
    getAndRemoveAttr(el, 'v-bind:' + name)
  if (dynamicValue != null) {
    return parseFilters(dynamicValue)
  } else if (getStatic !== false) {
    const staticValue = getAndRemoveAttr(el, name)
    if (staticValue != null) {
      return JSON.stringify(staticValue)
    }
  }
}
// note: this only removes the attr from the Array (attrsList) so that it
// doesn't get processed by processAttrs.
// By default it does NOT remove it from the map (attrsMap) because the map is
// needed during codegen.
export function getAndRemoveAttr (
  el: ASTElement,
  name: string,
  removeFromMap?: boolean
): ?string {
  let val
  if ((val = el.attrsMap[name]) != null) {
    const list = el.attrsList
    for (let i = 0, l = list.length; i < l; i++) {
      if (list[i].name === name) {
        list.splice(i, 1) // 从attrsList删除一个属性,不会从attrsMap删除
        break
      }
    }
  }
  if (removeFromMap) {
    delete el.attrsMap[name]
  }
  return val
}

二、如何获取v-bind的值

以下面代码为例从源码分析vue是如何获取v-bind的值。

会从记下几个场景去分析:

  • 常见的key属性
  • 绑定一个普通html attribute:title
  • 绑定classstyle
  • 绑定一个html DOM property:textContent
vBind:{
    key: +new Date(),
    title: "This is a HTML attribute v-bind",
    class: "{ borderRadius: isBorderRadius }"
    style: "{ minHeight: 100 + 'px' , maxHeight}"
    text-content: "hello vue v-bind"
}

<div
   v-bind:key="vBind.key"
   v-bind:title="vBind.title"
   v-bind:class="vBind.class"
   v-bind:style="vBind.style"
   v-bind:text-content.prop="vBind.textContent"
 />
</div>

1、v-bind:key源码分析

function processKey (el) {
  const exp = getBindingAttr(el, 'key')
   if(exp){
      ...
      el.key = exp;
   }
}

processKey函数中用到了getBindingAttr函数,由于我们用的是v-bind,没有用:,所以const dynamicValue = getAndRemoveAttr(el, 'v-bind:'+'key');,getAndRemoveAttr(el, 'v-bind:key')函数到attrsMap中判断是否存在 'v-bind:key',取这个属性的值赋为val并从从attrsList删除,但是不会从attrsMap删除,最后将 'v-bind:key'的值,也就是val作为dynamicValue,之后再返回解析过滤后的结果,最后将结果setprocessKey中将元素的key property。然后存储在segments中,至于segments是什么,在上面的源码中可以看到。

2、v-bind:title源码分析

title是一种“非vue特殊的”也就是普通的HTML attribute

function processAttrs(el){
     const list = el.attrsList;
     ...
     if (bindRE.test(name)) { // v-bind
        name = name.replace(bindRE, '')
        value = parseFilters(value)
        ...
        addAttr(el, name, value, list[i], ...)
      }
}
export const bindRE = /^:|^\.|^v-bind:/
export function addAttr (el: ASTElement, name: string, value: any, range?: Range, dynamic?: boolean) {
  const attrs = dynamic
    ? (el.dynamicAttrs || (el.dynamicAttrs = []))
    : (el.attrs || (el.attrs = []))
  attrs.push(rangeSetItem({ name, value, dynamic }, range))
  el.plain = false
}

通过阅读源码我们看出:对于原生的属性,比如title这样的属性,vue会首先解析出namevalue,然后再进行一系列的是否有modifiers的判断(modifier的部分在下文中会详细讲解),最终向更新ASTElementattrs,从而attrsListattrsMap也同步更新。

3、v-bind:class源码分析

cssclass在前端开发的展现层面,是非常重要的一层。 因此vue在对于class属性也做了很多特殊的处理。

function transformNode (el: ASTElement, options: CompilerOptions) {
  const warn = options.warn || baseWarn
  const staticClass = getAndRemoveAttr(el, 'class')
  if (staticClass) {
    el.staticClass = JSON.stringify(staticClass)
  }
  const classBinding = getBindingAttr(el, 'class', false /* getStatic */)
  if (classBinding) {
    el.classBinding = classBinding
  }
}

transfromNode函数中,会通过getAndRemoveAttr得到静态class,也就是class="foo";在getBindingAttr得到绑定的class,也就是v-bind:class="vBind.class"即v-bind:class="{ borderRadius: isBorderRadius }",将ASTElement的classBinding赋值为我们绑定的属性供后续使用。

4、、v-bind:style源码分析

style是直接操作样式的优先级仅次于important,比class更加直观的操作样式的一个HTML attribute。 vue对这个属性也做了特殊的处理。

function transformNode (el: ASTElement, options: CompilerOptions) {
  const warn = options.warn || baseWarn
  const staticStyle = getAndRemoveAttr(el, 'style')
  if (staticStyle) {
    el.staticStyle = JSON.stringify(parseStyleText(staticStyle))
  }
  const styleBinding = getBindingAttr(el, 'style', false /* getStatic */)
  if (styleBinding) {
    el.styleBinding = styleBinding
  }
}

transfromNode函数中,会通过getAndRemoveAttr得到静态style,也就是style="{fontSize: '12px'}";在getBindingAttr得到绑定的style,也就是v-bind:style="vBind.style"即v-bind:class={ minHeight: 100 + 'px' , maxHeight}",其中maxHeight是一个变量,将ASTElementstyleBinding赋值为我们绑定的属性供后续使用。

5、v-bind:text-content.prop源码分析

textContent是DOM对象的原生属性,所以可以通过prop进行标识。 如果我们想对某个DOM prop直接通过vue进行set,可以在DOM节点上做修改。

下面我们来看源码。

function processAttrs (el) {
  const list = el.attrsList
  ...
  if (bindRE.test(name)) { // v-bind
      if (modifiers) {
          if (modifiers.prop && !isDynamic) {
            name = camelize(name)
            if (name === 'innerHtml') name = 'innerHTML'
          }
       }
       if (modifiers && modifiers.prop) {
          addProp(el, name, value, list[i], isDynamic)
        }
   }
}
export function addProp (el: ASTElement, name: string, value: string, range?: Range, dynamic?: boolean) {
  (el.props || (el.props = [])).push(rangeSetItem({ name, value, dynamic }, range))
  el.plain = false
}
props?: Array<ASTAttr>;

通过上面的源码我们可以看出,v-bind:text-content.prop中的text-content首先被驼峰化为textContent(这是因为DOM property都是驼峰的格式),vue还对innerHtml错误写法做了兼容也是有心,之后再通过prop标识符,将textContent属性增加到ASTElement的props中,而这里的props本质上也是一个ASTAttr。

有一个很值得思考的问题:为什么要这么做?与HTML attribute有何异同?

  • 没有HTML attribute可以直接修改DOM的文本内容,所以需要单独去标识
  • 比通过js去手动更新DOM的文本节点更加快捷,省去了查询dom然后替换文本内容的步骤
  • 在标签上即可看到我们对哪个属性进行了v-bind,非常直观
  • 其实v-bind:title可以理解为v-bind:title.attr,v-bind:text-content.prop只不过vue默许不加修饰符的就是HTML attribute罢了

6、v-bind的修饰符.camel .sync源码分析

.camel仅仅是驼峰化,很简单。 但是.sync就不是这么简单了,它会扩展成一个更新父组件绑定值的v-on侦听器。

其实刚开始看到这个.sync修饰符我是一脸懵逼的,但是仔细阅读一下组件的.sync再结合实际工作,就会发现它的强大了。

<Parent
  v-bind:foo="parent.foo"
  v-on:updateFoo="parent.foo = $event"
></Parent>

在vue中,父组件向子组件传递的props是无法被子组件直接通过this.props.foo = newFoo去修改的。 除非我们在组件this.$emit("updateFoo", newFoo),然后在父组件使用v-on做事件监听updateFoo事件。若是想要可读性更好,可以在$emit的name上改为update:foo,然后v-on:update:foo

有没有一种更加简洁的写法呢??? 那就是我们这里的.sync操作符。 可以简写为:

<Parent v-bind:foo.sync="parent.foo"></Parent>

然后在子组件通过this.$emit("update:foo", newFoo);去触发,注意这里的事件名必须是update:xxx的格式,因为在vue的源码中,使用.sync修饰符的属性,会自定生成一个v-on:update:xxx的监听。

下面我们来看源码:

if (modifiers.camel && !isDynamic) {
  name = camelize(name)
}
if (modifiers.sync) {
  syncGen = genAssignmentCode(value, `$event`)
  if (!isDynamic) {
    addHandler(el,`update:${camelize(name)}`,syncGen,null,false,warn,list[i])
   // Hyphenate是连字符化函数,其中camelize是驼峰化函数
    if (hyphenate(name) !== camelize(name)) {
      addHandler(el,`update:${hyphenate(name)}`,syncGen,null,false,warn,list[i])
    }
  } else {
    // handler w/ dynamic event name
    addHandler(el,`"update:"+(${name})`,syncGen,null,false,warn,list[i],true)
  }
}

通过阅读源码我们可以看到: 对于v-bind:foo.sync的属性,vue会判断属性是否为动态属性。 若不是动态属性,首先为其增加驼峰化后的监听,然后再为其增加一个连字符的监听,例如v-bind:foo-bar.sync,首先v-on:update:fooBar,然后v-on:update:foo-bar。v-on监听是通过addHandler加上的。 若是动态属性,就不驼峰化也不连字符化了,通过addHandler(el,update:${name}, ...),老老实实监听那个动态属性的事件。

一句话概括.sync: .sync是一个语法糖,简化v-bind和v-on为v-bind.syncthis.$emit('update:xxx')。为我们提供了一种子组件快捷更新父组件数据的方式。

到此这篇关于带你理解vue中的v-bind的文章就介绍到这了,更多相关vue中的v-bind内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 在vue中v-bind使用三目运算符绑定class的实例

    如图所示: 通过动态的切换isOk就可以达到切换class的效果,isOk可以为一个表达式如num>1,如果data中num大于1,则显示classA,否则显示classB 以上这篇在vue中v-bind使用三目运算符绑定class的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • Vue中Class和Style实现v-bind绑定的几种用法

    项目开发中给元素添加/删除 class 是非常常见的行为之一, 例如网站导航都会给选中项添加一个 active 类用来区别选与未选中的样式,除了导航之外其他很多地方也都会用到这种方式处理选中与未选中. 除了设置 class 我们在项目中也经常设置元素的内联样式 style,在 jquery 时代我们大多数都是利用 addClass 与 removeClass 结合使用来处理 class 的添加/删除,利用 css() 方法设置/获取元素的内联样式. 那么在 vue 中 我们如何处理这类的效果呢?

  • 浅析Vue.js中v-bind v-model的使用和区别

    v-model 指令在表单控件元素上创建双向数据绑定,所谓双向绑定,指的就是我们在js中的vue实例中的data与其渲染的dom元素上的内容保持一致,两者无论谁被改变,另一方也会相应的更新为相同的数据 最基础的就是实现一个联动的效果 <body> <div class="app"> <span>Multiline message is:</span> <p>{{message}}</p> <br> &l

  • 一文读懂vue动态属性数据绑定(v-bind指令)

    v-bind的基本用法 一.本节说明 前面的章节我们学习了如何向页面html标签进行插值操作,那么如果我们想动态改变html标签的属性,该怎么办呢? 这就是我们这节开始要讲的内容v-bind. 二. 怎么做 ":"为v-bind的简写形式,也可称为语法糖 三. 效果 四. 深入 在上图中将a标签的href属性值设置为toutiao,VUE实例将自动去data里面寻找toutiao属性进行值绑定. 不只是a标签,所有的html标签属性都可以通过v-bind进行值绑定,然后通过改变数据动态

  • vue指令之表单控件绑定v-model v-model与v-bind结合使用

    一.表单控件绑定v-model v-model 双向数据绑定:一般用于表单元素,会忽略表单元素的value.checked.selected的初始值,且将Vue实例的数据作为数据来源. ① 单行文本框 input[type="text"] .多行文本框 textarea: v-model值绑定到value属性: <body> <div id="app"> <input type="text" v-model=&quo

  • vue基础之v-bind属性、class和style用法分析

    本文实例讲述了vue基础之v-bind属性.class和style用法.分享给大家供大家参考,具体如下: 一.属性 属性: v-bind:src="" width/height/title.... 简写: :src=""    推荐 <img src="{{url}}" alt="">    效果能出来,但是会报一个404错误 <img v-bind:src="url" alt="

  • 在vue中使用v-bind:class的选项卡方法

    //vue中的选项卡的实现,数据驱动dom,因此需要使用数据,来改变class: 详细见代码实现 <style> ul{overflow: hidden;} ul li{float: left;width: 150px;height: 35px;line-height: 35px;border: 1px solid red;list-style: none;cursor: pointer;} ul li.active{background-color: yellow} </style&g

  • 详解vue中v-bind:style效果的自定义指令

    自定义指令 1.什么是自定义指令 以 v- 为前缀,然后加上自己定义好的名字组成的一个指令就是自定义指令.为什么要有自定义指令呢?在有些时候,你仍然需要对普通的DOM元素进行底层的操作,这个时候就可以用到自定义指令. 2.自定义指令的语法 全局自定义指令 // 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时-- inserted: function (el) { // 聚焦元素 el.focus() } })

  • 利用Vue的v-for和v-bind实现列表颜色切换

    需求: 在页面上显示四个列表,初始时字体为黑色. 鼠标点击某一个列表时,该列表的颜色变为红色,其余列表仍为黑色. 代码实现: <!-- css --> <style> .red{ color: red; } </style> <!-- html --> <div id="app"> <ul> <li v-for="item,index in movies" :class="{re

  • Vue v-bind动态绑定class实例方法

    现在很多网站,都是希望动态切换class,更换css样式.下面我们使用Vue的v-bind绑定class v-bind动态绑定class 对象语法绑定(常用) red和size的值为true就在class显示red和size,false就不显示.下面有两种写法: <style> .red{ color: red; } .size{ font-size: 20px; } </style> <div id="app"> <ul> <!-

  • 详解vue中v-model和v-bind绑定数据的异同

    vue的模板采用DOM模板,也就是说它的模板可以当做DOM节点运行,在浏览器下不报错,绑定数据有三种方式,一种是插值,也就是{{name}}的形式,一种是v-bind,还有一种是v-model.{{name}}的形式比较好理解,就是以文本的形式和实例data中对应的属性进行绑定.比如: var app = new Vue({ el: '#app', template: '<div @click="toggleName">{{name}}</div>', data

  • 详解Vue的常用指令v-if, v-for, v-show,v-else, v-bind, v-on

    Vue.js的指令是以v-开头的,它们作用于HTML元素,指令提供了一些特殊的特性,将指令绑定在元素上时,指令会为绑定的目标元素添加一些特殊的行为,我们可以将指令看作特殊的HTML特性(attribute). Vue.js提供了一些常用的内置指令,如下: v-if指令 v-for指令 v-show指令 v-else指令 v-bind指令 v-on指令 1:v-if指令 v-if指令是条件渲染指令,它根据表达式的真假来删除和插入元素,它的基本语法如下: v-if="expression"

随机推荐