学而时习之 不亦说乎

Vue2 基础学习笔记

发表于:2021-09-01分类:Vue.js

Vue实例

var vm = new Vue({
  // 选项
})
//创建一个vue实例,选项里传入一个对象(选项对象)

//选项对象中添加数据
var data1 = { a: 1 }

// 选项对象的属性(property)被加入到一个 Vue 实例中
var vm = new Vue({
  data: data1 //其实是引用了data1对象
})

//因为是引用关系所以:
vm.a == data1.a //ture
data1.a = 3
vm.a //3
//反之也是
vm.a = 2
data1.a // 2

当数据被改变时视图会重新渲染,注意只有实例创建时就已经存在的data引用才会这样,后添加的不会更新,所以可以设定一些空的初始值,例如:

data: {
  newTodoText: '',
  visitCount: 0,
  hideCompletedTodos: false,
  todos: [],
  error: null
}

可以使用Object.freeze禁止再次渲染

var obj = {
  foo: 'bar'
}

Object.freeze(obj) //注意应该在实例化之前

new Vue({
  el: '#app',
  data: obj
})

obj.foo = 'hi';//这里不会有效

实例属性(实例 property),它们都有前缀$

var data1 = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data1
})

vm.$data === data1 // => true
vm.$el === document.getElementById('example') // => true


// $watch 是一个实例方法,用来监听值的变化,返回新值和旧值,并回调
vm.$watch('a', function (newValue, oldValue) {
  console.log(newValue,oldValue);//回调
})
vm.$data.a = 10;//a被改变
//控制台会输出 10 1

生命周期钩子

例如:created当实例被创建后执行

new Vue({
    el:'#example',
    data: {
        a: 1
    },
    created: function() {
    // 注意要 `this` 指向 它的 vue 实例
        console.log('a is: ' + this.a)
    }
})
// 当实例运行 控制台显示:a is: 1

beforeCreate

在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

created

在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。注意挂载阶段还没开始,$el property 目前尚不可用。

beforeMount

在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。

模板语法

插值

<span>姓名: {{ name }}</span> 
<span v-once>姓名: {{ name }}</span>
<!-- 
双括号进行文本插值(或称替换、更新),对应的就是data的name属性
还可以添加指令v-once 来一次性插值,之后不会更新,
需要注意会影响到这个节点上的其他数据哦!
-->


<p>文本: {{ rawHtml }}</p>
<p>解析为html: <span v-html="rawHtml"></span></p>
<!-- 
双大括号会插入普通文本,而非html代码
应该试域v-html指令来输出真正的代码
-->

<!-- 
绑定元素属性attribute,来修改元素属性的值,使用v-bind指令绑定某元素属性,可以用简写:
-->
<div v-bind:class="name"></div>
<div :class="name"></div>

<!-- 
JavaScript表达式,实际上可以在模板中直接使用js代码(限单个表达式,流程控制只能使用三元运算符),而不是只能插入值
-->
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>

对于布尔值的属性,存在就是意味着true,null、undefined 或 false为false

指令

指令是v-开头的特殊元素属性(attribute),指令的值预期是单个表达式(v-for除外),例如,下面的seen如果是ture,该p标签才会被渲染,也就是v-if根据表达式seen来判断是否渲染,

<p v-if="seen">现在你看到我了</p>

一些指令能够接收参数,例如v-bind,可以更新元素属性。例如下面v-bind可以通过name更新class的值

v-on可以用来监听Dom事件,比如点击click

<div v-bind:class="name"></div>

<a v-on:click="doSomething">点击执行某函数</a>
<a @click="doSomething">简写</a>

动态参数,简单说就是属性名称可以不确定,下面当data中的name = href,那么的第一行等价第二行。当然在v-on中也可以使用动态参数

<a v-bind:[name]="url"> 等价 </a>
<a v-bind:href="url"> 等价 </a>

<a v-on:[name]="doSomething"> eventName是上面我就监听什么事件 </a>
<!-- 在html文件里时需要注意避免使用大写字符来做动态参数,会被强制转为小写,其他情况不必考虑 -->

修饰符

在指令后通过.来调用的事件,就是修饰符比如

