vue2 基础(一):基础 api

156 阅读13分钟

Vue 介绍

Vue是一套构建用户页面的渐进式框架官网

Vue 被设计为可以自底向上 逐层应用

渐进式

一层一层、一步步来做的事情

渐进式示意图.jpg

  • 声明式渲染
  • 组件系统
  • 路由
    • vue-router
  • 大规模状态管理
    • vuex
  • 构建系统
    • vue-cli
    • vue-test-utils

安装

<script> 引用

CDN

直接用 <script> 标签引入:

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

npm

利用 node_modules :

npm init -y
npm i vue

本地引用:

<script src="./node_modules/vue/dist/vue.js"></script>

本地引用速度更快一些

使用

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    
    <body>
        <div id="app">{{msg}}</div>

        <script>
            const app = new Vue({
                el: "#app", // 选择器
                data: { // 数据
                    // msg => 响应式对象
                    msg: "Hello,World",
                },
            });
        </script>
        
    </body>
</html>

现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。打开你的浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 app.msg 的值,你将看到上例相应地更新。

注意我们不再和 HTML 直接交互了。一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app) 然后对其进行完全控制。那个 HTML 是我们的入口,但其余都会发生在新创建的 Vue 实例内部。

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统(声明式渲染)

  • 模板
  • 数据

vue 模型:

  • view:视图 => dom 节点
  • model:模型 => new Vue() 中的 js 对象
  • viewModel:new Vue()

模板语法渲染

Vue实例

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:

var vm = new Vue({
  // 选项
})

数据与方法

当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。当这些数据改变时,视图会进行重渲染。

只有当实例被创建时就已经存在于 data 中的 property 才是响应式的

Object.freeze()

使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化

var obj = {
  foo: 'bar'
}

Object.freeze(obj)

new Vue({
  el: '#app',
  data: obj
})
<div id="app">
  <p>{{ foo }}</p>
  <!-- 这里的 `foo` 不会更新! -->
  <button v-on:click="foo = 'baz'">Change it</button>
</div>

实例 property 与方法

  • vm
  • 数据与方法
    • $xxx
      • public
    • _xxx
      • private

$ 开头的是 公有对象,以 _ 开头的是 私有对象。

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

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

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})

基础 api

