简介
Vue - 介绍
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
后起之秀,生态完善,已然称为前端工程师必备技能。
Vue - 特点
- 声明式渲染
- 组件化模式
- 使用 虚拟 DOM + 优秀的 Diff 算法 ,高复用 DOM 节点
Vue - 文档
Vue - MVVM模型
虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm
(ViewModel 的缩写) 这个变量名表示 Vue 实例。
- M - 模型(Model):对应 data 中的数据
- V - 视图(View):对应模板
- VM - 视图模型(ViewModel):对应 Vue 实例对象

Vue - 引入
通过 <script> 标签引入远程 Vue.js
- 对于制作原型或学习,你可以这样使用最新版本:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
- 对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
- 如果你使用原生 ES Modules,这里也有一个兼容 ES Module 的构建文件:
<script type="module"> import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.esm.browser.js' </script>
通过 <script> 标签引入本地 Vue.js
开发版本包含完整的警告和调试模式
生产版本删除了警告,33.46KB min+gzip
使用 NPM 安装
# 最新稳定版 $ npm install vue
实例
实例 - 创建一个 Vue 实例
每个 Vue 应用都是通过用 Vue
函数创建一个新的 Vue 实例开始的:
注意:Vue 实例所管理的函数不能用箭头函数,否则 this 指向的不是当前 vm 而是 window
var vm = new Vue({
el:'#root',
data:{
name:'nayst'
},
methods:{
method1(){
}
},
...
})
el
还可以利用$mount
挂载到 vm 上:
var vm = new Vue({
})
vm.$mount('#root')
data
除了上述的对象式写法,还有一种常用的函数式写法:
var vm = new Vue({
data() {
return {
name:'test'
}
}
})
在组件中,data
必须使用_函数式_写法!
实例 - 数据与方法
当一个 Vue 实例被创建时,它将 data
对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true
// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
实例 - 生命周期钩子
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
例如 created
钩子可以用来在一个实例被创建之后执行代码:
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
// => "a is: 1"
实例 - 生命周期图示
下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。