<form v-on:submit.prevent="onSubmit">当事件触发,调用event.preventDefault()的修饰符</form>

计算属性和侦听器

计算属性:在上面的演示中,知道了可以在模板中使用表达式,这非常方便,但是模板中有太多逻辑也可能难以维护。所以这种情况建议使用计算属性,也就是所调用该属性相当于调用方法

在date中添加方法会无效,会文本化输出,估计是toString了

<div id="example">
  <p>正常文字: "{{ message }}"</p>
  <p>反向文字: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {//添加计算属性的方法
    // reversedMessage 声明此属性 并添加方法
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})
/*
渲染得到:
正常文字: "Hello"
反向文字: "olleH"
*/

console.log(vm.reversedMessage) // 计算属性支持通过vm调用
vm.message = 'Goodbye' //修改message的值
console.log(vm.reversedMessage) // => 'eybdooG'
/*
重新渲染得到
正常文字: "Goodbye"
反向文字: "eybdooG"
控制台得到
olleH
eybdooG
*/

计算属性缓存vs方法

//通过在方法里添加方法,也可以在表达式中调用
//{{ reversedMessage() }} 注意多了括号
methods:{
     reversedMessage: function(){
         return this.message.split('').reverse().join('')
     } 
  }

结果是一样的,它们的区别是计算属性是是有缓存的,前中只要message没有变化,多次访问计算属性会立即得到结果,而方法会每次都调用使用。所以需要缓存就用计算属性,不需要就用方法。

  computed: {
    now: function () {
      return Date.now() //计算函数中的多次调用不会变化
    }
  },
  methods:{
      now: function () {
        return Date.now() //方法中的这个多次调用可以看到时间戳不同
      }
  }

计算属性vs侦听属性

通常情况下当需要跟随其他数据变动而变动的时候,计算属性比watch更适合

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Ji',
    lastName: 'Liang',
    fullName: 'Ji Liang' //完整姓名
  },
  watch: {//侦听(监听)属性
    firstName: function (val) {//如果firstName有变就改变fullname
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {//如果lastName有变就改变fullname
      this.fullName = this.firstName + ' ' + val
    }
  }
})

//优化一下:
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {//fullname作为计算属性,拼接得到全面,不再分别监听变化,只要变化就会自动同步渲染
      return this.firstName + ' ' + this.lastName
    }
  }
})
//缺点是不能直接操作fullname了,但下例中可以通过添加set操作

计算属性的setter

计算属性默认只有geter,需要的适合也可以添加seter

computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
//这样就能直接操作fullname并同步更新first和last

侦听器

计算属性虽然很适合大多数情况,但是有时还是需要侦听器的,当需要数据变化是执行异步或开销较大的操作是,推荐使用侦听器。

<div id="watch-example">
  <p>
   问一个yes或no的问题:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
    //为了使用axios.get异步方法引入axios
    //为了使用_.debounce(防抖动)和_.capitalize(首字母大写),引入lodash
var watchExampleVM = new Vue({
  el: '#watch-example',//绑定元素
  data: {
    question: '',//声明问题
    answer: '你可以提问了!'//声明答案提示
  },
  watch: {
    // 监听 `question` 如果发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = '等待您输入完成...'
      this.debouncedGetAnswer()//调用防抖动方法
    }
  },
  created: function () {//初始化方法
      //因为lodash的_.debounce返回的是个方法所以可以在初始化时将它装在debouncedGetAnswer中调用
      //也可以在上例中使用 _.debounce(this.getAnswer, 500)() 调用(最后加了括号代表立即执行)
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)//防止用户没输入完,反复调用api,这里延期0.5秒
    //完全停止输入0.5秒后才调用this.getAnswer方法
  },
  methods: {//添加getAnswer方法
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = '问题应该以?结尾'
        return //结束方法
      }
      this.answer = '我想想...'
      var vm = this //添加this
      axios.get('https://yesno.wtf/api')//异步调用
        .then(function (response) {//成功就then
          vm.answer = _.capitalize(response.data.answer)
          //调用lodash的_.capitalize将首字母大写并赋值给this.ansewer,答案重新渲染
        })
        .catch(function (error) {
          vm.answer = '错误: ' + error
        })
    }
  }
})
</script>

