Vue第二次见面

前言

第二次要与VUE见面了,好兴奋啊!虽然不知道为啥兴奋呢!

Vue我的首次遇见之后,我又要开始我的“操作了”。

基本创建都在 Vue我的首次遇见 中。

这里面内容大都来自runoob
因为我这里用的是router,所以作了部分修改

Vue.js 模板语法

Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。

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

结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。


插值

文本

数据绑定最常见的形式就是使用

1
{{...}}

(双大括号)的文本插值:

打开 index.html

1
2
3
4
5
6
7
<div id="app"></div>
<!-- built files will be auto injected -->
<div id="vue_det">
<h1>author : {{author}}</h1>
<h1>date : {{date}}</h1>
<h1>{{description()}}</h1>
</div>

打开 src/main.js,这里面放的是当前routerjs脚本的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在最后添加 ,因为最上面已经添加了 import Vue from 'vue' 所以就可以直接用了
// eslint-disable-next-line no-unused-vars
new Vue({
el: '#vue_det',
data: {
author: 'maxzhao',
date: '2019-04-24'
},
methods: {
description: function () {
return this.author + ' - 学的不仅是技术,更是梦想!'
}
}
})

保存之后刷新页面(如果是按照我的安装,则自动添加热加载);

image.png

Html

使用 v-html 指令用于输出 html 代码:

打开 index.html

1
2
3
4
5
6
7
8
<div id="app"></div>
<!-- built files will be auto injected -->
<div id="vue_det">
<h1>author : {{author}}</h1>
<h1>date : {{date}}</h1>
<h1>{{description()}}</h1>
<div v-html="message"></div>
</div>

打开 src/main.js

1
2
3
4
5
6
7
8
new Vue({
el: '#vue_det',
data: {
author: 'maxzhao',
date: '2019-04-24',
message: '<h1>maxzhao_v-html</h1>'
},
//****

自己测试前段界面哦。

属性

HTML 属性中的值应使用 v-bind 指令。

以下实例判断 class1 的值,如果为 true 使用 class1 类的样式,否则不使用该类:

v-bind 指令

打开 index.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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue_maxzhao</title>
<style>
.class1{
background: #444;
color: #eee;
}
</style>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<div id="vue_det">
<h1>author : {{author}}</h1>
<h1>date : {{date}}</h1>
<h1>{{description()}}</h1>
<div v-html="message"></div>
<label for="r1">修改颜色</label><input type="checkbox" v-model="use" id="r1">
<br><br>
<div v-bind:class="{'class1': use}">
v-bind:class 指令
</div>
</div>
</body>
</html>

打开 src/main.jsdata 中添加,use: false

表达式

Vue.js 都提供了完全的 JavaScript 表达式支持。

打开 index.html

1
2
3
4
5
6
7
8
9
<!--  **   -->      
<div v-bind:class="{'class1': use}">
v-bind:class 指令
</div>
<br>
{{5+5}}<br>
{{ ok ? 'YES' : 'NO' }}<br>
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id">maxzhao</div>

打开 src/main.jsdata 中添加

1
2
3
use: false,
ok: true,
id: 1

指令

指令是带有 v- 前缀的特殊属性。

指令用于在表达式的值改变时,将某些行为应用到 DOM 上。如下例子:

v-if

打开 index.html,接着添加:

1
2
3
4
5
6
7
<br>
<p v-if="seen">现在你看到我了</p>
<template v-if="ok">
<h1>maxzhao</h1>
<p>学的不仅是技术,更是梦想!</p>
<p>哈哈哈,打字辛苦啊!!!</p>
</template>

打开 src/main.jsdata 中添加

1
seen: true

这里, v-if 指令将根据表达式 seen 的值(true 或 false )来决定是否插入 p 元素。

参数

参数在指令后以冒号指明。例如, v-bind 指令被用来响应地更新 HTML 属性:

打开 index.html,接着添加:

1
2
<br>
<pre><a v-bind:href="url">菜鸟教程</a></pre>

打开 src/main.jsdata 中添加

1
url: 'http://localhost:8080'

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。

另一个例子是 v-on 指令,它用于监听 DOM 事件:

打开 index.html,接着添加:

1
<pre><a v-on:click="alertMsg">弹出警示</a></pre>

打开 src/main.jsmethods 中添加

1
2
3
4
5
6
7
8
methods: {
description: function () {
return this.author + ' - 学的不仅是技术,更是梦想!'
},
alertMsg: function () {
alert('弹出警示')
}
}

在这里参数是监听的事件名。

修饰符

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

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


用户输入

在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定:

双向数据绑定

打开 index.html,接着添加:

1
2
<p>{{ message }}</p>
<input v-model="message">

v-model 指令用来在 input、select、textarea、checkbox、radio 等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。

按钮的事件我们可以使用 v-on 监听事件,并对用户的输入进行响应。

以下实例在用户点击按钮后对字符串进行反转操作:

字符串反转

打开 index.html,接着添加:

1
2
<p>{{ message }}</p>
<input v-model="message">

main.js中的 methods 中添加

1
2
3
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}

现在大家已经可以知道数据和方法的“存放处”了,接下来就不在详细叙述了,只是走流程。


过滤器

Vue.js 允许你自定义过滤器,被用作一些常见的文本格式化。由”管道符”指示, 格式如下:

1
<h1>{{ message | reverseMessage }}</h1>

过滤器函数接受表达式的值作为第一个参数。

以下实例对输入的字符串第一个字母反转

1
2
3
4
5
6
7
8
9
10
11
12
13
// 与methods同级别添加filters属性
filters: {
reverseMessage: function (value) {
if (!value) return ''
value = value.toString()
return value.split('').reverse().join('')
},
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(11).toUpperCase() + value.slice(1)
}
}

