Vue.js 学习笔记 -- 基础

Vue.js 学习笔记


零、介绍

Vue是一套用于构建用户界面的渐进式框架。
1、起步
在 html 文件中引入 vue

<!DOCTYPE html>
<!-- 开发环境版本,包含有帮助的命令行警告信息 -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

2、Vue 实例
所有的 Vue 组件都是 Vue 实例,会接受相同的选项对象(一些根实例特有的选项除外)。

var app = new Vue( 选项对象 );

(1)数据与方法

var app = new Vue({ 
	el: '#app',
	data: {
		message: 'hello world!',
	}
});

除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法,它们都有前缀 $,以便与用户自定义的属性区分开。例如:

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` 改变后调用
})

(2)实例声明周期钩子
不要在选项属性或回调上使用箭头函数。因为箭头函数是和父级上下文绑定在一起的, this 不会是 Vue 实例。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5CFjQVek-1587351016118)(./1551073664887.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N0qPlvNJ-1587351016121)(./1551073685720.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vc1AipcE-1587351016122)(./1551073706963.png)]

一、声明式渲染

Vue.js 的核心是允许采用简洁的模板语法来声明式地将数据渲染进DOM的系统。
1、插值
(1)文本
(a)、文本插值

<div id="app">
	{{ message }}
</div>

var app = new Vue({
	el: '#app',
	data: {
		message: 'Hello Vue!'
	}
});

(b)指令绑定

<div id="app">
	<span v-bind:title="date">
	 鼠标在这悬停几秒,查看在此处动态绑定的提示信息!
	 </span>
</div>

var app = new Vue({
	el: 'app',
	data: {
		date: '页面加载于 ' + new Date().toLocaleString();
	}
});

(2)原始 HTML
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击(脚本注入攻击)。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。

<div id="app">
	<p>使用双括号来插入 HTML 代码时,会当做普通的文本插入:{{ rawHtml }}</p>
		<p v-html="rawHtml">使用 v-html 指令来插入原始 HTML,就可以作为 HTML 来插入到页面中。</p>
</div>

<script>
	var app = new Vue({
		el: '#app',
		data: {
			rawHtml: '<span style="color: red">这些文字应该是红色的!</span>'
		}
	});
</script>

(3)特性
双括号插值语法不能作用在 HTML 特性上,这时候需要使用 v-bind 指令。

<div v-bind:id="dynamicId"></div>

(4)使用 JavaScript 表达式

{{ num + 1 }}
{{ ok? 'YES': 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>

每个绑定都只能包含单个表达式

// 这是语句,不是表达式
{{ var a = 1 }}
// 流控制语句也不会生效,可使用三元表达式来处理
{{ if(ok){ return message } }}

2、指令
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
(1)参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如:

<a v-bind:href="url">...</a>
<a v-on:click="doSomething">...</a>

(2)动态参数

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

<script>
	var app = new Vue({
		el: '#app',
		data: {
			attributeName: 'href'
		}
	})
</script>

这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data 属性 attributeName,其值为 “href”,那么这个绑定将等价于 v-bind:href。
动态参数表达式有一些语法约束,因为某些字符,例如空格和引号,放在 HTML 特性名里是无效的。变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。

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

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

3、缩写
v-bind —> :
v-on —> @
v-slot —> #

二、条件与循环

1、v-if

<div v-if="awesome">Vue is awesome!</div>
<div v-else>Oh no!</div>

(1)在 template 元素上使用 v-if 条件渲染分组

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

(2)v-else 元素必须跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
(3)类似于 v-else,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。

<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>

(4)用 key 管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 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 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。

这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 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 元素仍然会被高效地复用,因为它们没有添加 key 属性。

(5)v-show

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

v-show 与 v-if 不同的地方在于,带有 v-show 的元素始终会渲染并保存在 DOM 中。v-show 只是简单的切换元素的 display 属性。
v-show 不支持 template 元素,也不支持 v-else

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

2、v-for
(1)遍历数组

<div id="app">
	<ol>
		<li v-for="(item, index) in items">{{  index + 1}} - {{ item }}</li>
	</ol>
</div>

<script>
	var app = new Vue({
		el: '#app',
		data: {
			items: [
					{ message: 'apple'    
					},
				{
			message: 'pine'	
				}
			]
		}
	});
</script>

(2)遍历对象

<div id="app">
	<ul v-for="(value, key, index) in items" :key="index">
		<li>{{ index + 1 }} - {{ key }} - {{ value }}</li>
	</ul>
</div>

<script>
	new Vue({
		el: '#app',
		data: {
			items: {
				firstName: 'John',
				lastName: 'Doe',
				age: 30
			}
		}
	})
</script>

(3)key
建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key 并不与 v-for 特别关联,key 还具有其他用途,我们将在后面的指南中看到其他用途。

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

(4)数组更新检测
(a)变异方法(会改变原数组)
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
(b)替换数组(不会改变原数组,总会返回一个新数组)
filter()
concat()
slice()

注意事项
(1)由于 JavaScript 的限制,Vue 不能检测以下变动的数组:

当你利用索引直接设置一个项时,例如:

vm.items[indexOfItem] = newValue

当你修改数组的长度时,例如:

vm.items.length = newLength

为了解决第一类问题,以下两种方式都可以实现和

vm.items[indexOfItem] = newValue 

相同的效果,同时也将触发状态更新:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

vm.$set(vm.items, indexOfItem, newValue)

为了解决第二类问题,你可以使用 splice:

vm.items.splice(newLength)

(2)对象更改检测注意事项
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:

var vm = new Vue({
data: {
a: 1
}
})
// vm.a 现在是响应式的

vm.b = 2
// vm.b 不是响应式的
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:

var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})

你可以添加一个新的 age 属性到嵌套的 userProfile 对象:

Vue.set(vm.userProfile, ‘age’, 27)
你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:

vm.$set(vm.userProfile, ‘age’, 27)
有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:

Object.assign(vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})
你应该这样做:

vm.userProfile = Object.assign({}, vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

三、处理用户输入

v-on 指令添加事件监听器,通过它调用在 Vue 实例中定义的方法。

<div id="app-5">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">逆转消息</button>
</div>
var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

四、表单输入与应用状态之间的双向绑定

v-model
可以用 v-model 指令在表单 input、textarea、select 元素上创建双向数据绑定。它会根据控件类型来自动选取正确的方式来更新元素。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
1、基础用法
v-model 在内部使用不同的属性为不同的输入元素并抛出不同的事件:

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

(1)文本

<div id="app-6">
  <p>{{ message }}</p>
  <input v-model="message">
</div>

var app6 = new Vue({
  el: '#app-6',
  data: {
    message: 'Hello Vue!'
  }
})

(2)多行文本

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

(3)复选框
单个复选框,绑定到布尔值。

<input type="checkbox" v-model="checked">
            <label for="checkbox">
                {{ checked }}
            </label>
            <hr>
 new Vue({
                el: '#app',
                data: {
                    checked: false,
                    checkedNames: [],
                }
            });

多个复选框,绑定到同一个数组。

 <input type="checkbox" value="jack" id="jack" v-model="checkedNames">
            <label for="jack">jack</label>
            <input type="checkbox" value="john" id="john" v-model="checkedNames">
            <label for="john">john</label>  
            <input type="checkbox" value="jasmine" id="jasmine" v-model="checkedNames">
            <label for="jasmine">jasmine</label>
            <p>checkedNames: {{ checkedNames }}</p>
            <hr>
            
 new Vue({
                el: '#app',
                data: {
                    checked: false,
                    checkedNames: [],
                }
            });

(4)单选按钮

<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>
new Vue({
  el: '#example-4',
  data: {
    picked: ''
  }
})

(5)选择框
单选时绑定到一个值

<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>
new Vue({
  el: '...',
  data: {
    selected: ''
  }
})

多选是绑定到一个数组

<div id="example-6">
  <select v-model="selected" multiple style="width: 50px;">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <br>
  <span>Selected: {{ selected }}</span>
</div>
new Vue({
  el: '#example-6',
  data: {
    selected: []
  }
})

2、修饰符
(1) .lazy
在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >

(2) .number
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

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

这通常很有用,因为即使在 type=“number” 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。
(3) .trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

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

五、组件化应用构建

在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EFYCWLX9-1587351016126)(./1551018496962.png)]
因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

<div id="app">
            <button-counter></button-counter>
            <button-counter></button-counter>
            <button-counter></button-counter>
        </div>

        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script>
            Vue.component('button-counter', {
                data: function(){
                    return {
                        counter: 0,
                    };
                },
                methods: {
                    add: function(){
                        this.counter += 1;
                    }
                },
                template: '<button @click="add">You have clicked this button {{ counter }} times!</button>'
            });
            new Vue({
                el: '#app',
            });
        </script>

1、data 必须是一个函数
一个组件的 data 选项必须是一个函数,这样,每个实例都可以维护一份被返回的对象的独立的拷贝。

data: function(){
	return {
		count: 0,
	};
},

2、组件注册
(1)全局注册
全局注册的组件在其他组件中也可以直接使用。

Vue.component('my-component-name', {

})

(2)局部注册

var ComponentA = {...};
var ComponentB = {...};
var ComponentC = {...};

new Vue({
	el: '#app',
	components: {
		'component-a': ComponentA,
		'component-b': ComponentB
	}
});

局部注册的组件在其它组件中不可以直接使用。如果要在组件B中使用组件A,需要先在组件B中注册组件A。

var ComponentA = {...};

var ComponentB = {
	...
	components: {
		'component-a': ComponentA
	}
}

3、通过 prop 向子组件中传递数据

Vue.component('blog-list', {
	props: [ 'title' ],
	template: '<h3>{{  title }}</h3>'
});

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

4、每个组件必须只有一个根元素。

5、监听子组件事件

<blog-post
  ...
  v-on:enlarge-text="postFontSize += 0.1"
></blog-post>
同时子组件可以通过调用内建的 $emit 方法 并传入事件名称来触发一个事件:

<button v-on:click="$emit('enlarge-text')">
  Enlarge text
</button>

6、在组件上使用 v-model

<div id="app">
            <custom-input v-model="text"></custom-input>
            <p>value: {{ text }}</p>
        </div>

        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script>
            Vue.component('custom-input', {
                props: [ 'value' ],
                template: `
                    <input type="text" :value="value" @input="$emit('input', $event.target.value)">
                `
            });

            new Vue({
                el: '#app',
                data: {
                    text: '',
                }
            });
        </script>

7、通过插槽分发内容

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

8、动态组件
上述内容可以通过 Vue 的 元素加一个特殊的 is 特性来实现:

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

在上述示例中,currentTabComponent 可以包括

已注册组件的名字,或
一个组件的选项对象

六、计算属性和侦听器

1、计算属性(针对复杂逻辑)

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

2、计算属性缓存 vs 方法
可以将一个函数定义为一个方法,也可以将它定义为一个计算属性。这两种方式的最终结果是完全相同的。但是,**计算属性会基于他们的依赖来进行缓存。**只有相关依赖发生改变时,计算属性才会需要重新求值。而,相关依赖没有发生改变时,访问计算属性,并不需要重新计算,再次执行函数。但是,对于函数而言,每当触发重新渲染时,每次调用都会触发重新计算一遍的。如果某一个值的计算需要大量的资源时,使用计算属性更加的合理。

3、计算属性 vs 侦听属性
当有一些数据需要随着其它数据变动而变动时 ,可以考虑使用侦听属性。
(1)侦听属性

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

4、计算属性的 setter
计算属性默认只有 getter,在需要的时候,自己可以提供一个 setter。

 <div id="app">
            fullName: {{ fullName }}
        </div>
    
        <!-- <script src="" async defer></script> -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    firstName: 'Foo',
                    lastName: 'Bar',
                    fullName: 'Foo Bar'
                },
                watch: {
                    firstName: function(val){
                        this.fullName = val + ' ' + this.lastName;
                    },
                    lastName: function(val){
                        this.fullName = this.firstName + ' ' + val;
                    }
                }
            });
        </script>

5、侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

七、Class 与 Style 绑定

1、绑定 HTML Class
当在一个自定义组件上使用 class 属性时,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。
(1)对象语法
可以传给 v-bind:class 一个对象,以动态地切换 class。

<div v-bind:class="{ active: isActive }"></div>

可以在对象中传入更多属性来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class 属性共存。

<div
  class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>

data: {
  isActive: true,
  hasError: false
}

绑定的数据对象不必内联定义在模板里:

<div v-bind:class="classObject"></div>
data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:

<div v-bind:class="classObject"></div>
data: {
  isActive: true,
  error: null
},
computed: {
  classObject: function () {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}

(2)数组语法

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

data: {
	activeClass: 'active',
	errorClass: 'textDanger'
}

渲染为:

<div class="active  textDanger"></div>

如果你也想根据条件切换列表中的 class,可以用三元表达式:

<div v-bind:class="[isActive ? active: '', errorClass]">
</div>

等价于在数组语法中使用对象语法:

<div v-bind:class="[{active:isActive}, errorClass]">
</div>

2、绑定内联样式
(1)对象语法

<div :style="styleObject"></div>

data: {
	styleObject: {
		color: 'red',
		fontSize: '12px'
	}
}

(2)数组语法
(a)v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:

<div v-bind:style="[baseStyles, overridingStyles]"></div>

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

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

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

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

八、事件处理

1、监听事件
v-on 指令监听 DOM 事件,并在触发时运行一些 JS 代码。

2、事件处理方法

<div id="example-2">
  <!-- `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