Class与Style绑定

操作元素的class和内联(style)样式,是很常见的需求,因为它们都是元素的属性(attribute),所以应该使用v-bind处理它们,可以通过表达式计算出结果就行,不过简单拼接很不优雅,vue有专门的办法,结果可以是对象或数组

绑定class

<div v-bind:class="{ active: isActive }"></div>
<div class="active">当isActive为真值时</div>

<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"
>可以和已有的class共存</div>
<div class="static active text-danger">当isActive、hasError为真值时(引号不用担心会自适应)</div>

<div class="static" v-bind:class="classObject">也可以传递绑定对象</div>
<div class="static active text-danger">当下例运行时</div>
data: {
  classObject: {//对象
    active: true,
    'text-danger': true
  }
}

发现了?是的可以用计算属性

<div id="a" v-bind:class="classObject"></div>
<script>
var watchExampleVM = new Vue({
    el:"#a",
    data: {
        isActive: true,
        error: null
    },
    computed: {
        classObject: function () {
            return {
                active: this.isActive && !this.error,
                'text-danger': this.error && this.error.type === 'fatal'
            }
        }
    }
})
</script>

<div id="a" class="active">结果</div>

数组方法:

<div v-bind:class="[activeClass, errorClass]">传递数组</div>

<script>
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}
</script>

<div class="active text-danger">结果</div>
<div v-bind:class="[isActive ? activeClass : '', errorClass]">三元运算符也可以</div>
<div v-bind:class="[{ active: isActive }, errorClass]">数组中可以加带对象</div>

在组件上使用

组件中使用相当于插入新的class,原模板中的class不变

Vue.component('my-component', {
  template: '<p class="foo bar">Hi</p>'
})
//为模板的根元素添加class
<my-component class="baz boo"></my-component>
<!-- 渲染后 -->
<p class="foo bar baz boo">Hi</p>

<my-component v-bind:class="{ active: is }"></my-component>
<!-- 参数绑定也可以,如果is时真值,渲染后 -->
<p class="foo bar active">Hi</p>

绑定内联样式

当v-bind绑定了style,就可以使用对象来写css

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

<script>
data: {
  activeColor: 'red',
  fontSize: 30 //这里也可以直接使用'30px'字符串,注意上面也要修改
}
</script>

<div v-bind:style="styleObject"></div>
<script>
    //直接使用对象更清晰
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}
</script>

<div v-bind:style="[styleObject, overridingStyles]">数组可以将多个对象应用到同一个元素</div>
<script>
data: {
    styleObject: {
        color: 'red',
        fontSize: '13px'
    },
    overridingStyles:{
        'font-weight': 'bold',
    }
}
</script>

当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,比如 transform,Vue会自动侦测并添加相应的前缀。

<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">
多重值:可以为某属性指定一个多值的数组,提供多个前缀,这样只会渲染最后一个被浏览器支持的值
</div>

条件渲染

v-if

<h1 v-if="a">当a是真值的时候才会被渲染</h1>
<h1 v-else-if="b">当b是真值的时候</h1>
<h1 v-else>a不是真值</h1>

需要注意它们三个必须连续使用不能跨元素使用

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>v-if 是个指令,所以必须添加在一个元素上,使用特有的template元素,这样渲染结果最终就不含template,只有内容</p>
</template>

使用key来管理可复用的元素

vue会尽可能复用元素,这样是很好的可以快速渲染,但是也可能会遇到些问题

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address">
</template>

当通过loginType来控制切换不同输入框时,其实输入框本身没有变,vue只是简单的修改了placeholder,如果你在输入框里输入了东西,它们切换时是没有变化的。

只需要添加一个具有唯一值的key就可以了,这时候vue每次切换都会重新渲染输入框(例子中的label仍会被复用,因为它们没有加key)

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

v-show

<h1 v-show="ok">Hello!</h1>

show和if的最大区别是 if是真正的渲染,show只是简单的通过display控制元素的隐藏与否,show不支持<template>元素,也不支持v-else

v-if vs v-show

if是真正的条件渲染,可以保证切换过程中条件块内的事件监听器和子组件适当的销毁或重建