过滤器可以串联:

1
<h1>{{ message |reverseMessage | capitalize }}</h1>

过滤器是 JavaScript 函数,因此可以接受参数:

1
<h1>{{ message |reverseMessage | capitalize | addStr('--maxzhao--') }}</h1>
1
2
3
4
5
addStr: function (value, addStr) {
if (!value) return ''
value = value.toString()
return value + addStr
}

这里,message 是第一个参数,字符串 ‘arg1’ 将传给过滤器作为第二个参数, arg2 表达式的值将被求值然后传给过滤器作为第三个参数。


缩写

v-bind 缩写

Vue.js 为两个最为常用的指令提供了特别的缩写:

1
2
<!-- 完整语法 -->  <a  v-bind:href="url"></a>  
<!-- 缩写 --> <a :href="url"></a></pre>

v-on 缩写

1
2
3
<a  v-on:click="doSomething"></a>  
<!-- 缩写 -->
<a @click="doSomething"></a>

起步结束了,下面开始一些有难度的操作(学过之后简直毫无难度)。

Vue.js 条件与循环

条件判断

v-if 指令

条件判断使用 v-if 指令:

在元素 和 template 中使用 v-if 指令:

1
2
3
4
5
<template v-if="ok">
<h1>maxzhao</h1>
<p>学的不仅是技术,更是梦想!</p>
<p>哈哈哈,打字辛苦啊!!!</p>
</template>

这里, v-if 指令将根据表达式 ok的值(true 或 false )来决定是否插入template元素。

在字符串模板中,如 Handlebars ,我们得像这样写一个条件块:

1
2
3
4
<!-- Handlebars 模板,暂时还不知道有啥用 -->
{{#if ok}}
<h1>Yes</h1>
{{/if}}

v-else 指令

可以用 v-else 指令给 v-if 添加一个 “else” 块:

随机生成一个数字,判断是否大于0.5,然后输出对应信息:

1
2
3
4
5
6
<div v-if="Math.random() > 0.5">
Sorry
</div>
<div v-else>
Not sorry
</div>

v-else-if 指令

v-else-if 在 2.1.0 新增,顾名思义,用作 v-if 的 else-if 块。可以链式的多次使用:

判断 type 变量的值:

1
2
3
4
5
6
7
8
9
10
11
12
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
1
2
3
data: {
type: 'C'
}

v-else 、v-else-if 必须跟在 v-if 或者 v-else-if之后。

v-show 指令

我们也可以使用 v-show 指令来根据条件展示元素:

1
2
<h1 v-show="true">Hello!</h1>
<h1 v-show="false">don't show!</h1>

Vue.js 循环语句

v-for 指令

循环使用 v-for 指令。

v-for 指令需要以 site in sites 形式的特殊语法, sites 是源数据数组并且 site 是数组元素迭代的别名。

v-for 可以绑定数据到数组来渲染一个列表:

1
2
3
4
5
<ol>
<li v-for="site in sites">
{{ site.name }}
</li>
</ol>
1
2
3
4
5
6
7
data: {
sites: [
{ name: 'Runoob' },
{ name: 'Google' },
{ name: 'Taobao' }
]
}

模板中使用 v-for:

v-for 迭代对象

v-for 可以通过一个对象的属性来迭代数据:

1
2
3
4
5
<ul>
<li v-for="value in object">
{{ value }}
</li>
</ul>
1
2
3
4
5
6
7
8
  data: {
object: {
name: '菜鸟教程',
url: 'http://www.runoob.com',
slogan: '学的不仅是技术,更是梦想!'
}
}
})

第二个的参数为键名:

1
2
3
4
5
6
  <ul>
<li v-for="(value, key) in object">
{{ key }} : {{ value }}
</li>
</ul>
</div>

第三个参数为索引:

1
2
3
4
5
<ul>
<li v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</li>
</ul>

v-for 迭代整数

v-for 也可以循环整数

1
2
3
4
5
<ul>
<li v-for="n in 10">
{{ n }}
</li>
</ul>

v-for指令界面效果

2019-04-25 11-49-53 的屏幕截图.png

Vue.js 计算属性

计算属性关键词: computed。

计算属性在处理一些复杂逻辑时是很有用的。

可以看下以下反转字符串的例子:

1
2
3
4
5
6
<div>
{{ message.split('').reverse().join('') }}
</div>
<div>
{{ reversedMessage }}
</div>
1
2
3
4
5
6
7
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}

实例中声明了一个计算属性 reversedMessage 。

提供的函数将用作属性 vm.reversedMessage 的 getter 。

vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。

computed vs methods

我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行。

可以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。

computed setter

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

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
var vm = new Vue({
el: '#vue_det',
data: {
//****
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
},
site: {
// getter
get: function () {
return this.author + ' ' + this.slogan
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.author = names[0]
this.slogan = names[names.length - 1]
}
}
}
})
// 调用 setter, vm.name 和 vm.url 也会被对应更新
vm.site = 'maxzhao 学的不仅是技术,更是梦想!'
document.write('author: ' + vm.author)
document.write('<br>')
document.write('slogan: ' + vm.slogan)
document.write('<br>')
document.write('site: ' + vm.site)

引:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 把代码改了改,应该可以体现 computer 属性“依赖缓存”的概念以及与 method 的差别。如下面代码,cnt 是独立于 vm 对象的变量。在使用 reversedMessage 这个计算属性的时候,第一次会执行代码,得到一个值,以后再使用 reversedMessage 这个计算属性,因为 vm 对象没有发生改变,于是界面渲染就直接用这个值,不再重复执行代码。而 reversedMessage2 没有这个缓存,只要用一次,函数代码就执行一次,于是每次返回值都不一样。

var cnt=1;
var vm = new Vue({
el: '#app',
data: {
message: 'Runoob!'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
cnt+=1;
return cnt+this.message.split('').reverse().join('')
}
},
methods: {
reversedMessage2: function () {
cnt+=1;
return cnt+this.message.split('').reverse().join('')
}
}
})

Vue.js 监听属性

本章节,我们将为大家介绍 Vue.js 监听属性 watch,我们可以通过 watch 来响应数据的变化。

以下实例通过使用 watch 实现计数器:

1
2
3
4
<div>
<p style="font-size:25px;">计数器: {{ counter }}</p>
<button @click="counter++" style="font-size:25px;">点我</button>
</div>
1
2
3
4
5
6
7
8
9
10
var vm = new Vue({
//*******************
data: {
counter: 1
//*******************
});
// F12 打开开发者模式控制台观察效果
vm.$watch('counter', function(nval, oval) {
console.log('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
});

千米和米的换算

1
2
3
4
5
<br>
<div>
千米 : <input type="text" v-model="kilometers">
米 : <input type="text" v-model="meters">
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
data: {
//*************
kilometers : 0,
meters:0
},
watch : {
kilometers: function (newVal, oldVal) {
console.log('newVal=' + newVal + ' oldVal=' + oldVal)
this.kilometers = newVal
this.meters = this.kilometers * 1000
},
meters: function (newVal) {
this.kilometers = newVal / 1000
this.meters = newVal
}
}

控制台为什么会打印两次呢?因为kilometers改变执行一次,并且改变了 meters,所以执行的meters改变(又改变了kilometers),所以又打印了一次,但因为 meters在此时并没有改变,所以没有后续触发。

Vue.js 样式绑定

Vue.js class

class 与 style 是 HTML 元素的属性,用于设置元素的样式,我们可以用 v-bind 来设置样式属性。

Vue.js v-bind 在处理 class 和 style 时, 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。

class 属性绑定

我们可以为 v-bind:class 设置一个对象,从而动态的切换 class:

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
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue_maxzhao</title>
<style>
.class1 {
background: #444;
color: #eee;
}
.active {
background: #42b983;
}
.text-danger {
background: red;
}
</style>
</head>
<!-- ***************** -->
<!-- ***************** -->
<div v-bind:class="{ active: isActive }"></div>
<!-- 多个 -->
<div class="active"></div>
<div :class="{ active: isActive , 'text-danger': true }">
BBBBBBBBBBBBB
</div>
<!-- 对象 -->
<div :class="classObj">
AAAAAAAAAAAAAAAAAAAAAAAAAAA
</div>
<!-- 数组 , 等同于 class="active text-danger"-->
<div :class="[activeClass, errorClass]">
AAAAAAAAAAAAAAAAAAAAAAAAAAA
</div>
<!-- 数组中添加表达式-->
<div :class="[activeClass, true?errorClass:'']">C</div>
<div :class="[activeClass, false?errorClass:'']">D</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
data: {
//**********************
isActive: true,
activeClass: 'active',
errorClass: 'text-danger'
}
computed: {
//**********************
classObj: function () {
return {
active: true,
'text-danger': true
}
}
}

Vue.js style(内联样式)

这里的所有操作就不写在 js 中了,表达式等都是可以直接写在页面上的。

1
2
3
4
5
6
7
8
9
10
11
<br>
<!-- v-bind 的缩写还记得吧? ":"哦 -->
<div :style="{ color: 'green', fontSize: 30 + 'px' }">
maxzhao_style——普通绑定,'green'、30 为值
</div>
<div :style="{color: 'green',fontSize: '30px'}">
maxzhao_style——对象绑定,{color: 'green',fontSize: '30px'} 是对象
</div>
<div :style="[{color: 'green',fontSize: '30px'},{'font-weight': 'bold'}]">
maxzhao_style——数组对象绑定,[{color: 'green',fontSize: '30px'},{'font-weight': 'bold'}] 是数组对象绑定
</div>

Vue.js 事件处理器

v-on

事件监听可以使用 v-on 指令。

通常情况下,我们需要使用一个方法来调用 JavaScript 方法。

v-on 可以接收一个定义的方法来调用。

1
2
3
4
5
<br>
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
<button @click="say('hi')">Say hi</button>
<button @click="say('what')">Say what</button>

js 中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
methods: {
greet: function (event) {
// `this` 在方法里指当前 Vue 实例
console.log('Hello ' + this.author + '!')
// `event` 是原生 DOM 事件
if (event) {
console.log(event.target.tagName)
}
},
say: function (message) {
console.log(message)
}
}
// 还可以在最外层添加 vm.greet()来调用当前方法,直接调用的时候,event并不存在,因为没有点击事件

事件修饰符

Vue.js 为 v-on 提供了事件修饰符来处理 DOM 事件细节,如:event.preventDefault() 或 event.stopPropagation()。

Vue.js通过由点(.)表示的指令后缀来调用修饰符。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<br>
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis">click.stop</a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit">submit.prevent</form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat">click.stop.prevent</a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent>submit.prevent</form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>
<br>

按键修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 需要js methods
methods: {
clear: function (event) {
console.log('------ clear ----------')
if (event) {
console.log(event.target.tagName)
}
},
submit: function (event) {
console.log('------ submit ----------')
if (event) {
console.log(event.target.tagName)
console.log(event.keyCode)//输出当前key code的值
}
},
doSomething: function (event) {
console.log('------ doSomething ----------')
if (event) {
console.log(event.target.tagName)
console.log(event.keyCode)//输出当前ctrlKey 的bool值
}
}
//*****************************

Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

1
2
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input @keyup.13="submit" value="keyup = 13 ">

记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:

1
2
3
4
5
6
7
8
9
<!-- 同上 -->
<input v-on:keyup.enter="submit" value="keyup = 13 ">
<!-- 缩写语法 -->
<input @keyup.enter="submit" value="enter">
<!-- Alt + C -->
<input @keyup.alt.67="clear" value="Alt + C">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Ctrl + Click will Do something</div>
<br>

全部的按键别名:

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

Vue.js 表单

可以用 v-model 指令在表单控件元素上创建双向数据绑定。

v-model 会根据控件类型自动选取正确的方法来更新元素。\

常规表单

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
  <p>input 元素:</p>
<input v-model="author" placeholder="编辑我……">
<p>author: {{ author }}</p>

<p>textarea 元素:</p>
<p style="white-space: pre">{{ message }}</p>
<textarea v-model="message" placeholder="多行文本输入……">
</textarea>
<br>
<!-- for 后面加 checkbox或radio 的id,点击会选中或取消选中 -->
<p>单个复选框:</p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<p>多个复选框:</p>
<input type="checkbox" id="runoob" value="Runoob" v-model="checkedNames">
<label for="runoob">Runoob</label>
<input type="checkbox" id="google" value="Google" v-model="checkedNames">
<label for="google">Google</label>
<input type="checkbox" id="taobao" value="Taobao" v-model="checkedNames">
<label for="taobao">taobao</label>
<br>
<span>选择的值为: {{ checkedNames }}</span>
<br>
<div>
<input type="radio" id="cat" value="cat" v-model="picked">
<label for="cat">cat</label>
<br>
<input type="radio" id="dog" value="dog" v-model="picked">
<label for="google">dog</label>
<br>
<span>选中值为: {{ picked }}</span>
</div>
<br>
<select v-model="selected" name="fruit">
<option value="">选择一个网站</option>
<option value="www.baidu.com">baidu</option>
<option value="www.google.com">Google</option>
</select>

<div id="output">
选择的网站是: {{selected}}
</div>
1
2
3
4
checked : false,
checkedNames: [],
picked: 'cat',
selected: ''

修饰符

.lazy

在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:

1
2
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >

.number

如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值:

1
<input v-model.number="age" type="number">

这通常很有用,因为在 type=”number” 时 HTML 中输入的值也总是会返回字符串类型。

.trim

如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:

1
<input v-model.trim="msg">

Vue.js 组件component

组件(Component)是 Vue.js 最强大的功能之一。

组件可以扩展 HTML 元素,封装可重用的代码。

组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:

img

全局组件

注册一个全局组件语法格式如下:

1
Vue.component(tagName, options)

tagName 为组件名,options 为配置选项。注册后,我们可以使用以下方式来调用组件:

1
<tagName></tagName>

实例

1
2
3
4
5
6
7
8
9
10
11
12

<script>
// 注册,要在创建实例之前完成
Vue.component('maxzhao_all', {
template: '<h1>自定义组件 maxzhao_all !</h1>'
})
// 创建根实例
new Vue({
el: '#app'
})
</script>
<maxzhao_all></maxzhao_all>

局部组件

我们也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用:

1
2
3
<div id="vue_det">
<maxzhao></maxzhao>
<maxzhao_all></maxzhao_all>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 注册
Vue.component('maxzhao_all', {
template: '<h1>自定义组件 maxzhao_all ! </h1>'
})
var Child = {
// eslint-disable-next-line no-tabs
template: '<h1>自定义组件 maxzhao !</h1>'
}
// eslint-disable-next-line no-unused-vars
var vm = new Vue({
el: '#vue_det',
components: {
// <maxzhao> 将只在父模板可用
'maxzhao': Child
}

Prop

prop 是父组件用来传递数据的一个自定义属性。

父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”:

1
<maxzhao_all message1="hello!"></maxzhao_all>
1
2
3
4
5
6
// 注册
Vue.component('maxzhao_all', {
// 声明 props
props: ['message1'],
template: '<h1>自定义组件 maxzhao_all params:{{message1}} ! </h1>'
})

首次接触可能会很难理解,其实很简单,就像<div class="class1" ></div> 一样,为当前的divclass 的传递一个 class1的参数。

动态 Prop

1
<maxzhao_all v-bind:message1="author"></maxzhao_all><maxzhao_all :message1="author"></maxzhao_all>
1
2
3
data: {
author: 'maxzhao'
}

Prop 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 当前列表中的一条记录
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.name }}</li>'
})
//***************
data: {
sites: [
{ name: 'Runoob' },
{ name: 'Google' },
{ name: 'Taobao' }
]
}
1
2
3
<ol>
<todo-item v-for="item in sites" v-bind:todo="item"></todo-item>
</ol>

Prop 验证

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
Vue.component('example', {
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
})

type 可以是下面原生构造器:

  • String
  • Number
  • Boolean
  • Function
  • Object
  • Array

type 也可以是一个自定义构造器,使用 instanceof 检测。

自定义事件

父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!

我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:

  • 使用 $on(eventName) 监听事件
  • 使用 $emit(eventName) 触发事件

另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。

以下实例中子组件已经和它外部完全解耦了。它所做的只是触发一个父组件关心的内部事件。

1
2
3
4
5
6
<div id="counter-event-example">
<p>{{ total }}</p>
<!-- 监听自定义的事件-->
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Vue.component('button-counter', {
template: '<button v-on:click="incrementHandler">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
incrementHandler: function () {
this.counter += 1
this.$emit('increment')
}
}
})
var vm = new Vue({
el: '#vue_det',
data: {
//****************
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}

这个可能需要解释一下(自己的理解),首先加载当前的button按钮,当前按钮的click执行组件的incrementHandler,然后执行当前组件在html中的click事件,为什么要反向执行呢?因为页面中绑定的事件是 当前vue_det下的事件。

如果你想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。例如:

1
<my-component v-on:click.native="doTheThing"></my-component>

data 必须是一个函数

上面例子中,可以看到 button-counter 组件中的 data 不是一个对象,而是一个函数:

1
2
3
4
5
data: function () {
return {
count: 0
}
}

这样的好处就是每个实例可以维护一份被返回对象的独立的拷贝,如果 data 是一个对象则会影响到其他实例,如下所示(没有尝试,js基础知识):

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
<div id="components-demo3" class="demo">
<button-counter2></button-counter2>
<button-counter2></button-counter2>
<button-counter2></button-counter2>
</div>

<script>
var buttonCounter2Data = {
count: 0
}
Vue.component('button-counter2', {
/*
data: function () {
// data 选项是一个函数,组件不相互影响
return {
count: 0
}
},
*/
data: function () {
// data 选项是一个对象,会影响到其他实例
return buttonCounter2Data
},
template: '<button v-on:click="count++">点击了 {{ count }} 次。</button>'
})
new Vue({ el: '#components-demo3' })
</script>

Vue.js 自定义指令

除了默认设置的核心指令( v-model 和 v-show ), Vue 也允许注册自定义指令。

下面我们注册一个全局指令 v-focus, 该指令的功能是在页面加载时,元素获得焦点:

全局指令:实例 v-focus

1
2
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
1
2
3
4
5
6
7
8
// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
// 当绑定元素插入到 DOM 中。
inserted: function (el) {
// 聚焦元素
el.focus()
}
})