语法
模板语法 - 插值
插值语法用于解析标签体的内容,在双大括号之间加入 js 表达式。这种方式可以直接读取到data
中的所有属性。
<span>{{ msg }}</span>
对于所有的数据绑定,Vue.js 都提供了完全的 js 表达式支持。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
模板语法 - 指令
指令 (Directives) 是带有 v-
前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
例如,给标签绑定v-if
指令,通过改变seen
值的真假来 插入/移出 标签:
<p v-if="seen">现在你看到我了</p>
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind
指令可以用于响应式地更新 HTML attribute:
<a v-bind:href="url">...</a>
<!-- 可以简写成下面的形式 -->
<a :href="url">...</a>
另一个例子是 v-on
指令,它用于监听 DOM 事件:
<a v-on:click="doSomething">...</a>
<!-- 可以简写成下面的形式 -->
<a @click="doSomething">...</a>
数据
数据 - 单向绑定
通过v-bind
完成对数据的单向绑定,数据只能从data
流向页面。
<div id="root">
你原来的名字:<input type="text" v-bind:value="name">
</div>
数据 - 双向绑定
通过v-model
完成对数据的双向绑定,数据可以从data
流向页面,也可以从页面流向data
。
<div id="root">
你要修改的名字:<input type="text" v-model:value="name">
</div>
注意:(1)双向绑定一般都应用在表单类元素上。
(2)v-model:value 可以简写为 v-model ,因为 v-model 默认接收 value
数据 - 表单输入数据
你可以用 v-model
指令在表单 <input>
、<textarea>
及 <select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
在文本框、多行文本、下拉框中,在 data 中绑定 value 即可:
<form @submit.prevent="demo">
<input type="text" v-model="info.text">
</form>
在单选框中,需要绑定值,还要给标签配置 value :
<form @submit.prevent="demo">
<input type="radio" name="sex" v-model="info.sex" value="male">man
<input type="radio" name="sex" v-model="info.sex" value="famale">woman
</form>
在复选框中,需要绑定值,还要给标签配置 value ,绑定的值要为一个_数组_:
<form @submit.prevent="demo">
<input type="checkbox" v-model="info.hobby" value="sing">sing
<input type="checkbox" v-model="info.hobby" value="dance">dance
<input type="checkbox" v-model="info.hobby" value="study">study
</form>
需要配置的 data :
data:{
info:{
text:'',
sex:'famale',
hobby:[]
}
}
数据 - 数据代理
利用Object.defineProperty(obj, prop, desc)
操作数据,可以直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。
obj:需要定义属性的当前对象
prop:当前需要定义的属性名
desc:属性描述符
基本用法:
let Person = {
name:'',
}
Object.defineProperty(Person, 'name', {
value: 'jack',
enumberable:true,//是否可以枚举,默认为false
writable: true,// 是否可以改变,默认为false
configurable: true,//是否可以被删除,默认为false
})
进阶用法:
let Person = {
name:'',
age:'',
}
let number = 19
Object.defineProperty(Person, 'age', {
get(){
return number
},
set(value){
number = value
}
}
Vue 中的数据代理,是通过 vm 对象来代理 data 对象中的属性的操作(读/写)
Vue 中的数据代理的好处:更加方便的操作 data 中的数据
基本原理:通过
Object.defineProperty
把对象中的所有属性加到 vm 上,每一个属性都添加了对应的 getter/setter ,用于操作 data 中对应的属性
事件
事件 - 事件处理
可以用 v-on
指令监听 DOM 事件,并在触发时运行对应的代码。
<div>
<buttom @click="greet">Greet</buttom>
</div>
methods:{
greet(){
alert('Hello')
}
}
事件的回调需要配置在 methods 中,最终在 vm 上
methods 中配置的函数,不能用箭头函数,否则 this 就不是 vm 了
methods 中配置的函数,都是被 Vue 所管理的函数,this 指向的是 vm 或 组件实例对象
事件 - 事件修饰符
Vue.js 为 v-on
提供如下了事件修饰符,用于在处理事件时进行一些限制。
- prevent:阻止默认事件(常用)
- stop:阻止事件冒泡(常用)
- once:事件只触发一次(常用)
- capture:使用事件的捕获模式,即内部元素触发的事件优先在此处理,然后才交由内部元素进行处理
- self:只有 event.target 是当前操作的元素时才触发事件,即事件不是从内部元素触发的
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
<div>
<a href="http://www.baidu.com" @click.prevent="showInfo">点我提示</a>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>
</div>
事件 - 键盘事件
Vue.js 中的键盘事件有 keyup
(按下并抬起时触发)和 keydown
(按下时触发),并且可以在触发时添加修饰符:_enter_(回车)、_delete_(删除)、_esc_(退出)、_space_(空格)、_tab_(换行)、_up_(上)、_down_(下)、_left_(左)、_down_(右)。
其余未提供别名的按键,可以使用按键原始的 key 值去绑定,利用 event.keyCode 获取按键编码。
系统修饰键 Ctrl、Alt、Shift、Meta 用法特殊,当搭配 keydown 使用的时候,正常按下即触发;当搭配 keyup 的时候,需要按下修饰键时同时按下任意其他键,释放后触发。
<div>
<input type="text" placeholder="按下回车提示输入" @keydown.ctrl="showInfo">
</div>
属性
属性 - 计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。 Vue.js 中提供了_计算属性_,用于得到不存在的属性。例如:
<div id="root">
你的全名:<span>{{fullName}}</span>
</div>
data:{
firstName:'nayst',
lastName:'yang'
}
computed:{
fullName:{
//当 fullName 被 读取 时调用 get
get(){
console.log('get被调用了',this)
return this.firstName + '-' + this.lastName
},
//当 fullName 被 修改 时调用 set
set(value){
console.log('set',value)
}
}
}
当计算属性不需要setter
修改时,可以简写成如下形式:
//计算属性的简写
computed:{
fullName(){
console.log('get被调用了',this)
return this.firstName + '-' + this.lastName
}
}
原理:底层借助了Object.defineProperty
方法提供的 setter 和 getter
属性 - 监视属性
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:监视属性。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。例如:
<div>
<input type="text" placeholder="请输入您的问题" v-model="question">
</div>
data:{
question:''
},
watch:{
answer:{
immediate:true,//在初始化时调用一次 handler
// handler 在 answer 改变时调用
handler(newValue,oldValue){
console.log('question被修改了,原来是',oldValue,',现在是',newValue)
}
}
}
还有另一种命令式的 API 写法:
vm.$watch('answer',{
handler(newValue,oldValue){
console.log('question被修改了,原来是',oldValue,',现在是',newValue)
}
})
当需要监视对_多级结构_中某个属性变化时,需要开启深度监视:
data:{
numbers:{
a:1,
b:2
}
},
watch:{
numbers:{
deep:true//是否开启深度监视
handler(Value){
console.log('question被修改了',Value)
}
}
}
当_监视属性_中只需要 handler 时,可以采用如下简写形式:
watch:{
answer(newValue,oldValue){
console.log('question被修改了,原来是',oldValue,',现在是',newValue)
}
}
当然 API 写法也是可以简写的:
vm.$watch('answer',function(newValue,oldValue){
console.log('question被修改了,原来是',oldValue,',现在是',newValue)
})
绑定
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind
处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind
用于 class
和 style
时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组
绑定 - Class样式
我们可以传给 v-bind:class
一个字符串,以动态地切换 class:
<div>
<div class="basic" :class="class">
<span>{{ msg }}</span>
</div>
</div>
data:{
msg:'This is a test.',
class:'className'
}
同样的,我们也可以传_数组_和_对象_:
<div>
<div class="basic" :class="classArr">
<span>{{ msg1 }}</span>
</div>
<div class="basic" :class="classObj">
<span>{{ msg2 }}</span>
</div>
</div>
data:{
msg1:'This is an array.',
msg2:'This is an object',
classArr:['className1','className2','className3'],
classObj:{
className1:true,
className2:false,
className3:true
}
}
绑定 - Style样式
v-bind:style
的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象
<div>
<div class="basic" :style="styleObj">
<span>{{ msg }}</span>
</div>
</div>
data:{
msg:'This is an object.',
styleObj:{
fontsize: '40px',
color: 'red'
}
}
v-bind:style
的数组语法可以将多个样式对象应用到同一个元素上:
<div>
<div class="basic" :style="styleArr">
<span>{{ msg }}</span>
</div>
</div>
data:{
msg:'This is an array.',
styleObj:[
{
fontsize: '40px',
color: 'red'
},
{
backgroundColor: 'gray'
}
]
}
渲染
渲染 - 条件渲染
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 值的时候被渲染。v-else-if
,顾名思义,充当 v-if
的“else-if 块”,可以连续使用。v-else
指令用于表示 v-if
的“else 块”
<div>
<div v-if="n===1">Angular</div>
<div v-else-if="n===2">Vue</div>
<div v-else>Wrong choice!</div>
</div>
另一个用于根据条件展示元素的选项是 v-show
指令。用法大致一样:
<div>
<div v-show="false">You can't see me</div>
<div v-show="true">You can see me</div>
</div>
渲染 - 列表渲染
我们可以用 v-for
指令基于一个_数组_来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名
<ul>
<li v-for="(person,index) in persons" :key="index">
{{person.name}}-{{person.age}}
</li>
</ul>
data:{
persons:[
{id:'01',name:'Akko',age:'18'},
{id:'02',name:'Ashe',age:'19'},
{id:'03',name:'Luxa',age:'20'}
]
}
你也可以用 v-for
来遍历一个_对象_的 property
<ul>
<li v-for="(value,key) in cars" :key="key">
{{key}}-{{value}}
</li>
</ul>
data:{
cars:{
name:'奥迪A8',
price:'800k',
color:'black'
}
}
注意:key 在循环中作为_唯一标识_,不写默认为_循环的索引_
生命周期钩子
所有生命周期钩子的 this
上下文将自动绑定至实例中,因此你可以访问 data、computed 和 methods。这意味着你不应该使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos()
)。因为箭头函数绑定了父级上下文,所以 this
不会指向预期的组件实例,并且this.fetchTodos
将会是 undefined。