if具有惰性,如果初始条件是假就什么都不做,只有条件第一次变为真是才会开始渲染

show是不论如何都会被渲染的,只是基于css的隐藏和显示

if的开销一般较高,show只在初始渲染时有开销,如果需要频繁切换show好,如果运行条件很少改变,应该用if

避免在应该元素同时使用if和for,他们的优先级不同,for更高

列表渲染

v-for用法

v-for基于数组(或对象)来渲染一个列表

<ul id="example-1">
  <li v-for="item in items" :key="item.message">
    {{ item.message }}
  </li>
</ul>

<script>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [//数组
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
//得到:
</script>
<ul id="example-1" class="demo">
    <li>Foo</li>
    <li>Bar</li>
</ul>



<ul id="example-2">
  <li v-for="(item, index) in items"><!-- 添加索引参数,并调用父作用域的属性 -->
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
<script>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',//父作用域
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
//得到:
</script>
<ul id="example-2">
    <li>Parent - 0 - Foo</li>
    <li>Parent - 1 - Bar</li>
</ul>

还可以使用of替代in,更接近JavaScript的迭代器语法。下例就是这样遍历对象的。

v-for 遍历对象

<ul id="v-for-object">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
<script>
new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})
    //得到的不含键名,若需键名可以添加参数,还可以添加索引:
</script>
<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>
<div v-for="(value, name , index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>

维护状态 key

数据变化时,因为vue会尽可能的复用元素,而不是移动元素来匹配新的数据。之前的说过通过添加不重复的key可以避免复用,下面是for中复用的情况。

<div id="app">
    <ul>
        <li v-for="(item,index) in list" :class="item.name">
        {{item.id}}
            <input type="text" :placeholder="item.name">
            <button v-on:click="deleteLi(index)">删除</button>
        </li>
    </ul>
</div>

<script>
    //按钮调用了删除方法,携带索引去删除指定元素
new Vue({
    el: '#app',
    data: {//初始数组
        list: [{
            name: 'hello',
            id: 1
        }, {
            name: 'world',
            id: 2
        }]
    },
    methods: {
        deleteLi(index) {//删除方法
        this.list.splice(index, 1)//删除指定位置的1个元素
        }
    }
})
</script>

当在2个文本框中输入不同的内容例如a和b,然后删除第一个的时候,似乎第二个被删除了。

对比没有输入内容的情况,当删除id1的时候,只需要渲染id2 world,但是vue会复用,所以实际上是把原先id1的位置渲染为id2,这看起来没有问题

但是有了内容就不一样了,因为内容是临时dom状态并没有绑定数据,不会被重新渲染,而是继续存在。所以当删除id1时,重新渲染id2时,在原先id1的位置,渲染了 id2 和 world。又由于原先输入的a在那里,所以不会被覆盖被保留了。当你删除内容a时你会发现(placeholder属性的)world。

这就是就地更新

这种情况的解决方法和v-if那个渲染一样也需要添加不重复的key

<li v-for="(item,index) in list" :class="item.name" :key="item.id">
<!-- 注意不要使用索引index,因为当删除元素,索引也会改变 -->

数组更新检测

变更方法

vue监听数组的变更方法,并触发重新渲染,JavaScript有以下变更方法

push() 尾部添加1或多个元素
pop() 删除最后1个元素
shift() 删除第1个元素
unshift() 头部增加1或多个元素
splice() 指定位置删除或替换或插入元素
sort() 排序
reverse() 颠倒全部元素

替换数组

filter()concat()slice()这些方法不会改变原数组,而是返回新的数组

example1.items = example1.items.filter(1)

vue会智能的重用,而不是整个渲染,也就是说用一个含有相同元素的数组去替换原来的数组,效率很高。

过滤/排序

可以通过计算属性来过滤或排序数组

<li v-for="n in evenNumbers">{{ n }}</li>
<scrpit>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0 //能被2除尽的
    })
  }
}
</scrpit>

计算属性不适合的情况下,例如嵌套在v-for循环中,可以使用方法

在v-for里使用范围值

<div>
  <span v-for="n in 10">{{ n }} </span>
</div>

在template元素上使用v-for

因为template不会被渲染的,可以用它来包裹多个元素,实现渲染多个元素的目的

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