</div>
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!'

3、内联处理器中的方法
其实就是调用方法时,传入需要的参数。如果需要在内联语句处理器中访问原始的 DOM 事件,可以用特殊变量 $event 把它传入方法中使用。

button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
// ...
methods: {
  warn: function (message, event) {
    // 现在我们可以访问原生事件对象
    if (event) event.preventDefault()
    alert(message)
  }
}

4、事件修饰符
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。虽然在方法中,可以很轻松的实现这点,但是,更好的处理方式是:方法只有纯粹的数理逻辑,而不用去处理 DOM 事件细节。
Vue.js 提供的事件修饰符有:
.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>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发.请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

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

<input @keyup.enter="submit">

// 可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
<input @keyup.page-down="onPageDown">

Vue 提供了绝大多数常用的按键码的别名。
.enter
.tab
.delete
.esc
.space
.up
.down
.left
.right
(2)系统修饰符
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta

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

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

请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。

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

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

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

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

(4)鼠标按钮修饰符
.left
.right
.middle
这些修饰符会限制处理函数仅响应特定的鼠标按键。

九、官方路由( vue-router库 )

1、起步

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
  <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
// 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')

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

2、动态路由匹配
(1)一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UqyqmkPY-1587351016129)(./1551247508707.png)]

(2)复用组件时,想对路由参数的变化做出响应的话,可以 watch(检测变化)$route 对象。