生命周期钩子 - beforeCreate
在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用
生命周期钩子 - created
在实例创建完成后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且 $el
property 目前尚不可用
生命周期钩子 - beforeMount
在挂载开始之前被调用:相关的 render
函数首次被调用
该钩子在服务器端渲染期间不被调用
生命周期钩子 - mounted
实例被挂载后调用,这时 el
被新创建的 vm.$el
替换了。如果根实例挂载到了一个文档内的元素上,当 mounted
被调用时 vm.$el
也在文档内
注意 mounted
不会保证所有的子组件也都被挂载完成。如果你希望等到整个视图都渲染完毕再执行某些操作,可以在 mounted
内部使用 vm.$nextTick
mounted(){
this.$nextTick(function () {
// 仅在整个视图都被渲染之后才会运行的代码
})
}
该钩子在服务器端渲染期间不被调用
生命周期钩子 - beforeUpdate
在数据发生改变后,DOM 被更新之前被调用。这里适合在现有 DOM 将要被更新之前访问它,比如移除手动添加的事件监听器
该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务器端进行
生命周期钩子 - updated
在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
注意,updated
不会保证所有的子组件也都被重新渲染完毕。如果你希望等到整个视图都渲染完毕,可以在 updated
里使用 vm.$nextTick
updated(){
this.$nextTick(function () {
// 仅在整个视图都被重新渲染之后才会运行的代码
})
}
该钩子在服务器端渲染期间不被调用
生命周期钩子 - beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用
该钩子在服务器端渲染期间不被调用
生命周期钩子 - destroyed
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁
该钩子在服务器端渲染期间不被调用
脚手架
脚手架 - 简介
Vue CLI (_C_ommand _L_ine _I_nterface)是一个基于 Vue.js 进行快速开发的完整系统,提供:
- 通过
@vue/cli
实现的交互式的项目脚手架 - 通过
@vue/cli
+@vue/cli-service-global
实现的零配置原型开发 - 一个运行时依赖 (@vue/cli-service),该依赖:
- 可升级;
- 基于 webpack 构建,并带有合理的默认配置;
- 可以通过项目内的配置文件进行配置;
- 可以通过插件进行扩展
- 一个丰富的官方插件集合,集成了前端生态中最好的工具
- 一套完全图形化的创建和管理 Vue.js 项目的用户界面
Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject
脚手架 - 安装
全局安装 @vue/cli (仅首次)
npm install -g @vue/cli
切换到你要创建项目的目录,创建项目
vue create xxxx
启动项目
npm run serve
备注:切换淘宝镜像
npm config set registry https://registry.npm.taobao.org
脚手架 - 目录结构

