# vue基础2

# 表单输入处理

v-model指令在表单<input>,<textarea>,<select>元素上创建双向数据绑定,能根据控件类型自动选取正确的方法来更新元素。但是本质上v-model是语法糖,负责监听用户的输入事件以更新数据,并对一些极端场景进行特殊处理。

v-model会忽略所有表单元素的value,checked,selected属性的初始值而总是把Vue实例的数据作为数据来源。它在内部为不同的输入元素使用不同的属性和事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>vuejs测试</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            {{message}}
            <input v-model="message">
            <input v-bind:value="message" v-on:input="message = $event.target.value">
            <input :value="message" @input="handleChange">
        </div>
        <script type="text/javascript">
            var app = new Vue({
                el: '#app',
                data: {
                    message:"init info!",
                },
                methods:{
                    handleChange(e){
                        this.message=e.target.value;
                    },
                }
            });
        </script>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 基本使用

下面是v-model的使用样例:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>vuejs测试</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <div>
                单行文本<br>
                <input v-model="message" placeholder="edit me">
                <p>Message is: {{ message }}</p>
            </div>

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

            <div>
                单个复选框<br>
                <input type="checkbox" id="checkbox" v-model="checked">
                <label for="checkbox">{{ checked }}</label>
            </div>

            <div>
                多个复选框<br>
                <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>Checked names: {{ checkedNames }}</span>
            </div>

            <div>
                单选按钮<br>
                <div id="example-4">
                    <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: {{ picked }}</span>
                </div>
            </div>

            <div>
                选择框<br>
                <div id="example-5">
                    <select v-model="selected">
                        <option disabled value="">请选择</option>
                        <option>A</option>
                        <option>B</option>
                        <option>C</option>
                    </select>
                    <span>Selected: {{ selected }}</span>
                </div>
            </div>

            <div>
                多选选择框<br>
                <div id="example-6">
                    <select v-model="selecteds" multiple style="width: 50px;">
                        <option>A</option>
                        <option>B</option>
                        <option>C</option>
                    </select>
                    <br>
                    <span>Selected: {{ selecteds }}</span>
                </div>
            </div>

            <div>
                动态渲染的选择框<br>
                <select v-model="selected2">
                    <option v-for="option in options" v-bind:value="option.value">
                        {{ option.text }}
                    </option>
                </select>
                <span>Selected: {{ selected2 }}</span>
            </div>
        </div>
        <script type="text/javascript">
            var app = new Vue({
                el: '#app',
                data: {
                    message: "init info!",
                    checkedNames: [],
                    checked:'',
                    picked: '',
                    selected: '',
                    selecteds: [],
                    selected2: 'A',
                    options: [{
                            text: 'One',
                            value: 'A'
                        },
                        {
                            text: 'Two',
                            value: 'B'
                        },
                        {
                            text: 'Three',
                            value: 'C'
                        }
                    ],
                },
            });
        </script>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

# 值绑定

对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>vuejs测试</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <div>
                 当选中时,`picked` 为字符串 "a"
                <input type="radio" v-model="picked" v-bind:value="a">
                {{picked}}
            </div>

            <div>
                `toggle` 为 true 或 false
                <input type="checkbox" v-model="toggle1">
                {{toggle1}}
            </div>

            <div>
                当选中第一个选项时,`selected` 为字符串 "abc"
                <select v-model="selected">
                    <option v-bind:value="{ number: 123 }">123</option>
                </select>
                {{selected}}
            </div>
            
            <div>
                复选框绑定值
                <input
                  type="checkbox"
                  v-model="toggle2"
                  true-value="yes1"
                  false-value="no2"
                >
                {{toggle2}}
            </div>
        </div>
        <script type="text/javascript">
            var app = new Vue({
                el: '#app',
                data: {
                    a:1,
                    toggle1: '',
                    toggle2: '',
                    picked: '',
                    selected: '',

                },
            });
        </script>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 修饰符