const User = {
	template: '...',
	watch: {
		'$route'(to, from)     {
		// 对路由状态变化做出响应...
		}
	}
};

也可以使用 2.2 中引入的 beforeRouteUpdate 导航守卫。

const User = {
	template: '...',
	beforeRouteUpdate( to, from, next){
		// 要记得调用 next()
	}
}

(3)捕获所有路由 或 404 Not found 路由
含有通配符的路由应该放在最后。

{
	// 会匹配所有的路径,通常放在最后,用于客户端 404 错误
	path: '*',
}
{
	// 会匹配以'/user-'开头的任意路径
	path: '/user-*'
}

当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分:

// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'

(4)高级匹配模式
使用 path-to-regexp 作为路径匹配引擎,可使用自定义正则表达式来进行匹配。

(5)匹配优先级
谁先定义的,谁的优先级就最大。

2、嵌套路由

const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User,
      children: [
        {
          // 当 /user/:id/profile 匹配成功,
          // UserProfile 会被渲染在 User 的 <router-view> 中
          path: 'profile',
          component: UserProfile
        },
        {
          // 当 /user/:id/posts 匹配成功
          // UserPosts 会被渲染在 User 的 <router-view> 中
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]
})

3、编程式的导航
(1)router.push( location, onComplate?, onAbort? )
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GKW2Olu-1587351016131)(./1551260849958.png)]
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。