v-for和v-if一同使用

首先它们是不推荐在同一元素上使用的,因为for的优先级更换高,这意味着if将在每个循环中重复运行,当需要渲染部分内容时这种优先级很有用的。

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>
<!-- 只渲染未完成的(todo.isComplete不为真的) -->

在组件上使用v-for

在自定义组件上,可以像普通元素那样使用v-for,记得需要加key

<my-component v-for="item in items" :key="item.id"></my-component>

但是数据不会传入组件的,组件有自己的作用域,如果需要传递数据应该使用prop(自定义组件属性传递)

<my-component
  v-for="(item, index) in items"
  v-bind:item="item"
  v-bind:index="index"
  v-bind:key="item.id"
></my-component>

todo例子:

父组件可以使用 props 把数据传给子组件。子组件可以使用 $emit 触发父组件的自定义事件。例中中的$emit('remove'),就是该子组件调用父组件中remove的自定义事件

<div id="todo-list-example">
    <form v-on:submit.prevent="addNewTodo"><!-- 提交时调用add方法newtodo -->
        <label for="new-todo">添加待办</label>
        <input v-model="newTodoText" id="new-todo" placeholder="待办的事"><!-- v-model 双向绑定该元素 -->
        <button>添加</button>
    </form>
    <ul><!-- 调用自定义组件todo-item,带key,props方法导入内容title,绑定按钮事件 -->
        <li is="todo-item" v-for="(todo, index) in todos" v-bind:key="todo.id" v-bind:title="todo.title"
                    v-on:remove="todos.splice(index, 1)"></li>
    </ul>
</div>
<script>
    //自定义组件
    Vue.component('todo-item', {
        template: '\
            <li>\
              {{ title }}\
              <button v-on:click="$emit(\'remove\')">删除</button>\
            </li>\
        ',
        props: ['title']
        })
    //按钮调用了删除方法,携带索引去删除指定元素
    new Vue({
        el: '#todo-list-example',
        data: {
            newTodoText: '',//预声明vmodel的元素
            todos: [{//初始数据
                 id: 1,
                 title: '洗碗',
            },{
                id: 2,
                title: '扔垃圾',
            },{
                id: 3,
                title: '修剪草坪'
            }],
            nextTodoId: 4//初始
            },
            methods: {//添加方法
                addNewTodo: function() {
                    this.todos.push({//todos数组push新数据
                        id: this.nextTodoId++,
                        title: this.newTodoText
                    });
                    this.newTodoText = ''//清空待下次输入
                }
            }
    })
</script>

事件处理

监听事件

v-on可以监听dom的事件,下例是单击事件click,并调用一个方法,用的是表达式

<div id="example-1">
  <button v-on:click="counter += 1">+ 1</button>
  <p>{{ counter }}</p>
</div>
<script>
var example1 = new Vue({
  el: '#example-1',
  data: {
    counter: 0
  }
})
</script>

事件处理方法

调用的方法可以添加在methods中,不必是和上例那样写在v-on里

<div id="example-2">
  <!-- 绑定`greet`方法 -->
  <button v-on:click="greet">Greet</button>
</div>
<script>
var example2 = new Vue({
  el: '#example-2',
  data: {
    name: 'leon'//初始name
  },
  // 在 `methods` 对象中定义方法
  methods: {
    greet: function (event) {
      // 使用`this`访问name 
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM 事件
      if (event) {//输出tagname
        alert(event.target.tagName)
      }
    }
  }
})

//这个方法也可以在外面直接调用
example2.greet()
</script>

内联处理器中的方法

除了绑定方法,也可以使用内联javascript语句,

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
<script>
new Vue({
  el: '#example-3',
  methods: {//say方法
    say: function (message) {
      alert(message)
    }
  }
})
</script>

内联方法如果需要原始dom事件,可以使用特殊遍历$event传入

<button v-on:click="event($event)">
  Submit
</button>
<script>
methods: {
  event: function ( event) {
     // 现在我们可以访问原生事件对象
     console.log(event.target.name); //哈哈
     console.log(event.target.tagName); //BUTTON
  }
}
</script>

事件修饰符