一、模板语法

  • 文本
    • {{msg}}
    • <span v-once>{{ msg }}</span> 使用 v-once 指令,一次性插值,当数据改变时,插值处的内容不会更新。
  • HTML
    • <span v-html="rawHtml"></span> 使用 v-html 指令,输出 HTML。

    动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。

  • attribute
    • v-bind:class="className" 简写::class="className"
    • v-bind:绑定数据(表达式)到指定的属性上,简写为::
  • 支持 js 表达式
    • {{msg + "hello" + 123}}
    • <div :id="'list-' + id"></div>

    每个绑定都只能包含单个表达式。模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 MathDate

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
        <script src="./node_modules/vue/dist/vue.js"></script>
    </head>
    
    <body>
        <div id="app">
            <!-- {{}} -> 文本插值 -->
            <div>{{msg}}</div>
            <!-- 支持表达式 -->
            <div>{{msg + " hello" + 123}}</div>

            <!-- `v-once` 一次性插值。当数据改变时,插值处的内容不会更新 -->
            <div><span v-once>一次性插值:{{ msg }}</span></div>

            <!-- html -->
            <div>html:<span v-html="rawHtml"></span>`</div>

            <!-- v-bind -> 属性 -->
            <!-- <div v-bind:class="className">class</div> -->
            <!-- 简写: -->
            <div :class="className">class</div>
            <!-- 支持表达式 -->
            <div :style="'background:' + className">{{className}}</div>
        </div>
        <script>
            const app = new Vue({
                el: "#app",
                data: {
                    msg: "hello world",
                    className: "blue",
                    rawHtml: '<span style="color: red">This should be red.</span>'
                },
                mounted(){
                    setTimeout(() => {
                        this.msg = '你好'
                    },1500)
                }
            });

            console.log(app);
        </script>
    </body>
</html>

image.png

二、指令

指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

常用指令:

<!-- v-bind -->
<a v-bind:href="url">...</a>
<a :href="url">...</a>

<!-- v-on -->
<a v-on:click="doSomething">...</a>
<a @click="doSomething">...</a>

<!-- v-if -->
<p v-if="false">hbjhg</p>

<!-- v-show -->
<p v-show='false'></p>

<!-- v-for -->
<ul>
    <li v-for='(iten,index) in info' :key='index' >{{item}}</li>
</ul>

动态参数

从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:

<a v-bind:[attributeName]="url"> ... </a>

<a v-on:[eventName]="doSomething">

这里的 attributeName 和 eventName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。

动态参数的约束

    • 预期会求出一个字符串
    • 异常情况下值为 null,可以被显性地用于移除绑定
    • 任何其它非字符串类型的值都将会触发一个警告
  • 表达式
    • 不能使用空格和引号等字符
    • 避免使用大写字符来命名键名(浏览器会把 attribute 名全部强制转为小写)
<!-- 这会触发一个编译警告 -->
<a v-bind:['foo' + bar]="value"> ... </a>
<!-- 可使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式 -->
<!--
在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。
除非在实例中有一个名为“someattr”的 property,否则代码不会工作。
-->
<a v-bind:[someAttr]="value"> ... </a>

修饰符

修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

<form v-on:submit.prevent="onSubmit">...</form>

三、计算属性和侦听器

计算属性 computed

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

<div id="example">
  {{ message.split('').reverse().join('') }}
</div>

对于任何复杂逻辑,你都应当使用计算属性

computed 优点:

  • 可读性
  • 可缓存(基于它们的响应式依赖进行缓存)
computed: {
    reverseMsg() {
        // 可缓存,若 其依赖的属性 不改变,reverseMsg() 就只调用一次
        // 其返回值缓存,当调用 reverseMsg 时使用其缓存的返回值,更节省性能
        console.log(222)
        
        // 本质:依赖别的属性的属性
        // 必须 return
        // `this` 指向 vm 实例
        return this.msg.split("").reverse().join("");
    },
},

计算属性缓存 vs 方法:

<body>
    <div id="app">
        <!-- 模板内的表达式:简单运算 -->
        <!-- <div>{{msg.split("").reverse().join("")}}</div> -->
        
        <!-- 计算属性:复杂逻辑 -->
        <div>computed {{reverseMsg}}</div>

        <div>method {{reversHandler()}}</div>
    </div>
    <script>

        const app = new Vue({
            el: "#app",
            data: {
                // hello world -> dlrow olleh
                msg: "hello world",
                count: 1,
            },
            // 计算属性本质是一个属性,不可以加()
            computed: {
                reverseMsg() {
                    // 若 msg 不改变,reverseMsg() 就只调用一次
                    // 其返回值缓存,当调用 reverseMsg 时使用其缓存的返回值,更节省性能
                    console.log("computed");
                    return this.msg.split("").reverse().join("");
                },
            },

            methods: {
                reversHandler() {
                    // 每次调用都运算一次
                    console.log("methods");
                    return this.msg.split("").reverse().join("");
                },
            },
        });
    </script>
</body>

计算属性的 setter

计算属性默认只有 getter,不过在需要时你也可以提供一个 setter

compured:{
    // 默认只有 getter
    getSongSrc(){
        set(newVal){
            console.log(newVal)  // 索引
            this.currentIndex = newVal  // 改变当前歌曲索引
        }
        get(){
            return this.musicData [this.currentIndex].songSrc; // 切换歌曲
        }
    }
},
methods:{
    handlerClick(index){
        this.getSongSrc = index; // 设置值,调用 set 方法
    }
}

运行 handlerClick 函数 时,setter 会被调用,vm.currentIndex 也会相应地被更新。

带参数的计算

返回一个函数就行

<div class="promotion" v-if="isPromotion(item.products)">
    <div class="promotion-item" v-for="(v, i) in item.products" :key="i">
        {{v.name}}
    </div>
</div>
computed: {
    isPromotion(){
        return function(data){
            let arr = Object.keys(data);
            arr.length == 0 ? false : true;
        }
    }
}

侦听器

当需要在数据变化时执行异步或开销较大的操作时使用

  • deep深度监听,发现对象内部值的变化
  • immediate立马调用
watch: {
    // 懒执行:不会一上来就调用
    count(newVal, oldVal) {
        console.log("count", newVal, oldVal);
        // ...
    },
    
    // 设置 immediate 属性,一上来就调用
    msg: {
        handler(newValue, oldValue) {
            console.log(newValue, oldValue);
            
            // 更新多个值
            this.msg1 = newValue + ":msg1";
            this.msg2 = newValue + ":msg2";
            this.msg3 = newValue + ":msg3";

            // 请求后端接口,获取最新的数据
            axios("test").then((res) => {
                console.log(res);
            });
        },
        immediate: true, // 一上来就调用
    },
    
    
    // 不观察对象的内部变化,仅对象整个改变时调用
    user(newVal, oldVal) {
        console.log("user", newVal, oldVal);
    },
    
    // 设置 deep 属性,深度监听
    user: {
        handler(newValue, oldValue) {
            console.log(newValue, oldValue);
        },
        deep: true,
    }
    
    // 仅观察某个具体的值
    "user.age"(val) {
        console.log("age", val);
    },
},

总结:

计算属性 VS watch :

  • 计算属性:依赖别的属性,返回一个值,生成一个新的属性
  • watch:观察一个属性,进行多种运算,不需返回值。

四、Class 与 Style 绑定

  • class

    • 数组写法:
    <!-- 等同于:class="active text-danger" -->
    <div :class="[activeClass, errorClass]"></div>
    
    <!-- 等同于:class="active text-danger" -->
    <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
    
    <!-- 在数组语法中也可以使用对象语法 -->
    <!-- 等同于:class="active text-danger" -->
    <div v-bind:class="[{ active: isActive }, errorClass]"></div>
    
    data: {
        isActive: true
        activeClass: 'active',
        errorClass: 'text-danger'
    }
    
    • 对象写法:
    <!-- 等同于:class="active text-left" -->
    <div :class="{ active: isActive, 'text-left': hasError }"></div>
    
    <!-- 等同于:class="active" -->
    <div :class="classObject"></div>
    
    <!-- 等同于:class="active text-center" -->
    <div :class="classCom"></div>
    
    data: {
        isActive: true,
        hasError: true,
        error: null
        classObject: {
            active: true,
            'text-danger': false
        }
    },
    computed: {
        classCom: function () {
            return {
                active: this.isActive && !this.error,
                'text-center': this.hasError && !this.error
            }
        }
    }
    
  • style

    • 数组写法:将多个样式对象应用到同一个元素上
    <!-- 等同于:style="color: red; fontSize: 13px; text-align: left; margin-top: 10px;" -->
    <div :style="[style1, style2]"></div>
    
    data: {
        style1: {
            color: 'red',
            fontSize: '13px'
        },
        style2: {
            text-align: 'left',
            margin-top: '10px'
        }
    }
    
    • 对象写法:
    <!-- 等同于:style="color: red; fontSize: 30px;" -->
    <div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
    
    <!-- 等同于:style="color: red; fontSize: 13px;" -->
    <div v-bind:style="styleObject"></div>
    
    data: {
        activeColor: 'red',
        fontSize: 30,
        styleObject: {
            color: 'red',
            fontSize: '13px'
        }
    }
    

驼峰式(camelCase)命名可以不用带引号短横线分隔(kebab-case)命名必须用引号括起来

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

用在组件上

当在一个自定义组件上使用 class 时,这些 class 将被添加到该组件的根元素上面。这个元素上已有的 class 不会被覆盖。

Vue.component('my-component', {
  template: '<p class="foo bar">Hi</p>'
})

<my-component class="baz boo"></my-component>

<!-- HTML 将被渲染为 -->
<p class="foo bar baz boo">Hi</p>

多重值

从 2.3.0 起你可以为 style 绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:

<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex

五、条件渲染 与 列表渲染

条件渲染

  • v-if vs v-show
    • v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建

    • v-if惰性的 :如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

    • v-show 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 display 进行切换

    • 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

    • <template> 元素上使用 v-if,最终的渲染结果不包含 <template> 元素。而 v-show 不支持 <template> 元素。

<body>
    <div id="app">
        <!-- v-if -->
        <div>
            <div v-if="age === 18">"一朵花"</div>
            <div v-else-if="age > 18">"1"</div>
            <div v-else>"2"</div>
        </div>

        <!-- v-if vs v-show -->
        <div>
            <!-- 真正的销毁和重建 -->
            <div v-if="isShow">if</div>
        </div>

        <div>
            <!-- 基于 `display` 进行切换 -->
            <div v-show="isShow">show</div>
        </div>
    </div>
    
    <script>
        const app = new Vue({
            el: "#app",
            data: {
                msg: "hello world",
                age: 18,
                isShow: true,
            },
        });
    </script>
</body>

用 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不会清除用户已经输入的内容因为两个模板使用了相同的元素<input> 不会被替换掉——仅仅是替换了它的 placeholder

给元素添加一个具有唯一值的 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>

现在,每次切换时,输入框都将被重新渲染,而 <label> 元素仍然会被高效地复用

列表渲染

v-for

  • v-for in v-for of

    • 数组:2个参数 (val, index) 值 索引
    • 对象:3个参数 (val, key, index) 值 键名 索引

    在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。

  • 带有 v-for<template>

    • 循环渲染一段包含多个元素的内容
  • v-for 和 v-if 一同使用

  • 设置 key 属性

    • 复用
    • diff 算法优化
<ul>
    <!-- 数组 -->
    <li v-for="(user, index) in user">
      {{user.name}} --{{user.age}} --{{index}}
    </li>

    <!-- 对象 -->
    <li v-for="(user, key, index) of userInfo">
      {{user.name}} --{{user.age}} -- {{key}} --{{index}}
    </li>
</ul>
data: {
    users: [
        { name: "张三", age: 13 },
        { name: "李四", age: 15 },
        { name: "王五", age: 22 },
    ],
    userInfo: {
        zhangsan: {
            name: "xiaohong",
            age: 13,
        },
        lisi: {
            name: "xiaohei",
            age: 15,
        },
        wangwu: {
            name: "xiaohu",
            age: 22,
        },
    }
}

维护状态

使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。

这个默认的模式是高效的,但是只适用于 不依赖子组件状态或临时 DOM 状态(例如:表单输入值) 的列表。

我们可以 通过 :key 属性,来给每个循环节点添加一个标识

<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

建议尽可能在使用 v-for 时提供 key ,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

不要使用对象或数组之类的非基本类型值作为 v-forkey。请用字符串或数值类型的值。

数组更新检测

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新:

  • 变更方法:
    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()
  • 替换方法:返回一个新数组替换原数组
    • filter()
    • concat()
    • slice()

你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。深入响应式原理中有相关的讨论。

显示过滤/排序后的结果

有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。

  • 计算属性
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
    numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
    evenNumbers: function () {
        return this.numbers.filter(function (number) {
            return number % 2 === 0
        })
    }
}
  • 方法
    • 用于在计算属性不适用的情况下(例如 v-for):
<ul v-for="set in sets">
  <li v-for="n in even(set)">{{ n }}</li>
</ul>
data: {
    sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
},
methods: {
    even: function (numbers) {
        return numbers.filter(function (number) {
            return number % 2 === 0
        })
    }
}

v-forv-if 一同使用

当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将重复运行于每个 v-for 循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用:

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

在组件上使用 v-for

在自定义组件上,你可以像在任何普通元素上一样使用 v-for。为了把迭代数据传递到组件里,我们要使用 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 列表的完整例子:

<div id="todo-list-example">
    <form v-on:submit.prevent="addNewTodo">
        <label for="new-todo">Add a todo</label>
        <input v-model="newTodoText" id="new-todo" placeholder="E.g. Feed the cat">
        <button>Add</button>
    </form>
    
    <ul>
        <!-- is="todo-item" 这种做法在使用 DOM 模板时是十分必要的,
        因为在 <ul> 元素内只有 <li> 元素会被看作有效内容。
        这样做实现的效果与 <todo-item> 相同,但是可以避开一些潜在的浏览器解析错误。 -->
        <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>
Vue.component('todo-item', {
    template: '\
        <li>\
            {{ title }}\
            <button v-on:click="$emit(\'remove\')">Remove</button>\
        </li>\
    ',
    props: ['title']
})

new Vue({
    el: '#todo-list-example',
    data: {
        newTodoText: '',
        todos: [
            { id: 1, title: 'Do the dishes' },
            { id: 2, title: 'Take out the trash' },
            { id: 3, title: 'Mow the lawn' }
        ],
        nextTodoId: 4
    },
    methods: {
        addNewTodo: function () {
            this.todos.push({
                id: this.nextTodoId++,
                title: this.newTodoText
            })
            this.newTodoText = ''
        }
    }
})

DOM 模板解析说明

六、事件处理

v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。简写:@

<!-- js 表达式 -->
<button @click="counter += 1">Add 1</button>

<!-- 绑定方法 -->
<button @click="greet">sayHi</button>

<!-- 调用方法 -->
<button @click="say('what')">Say what</button>

<!-- 访问原始的 DOM 事件,用特殊变量 $event 把它传入方法 -->
<button @click="warn('Form cannot be submitted yet.', $event)">Submit</button>
data: {
    counter: 0
},
methods: {
    sayHi () {
        // `this` 在方法里指向当前 Vue 实例
        alert('Hello world!')
    },
    say (message) {
        alert(message)
    },
    warn: function (message, event) {
        // 现在我们可以访问原生事件对象
        if (event) {
            event.preventDefault()
        }
        alert(message)
    }
}

使用 v-on 的好处:

  • 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
  • 无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
  • 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。

修饰符

事件修饰符

  • .once 只触发一次
    • 可用于自定义的组件事件上
  • .stop 阻止事件冒泡
  • .capture 事件捕获
  • .self 元素自身点击时才触发
  • .prevent 阻止事件的默认行为
  • .passive 不阻止事件的默认行为
<!-- 事件只触发一次 -->
<a @click.once="doThis"></a>

<!-- 阻止事件冒泡 -->
<a @click.stop="doThis"></a>

<!-- 事件捕获 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div @click.capture="doThis">...</div>

<!-- 元素自身点击时才触发 -->
<div @click.self="doThat">...</div>

<!-- 阻止表单提交 -->
<form @submit.prevent="onSubmit"></form>

<!-- 不阻止事件的默认行为 -->
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<!-- 不要把 `.passive` 和 `.prevent` 一起使用,`.prevent` 将会被忽略 -->
<div @scroll.passive="onScroll">...</div>


<!-- 修饰符串联 -->
<a @click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form @submit.prevent></form>

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 @click.prevent.self阻止所有的点击,而 @click.self.prevent 只会阻止对元素自身的点击

按键修饰符

可将 KeyboardEvent.key 暴露的有效按键名转换为 kebab-case 来作为修饰符。使用 keyCode 也是允许的。

为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
<!-- 按下 `Enter` 时调用 -->
<input @keyup.enter="submit">

<!-- 按下 `PageDown` 时调用 -->
<input @keyup.page-down="onPageDown">

<!-- 按下 `PageDown` 时调用 -->
<input @keyup.13="submit">

你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名

// 可以使用 `@keyup.f1`
Vue.config.keyCodes.f1 = 112

keyCode 的事件用法已经被废弃了并可能不会被最新的浏览器支持。

系统修饰键

可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。

  • .ctrl
  • .alt
  • .shift
  • .meta
    • Mac -> command 键 (⌘)
    • Windows -> Windows 徽标键 (⊞)
    • Sun -> 实心宝石键 (◆)
  • .exact精确的系统修饰符组合触发事件

在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。

<!-- Alt + C -->
<input @keyup.alt.67="clear">

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


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

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

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

鼠标按钮修饰符

  • .left 左键
  • .right 右键
  • .middle 滚轮

七、表单输入绑定

你可以用 v-model 指令在表单元素上创建双向数据绑定(本质是个语法糖:(伪)双向数据绑定)监听用户的输入事件以更新数据

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

<input type="text" v-model="title" />

等价于:

<input type="text" :value="title" @input="title = $event.target.value">

v-model 在内部为不同的输入元素使用不同的 属性 并抛出不同的事件:

  • texttextarea
    • value 属性 和 input 事件;
  • checkboxradio
    • checked 属性 和 change 事件;
  • select
    • value 作为 prop 并将 change 作为事件。

对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件。

<input v-model="message" placeholder="edit me">

<textarea v-model="message" placeholder="add multiple lines"></textarea>

<!-- 单个复选框,绑定到布尔值:checked: false -->
<input type="checkbox" id="checkbox" v-model="checked">

<!-- 多个复选框,绑定到同一个数组:checkedNames: [] -->
<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>

<!-- 单选按钮,绑定到同一个值:picked: '' -->
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>

<!-- 下拉选框,绑定到同一个值:selected: '' -->
<select v-model="selected">
    <option disabled value="">请选择</option>
    <option value="A">A</option>
    <option value="B">B</option>
    <option value="C">C</option>
</select>

<!-- 单选按钮,选中为 'yes',未选中为 'no' -->
<input type="radio" v-model="toggle" true-value="yes" false-value="no">

<!-- 下拉选框,选项为一个对象 -->
<select v-model="selected">
    <!-- 内联对象字面量 -->
    <option :value="{ number: 123 }">123</option>
</select>

如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项

修饰符

  • .lazy:在 change 时而非 input 时更新
  • .number:将输入值转为数值类型
  • .trim:过滤用户输入的首尾空白字符
<!-- 在 change 时而非 input 时更新 -->
<input v-model.lazy="msg">

<!-- 将输入值转为数值类型 -->
<input v-model.number="age" type="number">

<!-- 过滤用户输入的首尾空白字符 -->
<input v-model.trim="msg">

在组件上使用 v-model

第二篇有。。。

动态切换组件(同一组件切换数据),dom不更新,$nextTick

www.cnblogs.com/wiliam/p/11…

过滤器

Vue 2.x 中,过滤器只能在 mustache 绑定和 v-bind 表达式(从 2.1.0 开始支持)中使用。 在组件内部用filters:{ 过滤器名:function(value){ return ... } } 调用过滤器:数据 | 过滤器名

1、局部过滤器

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

2、全局过滤器

Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

当全局过滤器和局部过滤器重名时,会采用局部过滤器。 3、过滤器可串联

{{ message | filterA | filterB }}

在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。

4、过滤器可接收参数:

{{ message | filterA('arg1', arg2) }}

这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。

<h1>{{msg | filterA('hehe')}}</h1>

data(){
	return{
	msg:'hello'
	}
}

vue.filter('filterA',function(value,arg1){
	return arg1+' '+value.split('').reverse().join('');//翻转
}

结果是:hehe olleh