router.push('home');

router.push({ path: 'home' });

router.push({ name: 'user', params: { userId: '123' } });

router.push({ path: 'register', query: { plan: 'private' } })

如果提供了 path,params 会被忽略,query却不会被忽略。如果要提供路由的 params ,可以将 path转变为用 name 表示。

const userId = '123';
// /user/123
router.push({ name: 'user', params: { userId } }); 

router.push({ path: '/user/${userId}' });

(2)router.replace( location, onComplete?, onAbort? )
用法和 router.push 很像,唯一不同的就是,它不会向 history 添加新纪录,而是跟它的方法名一样,替换掉当前的 history 记录。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZhLxXLgl-1587351016132)(./1551362983059.png)]

(3)router.go( n )
n —> 一个整数,表示在 history 记录中前进或者后退多少步,类似于 window.history.go( n )

router.go(1); //  ---> history.forward()

router.go(-1); // ---> history.back()

router.go(3);

// 如果 history 记录不够用,就会失败而已
router.go(100);
router.go(-100);

4、命名路由
在 routes 配置中给定某个路由设置的名称。

const router = new VueRouterr({
	routes: [{
		path:'/user/:userId',
		name: 'user',
		component: User
	}]
})

命名路由便于链接或跳转:

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

// 效果等价于
router.push({ name: 'user', params: { userId: 123 }});