js中的事件处理方法event.preventDefault()在vue中是使用事件修饰符来达到的,这样就只需要考虑逻辑,而不是去处理dom事件

  • .stop 阻止事件继续传播(冒泡传播由内到外)
  • .prevent 拦截默认事件
  • .capture 优先触发事件
  • .self 不参与事件传播,只有直接触发自己的时候才执行
  • .once 只触发一次,不能多次触发
  • .passive 执行默认事件,越过通过preventDefault替换的事件

按键修饰符

在监听键盘事件时,可以监测按键

<input @keyup.enter="submit" />
<!-- 可以将有效按键名使用 kebab-case命名法例如要使用pagedown键: -->
<input @keyup.page-down="onPageDown" />

vue内置了一些按键别名

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

系统修饰键

  • .ctrl
  • .alt
  • .shift
  • .meta win键或command键
  • .exact 精准控制按键修饰符
  • .left 鼠标
  • .right 鼠标
  • .middle 鼠标
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

<!-- 即使 Alt 或 Shift 和Ctrl被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 不能有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

表单输入绑定

基础用法

v-model在表单上可以双向数据绑定,它会在不同的表单项中自动选择合适的方法来更新。

v-model 在内部为不同的输入元素使用不同的 property (原型)并抛出不同的事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

v-model 会忽略所有表单元素的 valuecheckedselected attribute 的初始值而总是将当前活动实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。


<input v-model="message" placeholder="文本 (Text)" />
<p>Message is: {{ message }}</p>


<span>多行文本 (Textarea)</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br />
<textarea v-model="message" placeholder="add multiple lines"></textarea>

<!-- 复选框 (Checkbox) -->
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>

<!-- 多个复选框绑定到同一个数组,注意声明时应为数组:checkedNames:[] -->
<div id="v-model-multiple-checkboxes">
  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
  <label for="jack">Jack</label>
  <input type="checkbox" id="john" value="John" v-model="checkedNames" />
  <label for="john">John</label>
  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
  <label for="mike">Mike</label>
  <br />
  <span>多个复选: {{ checkedNames }}</span>
</div>


<div id="v-model-radiobutton">
  <input type="radio" id="one" value="One" v-model="picked" />
  <label for="one">One</label>
  <br />
  <input type="radio" id="two" value="Two" v-model="picked" />
  <label for="two">Two</label>
  <br />
  <span>单选框: {{ picked }}</span>
</div>


<div id="v-model-select" class="demo">
  <select v-model="selected">
    <option disabled value="">Please select one</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>选择框: {{ selected }}</span>
</div>


<select v-model="selected" multiple>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>
<span>多选选择框: {{ selected }}</span>


<!-- 渲染选择框值与文本不同 -->
<div id="v-model-select-dynamic" class="demo">
  <select v-model="selected">
    <option v-for="option in options" :value="option.value">
      {{ option.text }}
    </option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>

<script>
new Vue({
  el: '...',
  data: {
    selected: 'A',
    options: [
      { text: 'One', value: 'A' },
      { text: 'Two', value: 'B' },
      { text: 'Three', value: 'C' }
    ]
  }
})
</script>

值绑定

对于单选、复选、框选,v-model通常绑定的是静态字符串(复选框也可以是布尔值)

<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a" />

<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle" />

<!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

我们还可以把值绑定到动态属性上

<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
<script>
//选中
vm.toggle === 'yes'
//未选中
vm.toggle === 'no'
</script>

<input type="radio" v-model="pick" v-bind:value="a" />
<script>
data: {
    pick: "",
    a:"baidu"
}
    //vm.pick === vm.a
</script>

<select v-model="selected">
    <!-- 内联对象字面量 -->
    <option :value="{ number: 1 }">a</option>
    <option :value="{ number: 2 }">b</option>            
</select>
<script>
data: {
    selected:"",
}
    //vm.selected => object
    //vm.selected.number => 1 or 2
</script>

修饰符

.lazy默认情况下v-model触发的是input事件,也就是value变动就触发。此修饰符就是使触发事件变为change事件,也就是失去焦点且value有变化才触发。可以防止抖动

.number将用户输入的内容转为数值类型,v-model默认得到的是字符串类型

.trim自动去掉首尾空格

评论已关闭