我们也可以在实例使用 directives 选项来注册局部指令,这样指令只能在这个实例中使用:

局部指令:实例 v-focus

把上面的代码注释掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
var vm = new Vue({
el: '#vue_det',
directives: {
// 注册一个局部的自定义指令 v-focus
focus: {
// 指令的定义
inserted: function (el) {
// 聚焦元素
el.focus()
}
}
}
})

钩子

钩子函数

指令定义函数提供了几个钩子函数(可选):

  • bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
  • inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
  • update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
  • componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
  • unbind: 只调用一次, 指令与元素解绑时调用。

钩子函数参数

钩子函数的参数有:

  • el: 指令所绑定的元素,可以用来直接操作 DOM 。
  • binding: 一个对象,包含以下属性:
    • name: 指令名,不包括 v- 前缀。
    • value: 指令的绑定值, 例如: v-my-directive="1 + 1", value 的值是 2
    • oldValue: 指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
    • expression: 绑定值的表达式或变量名。 例如 v-my-directive="1 + 1" , expression 的值是 "1 + 1"
    • arg: 传给指令的参数。例如 v-my-directive:foo, arg 的值是 "foo"
    • modifiers: 一个包含修饰符的对象。 例如: v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }
  • vnode: Vue 编译生成的虚拟节点。
  • oldVnode: 上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。