文件
.gitnore
: Git 的忽略文件,在此描述哪些文件(夹)不接受 Git 的管理babel.config.js
:babel 的控制文件package.json
:包的说明书,存在于 npm 创建的工程中package-lock.json
:包版本控制文件,版本仓库README.md
:项目的描述文件文件夹
src
:存放以下内容——
main.js
:整个项目的入口文件——
App.vue
:页面的主要内容——
asserts
:存放项目里的静态资源,如图片、页签——
components
:存放组件public
:存放公共资源
组件
组件 - 基础
组件是可复用的 Vue 实例,且带有一个名字
注意:一个组件的 data
选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
通常一个应用会以一棵嵌套的组件树的形式来组织:

组件 - 非单文件组件
使用组件的步骤:
定义(创建)组件
使用 Vue.extend(options) 创建,其中的 options 和创建 Vue 实例时的 options 几乎一样
注册组件
- 局部注册:在创建 Vue 实例的时候传入 components 选项
- 全局注册:直接 Vue.component(‘组件名’,组件)
使用组件
编写组件标签:<组件名/>
<!-- 准备一个容器 -->
<div id="root">
<school></school>
</div>
const school = Vue.extend({
template:`<div><h2>学校名称:{{name}}</h2></div>`,
data(){
return{
name:'CCSU'
}
}
})
new Vue({
el:'#root',
components:{school}
})
注意:
- 组件名是多单词时,中间用
-
连接,如果在脚手架中,首字母大写即可 - 组件标签的自闭合只能在脚手架中用
组件 - 单文件组件
将非单文件组件的内容写作一个独立的.vue
文件,在脚手架中使用
<template>
<div>
</div>
</template>
<script>
export default {
name:'filename',
data(){
return{
}
},
methods:{
}
}
</script>
<style></style>
注意:这里的filename
必须与组件名保持一直,首字母大写
组件 - 组件嵌套
Vue 支持在组件中注册子组件
<!-- 准备一个容器 -->
<div id="root">
<school></school>
</div>
const student = Vue.extend({
template:`<div><h2>学生名称:{{name}}</h2></div>`,
data(){
return{
name:'Nayst'
}
}
});
const school = Vue.extend({
template:`
<div><h2>学校名称:{{name}}</h2></div>
<student></student>
`,
data(){
return{
name:'CCSU'
}
}
});
new Vue({
el:'#root',
components:{school}
});
组件 - ref
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs
对象上。
$refs是一个对象,存放注册过 ref
attribute 的所有 DOM 元素和组件实例
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素
如果用在子组件上,引用就指向组件实例(vc)
组件 - props
功能:让组件接受外部传过来的数据
传递数据:直接在标签内部写
<Student name="nayst" age="21"/>
接收数据:在组件内的 props 配置项内配置
//第一种:只接收数据
props:['name','age']
//第二种:限制类型
props:{
name:String,
age:Number
}
//第三种:限制类型、限制必要性、指定默认值,完整写法
props:{
name:{
type:String,
required:true,
default:'nayst'
},
age:{
type:Number,
required:false,
default:'18'
}
}
注意:props 是只读的,Vue 底层会监测你对 props 的修改,并且在修改后发出警告
组件 - mixin
功能:可以把多个文件共用的配置提取成一个混入对象
在 src 中创建 mixins.js
(名字随意),用于定义混入:
export const test = {
data(){
},
methods:{
}
}
局部引入:在组件中引入并使用:
import {test} from '../mixin'
export default {
name:'Student',
mixins:[test]
}
全局引入:在main.js
中引入:
import {test} from './mixin'
Vue.mixin(test)
组件 - plugin
功能:用于增强 Vue,本质是包含 install 方法的一个对象,install 的第一个参数是 Vue,第二个以后的参数是插件使用者传递的数据
在 src 中创建 plugins.js
(名字随意),用于定义插件:
export default {
install(Vue){
Vue.filter()//定义全局过滤器
Vue.directive()//定义全局指令
Vue.mixin()//定义混入
Vue.prototype.func = ()=>{}//在 Vue 原型上添加一个方法
}
}
在main.js
中引入并使用插件:
import plugins from './plugins'
Vue.use(plugins)
组件 - slot
功能:让父组件可以向子组件指定位置插入 html 结构,也是一种组件间的通信方式,适用于父组件给子组件传。
默认插槽
父组件:
<component> <div> ... </div> </component>
子组件:
<template> <div> <!-- 定义插槽 --> <slot>默认内容</slot> </div> </template>
具名插槽
父组件:
<component> <template slot="header"> <div>Header</div> </template> <template slot="footer"> <div>Footer</div> </template> </component>
子组件:
<template> <div> <slot name="footer">Default footer</slot> <slot name="header">Default header</slot> </div> </template>
作用域插槽:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
通信
通信 - 自定义事件
自定义事件,适用于子组件给父组件传递数据
不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称
第一步:定义一个自定义事件
methods:{
demo(value){
console.log('自定义事件被调用了,收到了',value)
}
}
第二步:给子组件绑定自定义事件
<Student @myEvent="demo"/>
第三步:在子组件内触发自定义事件
<button @click="$emit('myEvent',value)"></button>
通信 - 全局事件总线
全局事件总线(Global Event Bus),适用于任意组件间通信
第一步,安装全局事件总线
new Vue({
beforeCreate() {
Vue.prototype.$bus = this //$bus 就是当前的应用
}
})
第二步,利用事件总线发送数据
<button @click="$bus.$emit('test',studentName)"></button>
第三步,利用事件总线接收数据
mounted() {
this.$bus.$on('test',(data)=>{
})
},
beforeDestroy() {
this.$bus.$off('test')
}
注意:最好在 beforeDestroy 钩子中,用$off
去解绑当前组件用到的事件
通信 - 消息订阅与发布
消息订阅与发布(pubsub),适用于任意组件间通信
第一步:安装pubsub
npm i pubsub-js
第二步:在需要的组件中引入pubsub
import pubsub from 'pubsub-js'
第三步:利用pubsub
发布(发送)数据
<button @click="sendMsg"></button>
methods: {
sendMsg(){
pubsub.publish('test',data)
}
}
第四步:利用pubsub
订阅(接收)数据
mounted(){
this.pubId = pubsub.subscribe('test',(msgName,data)=>{
console.log('有人发布了test消息',msgName,data)//这里 msgName 就是 test
})
},
beforeDestroy() {
pubsub.unsubscribe(this.pubId)
}
过渡动画
Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。
过渡动画 - 单组件
Vue 提供了 transition
的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡
第一步:在目标元素外包裹<transition name="xxx">
<transition name="fade">
<p v-if="show">hello</p>
</transition>
第二步:定义 class 样式
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
过渡动画 - 类名
在进入/离开的过渡中,会有 6 个 class 切换。
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。