vue还可以使用修饰符来修改默认行为:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>vuejs测试</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <div>
                将input事件改为change事件之后{{message}}
                <input v-model.lazy="message" placeholder="edit me">
            </div>
            <div>
                自动转化用户输入为number:{{num}}
                <input v-model.number="num" type="number">
            </div>
            <div>
                过滤首尾字符串:{{testStr}}
                <input v-model.trim="testStr" >
            </div>
        </div>
        <script type="text/javascript">
            var app = new Vue({
                el: '#app',
                data: {
                    message: "",
                    num:100,
                    testStr:"n"
                },
            });
        </script>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 事件处理

前面我们提到可以用v-on来监听DOM事件,并触发时运行一些JavaScript代码:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <title>vuejs测试</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>

    <body>
        <div id="example-1">
            <button v-on:click="counter += 1">Add 1</button>
            <p>The button above has been clicked {{ counter }} times.</p>
        </div>
        <script type="text/javascript">
            var example1 = new Vue({
                el: '#example-1',
                data: {
                    counter: 0
                }
            })
        </script>

        <div id="example-2">
            <!-- `greet` 是在下面定义的方法名 -->
            <button v-on:click="greet">Greet</button>
        </div>
        <script type="text/javascript">
            var example2 = new Vue({
                el: '#example-2',
                data: {
                    name: 'Vue.js'
                },
                // 在 `methods` 对象中定义方法
                methods: {
                    greet: function(event) {
                        // `this` 在方法里指向当前 Vue 实例
                        alert('Hello ' + this.name + '!')
                        // `event` 是原生 DOM 事件
                        if (event) {
                            alert(event.target.tagName)
                        }
                    }
                }
            })

            // 也可以用 JavaScript 直接调用方法
            example2.greet() // => 'Hello Vue.js!'
        </script>
        <div id="example-3">
            <button v-on:click="say('hi')">Say hi</button>
            <button v-on:click="say('what')">Say what</button>
        </div>
        <script type="text/javascript">
            new Vue({
                el: '#example-3',
                methods: {
                    say: function(message) {
                        alert(message)
                    }
                }
            })
        </script>

    </body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# 事件修饰符

在事件处理过程中调用event.preventDefault() 或 event.stopPropagation()是常见的需求,我们认为方法应该只有纯粹的数据逻辑,而不去处理DOM事件细节。否则就得像下面这样:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <title>vuejs测试</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>

    <body>
        <div id="example-1">
            <div v-on:click="alertinfo">测试父类
                <button v-on:click="warn('Form cannot be submitted yet.', $event)">
                    测试结果1
                </button>
                <button v-on:click.stop="warn2">测试结果2</button>
            </div>
        </div>
        <script type="text/javascript">
            var example1 = new Vue({
                el: '#example-1',
                data: {
                    counter: 0
                },
                methods: {
                    warn: function(message, event) {
                        // 现在我们可以访问原生事件对象
                        if (event) {
                            //event.preventDefault()
                            event.stopPropagation();
                        }
                        alert(message)
                    },
                    alertinfo: function() {
                        alert("测试结果1!");
                    },
                    warn2:function(){
                        alert("测试结果2!");
                    }
                }
            })
        </script>

    </body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

为了解决这个问题,vue提供了事件修饰符,上面的.stop就是使用的事件修饰符,除此之外vue还提供了如下事件修饰符:

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive

它们的使用方式如下:

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

<!-- 不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

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

# sync修饰符

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。官方推荐以 update:myPropName 的模式触发事件取而代之。

比如用下面的方法来对其赋值:

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

this.$emit('update:title', newTitle)
1
2
3
4
5
6

为了方便起见,可以sync修饰符简化:

<text-document v-bind:title.sync="doc.title"></text-document>
1

注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync="doc.title + '!'" 是无效的)。取而代之的是,你只能提供你想要绑定的 property 名,类似 v-model。