以下实例演示了这些参数的使用:

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 注册一个全局自定义指令 v-focus
Vue.directive('maxzhao', {
bind: function (el, binding, vnode) {
var s = JSON.stringify
el.innerHTML =
'name: ' + s(binding.name) + '<br>' +
'value: ' + s(binding.value) + '<br>' +
'expression: ' + s(binding.expression) + '<br>' +
'argument: ' + s(binding.arg) + '<br>' +
'modifiers: ' + s(binding.modifiers) + '<br>' +
'vnode keys: ' + Object.keys(vnode).join(', ')
}
})

有时候我们不需要其他钩子函数,我们可以简写函数,如下格式:

1
<div v-maxzhao_dir:a.b.c='message' v-maxzhao_color="{ color: 'green', text: 'maxzhao' }" ></div>
1
2
3
4
Vue.directive('maxzhao_color', function (el, binding) {
// 设置指令的背景颜色
el.style.backgroundColor = binding.value.color
})

指令函数可接受所有合法的 JavaScript 表达式,以下实例传入了 JavaScript 对象:

1
2
3
4
5
Vue.directive('maxzhao_color', function (el, binding) {
// 简写方式设置文本及背景颜色
el.innerHTML = binding.value.text
el.style.backgroundColor = binding.value.color
})

