精华内容
下载资源
问答
  • Vue.jsVue基本指令Vue事件修饰符Vue绑定样式通过属性绑定设置class类样式通过属性绑定设置style行内样式 Vue基本指令 v-cloak : 解决插值表达式...v-bind:Vue提供用于绑定属性的指令,V-bind:要绑定的属性,也可...

    Vue基本指令

    v-cloak : 解决插值表达式闪烁的问题。
    v-text:没有出现闪烁问题,但是v-text会覆盖元素中原本的内容。但是插值表达式只会替换自己的占位符,不会把整个元素的内容清空。
    v-bind:Vue提供用于绑定属性的指令,V-bind:要绑定的属性,也可简写为:要绑定的属性,只能实现数据的单向绑定。
    v-on:事件绑定机制,同时要在Vue实例中写methods属性,它定义了当前vue实例所有可用的方法,可缩写为@。
    v-model:实现数据的双向绑定,可以实现表单元素和model数据间的双向绑定,只能运用在表单元素中。
    v-for:可循环普通数组,对象数组,对象。如果使用v-for迭代数字,值从1开始。在循环中key属性只能使用number和string,必须使用v-bind绑定属性

    <p v-for="(item, i) in list">索引值:{{ i }}---- 每一项:{{ item }}</p>
    
    <p v-for="(user,i) in list">ID: {{ user.id }}--- 名字:{{ user.name }}--- 索引:{{i}}</p>
    
    <p v-for="(val, key, i) in user">值是: {{ val }}--- 键是: {{ key }}---- 索引:{{ i }}</p>
    
    <p v-for="count in 10">这是第{{ count }}次循环</p>
    
    <p v-for="item in list" :key="item.id">
    

    v-if:每次都会重新删除或创建元素,有较高的切换性能消耗,如果元素可能永远也不会被显示出来被用户看到,则推荐使用v-if。
    v-show:每次不会重新进行DOM的删除和创建操作,只是切换了元素的display:none样式,有较高的初始渲染消耗,如果元素涉及到频繁的切换,推荐使用v-show。

    Vue事件修饰符

    .stop:组织事件冒泡。
    .prevent:组织默认行为。
    .capture:实现捕获触发事件的机制:从里到外触发。
    .self:实现只有点击当前元素的时候,才会触发事件处理函数。只会组织自身冒泡行为的触发,并不会组织所有元素的冒泡行为。
    .once:只触发一次点击事件。

    Vue绑定样式

    通过属性绑定设置class类样式

    1、直接传递一个数组,注意:这里的class需要v-bind做数据绑定

    <h1 :class="['thin','italic']">这是一个很大的h1,大到你无法想象!!!</h1>
    

    2、在数组中使用三元表达式

    <h1 :class="['thin','italic',flag?'active':'']">这是一个很大的h1,大到你无法想象!!!</h1>
    

    3、在数组中使用对象(键值对)代替三元表达式,提高代码的可读性

    <h1 :class="['thin','italic',{'active':flag}]">这是一个很大的h1,大到你无法想象!!!
    

    4、在为class使用v-bind绑定对象时,对象的属性是类名,可带也可不带引号;属性的值是一个标识符

    <h1 :class="{red:true, thin: true, italic: false, active: false}">这是一个很大的h1,大到你无法想象!!!</h1>
    

    通过属性绑定设置style行内样式

    1、直接使用:style形式

    <h1 :style="{color:'red','font-weight':200}">这是一个h1</h1>
    

    2、将样式对象定义到data中,引用到:style中

    data:{
                  styleObj1:{color:'red','font-weight':200},
                  styleObj2:{'font-style': 'italic'}  
                }
    
    <h1 :style="[styleObj1,styleObj2]">这是一个h1</h1>
    
    展开全文
  • 话不多说直接上干货vue中的指令分为内置指令和自定义指令,顾名思义内置指令是vue框架中带有的不需要用户去定义的指令;而vue自定义指令是由用户按照vue框架提供的方法自己编写的指令。本文将介绍vue的内置指令以及...

    d6a6217157c02f8d902e3f340db3b4a8.png

    话不多说直接上干货

    vue中的指令分为内置指令自定义指令,顾名思义内置指令是vue框架中带有的不需要用户去定义的指令;而vue自定义指令是由用户按照vue框架提供的方法自己编写的指令。

    本文将介绍vue的内置指令以及指令在vue.js2.0中的变化2个方面。

    正文:

    1.V-bind:

    V-bind主要用于动态绑定DOM元素属性,即元素属性值是由vue示例中的data决定

    <img v-bind:src='img'/>
    new vue({
        data:{
            img:'/img.jpg'
        }
    })
    以上V-bind还可以简写为:
    <img :src='img'/>

    V-bind还有三种修饰符(.sync/.once/.camel)。

    .sync用于组件props属性,即父组件传递子组件值的双向绑定,选择后,无论在那个组件中进行修改,其他组件中这个值也会随之更新。

    .once同.sync一样用于props属性,但.once进行的是单次绑定,和双向绑定正好相反,单次绑定是父组件将绑定数据传递给子组件之后,子组件单独维护这份数据,被传递的数据从此和父组件再无关系,父组件的数据发生变化也不会影响子组件中的数据。

    .camel是将绑定的组件转回驼峰命名。(注:只能用于普通的html属性绑定,通常会用svg标签下的属性。

    在Vue2.0之后的版本中,修饰符.sync和.once将不再被使用,并且规定组件之间仅能单向传递,子组件和父组件之间的数据修改只能通过事件处理。

    2.V-model:

    V-model用于动态绑定表单控件之间的值和vue实例之间的数据

    <input type = 'text' v-model = "msg"/>
    new Vue({
        data:{
            msg:"data"
        }
    })

    其中Vue.js还为表单提供了一些参数,有如下特征:

    ①.lazy:默认情况下V-model在input事件中同步输入框值与数据,加入lazy属性后会在change事件中同步。

    <input type = 'text' v-model = "msg" lazy/>

    ②.number:会自动将用户输入转为number类型(如果转换结果为NaN则返回原值)

    <input type = 'text' v-model = "msg" number/>

    注意:在vue2.0之后的版本中取消了lazy和number作为参数,而改用修饰符modifier来代替

    <input type = 'text' v-model.lazy="query"/> <input type = 'text' v-model.number="age"/>

    并且新增加了trim修饰符,用于去掉输入值的首尾空格

    <input type = 'text' v-model.trim="name"/>

    3.V-if、V-else、V-show:

    这三个指令主要用于根据条件来展示templete中的内容,其中重点是v-if和v-show的区别:

    v-if在FALSE的情况下并不会对模板内容进行编译,而v-show则是编译好之后对模板内容进行隐藏。

    因此v-if在切换值时的消耗要比v-show高,而在初始值为FALSE的情况下,v-if的初始渲染要稍快。

    4.V-for:

    v-for指令主要用于列表渲染,接受的数据形式为一个数组,v-for将重复渲染DOM元素及其内部的子元素,,并且可以通过设置别名的方式获取数组内部数据渲染到节点中。

    <ul>
            <li v-for="item in items">
                     <div>{{item}}</div>
            </li>
    </ul>

    值得注意的是,v-for除了可以遍历数组之外,还可以遍历对象:

    在v-for作用于内可以访问内置变量key,也可以使用(key,value)形式自定义key变量

    <li v-for="item in items"key="item">
        {{item}}
    </li>

    最后v-for还接受单个整数组,用作循环次数:

    <li v-for="n in 5">
        {{n}}
    </li>

    5.V-on

    v-on主要用于事件绑定,用法如下:

    <button v-on:click="sayFrom('from param')">say</button>
    
    method:{
         sayFrom:founction(from){
              alter(this.msg + ' ' + from);
          }
    }

    同一元素也可以通过v-on绑定多个事件函数并顺序执行

    <div v-on:click="sayFrom('first')"  v-on:click="sayFrom('second')">

    Vue.js为v-on提供了多个修饰符,方便我们处理一些DOM事件细节:

    ① .stop等同于调用event.stopPropagation()。

    ② .prevent等同于调用event.preventDefault()。

    ③ .capture使用capture模式添加事件监听器。

    ④ .self只当事件是从监听事件本身触发时才触发回调

    值得注意的是,除了事件修饰符之外,v-on还使用了按键修饰符,方便监听键盘的按键事件,例:

    <input v-on:keyup.13="submit"/>
    监听回车键(回车键keycodes为13),用于常用的回车键提交。

    6.V-text

    V-text的作用于文本插值比较相似,接受的参数也是string类型,文本插值本身就会被编译成一个V-text指令,但与文本插值不同的是V-text需要绑定在一个元素上。

    <span V-text="msg"></span>

    7.v-HTML

    与V-text作用大致相同,用于更新innerHTML,,参数类型也为string,接受的字符串不会进行编译,直接按照普通的HTML代码处理。文本插值{{{HTML}}}也会编译成v-HTML再进行处理。

    <div v-HTML="html"/>
    <div>{{{ html }}}</div>

    8.v-el

    简单地说v-el为DOM元素提供了一个索引,使得我们可以直接访问DOM元素。在语法上可以表示为被v-el绑定的元素可以被实例的$els属性调用;也可以在内部通过this调用。较为简单,不再细讲。

    9.v-ref

    v-ref是作用于子组件上的v-el,并且父组件可以通过实例内部的$refs来访问被v-ref绑定的子组件的DOM元素。

    10.v-pre

    v-pre命令相对简单,跳过被绑定的元素的编译,例

    <div v-pre>{{ msg }}</div>
    
    data:{
        msg: 'Hello'
    }

    则输出{{msg}}。

    11.v-once

    v-once是vue2.0之后的新增指令,用于标明元素或组件只渲染一次,即随后发生的数据的变化或更新,该元素都不会再进行编译和渲染。其作用是很大程度的提升了更新行为中页面的性能,可以略过一些不需要变化的步骤。

    以上内容出自笔者假期的自我修炼,至于为什么学习vue框架。。。当然是因为他火,在没有开始深入之前感觉风平浪静,越深入感觉越吃力,果然基础不牢,地动山摇,这篇文章也是靠着个人浅显的理解总结的,如果有什么不对的地方还请大佬评论指出。

    展开全文
  • Vue事件绑定原理

    2020-09-07 21:03:00
    v-on与@用于绑定事件监听器,事件类型由参数指定,表达式可以是一个方法名字或一个内联语句,如果没有修饰符也可以省略,用在普通元素上时,只能监听原生DOM事件,用在自定义元素组件上时,也可以监听子组件触发...

    Vue事件绑定原理

    Vue中通过v-on或其语法糖@指令来给元素绑定事件并且提供了事件修饰符,基本流程是进行模板编译生成AST,生成render函数后并执行得到VNodeVNode生成真实DOM节点或者组件时候使用addEventListener方法进行事件绑定。

    描述

    v-on@用于绑定事件监听器,事件类型由参数指定,表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略,用在普通元素上时,只能监听原生DOM事件,用在自定义元素组件上时,也可以监听子组件触发的自定义事件,在监听原生DOM事件时,方法以事件为唯一的参数,如果使用内联语句,语句可以访问一个$event property:v-on:click="handle('param', $event)",自2.4.0开始v-on同样支持不带参数绑定一个事件或监听器键值对的对象,注意当使用对象语法时,是不支持任何修饰器的。

    修饰符

    • .stop: 调用event.stopPropagation(),即阻止事件冒泡。
    • .prevent: 调用event.preventDefault(),即阻止默认事件。
    • .capture: 添加事件侦听器时使用capture模式,即使用事件捕获模式处理事件。
    • .self: 只当事件是从侦听器绑定的元素本身触发时才触发回调。
    • .{keyCode | keyAlias}: 只当事件是从特定键触发时才触发回调。
    • .native: 监听组件根元素的原生事件,即注册组件根元素的原生事件而不是组件自定义事件的。
    • .once: 只触发一次回调。
    • .left(2.2.0): 只当点击鼠标左键时触发。
    • .right(2.2.0): 只当点击鼠标右键时触发。
    • .middle(2.2.0): 只当点击鼠标中键时触发。
    • .passive(2.3.0): 以{ passive: true }模式添加侦听器,表示listener永远不会调用preventDefault()

    普通元素

    <!-- 方法处理器 -->
    <button v-on:click="doThis"></button>
    
    <!-- 动态事件 (2.6.0+) -->
    <button v-on:[event]="doThis"></button>
    
    <!-- 内联语句 -->
    <button v-on:click="doThat('param', $event)"></button>
    
    <!-- 缩写 -->
    <button @click="doThis"></button>
    
    <!-- 动态事件缩写 (2.6.0+) -->
    <button @[event]="doThis"></button>
    
    <!-- 停止冒泡 -->
    <button @click.stop="doThis"></button>
    
    <!-- 阻止默认行为 -->
    <button @click.prevent="doThis"></button>
    
    <!-- 阻止默认行为,没有表达式 -->
    <form @submit.prevent></form>
    
    <!--  串联修饰符 -->
    <button @click.stop.prevent="doThis"></button>
    
    <!-- 键修饰符,键别名 -->
    <input @keyup.enter="onEnter">
    
    <!-- 键修饰符,键代码 -->
    <input @keyup.13="onEnter">
    
    <!-- 点击回调只会触发一次 -->
    <button v-on:click.once="doThis"></button>
    
    <!-- 对象语法 (2.4.0+) -->
    <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
    

    组件元素

    <!-- 自定义事件 -->
    <my-component @my-event="handleThis"></my-component>
    
    <!-- 内联语句 -->
    <my-component @my-event="handleThis('param', $event)"></my-component>
    
    <!-- 组件中的原生事件 -->
    <my-component @click.native="onClick"></my-component>
    

    分析

    Vue源码的实现比较复杂,会处理各种兼容问题与异常以及各种条件分支,文章分析比较核心的代码部分,精简过后的版本,重要部分做出注释,commit idef56410

    编译阶段

    Vue在挂载实例前,有相当多的工作是进行模板的编译,将template模板进行编译,解析成AST树,再转换成render函数,而在编译阶段,就是对事件的指令做收集处理。
    template模板中,定义事件的部分是属于XMLAttribute,所以收集指令时需要匹配Attributes以确定哪个Attribute是属于事件。

    // dev/src/compiler/parser/index.js line 23
    export const onRE = /^@|^v-on:/
    export const dirRE = process.env.VBIND_PROP_SHORTHAND
      ? /^v-|^@|^:|^\.|^#/
      : /^v-|^@|^:|^#/
    // ...
    const dynamicArgRE = /^\[.*\]$/
    // ...
    export const bindRE = /^:|^\.|^v-bind:/
      
    // dev/src/compiler/parser/index.js line 757
    function processAttrs (el) {
      const list = el.attrsList
      let i, l, name, rawName, value, modifiers, syncGen, isDynamic
      for (i = 0, l = list.length; i < l; i++) {
        name = rawName = list[i].name
        value = list[i].value
        if (dirRE.test(name)) { // 匹配指令属性
          // mark element as dynamic
          el.hasBindings = true
          // modifiers
          modifiers = parseModifiers(name.replace(dirRE, '')) // 将修饰符解析
          // support .foo shorthand syntax for the .prop modifier
          if (process.env.VBIND_PROP_SHORTHAND && propBindRE.test(name)) {
            (modifiers || (modifiers = {})).prop = true
            name = `.` + name.slice(1).replace(modifierRE, '')
          } else if (modifiers) {
            name = name.replace(modifierRE, '')
          }
          if (bindRE.test(name)) { // v-bind // 处理v-bind的情况
            // ...
          } else if (onRE.test(name)) { // v-on // 处理事件绑定
            name = name.replace(onRE, '') // 将事件名匹配
            isDynamic = dynamicArgRE.test(name) // 动态事件绑定
            if (isDynamic) { // 如果是动态事件
              name = name.slice(1, -1) // 去掉两端的 []
            }
            addHandler(el, name, value, modifiers, false, warn, list[i], isDynamic) // 处理事件收集
          } else { // normal directives // 处理其他指令
            // ...
          }
        } else {
          // literal attribute // 处理文字属性
          // ...
        }
      }
    }
    

    通过addHandler方法,为AST树添加事件相关的属性以及对事件修饰符进行处理。

    // dev/src/compiler/helpers.js line 69
    export function addHandler (
      el: ASTElement,
      name: string,
      value: string,
      modifiers: ?ASTModifiers,
      important?: boolean,
      warn?: ?Function,
      range?: Range,
      dynamic?: boolean
    ) {
      modifiers = modifiers || emptyObject
      // passive 和 prevent 不能同时使用,具体是由passive模式的性质决定的
      // 详细可以参阅 https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
      // warn prevent and passive modifier
      /* istanbul ignore if */
      if (
        process.env.NODE_ENV !== 'production' && warn &&
        modifiers.prevent && modifiers.passive
      ) {
        warn(
          'passive and prevent can\'t be used together. ' +
          'Passive handler can\'t prevent default event.',
          range
        )
      }
      // 标准化click.right和click.middle,因为它们实际上不会触发。
      // 从技术上讲,这是特定于浏览器的,但是至少目前来说,浏览器是唯一具有右键/中间点击的目标环境。
      // normalize click.right and click.middle since they don't actually fire
      // this is technically browser-specific, but at least for now browsers are
      // the only target envs that have right/middle clicks.
      if (modifiers.right) { // 将鼠标右键点击标准化 右键点击默认的是 contextmenu 事件
        if (dynamic) { // 如果是动态事件
          name = `(${name})==='click'?'contextmenu':(${name})` // 动态确定事件名
        } else if (name === 'click') { // 如果不是动态事件且是鼠标右击
          name = 'contextmenu' // 则直接替换为contextmenu事件
          delete modifiers.right // 删除modifiers的right属性
        }
      } else if (modifiers.middle) { // 同样标准化处理鼠标中键点击的事件
        if (dynamic) { // 如果是动态事件
          name = `(${name})==='click'?'mouseup':(${name})` // 动态确定事件名
        } else if (name === 'click') { // 如果不是动态事件且是鼠标中键点击
          name = 'mouseup' // 处理为mouseup事件
        }
      }
      // 下面是对捕获、一次触发、passive模式的modifiers处理,主要是为事件添加 !、~、& 标记
      // 这一部分标记可以在Vue官方文档中查阅 
      // https://cn.vuejs.org/v2/guide/render-function.html#%E4%BA%8B%E4%BB%B6-amp-%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6
      // check capture modifier
      if (modifiers.capture) {
        delete modifiers.capture
        name = prependModifierMarker('!', name, dynamic)
      }
      if (modifiers.once) {
        delete modifiers.once
        name = prependModifierMarker('~', name, dynamic)
      }
      /* istanbul ignore if */
      if (modifiers.passive) {
        delete modifiers.passive
        name = prependModifierMarker('&', name, dynamic)
      }
      
      // events 用来记录绑定的事件
      let events
      if (modifiers.native) { // 如果是要触发根元素原生事件则直接取得nativeEvents
        delete modifiers.native
        events = el.nativeEvents || (el.nativeEvents = {})
      } else { // 否则取得events
        events = el.events || (el.events = {})
      }
        
      // 将事件处理函数作为handler
      const newHandler: any = rangeSetItem({ value: value.trim(), dynamic }, range)
      if (modifiers !== emptyObject) {
        newHandler.modifiers = modifiers
      }
    
     // 绑定的事件可以多个,回调也可以多个,最终会合并到数组中
      const handlers = events[name]
      /* istanbul ignore if */
      if (Array.isArray(handlers)) {
        important ? handlers.unshift(newHandler) : handlers.push(newHandler)
      } else if (handlers) {
        events[name] = important ? [newHandler, handlers] : [handlers, newHandler]
      } else {
        events[name] = newHandler
      }
    
      el.plain = false
    }
    

    代码生成

    接下来需要将AST语法树转render函数,在这个过程中会加入对事件的处理,首先模块导出了generate函数,generate函数即会返回render字符串,在这之前会调用genElement函数,而在上述addHandler方法处理的最后执行了el.plain = false,这样在genElement函数中会调用genData函数,而在genData函数中即会调用genHandlers函数。

    // dev/src/compiler/codegen/index.js line 42
    export function generate (
      ast: ASTElement | void,
      options: CompilerOptions
    ): CodegenResult {
      const state = new CodegenState(options)
      const code = ast ? genElement(ast, state) : '_c("div")'
      return {
        render: `with(this){return ${code}}`, // 即render字符串
        staticRenderFns: state.staticRenderFns
      }
    }
    
    // dev/src/compiler/codegen/index.js line 55
    export function genElement (el: ASTElement, state: CodegenState): string {
        // ...
        let code
        if (el.component) {
          code = genComponent(el.component, el, state)
        } else {
          let data
          if (!el.plain || (el.pre && state.maybeComponent(el))) {
            data = genData(el, state)
          }
    
          const children = el.inlineTemplate ? null : genChildren(el, state, true)
          code = `_c('${el.tag}'${
            data ? `,${data}` : '' // data
          }${
            children ? `,${children}` : '' // children
          })`
        }
        // ...
    }
    
    // dev/src/compiler/codegen/index.js line 219
    export function genData (el: ASTElement, state: CodegenState): string {
      let data = '{'
      // ...
      // event handlers
      if (el.events) {
        data += `${genHandlers(el.events, false)},`
      }
      if (el.nativeEvents) {
        data += `${genHandlers(el.nativeEvents, true)},`
      }
      // ...
      data = data.replace(/,$/, '') + '}'
      // ...
      return data
    }
    
    // dev/src/compiler/to-function.js line 12 
    function createFunction (code, errors) {
      try {
        return new Function(code) // 将render字符串转为render函数
      } catch (err) {
        errors.push({ err, code })
        return noop
      }
    }
    

    可以看到无论是处理普通元素事件还是组件根元素原生事件都会调用genHandlers函数,genHandlers函数即会遍历解析好的AST树中事件属性,拿到event对象属性,并根据属性上的事件对象拼接成字符串。

    // dev/src/compiler/codegen/events.js line 3
    const fnExpRE = /^([\w$_]+|\([^)]*?\))\s*=>|^function(?:\s+[\w$]+)?\s*\(/
    const fnInvokeRE = /\([^)]*?\);*$/
    const simplePathRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/
    
    // dev/src/compiler/codegen/events.js line 7
    // KeyboardEvent.keyCode aliases
    const keyCodes: { [key: string]: number | Array<number> } = {
      esc: 27,
      tab: 9,
      enter: 13,
      space: 32,
      up: 38,
      left: 37,
      right: 39,
      down: 40,
      'delete': [8, 46]
    }
    // KeyboardEvent.key aliases
    const keyNames: { [key: string]: string | Array<string> } = {
      // #7880: IE11 and Edge use `Esc` for Escape key name.
      esc: ['Esc', 'Escape'],
      tab: 'Tab',
      enter: 'Enter',
      // #9112: IE11 uses `Spacebar` for Space key name.
      space: [' ', 'Spacebar'],
      // #7806: IE11 uses key names without `Arrow` prefix for arrow keys.
      up: ['Up', 'ArrowUp'],
      left: ['Left', 'ArrowLeft'],
      right: ['Right', 'ArrowRight'],
      down: ['Down', 'ArrowDown'],
      // #9112: IE11 uses `Del` for Delete key name.
      'delete': ['Backspace', 'Delete', 'Del']
    }
    }
    
    // dev/src/compiler/codegen/events.js line 37
    // #4868: modifiers that prevent the execution of the listener
    // need to explicitly return null so that we can determine whether to remove
    // the listener for .once
    const genGuard = condition => `if(${condition})return null;`
    const modifierCode: { [key: string]: string } = {
      stop: '$event.stopPropagation();',
      prevent: '$event.preventDefault();',
      self: genGuard(`$event.target !== $event.currentTarget`),
      ctrl: genGuard(`!$event.ctrlKey`),
      shift: genGuard(`!$event.shiftKey`),
      alt: genGuard(`!$event.altKey`),
      meta: genGuard(`!$event.metaKey`),
      left: genGuard(`'button' in $event && $event.button !== 0`),
      middle: genGuard(`'button' in $event && $event.button !== 1`),
      right: genGuard(`'button' in $event && $event.button !== 2`)
    }
    
    // dev/src/compiler/codegen/events.js line 55
    export function genHandlers (
      events: ASTElementHandlers,
      isNative: boolean
    ): string {
      const prefix = isNative ? 'nativeOn:' : 'on:'
      let staticHandlers = ``
      let dynamicHandlers = ``
      for (const name in events) { // 遍历AST解析后的事件属性
        const handlerCode = genHandler(events[name]) // 将事件对象转换成可拼接的字符串
        if (events[name] && events[name].dynamic) {
          dynamicHandlers += `${name},${handlerCode},`
        } else {
          staticHandlers += `"${name}":${handlerCode},`
        }
      }
      staticHandlers = `{${staticHandlers.slice(0, -1)}}`
      if (dynamicHandlers) {
        return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])`
      } else {
        return prefix + staticHandlers
      }
    }
    
    // dev/src/compiler/codegen/events.js line 96
    function genHandler (handler: ASTElementHandler | Array<ASTElementHandler>): string {
      if (!handler) {
        return 'function(){}'
      }
    
      // 事件绑定可以多个,多个在解析AST树时会以数组的形式存在,如果有多个则会递归调用getHandler方法返回数组。
      if (Array.isArray(handler)) {
        return `[${handler.map(handler => genHandler(handler)).join(',')}]`
      }
    
      const isMethodPath = simplePathRE.test(handler.value) // 调用方法为 doThis 型
      const isFunctionExpression = fnExpRE.test(handler.value) // 调用方法为 () => {} or function() {} 型
      const isFunctionInvocation = simplePathRE.test(handler.value.replace(fnInvokeRE, '')) // 调用方法为 doThis($event) 型
    
      if (!handler.modifiers) { // 没有修饰符
        if (isMethodPath || isFunctionExpression) { // 符合这两个条件则直接返回
          return handler.value
        }
        /* istanbul ignore if */
        if (__WEEX__ && handler.params) {
          return genWeexHandler(handler.params, handler.value)
        }
        return `function($event){${ // 返回拼接的匿名函数的字符串
          isFunctionInvocation ? `return ${handler.value}` : handler.value
        }}` // inline statement
      } else { // 处理具有修饰符的情况
        let code = ''
        let genModifierCode = ''
        const keys = []
        for (const key in handler.modifiers) {  // 遍历modifiers上记录的修饰符
          if (modifierCode[key]) {
            genModifierCode += modifierCode[key]  // 根据修饰符添加对应js的代码
            // left/right
            if (keyCodes[key]) {
              keys.push(key)
            }
          } else if (key === 'exact') { // 针对exact的处理
            const modifiers: ASTModifiers = (handler.modifiers: any)
            genModifierCode += genGuard(
              ['ctrl', 'shift', 'alt', 'meta']
                .filter(keyModifier => !modifiers[keyModifier])
                .map(keyModifier => `$event.${keyModifier}Key`)
                .join('||')
            )
          } else {
            keys.push(key) // 如果修饰符不是以上修饰符,则会添加到keys数组中
          }
        }
        if (keys.length) {
          code += genKeyFilter(keys) // 处理其他修饰符 即keyCodes中定义的修饰符
        }
        // Make sure modifiers like prevent and stop get executed after key filtering
        if (genModifierCode) {
          code += genModifierCode
        }
        // 根据三种不同的书写模板返回不同的字符串
        const handlerCode = isMethodPath
          ? `return ${handler.value}($event)`
          : isFunctionExpression
            ? `return (${handler.value})($event)`
            : isFunctionInvocation
              ? `return ${handler.value}`
              : handler.value
        /* istanbul ignore if */
        if (__WEEX__ && handler.params) {
          return genWeexHandler(handler.params, code + handlerCode)
        }
        return `function($event){${code}${handlerCode}}`
      }
    }
    
    // dev/src/compiler/codegen/events.js line 175
    function genFilterCode (key: string): string {
      const keyVal = parseInt(key, 10)
      if (keyVal) { // 如果key是数字,则直接返回$event.keyCode!==${keyVal}
        return `$event.keyCode!==${keyVal}`
      }
      const keyCode = keyCodes[key]
      const keyName = keyNames[key]
      // 返回_k函数,它的第一个参数是$event.keyCode,
      // 第二个参数是key的值,
      // 第三个参数就是key在keyCodes中对应的数字。
      return (
        `_k($event.keyCode,` +
        `${JSON.stringify(key)},` +
        `${JSON.stringify(keyCode)},` +
        `$event.key,` +
        `${JSON.stringify(keyName)}` +
        `)`
      )
    }
    

    事件绑定

    前面介绍了如何编译模板提取事件收集指令以及生成render字符串和render函数,但是事件真正的绑定到DOM上还是离不开事件注册,此阶段就发生在patchVnode过程中,在生成完成VNode后,进行patchVnode过程中创建真实DOM时会进行事件注册的相关钩子处理。

    // dev/src/core/vdom/patch.js line 33
    const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
    
    // dev/src/core/vdom/patch.js line 125
    function createElm (
        vnode,
        insertedVnodeQueue,
        parentElm,
        refElm,
        nested,
        ownerArray,
        index
      ) {
      // ...
      if (isDef(data)) {
        invokeCreateHooks(vnode, insertedVnodeQueue)
      }
      // ...
    }
    
    // dev/src/core/vdom/patch.js line 303
    // 在之前cbs经过处理 
    // 这里cbs.create包含如下几个回调:
    // updateAttrs、updateClass、updateDOMListeners、updateDOMProps、updateStyle、update、updateDirectives
    function invokeCreateHooks (vnode, insertedVnodeQueue) {
        for (let i = 0; i < cbs.create.length; ++i) {
          cbs.create[i](emptyNode, vnode)
        }
        i = vnode.data.hook // Reuse variable
        if (isDef(i)) {
          if (isDef(i.create)) i.create(emptyNode, vnode)
          if (isDef(i.insert)) insertedVnodeQueue.push(vnode)
        }
    }
    

    invokeCreateHooks就是一个模板指令处理的任务,他分别针对不同的指令为真实阶段创建不同的任务,针对事件,这里会调updateDOMListeners对真实的DOM节点注册事件任务。

    // dev/src/platforms/web/runtime/modules/events.js line 105
    function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {
      if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {  // on是事件指令的标志
        return
      }
      // 新旧节点不同的事件绑定解绑
      const on = vnode.data.on || {}
      const oldOn = oldVnode.data.on || {}
      // 拿到需要添加事件的真实DOM节点
      target = vnode.elm
      // normalizeEvents是对事件兼容性的处理
      normalizeEvents(on)
      // 调用updateListeners方法,并将on作为参数传进去
      updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context)
      target = undefined
    }
    
    // dev/src/core/vdom/helpers/update-listeners.js line line 53
    export function updateListeners (
      on: Object,
      oldOn: Object,
      add: Function,
      remove: Function,
      createOnceHandler: Function,
      vm: Component
    ) {
      let name, def, cur, old, event
      for (name in on) { // 遍历事件
        def = cur = on[name]
        old = oldOn[name]
        event = normalizeEvent(name)
        /* istanbul ignore if */
        if (__WEEX__ && isPlainObject(def)) {
          cur = def.handler
          event.params = def.params
        }
        if (isUndef(cur)) { // 事件名非法的报错处理
          process.env.NODE_ENV !== 'production' && warn(
            `Invalid handler for event "${event.name}": got ` + String(cur),
            vm
          )
        } else if (isUndef(old)) { // 旧节点不存在
          if (isUndef(cur.fns)) { // createFunInvoker返回事件最终执行的回调函数
            cur = on[name] = createFnInvoker(cur, vm)
          }
          if (isTrue(event.once)) {  // 只触发一次的事件
            cur = on[name] = createOnceHandler(event.name, cur, event.capture)
          }
          // 执行真正注册事件的执行函数
          add(event.name, cur, event.capture, event.passive, event.params)
        } else if (cur !== old) {
          old.fns = cur
          on[name] = old
        }
      }
      for (name in oldOn) { // 旧节点存在,解除旧节点上的绑定事件
        if (isUndef(on[name])) {
          event = normalizeEvent(name)
          // 移除事件监听
          remove(event.name, oldOn[name], event.capture)
        }
      }
    }
    
    // dev/src/platforms/web/runtime/modules/events.js line 32
    // 在执行完回调之后,移除事件绑定
    function createOnceHandler (event, handler, capture) {
      const _target = target // save current target element in closure
      return function onceHandler () {
        const res = handler.apply(null, arguments)
        if (res !== null) {
          remove(event, onceHandler, capture, _target)
        }
      }
    }
    

    最终添加与移除事件都是调用的addremove方法,最终调用的方法即DOMaddEventListener方法与removeEventListener方法。

    // dev/src/platforms/web/runtime/modules/events.js line 46
    function add (
      name: string,
      handler: Function,
      capture: boolean,
      passive: boolean
    ) {
      // async edge case #6566: inner click event triggers patch, event handler
      // attached to outer element during patch, and triggered again. This
      // happens because browsers fire microtask ticks between event propagation.
      // the solution is simple: we save the timestamp when a handler is attached,
      // and the handler would only fire if the event passed to it was fired
      // AFTER it was attached.
      if (useMicrotaskFix) {
        const attachedTimestamp = currentFlushTimestamp
        const original = handler
        handler = original._wrapper = function (e) {
          if (
            // no bubbling, should always fire.
            // this is just a safety net in case event.timeStamp is unreliable in
            // certain weird environments...
            e.target === e.currentTarget ||
            // event is fired after handler attachment
            e.timeStamp >= attachedTimestamp ||
            // bail for environments that have buggy event.timeStamp implementations
            // #9462 iOS 9 bug: event.timeStamp is 0 after history.pushState
            // #9681 QtWebEngine event.timeStamp is negative value
            e.timeStamp <= 0 ||
            // #9448 bail if event is fired in another document in a multi-page
            // electron/nw.js app, since event.timeStamp will be using a different
            // starting reference
            e.target.ownerDocument !== document
          ) {
            return original.apply(this, arguments)
          }
        }
      }
      target.addEventListener(
        name,
        handler,
        supportsPassive
          ? { capture, passive }
          : capture
      )
    }
    
    // dev/src/platforms/web/runtime/modules/events.js line 92
    function remove (
      name: string,
      handler: Function,
      capture: boolean,
      _target?: HTMLElement
    ) {
      (_target || target).removeEventListener(
        name,
        handler._wrapper || handler,
        capture
      )
    }
    

    每日一题

    https://github.com/WindrunnerMax/EveryDay
    

    参考

    https://cn.vuejs.org/v2/api/#v-on
    https://juejin.im/post/6844903919290679304
    https://juejin.im/post/6844904061897015310
    https://juejin.im/post/6844904126250221576
    https://segmentfault.com/a/1190000009750348
    https://blog.csdn.net/weixin_41275295/article/details/100549145
    https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
    https://github.com/liutao/vue2.0-source/blob/master/%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86.md
    
    展开全文
  • v-on 事件指令用于绑定事件。 1 基础用法 v-on 指令绑定事件后,就会监听相应事件。 html: 已点击 {{count}} 次 <button @click=count++>点我</button> 注意: @click 是 v-on:click 简写形式...
  • vue中的指令

    2019-11-05 19:30:16
    v-bind:提供用于绑定属性的指令,简写“:”,后面跟上要绑定的属性。 v-bind可以写合法的js表达式。 v-on事件绑定机制,简写:“@” 事件修饰符: stop阻止冒泡; prevent阻止默认事件; capture添加事件监听...

    v-bind:提供用于绑定属性的指令,简写“:”,后面跟上要绑定的属性。

    v-bind可以写合法的js表达式。

    v-on事件绑定机制,简写:“@”

    事件修饰符:

    • stop阻止冒泡;
    • prevent阻止默认事件;
    • capture添加事件监听器时使用事件捕获模式;
    • self只当事件在该元素本身(biubiu不是子元素)触发时触发回调;只有点击当前函数的时候才触发当前函数
    • once事件只触发一次

    Vue中的v-model唯一的双向绑定

    表单元素可以与用户进行交互,v-bind只能实现数据的单向绑定,从M自动绑定到V中,无法实现数据的双向绑定

    v-model可以实现表单元素和Model中数据的绑定,且v-model只能应用于表单元素。

    展开全文
  • v-bind <body> <div id="app">...--v-bind: 是vue中提供的用于绑定属性的指令--> <!--<input type="button" value="按钮" v-bind: title="mytitle">--> <input type="...
  • vue中常见的指令

    千次阅读 2020-06-15 11:10:29
    下面介绍一下自己对vue中一些常见指令的理解,以便自己以后方便查找: v-model指令用于表单输入,实现表单控件和数据双向绑定。 <input type="text" v-model:value="msg"> <!-- value可以省略 因为v-...
  • v-on 事件指令用于绑定事件。 1 基础用法 v-on 指令绑定事件后,就会监听相应事件。 html: &lt;div id="app"&gt; &lt;h3&gt;已点击 {{count}} 次&lt;/h3&gt; &lt;button...
  • 指令Vue中最常用一项功能,它带有v-前缀。指令的主要职责就是当表达式值发生改变时,相应将某些行为应用到DOM上。 注意:数据驱动DOM是Vue核心理念,我们无需主动操作DOM,只需要维护好数据,DOM事vue...
  • Vue中如何监听事件呢?使用v-on指令 v-on介绍 作用:绑定事件监听器 缩写:@ 预期:Function | Inline Statement | Object 参数:event 使用 我们用一个监听按钮点击事件,来简单看看v-on使用 下面代码中...
  • v-bind:是Vue中,提供的用于绑定属性的指令 1. 直接使用指令`v-bind` 2. 使用简化指令`:` 3. 在绑定的时候,拼接绑定内容:`:title="btnTitle + ', 这是追加的内容'"` 4.v-bind中,可以写合法的JS表达式 &...
  • 先了解一下,在vue ,有很多内置的指令. 比如: v-for 用于遍历 v-if & v-show 用于隐藏和显示元素(区别在于后者是修改display:block|none,前者是不创建把元素从dom删除或者创建. v-bind: 属性绑定,把数据绑定...
  • v-text 只解析文本信息 ...v-on 绑定事件,可以用@符号来代替 v-bind 绑定元素一个或者多个属性,用于动态更新html上元素标签内容 v-slot 插槽 v-pre 跳过自己以及子元素编译过程 v-model 数据双向绑定 v-cloc
  • 区别:v-text会覆盖元素原本内容,但是 插值表达式 只会替换自己这个占位符,不会把 整个元素内容清空 例: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A71AM3tU-...
  • Vue9个常用的指令

    2020-11-10 11:37:27
    vue生命周期:vue中的每个组件都是独立,每个组件都有自己生命周期:组件创建->数据初始化->挂载->更新->销毁。 一内容,事件绑定 v-text 设置标签文本值(纯文本) v-html 设置标签...
  • Vue的内置指令介绍

    2019-01-31 14:57:45
    内置指令就是我们常常用到,一下是我整理Vue中的所有内置指令!希望对大家有所帮助!!! 1、v-bind:响应并更新DOM特性;例如:v-bind:href v-bind:class v-bind:title v-bind:bb 2、v-on:用于监听DOM事件; ...
  • VUE常用指令

    2020-03-17 13:52:56
    VUE常用指令 在学习前端的过程,学了一段时间的VUE,先说手为...vue使用最频繁的指令之一,用于绑定事件 使用方法一: <input type=“button” value=“开启” v-on:click="go"> 使用方法二(简写): <...
  • Vue的简单指令

    2017-02-23 09:49:13
    @click 是v-on:click简写,绑定事件监听 v-model 在表单控件输入和应用状态做双向数据绑定 v-bind 在变量与属性值直接数据绑定 重点说一下v-for: 1.用法:用于列表编写,绑定数据到数组来渲染一个列表...
  • v-text、v-bind、v-on

    2020-02-21 13:30:02
    v-text:默认没有闪烁问题;会覆盖原来元素中的内容(插值表达式不会);不会解析html v-bind:vue中用于绑定属性的指令,可以简写为:。 v-on:vue中用于绑定事件的指令,可以简写为@。 ...
  • Vue中的v-model相关

    2020-10-16 15:24:04
    Vue提供了v-model指令,用于双向绑定事件 v-model双向绑定data内数据,当修改input表单内内容时,data数据也会发生变化,当data内数据发生变化时,表单内容发生变化 如果使用v-bind:value使表单绑定data数据,当...
  • Vue常用指令总结

    2020-03-31 21:50:02
    文章目录常用指令v-cloak:防止页面加载时出现闪烁问题v-text:将数据填充到标签v-html:将HTML片段填充到标签v-pre:显示原始信息v-once:只编译一次v-model:用于双向数据绑定v-on:绑定事件事件函数传递参数事件修饰...
  • vue {基础,指令 001}

    2020-08-06 17:45:10
    指令v-cloakv-textv-htmlv-prev-once双向数据绑定v-modelmvvmv-onv-on事件函数传入参数事件修饰符按键修饰符自定义按键修饰符别名v-bind绑定对象绑定class绑定对象和绑定数组 区别绑定style分支结构v-if 使用...
  • 在前面我们学习了Vue给我们提供几个指令 v-if 和v-else : 判断为真则显示为假则删除标签。 v-model :将data中的某些数据与输入框绑定,能通过输入框动态改变data中的数据值,在input textarea select...
  • 1.概念:带有v-前缀特殊属性,指令用于表达式值改变时,将某些行为应用到DOM节点 2.v-bind指令:html属性中的值更新时使用 3.v-if v-else指令:这个对于程序员来说就很熟悉了,逻辑判断语句 4.v-model指令:实现...
  • vue.js给input添加事件的方法:1、...【相关文章推荐:vue.js】vue.js给input添加事件的方法:vue.js 库的 v-on 指令用于绑定事件监听器, v-on 指令后可以带参数, 还可以增加修饰符.添加事件监听 v-on(1) 使用 v...
  • Vue | 10 表单输入绑定

    2018-11-22 19:31:34
    内容提要: 基本用法:文本、多行文本、复选框、单选框、下拉列表; 复选框、单选框、列表选项绑定;...虽然很神奇,v-model是一个语法糖,它在用于输入的事件中更新数据,加上一些边缘情况特殊处理。 ...
  • 指明特殊后缀,用于指出一个指令应该以特殊方式绑定。 一、事件处理修饰符 1. 事件修饰符 在事件处理程序调用 event.preventDefault() 或 event.stopPropagation() 是非常常见需求. 尽管我们可以在方法轻松...
  • 先谈谈我对vue 一些指令的理解吧;...v-model 是双向数据的绑定,只能用于表单, 而值得一说v-model 相当于是 v-bind 与v-on 语法糖; 因为使用v-bind 与v-on 也可以实现双向数据的绑定; 使用event....
  • v-bind:用于动态绑定DOM元素属性。元素属性实际值是由实例data属性提供。...v-on:用于事件绑定。 v-text:参数为string,作用是更新元素textContent。与{{}}其实是差不多,使用上有区别。v-text是绑定在某...

空空如也

空空如也

1 2 3 4 5 6
收藏数 114
精华内容 45
关键字:

vue中用于绑定事件的指令

vue 订阅