当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync 修饰符和 v-bind 配合使用,这样会把 doc 对象中的每一个 property (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器。

<text-document v-bind.sync="doc"></text-document>
1

下面是一个使用sync的例子:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <title></title>
    </head>

    <body>

        <div id="vueComponent">
            <div class="details">
                <myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
                <button @click="changeValue">toggle</button>
            </div>
        </div>


        <script>
            Vue.component('mycomponent', {
                template: `<div v-if="show">
                    <p>默认初始值是{{show}},所以是显示的</p>
                    <button @click.stop="closeDiv">关闭</button>
                 </div>`,
                props: ['show'],
                methods: {
                    closeDiv() {
                        this.$emit('update:show', false); //触发 input 事件,并传入新值
                    }
                }
            })


            var vm2 = new Vue({
                el: "#vueComponent",
                data: {
                    valueChild: true,
                },
                methods: {
                    changeValue() {
                        this.valueChild = !this.valueChild
                    }
                },
            });
        </script>

    </body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 按键修饰符

在监听键盘鼠标时我们经常需要检查详细的按键,vue允许为其添加按键修饰符,常用的按键码有:

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

<input v-on:keyup.page-down="onPageDown">

<!--可以使用keycode-->
<input v-on:keyup.13="submit">
1
2
3
4
5
6
7

# 系统修饰键

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

  • .ctrl
  • .alt
  • .shift
  • .meta
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>s
1
2
3
4
5

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件:

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

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

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>
1
2
3
4
5
6
7
8

鼠标按钮修饰符:

  • .left
  • .right
  • .middle

# Axios

Vuejs并没有直接处理ajax的组件,可使用axios和vue-resource组件完成对异步请求的操作,当vue更新到2.0后,作者宣告不再对vue-resource更新,而是推荐axios。

Axios是一个基于promise的HTTP库,可以用在浏览器和node.js中。它的特性有:

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF

# 使用方法

文档详见axios中文文档 (opens new window)

axios的API主要有两种:

  1. axios(config)
  2. axios(url[, config])

比如:

// 发送 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});
1
2
3
4
5
6
7
8
9

但是为了方便,为所有支持的方法提供了别名:

  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.options(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
// 上面的请求也可以这样做
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

//执行多个并发请求
function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  }));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

还有处理并发请求的助手函数:

  • axios.all(iterable)
  • axios.spread(callback)

其他关于axios的配置及其响应结构可去官方文档查阅。

# vue-axios

axios基于vue做了轻度整合,可使用npm安装:

npm install --save axios vue-axios

在代码入口引入,vue,axios,vue-axios:

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios)
1
2
3
4
5

下面就可以使用:

Vue.axios.get(api).then((response) => {
  console.log(response.data)
})

this.axios.get(api).then((response) => {
  console.log(response.data)
})

this.$http.get(api).then((response) => {
  console.log(response.data)
})
1
2
3
4
5
6
7
8
9
10
11

# 案例

下面出自vue官方文档-侦听器 (opens new window)

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <title>vuejs测试</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>

    <body>
        <div id="watch-example">
            <p>
                Ask a yes/no question:
                <input v-model="question">
            </p>
            <p>{{ answer }}</p>
        </div>
        <!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
        <!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
        <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>
            var watchExampleVM = new Vue({
                el: '#watch-example',
                data: {
                    question: '',
                    answer: 'I cannot give you an answer until you ask a question!'
                },
                watch: {
                    // 如果 `question` 发生改变,这个函数就会运行
                    question: function(newQuestion, oldQuestion) {
                        this.answer = 'Waiting for you to stop typing...'
                        this.debouncedGetAnswer()
                    }
                },
                created: function() {
                    // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
                    // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
                    // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
                    // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
                    // 请参考:https://lodash.com/docs#debounce
                    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
                },
                methods: {
                    getAnswer: function() {
                        if (this.question.indexOf('?') === -1) {
                            this.answer = 'Questions usually contain a question mark. ;-)'
                            return
                        }
                        this.answer = 'Thinking...'
                        var vm = this
                        axios.get('https://yesno.wtf/api')
                            .then(function(response) {
                                vm.answer = _.capitalize(response.data.answer)
                            })
                            .catch(function(error) {
                                vm.answer = 'Error! Could not reach the API. ' + error
                            })
                    }
                }
            })
        </script>

    </body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67