Vue.js 路由(终于到了激动人心,阔步前进的时刻)

本章节我们将为大家介绍 Vue.js 路由。

Vue.js 路由允许我们通过不同的 URL 访问不同的内容。

通过 Vue.js 可以实现多视图的单页Web应用(single page web application,SPA)。

Vue.js 路由需要载入 vue-router 库

中文文档地址:vue-router文档

打住:如果是按照本文的安装方式,则默认已经安装了路由


安装

1、直接下载 / CDN

1
wget https://unpkg.com/vue-router/dist/vue-router.js

2、NPM

推荐使用淘宝镜像:

1
cnpm install vue-router

简单实例

Vue.js + vue-router 可以很简单的实现单页应用。

是一个组件,该组件用于设置一个导航链接,切换不同 HTML 内容。 to 属性为目标地址, 即要显示的内容。

以下实例中我们将 vue-router 加进来,然后配置组件和路由映射,再告诉 vue-router 在哪里渲染它们。

HTML代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app-router-demo">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>

JavaScript 代码

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
// 0. 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义(路由)组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]

// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写)相当于 routes: routes
})

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount('#app-router-demo')

// 现在,应用已经启动了!

打眼一看,怎么还是和安装的不一样?这时候的我也是一脸懵逼。先尝试一下这个。最终还是不行,因为new VueRouter这个是直接报错的。采用当前的方式引入:

1
2
3
4
5
6
7
8
9
10
11
12
import VueRouter from 'vue-router'
// demo
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})

接下来我把教程的所有代码都删掉了,直接看脚手架的源码。

直接上手脚手架

为了方便阅读,把router/index.js改为router/indexRouter.js,此时,会有 test/unit/jest.conf.js中的 js 路径修改。

当我想把indexRouter.js移动到components目录下的时候,发现界面报错了。

1
2
3
4
5
6
7
 ERROR  Failed to compile with 2 errors   
These dependencies were not found:

* vue-router/types in ./src/router/indexRouter.js
* vue/types in ./src/router/indexRouter.js

To install them, you can run: npm install --save vue-router/types vue/types

我就按照提示进行了安装

1
2
3
npm install --save vue-router/types vue/types
# 报错 找不到 赶紧
npm i 一下

提示:此处有一个build/webpack.base.conf.js文件下的

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
// ************************
resolve: {
// 在 import 的可以省略文件的后缀名
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
// 此处设置@ 代表src目录,等同于‘/’
'@': resolve('src'),
}
},

提示: /home/maxzhao/code/vue/vue_maxzhao/src/router/index.js文件下

1
2
3
4
5
6
7
8
9
10
// exxport default 只能导出一个对象,直接引入就可以使用
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})

导出多个对象

1
2
3
4
export const str = 'hello world';
export function f(a){ return a+1;}
// 在其它文件中引入
import { str, f } from 'demo1'

实践

src/router/index.js中插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import HelloMaxzhao from '@/components/HelloMaxzhao'

Vue.use(Router)

export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/hello_maxzhao',
name: 'HelloMaxzhao',
component: HelloMaxzhao
}
]
})

新建src/components/HelloMaxzhao.vue

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
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>author:{{author}}</h2>
</div>
</template>