5、命名视图
(1)命名视图
如果 router-view 没有设置名字,那么,默认为 default。

<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>

const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})

(2)嵌套命名视图

{
  path: '/settings',
  // 你也可以在顶级路由就配置命名视图
  component: UserSettings,
  children: [{
    path: 'emails',
    component: UserEmailsSubscriptions
  }, {
    path: 'profile',
    components: {
      default: UserProfile,
      helper: UserProfilePreview
    }
  }]
}

6、重定向
(1)路由重定向

const routes = [
	{ path: '/a', redirect: '/b' },
	{ path: '/foo', redirect: { name: 'baz'} },
	{ path: '/one', redirect: to => {
		// 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
	}
]

(2)别名
/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。
这其实就是,不管用户访问的是 /a 还是 /b,映射的组件都是同一个,而路由的名字就是用户访问的那个,不会做替换。

const routes = [
	{ path: '/a', component: A, alias: '/b' }
];

“别名”的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。

7、路由组件传参
用 props 来传递路由参数,从而接触 $route 和组件之间的耦合。

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

8、HTML5 History 模式
vue-router 默认使用 hash 模式–使用 URL 的 hash 来模拟一个完整的 URL。于是当 URL 改变时,页面不会重新加载。

如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

const router = new VueRouter({
	mode: 'history',
	routes: [...]
});

不过这种模式要玩好,还需要后台配置支持。所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

原生 Node.js

const http = require('http')
const fs = require('fs')
const httpPort = 80

http.createServer((req, res) => {
  fs.readFile('index.htm', 'utf-8', (err, content) => {
    if (err) {
      console.log('We cannot open "index.htm" file.')
    }

    res.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8'
    })

    res.end(content)
  })
}).listen(httpPort, () => {
  console.log('Server listening on: http://localhost:%s', httpPort)
})

基于 Node.js 的 Express
对于 Node.js/Express,请考虑使用 **connect-history-api-fallback **中间件。

发布了9 篇原创文章 · 获赞 1 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_36291747/article/details/105629623