过度动画 - JS 钩子
可以在 attribute 中声明 JavaScript 钩子,通过 vue 提供给的动画钩子函数来绑定事件,然后在事件函数中处理对应的动画
入场动画:
before-enter 动画入场运动前一刻执行
enter 动画运动时执行
after-enter 在动画 enter 函数中运行完毕并调用回调 done 时执行
离场动画:(用法同上)
before-leave
leave
after-leave
例子:图片自跳动
<transition appear name="fade" @after-enter="show=false" @after-leave="show=true">
<img src="../assets/logo.png" v-if="show">
</transition>
.fade-enter-active, .fade-leave-active {
transition:opacity 1s;
}
.fade-enter, .fade-leave-to{
opacity:0;
}
添加 appear 属性后,网页才会一打开就有入场动画,否则需要触发
过渡动画 - 多组件
多个组件的过渡简单很多 - 我们不需要使用 key
attribute。相反,我们只需要使用动态组件:
<button @click="show = !show">test</button>
<transition-group name="fade">
<h1 v-show="show" key="1">TEST1</h1>
<h1 v-show="!show" key="2">TEST2</h1>
</transition-group>
过渡动画 - 第三方动画
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<div id="example-3">
<button @click="show = !show">切换</button>
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">hello</p>
</transition>
</div>
数据请求
如果你的前端应用和后端 API 服务器没有运行在同一个主机上,你需要在开发环境下将 API 请求代理到 API 服务器,这个问题可以通过 vue.config.js
中的 devServer.proxy
选项来配置
数据请求 - Axios
数据请求 - 代理
在 vue.config.js
中添加如下配置
module.exports = {
devServer: {
proxy: 'http://localhost:4000'
}
}
Vuex
Vuex - 简介
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,也是一种组件间通信的方式
Vuex - 使用
安装
Vuex
npm install vuex
创建文件
src/store/index.js
//引入
import Vue from 'vue'
import Vuex from 'vuex'
//应用
Vue.use(Vuex)
//响应组件中的动作
const actions = {}
//操作数据
const state = {}
//存储数据
const mutations = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
- 在
main.js
中配置
//引入store
import store from './store'
//使用store
new Vue({
store
})
Vuex - 原理图