<script>
export default {
name: 'HelloMaxzhao',
data () {
return {
msg: 'Hello MaxZhao',
author: 'maxzhao'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
</style>

浏览器访问:http://localhost:8080/#/hello_maxzhao

点击过的导航链接都会加上样式 **class =”router-link-exact-active router-link-active”**。


接下来我们可以了解下更多关于 的属性。

to

表示目标路由的链接。 当被点击后,内部会立刻把 to 的值传到 router.push(),所以这个值可以是一个字符串或者是描述目标位置的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<router-link to="/hello_maxzhao" >to /hello_maxzhao</router-link><br>
<!-- 字符串 -->
<router-link to="hello_maxzhao">hello_maxzhao</router-link><br>
<!-- 渲染结果 -->
<a href="#/hello_maxzhao">a href=hello_maxzhao</a><br>
<!-- 使用 v-bind 的 JS 表达式 -->
<router-link v-bind:to="'hello_maxzhao'">v-bind:to hello_maxzhao</router-link><br>
<!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->
<router-link :to="'hello_maxzhao'">:to hello_maxzhao</router-link><br>
<!-- 同上 -->
<router-link :to="{ path: 'hello_maxzhao' }">:to="{ path: 'hello_maxzhao' }"</router-link><br>
<!-- 命名的路由 -->
<router-link :to="{ name: 'HelloMaxzhao', params: { userId: 123 }}">命名的路由 HelloMaxzhao</router-link><br>
<!-- 带查询参数,下面的结果为 /hello_maxzhao?plan=private -->
<router-link :to="{ path: 'hello_maxzhao', query: { plan: 'private' }}">带查询参数 hello_maxzhao</router-link>

replace

设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),导航后不会留下 history 记录。

1
<router-link :to="{ path: '/abc'}" replace></router-link>

append

设置 append 属性后,则在当前 (相对) 路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b

1
<router-link :to="{ path: 'relative/path'}" append></router-link>

tag

有时候想要 <router-link> 渲染成某种标签,例如 <li>。 于是我们使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航。

1
2
3
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染结果 -->
<li>foo</li>

active-class

设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。

1
2
3
4
5
6
7
8
9
<style>
._active{
background-color : red;
}
</style>
<p>
<router-link v-bind:to = "{ path: '/route1'}" active-class = "_active">Router Link 1</router-link>
<router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>

注意这里 class 使用 **active_class=”_active”**。

exact-active-class

配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。

1
2
3
4
<p>
<router-link v-bind:to = "{ path: '/route1'}" exact-active-class = "_active">Router Link 1</router-link>
<router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>

router-link 默认情况下的路由是模糊匹配,例如当前路径是 /article/1 那么也会激活 ,所以当设置 exact-active-class 以后,这个 router-link 只有在当前路由被全包含匹配时才会被激活 exact-active-class 中的 class,例如:

1
<router-link to="/article" active-class="router-active"></router-link>

当用户访问 /article/1 时会被激活为:

1
<a href="#/article" class="router-active" rel="nofollow"></a>

而当使用:

1
<router-link to="/article" exact-active-class="router-active"></router-link>

当用户访问 /article/1 时,不会激活这个 link 的 class:

1
<a href="#/article" rel="nofollow"></a>

event

声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。

1
<router-link v-bind:to = "{ path: '/route1'}" event = "mouseover">Router Link 1</router-link>

以上代码设置了 event 为 mouseover ,及在鼠标移动到 Router Link 1 上时导航的 HTML 内容会发生改变。

NPM 路由实例

接下来我们演示了一个使用 npm 简单的路由实例,开始前,请先下载该实例源代码:

路由实例

你也可以在 Github 上下载:https://github.com/chrisvfritz/vue-2.0-simple-routing-example

下载完后,解压该目录,重命名目录为 vue-demo,vu 并进入该目录,执行以下命令:

1
2
3
4
5
# 安装依赖,使用淘宝资源命令 cnpm
cnpm install

# 启动应用,地址为 localhost:8080
cnpm run dev

如果你需要发布到正式环境可以执行以下命令:

1
cnpm run build

执行成功后,访问 http://localhost:8080 即可看到如下界面:

img

beforeEach 跳转路由前

1
2
3
router.beforeEach((to, from, next) => {
// 做一些操作,比如判断当前用户 token 是否过期
});
  • to: Route: 即将要进入的目标 路由对象

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    • next(‘/‘) 或者 next({ path: ‘/‘ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

afterEach 跳转路由后

1
2
3
router.afterEach(() => {
NProgress.done(); // 结束Progress
});

Vue.js 混入

混入 (mixins)定义了一部分可复用的方法或者计算属性。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。

来看一个简单的实例:

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义一个混入对象
var myMixin = {
created: function () {
this.startmixin()
},
methods: {
startmixin: function () {
document.write('欢迎来到混入实例')
}
}
}
var Component1 = Vue.extend({
mixins: [myMixin]
})
new Component1()

选项合并

当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。

比如,数据对象在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先

以下实例中,Vue 实例与混入对象包含了相同的方法。从输出结果可以看出两个选项合并了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var mixin = {
created: function () {
document.write('混入调用' + '<br>')
}
}
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: {App},
template: '<App/>',
mixins: [mixin],
//created 参数
created: function () {
document.write('组件调用' + '<br>')
}
})

输出结果为:

1
2
混入调用
组件调用

如果 methods 选项中有相同的函数名,则 Vue 实例优先级会较高。如下实例,Vue 实例与混入对象的 methods 选项都包含了相同的函数:

相同的函数名

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
var mixin = {
methods: {
hellworld: function () {
document.write('HelloWorld 方法' + '<br>');
},
samemethod: function () {
document.write('Mixin:相同方法名' + '<br>');
}
}
}
/* eslint-disable no-new */
var vm = new Vue({
el: '#app',
router,
components: {App},
template: '<App/>',
mixins: [mixin],
methods: {
start: function () {
document.write('start 方法' + '<br>');
},
samemethod: function () {
document.write('Main:相同方法名' + '<br>');
}
}
})
vm.hellworld();
vm.start();
vm.samemethod();

输出结果为:

1
2
3
HelloWorld 方法
start 方法
Main:相同方法名

以上实例,我们调用了以下三个方法:

1
2
3
vm.hellworld();
vm.start();
vm.samemethod();

从输出结果 methods 选项中如果碰到相同的函数名则 Vue 实例有更高的优先级会执行输出。并且全局方法不执行。


全局混入

也可以全局注册混入对象。注意使用! 一旦使用全局混入对象,将会影响到 所有 之后创建的 Vue 实例。使用恰当时,可以为自定义对象注入处理逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption;
// this.$options 可以获取当前vue对象
if (myOption) {
console.log(this.$options)
document.write(myOption)
}
}
})
/* eslint-disable no-new */
var vm = new Vue({
el: '#app',
router,
components: {App},
template: '<App/>',
myOption: 'hello!'
})

谨慎使用全局混入对象,因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)。

Vue.js Ajax(vue-resource)

Vue 要实现异步加载需要使用到 vue-resource 库。

1
<script src="https://cdn.staticfile.org/vue-resource/1.5.1/vue-resource.min.js"></script>

Get 请求

以下是一个简单的 Get 请求实例,请求地址是一个简单的 txt 文本:

如果需要传递数据,可以使用 this.$http.get(‘get.php’,{params : jsonData}) 格式,第二个参数 jsonData 就是传到后端的数据。

1
2
3
4
5
this.$http.get('get.php',{params : {a:1,b:2}}).then(function(res){
document.write(res.body);
},function(res){
console.log(res.status);
});

post 请求

post 发送数据到后端,需要第三个参数 **{emulateJSON:true}**。

emulateJSON 的作用: 如果Web服务器无法处理编码为 application/json 的请求,你可以启用 emulateJSON 选项。

demo_test_post.php 代码如下:

1
2
3
4
5
6
7
<?php
$name = isset($_POST['name']) ? htmlspecialchars($_POST['name']) : '';
$city = isset($_POST['url']) ? htmlspecialchars($_POST['url']) : '';
echo '网站名: ' . $name;
echo "\n";
echo 'URL 地址: ' .$city;
?>

语法 & API

你可以使用全局对象方式 Vue.http 或者在一个 Vue 实例的内部使用 this.$http来发起 HTTP 请求。

1
2
3
4
5
6
7
// 基于全局Vue对象使用http
Vue.http.get('/someUrl', [options]).then(successCallback, errorCallback);
Vue.http.post('/someUrl', [body], [options]).then(successCallback, errorCallback);

// 在一个Vue实例内使用$http
this.$http.get('/someUrl', [options]).then(successCallback, errorCallback);
this.$http.post('/someUrl', [body], [options]).then(successCallback, errorCallback);

vue-resource 提供了 7 种请求 API(REST 风格):

1
2
3
4
5
6
7
get(url, [options])
head(url, [options])
delete(url, [options])
jsonp(url, [options])
post(url, [body], [options])
put(url, [body], [options])
patch(url, [body], [options])

除了 jsonp 以外,另外 6 种的 API 名称是标准的 HTTP 方法。

options 参数说明:

参数 类型 描述
url string 请求的目标URL
body Object, FormData, string 作为请求体发送的数据
headers Object 作为请求头部发送的头部对象
params Object 作为URL参数的参数对象
method string HTTP方法 (例如GET,POST,…)
timeout number 请求超时(单位:毫秒) (0表示永不超时)
before function(request) 在请求发送之前修改请求的回调函数
progress function(event) 用于处理上传进度的回调函数 ProgressEvent
credentials boolean 是否需要出示用于跨站点请求的凭据
emulateHTTP boolean 是否需要通过设置X-HTTP-Method-Override头部并且以传统POST方式发送PUT,PATCH和DELETE请求。
emulateJSON boolean 设置请求体的类型为application/x-www-form-urlencoded

通过如下属性和方法处理一个请求获取到的响应对象:

属性 类型 描述
url string 响应的 URL 源
body Object, Blob, string 响应体数据
headers Header 请求头部对象
ok boolean 当 HTTP 响应码为 200 到 299 之间的数值时该值为 true
status number HTTP 响应码
statusText string HTTP 响应状态
方法 类型 描述
text() 约定值 以字符串方式返回响应体
json() 约定值 以格式化后的 json 对象方式返回响应体
blob() 约定值 以二进制 Blob 对象方式返回响应体

axios(听说比vueresource更好)

基于promise用于浏览器和node.js的http客户端

特点

  • 支持浏览器和node.js
  • 支持promise
  • 能拦截请求和响应
  • 能转换请求和响应数据
  • 能取消请求
  • 自动转换JSON数据
  • 浏览器端支持防止CSRF(跨站请求伪造)

安装

npm安装

1
$ npm install axios

bower安装

1
$ bower install axios

通过cdn引入

1
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

例子

发起一个GET请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

// Optionally the request above could also be done as
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

发起一个POST请求

1
2
3
4
5
6
7
8
9
10
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

同时发起多个请求

1
2
3
4
5
6
7
8
9
10
11
12
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) {
// Both requests are now complete
}));

官方地址:http://www.axios-js.com/zh-cn/docs/

其它插件

NProgress 进度条

1
2
3
4
NProgress.start() // —start 
NProgress.set(0.4)// — 将进度设置到具体的百分比位置
NProgress.inc() // — 少量增加进度
NProgress.done()// — 将进度条标为完成状态

vuedraggable 拖动

vue-count-to 数字滚动

showdown 使页面支持 markdown

Mock.mock() 拦截请求响应数据

参考

问题

Failed to mount component: template or render function not defined.

1
2
3
4
5
6
7
[Vue warn]: Failed to mount component: template or render function not defined.

found in

---> <Anonymous>
<App> at src/App.vue
<Root>

因为 vue 版本高了,需要在 require 之后 加上 default, 来指定文件中的 export default

1
2
Try adding .default to the end of your require().
component: require('./index/index.vue').default

You cannot set a form field before rendering a field associated with the value

只能少传参,不能多传、错传参数。

1
2
3
let thisDetail = pick(that.detail, 'money',
'goodsName', 'remark', 'delStatus', 'expenseTime');
that.form.setFieldsValue(thisDetail);

本文地址: https://github.com/maxzhao-it/blog/post/36127/