Vuex - state
Vue 管理的状态对象,存放数据的地方
注意:state 应该是唯一的
Vuex - actions
值为一个对象,包含多个响应用户动作的回调函数
通过 commit( )来触发 mutation 中函数的调用,间接更新 state
在组件中使用: $store.dispatch(‘对应的 action 回调名’) 触发回调
可以包含异步代码(定时器, ajax 等等)
Vuex - mutations
- 值是一个对象,包含多个直接更新 state 的方法
- 在 action 中使用:commit(‘对应的 mutations 方法名’) 触发
- mutations 中方法的特点:不能写异步代码、只能单纯的操作 state
Vuex - getters
- 值为一个对象,包含多个用于返回数据的函数,类似于计算属性
- 在组件中调用 $store.getters.xxx
路由
路由 - 简介
路由就是对应的映射关系,一组路由就是一组key <-> value
键值对
key 就是路径
value 在前端中,是一个组件,当 key 改变时,会在页面上展示相应的 value 组件
value 在后端中,是一个函数,当 key 改变时,会处理客户端提交的请求
路由 - 使用
- 安装 vue-router :
npm i vue-router
- 创建文件:
src/router/index.js
- 在
main.js
引入:import VueRouter from 'vue-router'
- 在
main.js
使用:Vue.use(VueRouter)
路由 - 配置
import VueRouter from 'vue-router'
//引入组件
import Home from '../views/Home'
//创建并暴露一个路由
export default new VueRouter({
routes: [
{
name:'Home',
path:'/',
component:Home
},
],
mode: 'history'
})
路由 - 嵌套
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:
/user/foo/profile /user/foo/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
借助 vue-router
,使用嵌套路由配置,就可以很简单地表达这种关系
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
}
]
}
]
})
路由 - 编程式导航
除了使用 <router-link>
创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现
router.push(location, onComplete?, onAbort?)
在 Vue 实例内部,你可以通过 $router
访问路由实例,因此你可以调用 this.$router.push()
,可以跳转到指定的页面。当你点击 <router-link>
时,这个方法会在内部调用,所以说,点击 <router-link :to="...">
等同于调用 router.push(...)
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了 path
,params
会被忽略,上述例子中的 query
并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name
或手写完整的带有参数的 path
:
const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
router.replace(location, onComplete?, onAbort?)
跟
router.push
很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录router.go(n)
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似
window.history.go(n)
例子
// 在浏览器记录中前进一步,等同于 history.forward() router.go(1) // 后退一步记录,等同于 history.back() router.go(-1) // 前进 3 步记录 router.go(3) // 如果 history 记录不够用,那就默默地失败呗 router.go(-100) router.go(100)
路由 - 命名
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes
配置中给某个路由设置名称
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
要链接到一个命名路由,可以给 router-link
的 to
属性传一个对象:
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
这跟代码调用 router.push()
是一回事:
router.push({ name: 'user', params: { userId: 123 } })
这两种方式都会把路由导航到 /user/123
路径
路由 - 重定向
重定向也是通过 routes
配置来完成,下面例子是从 /a
重定向到 /b
:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
重定向的目标也可以是一个命名的路由:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
甚至是一个方法,动态返回重定向目标:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
路由 - 别名
“重定向”的意思是,当用户访问 /a
时,URL 将会被替换成 /b
,然后匹配路由为 /b
,那么“别名”又是什么呢
/a
的别名是 /b
,意味着,当用户访问 /b
时,URL 会保持为 /b
,但是路由匹配则为 /a
,就像用户访问 /a
一样
上面对应的路由配置为:
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
“别名”的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构
路由 - 传参
在组件中使用 $route
会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性
使用 props
将组件和路由解耦,如果 props
被设置为 true
,route.params
将会被设置为组件属性:
取代与 $route
的耦合
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [{ path: '/user/:id', component: User }]
})
通过 props
解耦
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 }
}
]
})
这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试
如果 props
是一个对象,它会被按原样设置为组件属性。当 props
是静态的时候有用
const router = new VueRouter({
routes: [
{
path: '/promotion/from-newsletter',
component: Promotion,
props: { newsletterPopup: false }
}
]
})
你可以创建一个函数返回 props
。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等
const router = new VueRouter({
routes: [
{
path: '/search',
component: SearchUser,
props: route => ({ query: route.query.q })
}
]
})
URL /search?q=vue
会将 {query: 'vue'}
作为属性传递给 SearchUser
组件
路由 - history
vue-router
默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载
如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState
API 来完成 URL 跳转而无须重新加载页面
const router = new VueRouter({
mode: 'history',
routes: [...]
})
当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id
不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id
就会返回 404,这就不好看了
所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html
页面,这个页面就是你 app 依赖的页面