Vue 入门详解大全
一、Vue 简介
1.1 简介
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式的 js 框架,发布于 2014 年 2 月。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-router,vue-resource,vuex)或既有项目整合。
- 渐进式:明式渲染 -> 组件系统 -> 客户端路由 -> 集中式状态管理 -> 项目构建
- 官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)
- https://v2.cn.vuejs.org/
- 优点:
- 易用:熟悉 HTML,CSS,JavaScript 知识后,可快速上手 Vue
- 灵活:渐进式框架
- 高效: api20kb, 加载快,内部虚拟 DOM
- vue2 版本 (2023.12 停更)、Vue3
- 作者 尤雨溪
1.2 MVVM 模式的实现者 —— 双向数据绑定模式
Model:模型层,在这里表示 JavaScript 对象
View:视图层,在这里表示 DOM(HTML 操作的元素)
ViewModel:连接视图和数据的中间件,Vue.js 就是 MVVM 中的 ViewModel 层的实现者
在 MVVM 架构中,是不允许 数据 和 视图 直接通信的,只能通过 ViewModel 来通信,而 ViewModel 就是定义了一个 Observer 观察者
- ViewModel 能够观察到数据的变化,并对视图下对应的内容进行更新
- ViewModel 能够监听到视图的变化,并能够通知数据发生改变
至此,我们就明白了,Vue.js 就是一个 MVVM 的实现者,他的核心就是实现了
DOM 监听与数据绑定

1.3 其它 MVVM 实现者
- AngularJS简单介绍一下,AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVVM、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。
- ReactJSReact 引入了虚拟 DOM(Virtual DOM)的机制:在浏览器端用 Javascript 实现了一套 DOM API。基于 React 进行开发时所有的 DOM 构造都是通过虚拟 DOM 进行,每当数据变化时,React 都会重新构建整个 DOM 树,然后 React 将当前整个 DOM 树和上一次的 DOM 树进行对比,得到 DOM 结构的区别,然后仅仅将需要变化的部分进行实际的浏览器 DOM 更新。
- 微信小程序微信小程序的视图层和数据层就是通过 MVVM 进行绑定的。
1.4 为什么要使用 Vue.js
- 轻量级,体积小是一个重要指标。Vue.js 压缩后有只有 20 多 kb (Angular 压缩后 56kb+,React 压缩后 44kb+)
- 移动优先。更适合移动端,比如移动端的 Touch 事件
- 易上手,学习曲线平稳,文档齐全
- 吸取了 Angular(模块化)和 React(虚拟 DOM)的长处,并拥有自己独特的功能,如:计算属性
- 开源,社区活跃度高
1.5 Vue.js 的两大核心要素
1.5.1 数据驱动

1.5.2 组件化
- 页面上每个独立的可交互的区域视为一个组件
- 每个组件对应一个工程目录,组件所需的各种资源在这个目录下就近维护
- 页面不过是组件的容器,组件可以嵌套自由组合(复用)形成完整的页面
二、Vue 的初体验
开发工具
- VScode
- HBuilder
- WebStorm
- IDEA
2.1 在页面引入 vue 的 js 文件
注意:cdn 是一种加速策略,能够快速的提供 js 文件
也可以自己下载到本地,自己引入 vue.js
// 在线cdn
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
// 也可以自己下载到本地
<!-- 引入js文件 -->
<script src="../js/vue-2.7.16.js"></script>
2.2 准备 dom
<!-- 创建一个div,id是app -->
<div id="app"></div>
2.3 创建 vue 对象,设计对象的内容
其中该 vue 对象,绑定了页面中 id 是 app 的那个 div
<script>
// 创建Vue对象,注意大小写,构造器中是一个json
new Vue({
el:"#app", // 这是el是element缩写,目的是选择元素,所以此处是选择器
data: { // 这是data,数据,定义#app选中的区域可以使用的数据
name:"哈喽 小尤"
}
})
</script>
- el: element 的简称,也就是 Vue 实例挂载的元素节点,值可以是 CSS 选择符,或实际 HTML 元素,或返回 HTML 元素的函数。
- data: 用于提供数据的对象,里面存放 json 数据。
2.4 在页面的元素中使用插值表达式来使用 vue 对象中的内容
<div id="app">
{{ title }}
</div>
三、 插值表达式
插值表达式是 {{}}, 即双花括号
3.1 运算
<div id="app">
{{ 1 + 1}}<br>
{{ 1 - 1}}<br>
{{ 2 * 2}}<br>
{{ 2 / 2}}<br>
{{ 2 +"--"+ 2}}<br> <!-- 字符串拼接 -->
{{ 2 > 1 ? "2大":"1大"}}<br>
</div>
3.2 取数据
插值表达式的可以是在 View 中获得 Model 中的内容
Model 中的内容如下:
<body>
<div id="app">
{{title }},
{{arr}},
{{user}},
{{user.age}},
{{user.username}}
</div>
</body>
<scrip>
new Vue({
el:"#app",
data:{
title:"hello world!",
arr:[11,22,33],
user:{
age:18,
username:"taotie"
}
}
});
</script>
3.3 获取数组中的内容
<div id="app">
{{arr[2]}}
</div>
此时,页面上会显示 “33”,也就是数组中的第三个元素被获取。
3.4 获取对象中的属性
<div id="app">
{{ user.username }}
</div>
此时,页面上会显示 “taotie”,也就是对象中 username 属性的值。
3.5 调用 Vue 中的方法
重点 1: 在哪里定义方法?方法的定义语法?
<div id="app">
{{ fun1() }} <!-- 注意还要加参数列表() -->
{{ fun2(66) }}
</div>
<script src="../js/vue.js"></script>
<script>
new Vue({
el: "#app",
data: { // 定义数据
},
methods: { // 定义函数
// key:value
fun1:function(){
alert("调用fun1执行")
console.log("fun1输出");
},
// 上面函数定义,有简化写法
fun2(a){
console.log("fun2输出" , a)
// 返回值在页面展现
return "fun22222"
}
}
})
</script>
调用了 vue 对象中的 fun2 方法,控制台输出内容,页面并展示了方法的返回值。
总结
1.插值表达式可以运算 2.插值表达式可以取data中的数据 3.插值表达式可以调用方法
五、Vue 的分支
vue 中有很多指令
形如: v-xxx
5.1 v-if
Vue 中的分支语句 v-if 非常好理解,逻辑跟 Java 中的 if-else 相同。
作用:根据 if 的对错来决定要不要展现该 标签内容
v-if 语句块包含以下内容:
- v-if
- v-else
- v-else-if
接下来以一个简单例子即可理解:

从这个例子可以看出,vue 对象中的 data 提供了分支的条件。根据条件,如果是 true,则 v-if 的内容就会
显示,反之不显示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/vue-2.7.16.js"></script>
</head>
<body>
<div id="app">
<p v-if="score > 90">优秀</p>
<p v-else-if="score > 80">良好</p>
<p v-else-if="score > 70">及格</p>
<p v-else>不及格</p>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
score: 88
}
})
</script>
</html>
5.2 v-show
v-if 和 v-show 之间有着看似相同的效果,但优化上却有区别。先看下面这个例子:
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<p v-show="rich">
有钱!
</p>
<p v-if="rich">
有钱!
</p>
<button type="button" @click="rich=!rich">今晚彩票开奖</button>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
rich:false
},
methods:{
}
});
</script>
</html>
通过点击 “今晚彩票开奖” 按钮,能切换 rich 的值,此时发现,v-if 和 v-show 的显示状态都会来回切换。看起来是一样的,但通过查看控制台代码发现,
**v-show实际会将p标签的css样式的display属性设为none来达到隐藏的效果。

而v-if 是直接在页面上添加和删除 p 标签来达到效果
总结:因此 v-show 在反复切换的应用场景下,效率比 v-if 更高。
六、Vue 的循环 v-for【重点】
Vue 中的循环关键字并没有 Java 的那么多,只有 v-for,但用法上有多种。接下来我们来逐一介绍。
6.1 普通的 for 循环
我们需要定义数据源,然后通过 v-for 来遍历数据源,再使用插值表达式输出数据。
<body>
<div id="app">
<ul>
<!-- in是关键词,args是下方data中定义的数组,a是遍历出的结果变量 -->
<li v-for="a in args">{{a}}</li>
</ul>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
args:[1,2,3,4,5,6]
}
});
</script>
在这个例子中,数据源提供了一个数组。视图层通过 v-for 来循环输出多个 li 标签,非常简单。
6.2 带着索引的 for
<body>
<div id="app">
<ul>
<li v-for=" (a,i) in args" :key='i'>{{i}}{{a}}</li>
</ul>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
args:[1,2,3,4,5,6]
}
});
</script>
(a,i) 顺序建议不要变, 第一个就是遍历的得到的值,第二个就是下标
此时的 i 就是每次循环的循环变量 ,从 0 开始一直到元素个数 - 1
6.3 遍历对象数组填充表格【特别重点】
List<User> users = new ArrayList<>();
users.add (new User (1, "张三"));
users.add (new User (2, " 李四));
[
{
id:1,
name:"张三"
},
{
id:2,
name:“lsii ”
}
]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>简单遍历数组</h2>
<ul>
<li v-for="e in arr">{{e}}</li>
</ul>
<hr>
<h2>遍历,携带下标</h2>
<ul>
<li v-for="(e,i) in arr">{{e}}==>{{i+1}}</li>
</ul>
<h2>遍历,取出对象</h2>
<table border="2">
<tr>
<td>序号</td>
<td>用户id</td>
<td>姓名</td>
<td>性别</td>
</tr>
<tr v-for="(user,i) in userList">
<td>{{i+1}}</td>
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.sex}}</td>
</tr>
</table>
</div>
<script src="../js/vue-2.7.16.js"></script>
<script>
new Vue({
el:"#app",
data:{
arr:[11,33,44,66],
userList:[
{id:1101,name:"老王",sex:"男"},
{id:1102,name:"老李",sex:"女"},
{id:1103,name:"老刘",sex:"女"},
]
}
})
</script>
</body>
</html>
| 页面效果 |
|---|
|
|
6.4 带着:key 遍历
<!-- v-for是循环,所在标签会随着循环重复出现 -->
<!-- in是关键词,右边是下方定义的数组,左边是遍历得到的数据-->
<!-- 建议在给每个循环中,设置:key,key的值唯一即可 -->
<div v-for="e in arr" v-bind:key="e">{{e}}</div>
七、Vue 的属性绑定【重点】
Vue 提供了多个关键字,能快速的将数据对象中的值绑定在视图层中,即 MVVM。
7.1 v-model
通过 v-model 将 input 标签的value 值与 vue 对象中的 data 属性值进行绑定。
ps: v-model 就是实现 Vue 的 mvvm 思想的方式
<body>
<div id="app">
输入值 <input v-model="username">
<hr>
username={{username}}
</div>
<script src="../js/vue-2.7.16.js"></script>
<script>
new Vue({
el:"#app",
data:{
username:"admin"
},
methods:{
}
})
</script>
</body>
此时 input 标签中加入了 “
**v-model='username'**”,表示 input 的 value 值与 vue 对象 data 中的 username 属性绑定,当在 input 输入框中输入内容会实时修改 username 的值。于是 {{username}} 插值表达式能实时输出 input 输入框内的值。
| ** 页面效果如下:** |
|---|
|
|
7.2 v-bind
我们知道插值表达式是不能写在 html 的标签的属性内的,那如果一定要用 vue 中的属性作为 html 标签的属性的内容,就可以通过 v-bind 进行属性绑定。
换句话,使用 v-bind, 将 html 中其他属性都变成变量来绑定
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div id="app">
<!-- <input v-bind:type="dyncType">
<a v-bind:href="href">点击跳转</a>
<img v-bind:src="src"> -->
<input :type="dyncType">
<a :href="href">点击跳转</a>
<img :src="src">
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
new Vue({
el:'#app',
data:{
dyncType:"radio",
href:"http://www.jd.com",
src:"../images/1.jpg"
}
})
</script>
</html>
这样,a 标签内的 href 属性就可以使用 vue 对象中的属性值。
注意:v-bind 也可以简写,使用冒号 “
:” 来代替。html
预览
<a v-bind:href='link'></a> 等价于==> <a :href='link'>
八、Vue 的事件绑定【重点】
语法:v-on: 事件名称
可以简写为: @事件名称
:::danger注意: v-on 都能绑定哪些事件?其实 vue 并没提供什么事件,只是换了写法而已。所以之前原生 js 有哪些 dom 事件,现在 vue 就可以使用哪些事件
:::
<button v-on:click="start()">开始</button>
<button @click="begin()">开始</button>
事件中触发的函数,在 vue 实例中的 methods 中进行定义,可以按照如下两种方式进行定义:
<script type="text/javascript">
var vue = new Vue({
el : "#app",
data: {
},
methods:{
// 方式1
// 函数名: function(参数)
start:function(){
},
// 方式2
// 函数名(参数)
begin(){
}
}
})
</script>
** 练习 1: **
设计一个 div, 其中有数字,再设计一个按钮,点击按钮使数字递增 10

练习
输入 A,B 值后,点击计算按钮,在结果处展示相加后的结果


<body>
<div id="app">
<h2>简易加法计算器</h2>
数组A <input v-model="num1"> <br>
数组B <input v-model="num2"> <br>
<button @click="add">+</button>
<button @click="clear">clear</button>
<br>
结果: {{result}} <span></span>
</div>
<script src="../js/vue-2.7.16.js"></script>
<script>
new Vue({
el:"#app",
data:{
num1: null,
num2: null,
result: null
},
methods:{
add(){
this.result = parseInt(this.num1) + parseInt(this.num2)
},
clear(){
this.num1 = null
this.num2 = null
this.result = null
}
}
})
</script>
</body>
</html>
扩展:表单修饰符
练习中涉及到表单数据转字符串问题,获取的表单数据是字符串, 需要使用 **parseInt ()** 转成数字
vue 提供了表单修饰符表单输入绑定 — Vue.js (vuejs.org)
其中:
**number**, 可以使表单的输入的数据直接就是数字,不需要再解析<input v-model.number="age" type="number">除了.number 修饰符外,还有其他的修饰符,比如
.trim.lazy
练习
动态添加对象
效果图如下,输入数据,动态的添加到页面中
ps: 列表初始数据有 3 个 1-zs,2-ls,3-ww

<body>
<div id="app">
<!-- 输入框 -->
<div>
<label>ID
<input type="text" v-model="id">
</label>
<label>Name
<input type="text" v-model="name">
</label>
<button v-on:click="add">添加</button>
</div>
<!-- 列表 -->
<div>
<ul>
<li v-for="item in list">{{item.id +'---'+item.name}}</li>
</ul>
</div>
</div>
<script type="text/javascript" src="js/vue.min.js"></script>
<script type="text/javascript">
var app = new Vue({
el:'#app',
data:{
id: 0,
name:'',
list:[
{id:1,name:'zs'},
{id:2,name:'ls'},
{id:3,name:'ww'}
]
},
methods:{
add:function(){
// 获得输入的值
var id_ = this.id;
var name_ = this.name;
// 向数组中添加元素
this.list.push({id:id_,name:name_});
}
}
});
</script>
九、计算属性:computed
9.1 什么是计算属性
计算属性的重点突出在 属性 两个字上(属性是名词,就是存储数据的),首先它是个 属性 其次这个属性有 计算 的能力(计算是动词),这里的 计算 就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;
为什么要用,什么时候用计算属性?
- 当 {{}} 表达式内写很多复杂代码时使用计算属性来抽取代码
9.2 演示
官网演示很详细...计算属性 — Vue.js (vuejs.org)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
展示内容:{{message}}
<hr>
<!-- 反转后1 先拆分得到数组:{{message.split("")}} <br>[a, b, c] -->
<!-- 反转后2 数组反转:{{message.split("").reverse()}} <br>[c, b, a] -->
<!-- 数组再变字符串: -->
<!-- 使用{{}}做运算,代码复杂cba -->
{{message.split("").reverse().join("-")}} <br>
<hr>
<!-- 使用计算属性,将代码抽取到计算属性中,这里只需要调用即可,简单方便 -->
{{reverseString}}
</div>
<script src="../js/vue-2.7.16.js"></script>
<script>
new Vue({
el:"#app",
data:{
message:"abc"
},
methods:{},
computed:{ // 计算属性
reverseString(){
// 计算,返回该结果,上方使用{{}}取出结果
return this.message.split("").reverse().join("")
}
}
})
</script>
</body>
</html>
9.3 计算属性与方法的区别
看下面这个例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
展示内容:{{message}}
<hr>
<!-- 反转后1 先拆分得到数组:{{message.split("")}} <br> -->
<!-- 反转后2 数组反转:{{message.split("").reverse()}} <br> -->
<!-- 数组再变字符串: -->
<!-- 使用{{}}做运算,代码复杂 -->
{{message.split("").reverse().join("")}} <br>
<hr>
<!-- 使用计算属性,将代码抽取到计算属性中,这里只需要调用即可,简单方便 -->
{{reverseString}}
<hr>
<h2>演示计算属性和函数的区别</h2>
函数获取时间:{{getCurrentTime()}} <br>
计算属性获取数据:{{getCurrentTime2}}
</div>
<script src="../js/vue-2.7.16.js"></script>
<script>
var vue = new Vue({
el:"#app",
data:{
message:"abc"
},
methods:{
getCurrentTime(){ // 次次调用次次执行
return Date.now();
}
},
computed:{ // 计算属性,只会在加载时计算一次,结果缓存存储,后续使用直接取值不会再运算
reverseString(){
// 计算,返回该结果,上方使用{{}}取出结果
return this.message.split("").reverse().join("")
},
getCurrentTime2(){
return Date.now();
}
}
})
</script>
</body>
</html>
计算属性和函数长得类似,需要注意 设计时二者名字不能重复
说明
- methods:定义方法,调用方法使用 currentTime1 (),需要带括号,每次都会重新执行
- computed:定义计算属性,调用属性使用 currentTime2,不需要带括号,计算一次重复使用 (将结果缓存起来重复使用);
注意:methods 和 computed 里不能重名
测试效果
仔细看图中说明,观察其中的差异

9.4 结论
调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点;计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销
计算属性的执行时机是在 created 钩子函数之前执行!且只执行一次!
- 函数每次调用都会执行
- 计算属性是页面加载时执行一次,后续每次调用都是之前的值不再计算
十、Vue 的组件化【理解】
Vue 的组件化设计思想借鉴了 Java 的面向对象思想:封装的思想!
也就是说,在实际的 vue 项目中,以及使用了 Vue 框架的项目中,Vue 的对象都会以组件的形式出现,能被反复使用。
也可以这么理解 **,Vue 的组件是将一些代码封装到组件中 **, 使用时只需要通过一个组件名既可使用该组件的功能
要想实现组件化,需要在页面中注册组件:
- 全局注册
- 本地注册
组件其实就是页面内的部分代码,完成某个功能
10.1 组件的全局注册
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue组件的全局注册</title>
</head>
<body>
<div id="app">
<model1></model1>
<model1></model1>
<model1></model1>
</div>
<hr/>
<div id="app1">
<model1></model1>
<model1></model1>
<model1></model1>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
//通过Vue.component实现组件的全局注册,全局注册后的组件可以被重复使用。
Vue.component("model1",{
template:"<div><h1>{{title}}</h1><button type='button' @click='btnfn'>点我</button></div>",
data:function(){
return {
title:"hello vue"
}
},
methods:{
btnfn:function(){
alert("hello !!!");
}
}
});
new Vue({
el:'#app'
})
new Vue({
el:'#app1'
})
</script>
</html>
10.2 组件的本地注册
vue 的全局注册,也就意味着在页面的任意一个被 vue 绑定过的 div 中,都可以使用全局注册了的 vue 组件。
但是,如果是对 vue 组件进行本地注册,那么在其他被 vue 绑定的 div 中,不能使用该组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue组件的本地(局部)注册</title>
</head>
<body>
<div id="app">
<model11></model11>
</div>
<hr/>
<!--在这里使用组件model11会报错-->
<div id="app1">
<model11></model11>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
new Vue({
el:'#app',
components:{
"model11":{
template:"<div><h1>{{title}}</h1><button type='button' @click='btnfn'>点我</button></div>",
data:function(){
return {
title:"hello vue"
}
},
methods:{
btnfn:function(){
alert("hello !!!");
}
}
}
}
})
new Vue({
el:'#app1'
})
</script>
10.3 注意特性【重点】
- 组件是一段完整的页面代码,包括 html 标签,js 数据和函数
- 组件要设置名字,使用时是将名字作为标签使用
- componet 中注册的组件中的 data,必须是以函数的形式,且 return 返回数据
- template 标签内,必须有且只能有一个根标签,且一般都是 div 标签。
- template 支持模板字符串


data:function(){
return {
title:"hello vue"
}
}
data:{}
// 简化成
data(){
return {
title:"hello vue"
}
}
十一、 组件的生命周期【面试】
Vue 中的组件也是有生命周期的。一个 Vue 组件会经历:初始化、创建、绑定、更新、销毁等阶段,不同的阶段,都会有相应的 **生命周期钩子 hook 函数** 被调用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/vue-2.7.16.js"></script>
</head>
<body>
<div id="app">
<p>{{msg}}</p>
<p id="n1">{{name}}</p>
<button @click="update">更新name</button>
<button @click="destroye()">销毁按钮</button>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
msg: 'hello world',
name: 'zhangsan'
},
methods: {
update() {
this.name = Math.random() + "李四";
},
destroye() {
this.$destroy()
}
},
beforeCreate() {//创建vue之前
console.log('beforeCreate')
console.log(this.msg)//undefined 创建vue实例之前,数据还没有被初始化
},
created() {//创建vue实例之后
console.log('created')
console.log(this.msg)//hello world
},
beforeMount() {//挂载之前
console.log('beforeMount')
},
mounted() {//挂载之后
console.log('mounted')
},
beforeUpdate() {//更新之前
console.log(this.name)
console.log(document.getElementById('n1').innerText)//老数据
console.log('data更新之前执行')
},
updated() {//更新之后
console.log('data更新之后执行')
console.log(document.getElementById('n1').innerText)//新数据
console.log(this.name)
},
beforeDestroy() {//销毁之前
console.log('beforeDestroy')
},
destroyed() {//销毁之后
console.log('destroyed')
},
})
</script>
</html>
| ** 钩子函数 ** | 解释 |
|---|---|
| beforeCreate | 在实例初始化之后,数据观测和事件配置之前被调用 此时 data 和 methods 以及页面的 DOM 结构都没有初始化 什么都做不了 |
| **created ** | 在实例创建完成后被立即调用此时 data 和 methods 已经可以使用 但是页面还没有渲染出来,可以在这里进行数据的初始化、异步请求数据、监听事件等操作 |
| beforeMount | 在挂载开始之前被调用 此时页面上还看不到真实数据 只是一个模板页面而已 |
| mounted | mounted 钩子函数是组件挂载到 DOM 后被调用的。在这个阶段可以访问到组件所依赖的 DOM 元素,进行 DOM 操作、绑定事件等 |
| beforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前。 页面上数据还是旧的 |
| updated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 页面上数据已经替换成最新的 |
| beforeDestroy | 实例销毁之前调用 |
| destroyed | 实例销毁后调用 |
| 组件的生命周期钩子 |
|---|
|
|
十二、Vue-Cli 搭建 Vue 项目【重点】
12.1 什么是 vue-cli
cli: Command Line 命令行工具,vue-cli 就是 vue 的命令行工具,也称之为脚手架,使用 vue-cli 提供的各种命令可以拉取、创建、运行我们需要使用到的框架,比如 webpack、Element UI、Element Admin 等等。那么要想使用 vue-cli 命令,需要先安装 node.js。
12.2 node.js 的介绍及安装
node.js 的介绍
node.js 提供了前端程序的运行环境,可以把 node.js 理解成是运行前端程序的服务器。
node.js 的安装
从官网下载安装即可:http://nodejs.cn/download/


首先自己手动创建一个文件夹 nodejs
然后再选择该路径安装

这个切换路径是,需要自己手动创建一个文件夹 nodejs

不要勾选这个,否则会下载很多东西


测试 node.js 是否安装成功: 在 DOS 窗口中输入 “node -v” 查看版本,如果看到版本,就表示安装成功。(环境变量已经自动配置好)
node -v 是 查看 node 的版本
npm -v 是 查看 npm 版本
解释:
npm 是安装工具的工具包,类似于 Linux 中 yum,Linux 中安装软件 yum install
node 中安装软件使用 npm install
设置 npm 源,配置镜像后,下载资源就会很快.
npm config set registry https://registry.npmmirror.com
12.3 使用 node.js 安装 vue-cli
(管理员权限) 使用如下命令安装 vue-cli
npm install @vue/cli -g
# 如果出错,可以卸载,重新执行上一步安装
npm uninstall @vue/cli -g
- npm: 使用 node.js 的命令
- install: 安装
- @vue/cli: 要安装的 vue-cli
- -g: 全局安装
当出现以下界面,表示正在安装:
|
|
安装 vue-cli 检测
- vue -V

12.4 使用 vue-cli 命令创建项目
进入工作空间,执行命令创建
- vue create 项目名
注意 bug: 创建项目时如果出现报错,提示
no permitted即没有权限在此处创建项目,这个是因为磁盘权限不够,需要修改硬盘权限


选择具体 features

选择 vue 版本

如果选择了 Router, 那么就会有这个

一般选择第一项,为的是将文件分离打包

是否将此操作存储为模板,本例选择 n, 选择为 y 就会在下次创建时使用

创建完毕

运行项目


12.6 项目结构

| 文件名 | 解释 |
|---|---|
| build | webpack 的一些配置 |
| config | 不同环境的配置,比如开发环境,生产环境等 |
| node_modules | npm install 下载的依赖包 |
| public | 用于存放公共 js 文件夹、json 文件和图片等 |
| dist | 存放项目打包后的文件 |
| src | 项目的源码 |
| src/assets | 存放静态资源文件,如图片等 |
| src/components | 存放公共组件 |
| src/router | 静态路由表 |
| src/store | vuex |
| src/views | 开发的页面资源,路由页面相关的 |
| src/main.js | 对应 App.vue 创建 vue 实例,也是**入口文件** |
| src/App.vue | 入口页面 |
| gitignore | git 管理时,忽略的文件或文件夹 |
| package.json | 项目的核心配置记录,可以查看当前安装依赖的版本号 |
| index.html | 首页的入口 |
| vue.config.js | 可选的配置文件,存放在根目录中,@vue/cli-service 启动的时候会自动加载 |
可以在 package.json 中修改端口


12.7 启动项目
进入项目,使用命令行输入
**npm run serve**启动

ps:
- 可以使用 vscode 打开项目,再在 vscode 终端敲命令 启动
- 也可以使用 idea 打开项目,在终端敲命令 启动
总结
牢记 Vue 的基本结构\
<body>
<div id="app">
{{}}
<div v-if="变量" v-for=""></div>
<img v-bind:src="" @click="">
</div>
<script src="../js/vue-2.7.16.js"></script>
<script>
new Vue({
el:"#app",
data:{}, // 定义变量
methods:{}, // 定义函数
computed:{}, // 定义计算属性
components:{} // 定义组件
})
</script>
</body>
- 记住每个指令的作用
- v-if 和 v-show
- v-for
- v-bind 和 v-model
- v-on
- 记住计算属性的
- 目的
- 写法
- 和函数的区别
- 知道组件
- 为什么需要组件?有什么作用?
- 怎么写的?
- 需要有组件名
- 组件模版有 templet 写标签,data 定义数据,methods 定义函数
- 怎么用的?
- 组件名当标签名
- 有什么需要注意的语法要求?
- templet 中写标签只能有标签
- ...
- 会使用 vue-cli 创建项目
- 会用命令创建项目
- 运行项目
- 点开项目的每个文件看看
- 记住 src 下的每个文件的作用
十三、idea 开发 vue
idea 中安装 Vue 插件 (2023 版本已经无需安装,自带就有)

然后使用 idea 打开之前创建的 vue 项目

启动项目

方案 1: 点击旁边按钮启动
方案 2: 下方终端输入命令 npm run serve
如上,如果脚本改成 dev, 那么就需要改成命令为 npm run dev
idea 中的终端无法执行 npm run serve 启动项目,提示 npm 不是内部或外部命令
解决方案 1: 以管理员身份启动 idea, 打开项目运行
解决方案 2: 不要使用 powershell, 也就是 ps 方式执行 cmd, 换成默认的 cmd
十四、单文件组件【重点】
14.1 介绍
以前
以前是创建了很多.html 的文件组成一个完整项目
现在
vue 项目中是只有一个页面,是一种 spa (单页面程序)
如何实现,一个页面实现整个项目?
就是使用各种组件组装成一个页面. ****
但是,昨天学的组件
使用 Vue.component 来定义全局组件,紧接着用 new Vue ({el: '#app'}) 在每个页面内指定一个容器元素。或者在 Vue 对象内本地注册组件
这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图。
但当在更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显:
- 全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复
- 字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的
\- 不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
- 没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug (formerly Jade) 和 Babel
现在
文件扩展名为 **.vue** 的 single-file components (单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。
单文件组件的组成结构:
- template 组件的模块区域 (html)
- script 业务逻辑区域 (js)
- style 样式区域 (css)
14.2 使用
在工程的 src/components 下创建一个 Demo1 单文件 Demo1 就是自定义的组件
创建后好,组件内部结构
正常的写代码
<template>
<!-- 组件的模板,要求只能有一个根元素 -->
<div>
<h1>组件的模板</h1>
<p>取出数据: {{ msg }}</p>
<button @click="dianji">按钮</button>
<p>计算属性: {{ reverseMsg }}</p>
</div>
</template>
<script>
export default {
name: "Demo1", // 组件的名称
data() { // 组件的data数据,只能是函数,不能是对象,且必须返回一个对象,数据放在return的对象中
return {
msg: '组件的data数据'
}
},
methods: {
dianji() {
alert("按钮被点击了")
}
},
// 组件的计算属性
computed: {
reverseMsg() {
return this.msg.split('').reverse().join('')
}
}
}
</script>
<style scoped>
div{
color: red;
}
</style>
组件定义完毕后,需要在其他页面使用
在 HomeView.vue 中使用
- 哪里使用组件,哪里注册
- 注册完,组件名当标签用

注意:
其中./ 是相对路径,@是从根路径
// @是从根路径
import Demo1 from "@/components/Demo1.vue";
// ./ 是相对路径
import Demo2 from "./components/Demo2.vue";
十五、Vue 组件间的参数传递 [熟悉]
15.1 父传子 [熟悉]
父组件中使用子组件时,在标签内设置属性

传递是数字,布尔值需要 v-bind:【真实麻烦 】
传递是变量也需要 v-bind:
子组件中设置 props 接收传递过来的数据即可

15.2 子传父 [了解]
使用组件中的 props 属性是无法完成子组件向父组件传值的
那么,如果需要子组件向父组件传递值该如何做?
- Vue 提供了方案: 在子组件中使用自定义事件向父组件传递数据.
具体步骤
- 1. 子组件通过 **$emit** 函数将数据发给父组件
this.$emit (' 父组件中使用子组件时定义的事件名 ', 数据)
- 2. 父组件在使用子组件的时候,定义一个 @事件,这个事件名是 $emit 中自定义的事件名
- 3. 父组件中,需要设计这个 @事件的响应函数,函数中设计参数,这个参数就是子组件传递过来的数据
需求:子组件中设置输入框,输入完,点击按钮,将数据传递给父组件
<template>
<div>
我是子组件Son,我收到了父组件的参数:
姓名:{{name}}
年龄:{{age}}
老家:{{home}} <br>
<input type="text" v-model="num">
<button @click="sendNum">发送给父组件</button>
</div>
</template>
<script>
export default {
name: "Son",
props:["age","name","home"],
data() {
return {
num:null
}
},
methods: {
sendNum(){
// 1 子组件向父组件传递数据,使用事件触发
// this.$emit("父组件中自定义的事件名",要传递的数据)
this.$emit("receiveData",this.num)
}
}
}
</script>
<style scoped>
</style>
父组件中,在子组件标签定义自定义事件准备接收子组件发来的数据
<template>
<div>
<h2>我是Father组件</h2>
<span>从儿子组件传递过来的数据:{{num}}</span>
<!-- 这里是Father组件,使用子组件son-->
<!-- 给组件传参,就是设置自定义属性 -->
<!--@receiveData是自定义事件-->
<Son :age="18" name="曹丕" :home="home"
@receiveData="getSonNum"></Son>
</div>
</template>
<script>
import Son from "@/views/Son.vue";
export default {
name: "Father",
components: {Son},
data() {
return {
home:"魏国许都",
num:null
}
},
methods: {
getSonNum(num){
console.log("我是Father组件,我收到了子组件传递过来的数据:",num)
this.num = num;
}
}
}
</script>
<style scoped>
</style>
执行流程解析

十六、Vue-router 路由【重点】
16.1 介绍
路由的本质就是一种对应关系,比如说我们在 url 地址中输入我们要访问的 url 地址之后,浏览器要去请求这个 url 地址对应的资源。
那么 url 地址和真实的资源之间就有一种对应的关系,就是路由。

根据不同的事件来显示不同的页面内容
16.2 Vue 的路由 (先了解,学过后再回头细看!!!)
Vue 默认属于单页面的程序,主要通过 URL 中的 hash 来实现不同页面之间的切换。这种切换方式称作前端路由。
Vue 中使用路由的方式,现在是通过使用 Vue 的核心插件 VueRouter 实现的
Vue Router的核心作用
跳转/切换页面
声明式 (写标签)
<router-link to="要跳转的组件名称">
编程式 (写代码)
this.$router.push("要跳转的组件名称")
跳转携带数据
声明式导航传递参数通过路径拼接的
/about/1001
在index.js的路径中 通过/about/:id 绑定数据
接收 this.$router.params.参数名
编程式导航实现传递参数
传递 this.$router.push({path:'路径',query:{参数名:值,...}})
接收 this.$router.query.参数名
路由模式
hash模式:监听浏览器地址hash值变化,执行相应的js切换网页;
history模式:利用history API实现url地址改变,网页内容改变;
它们最明显的区别就是hash会在浏览器地址后面增加#号
16.3 安装路由模块

使用命令:
npm install vue-router
# 如果报错,可能是npm版本过高,存在兼容性问题,那么使用如下命令
npm install --legacy-peer-deps vue-router@3.5.2
npm install --legacy-peer-deps vue-router@3.1.3
ps: 今天练习过一次以后,后续在创建项目时可以直接选择路由部分内容,创建出的项目直接就配置好了
16.4 解读 AboutView 和 HomeView 的路由
路由入口/切入点
声明式写标签开始路由
或者是编程式写js开始路由
路由匹配规则/映射表
src/router/index.js
展现该页面(路由的出口)
<router-view>



16.5 演示:声明式路由 [重点]
需求:新增一个按钮 / 链接,点击实现切换个人信息页面

16.5.1 创建 MyInfo.vue
路由页面建议创建在 **src/views/** 下
components 文件夹下是公共组件
views 文件夹下是 路由组件
<template>
<div>
<h1>MyInfo</h1>
<p>这是个人信息页面</p>
<img src="../assets/James.png" alt="詹姆斯高斯林">
</div>
</template>
<script>
export default {
name: "MyInfo",
data() {
return {}
},
methods: {}
}
</script>
<style scoped>
</style>
16.5.2 设置路由入口
在 App.vue 页面中设置声明式路由

16.5.3 设置映射规则
在 **/src/router/index.js** 文件
填充以下内容
import Vue from 'vue';
import Router from 'vue-router';
import myInfo from "@/views/MyInfo.vue";
Vue.use(Router)//引入路由
export default new Router({
// 路由规则
routes: [
{
path: '/home',//路由地址
name: 'Home',
component: () => import('@/views/Home.vue') // 跳转到这个组件
},
{
path: '/info',//路由地址
name: 'MyInfo',
component: myInfo // 跳转到这个组件
},
]
})

注意写完 route 的 index.js 之后 没用 一定找到 main.js 将 router 进行挂载

16.5.4 设置路由填充位 (展现)
App.vue 中设置路由展示位<router-view>
在哪个页面设置展示位<router-view> , 组件就在哪个页面展示出来

16.6 演示:编程式路由 [重点]
刚才演示的是声明式路由,现在使用编程式路由
演示:公告页面切换

16.6.1 创建公告页 Note.vue
<template>
<div>
<h1>公告页面</h1>
<ul>
<li>“东风-5C烧饼 吸引范围覆盖全网”新</li>
<li>空军航空开放活动最新剧透来了</li>
<li>公安部公布10起网络违法犯罪案例</li>
</ul>
</div>
</template>
16.6.2 设置路由入口 (编程式路由)
App.vue 设置标签,绑定事件,事件触发函数,函数内编写代码实现路由
this.$router.push ("/ 路径")
这个函数的作用类似 <router-link to="">

- 这里写的是this.$router
- 不是this.$route !!!!!!
16.6.3 src/router/index.js 设置路由规则

16.6.4 bug: 第一次点击跳转没事,再点一次报错
解决:
- 方式 1: 设置异常捕获
// 在router中的index.js中加上以下代码,注意加在use之前写
const routerPush = VueRouter.prototype.push;
VueRouter.prototype.push = function (location) {
return routerPush.call(this, location).catch((err) => {});
};
方式 2:
在编程式是路由,即 this.$router.push () 修改成这样
const currentRoute = this.$router.currentRoute;
if (currentRoute.path !== '/note') {
this.$router.push({
path:"/note"
})
}
16.7 路由跳转时参数的传递
16.7.1 声明式路由传参 (路径里面拼数据)
演示 App.vue 中将数据传递给 MyInfo.vue
在路由跳转时路径中传参
<!--演示声明式路由传参数
1) 路径中设置参数
2) 路由映射规则中设置变量承载参数
3) 可以在组件中通过$route.params获取参数
-->
<router-link to="/info/9527" tag="button">Info信息</router-link> |
在路由表的规则中设变量

在 MyInfo.vue 中接参
使用 this.$route.params.xxx 接收参数
<template>
<div>
<h1>MyInfo</h1>
<p>这是个人信息页面</p>
<!--获取路由参数,此处的id是路由映射规则中设置的变量-->
<p>当前路由参数id: {{ this.$route.params.id }}</p>
<p>当前路由参数id: {{ uid }}</p>
<img src="../assets/James.png" alt="詹姆斯高斯林">
</div>
</template>
<script>
export default {
name: "MyInfo",
data() {
return {
uid: this.$route.params.id
}
},
methods: {}
}
</script>
<style scoped>
</style>
16.7.2 编程式路由传参
演示 App.vue 中将数据传递给 Note.vue
App.vue 的编程跳转 js 中设置要传递的数据
methods:{
toNote(){
this.$router.push({
path:"/note",
query: { //通过query携带数据,最终url /note?username=老王&password
username:"曹植"
}
})
// 详情看路由官方文档说明https://v3.router.vuejs.org/zh/guide/essentials/navigation.html
}
}
Note.vue 中接收参数
<template>
<div>
<h1>网站公告</h1>
<span>username --> {{username}}</span>
</div>
</template>
<script>
export default {
name: "Note",
data:function(){
return {
username:this.$route.query.username
// 注意是query,因为是按照路径方式发送,注意查看浏览器url
}
}
}
</script>
16.7.3 总结两种传参方式

16.8 嵌套路由 [重点]
多层多级组件嵌套时的路由

需求:在个人中心组件中,再设置三个链接,可以点击切换 [头像 | 家庭信息 | 学校信息]
代码:
创建三个组件页面 Touxiang.vue,Family.vue,School.vue
.... 省略过程
在 MyInfo 组件中设置路由链接发出请求路径,并且设置路由填充位
<p>这是个人信息页面</p>
<hr>
<nav>
<!-- 导航链接,设置子路由,
/info/8888 , 是为了匹配路由规则中定义的变量:id
-->
<router-link to="/info/8888/school">学校信息</router-link> |
<router-link to="/info/8888/family">家庭信息</router-link> |
<router-link to="/info/8888/avatar">头像信息</router-link> |
</nav>
<router-view></router-view>
在 router/index.js 中设置 myinfo 组件的子路由 children

设置路由填充 router-view

如果有路径中有数据的话,子组件一样可以取值
16.9 路由重定向 [了解]

16.10 路由守卫 (导航守卫) [重要 | 读懂作用]
官方资料: 导航守卫 | Vue Router (vuejs.org)
自己理解:很像 java 拦截器,这里拦截的路由的进程
路由守卫又分为
- 前置路由守卫 router.beforeEach (to,form,next)
- 后置路由守卫 router.afterEach
// 创建VueRouter对象,其中配置路由信息
const router = new VueRouter({...})
// 前置路由守卫,即跳转页面前生效,一般都是验证身份/权限
router.beforeEach((to, from, next) => {
console.log("to,要路由取的目的页面",to)
console.log("from,从哪个路由出的",from)
// 相当于放行
next()
// 让这次路由去/login路径
// next("/login")
})
router.afterEach((to, from) => {
console.log("后置守卫")
})
// 导出该对象
export default router
16.11 路由元信息 [了解]
官方资料: 路由元信息 | Vue Router (vuejs.org)
个人理解:路由元信息,就是在路由设置中,定义了一个数据,在路由过程带上这个数据,做一些逻辑上的处理
定义路由元信息

在路由守卫中取出

复习
- Vue 语法
- Vue 对象
new Vue({
el:"",
data:{},
methods:{},
computed:{},
components:{},
created(){},
props:{}
})
Vue 各种指令
- v-if,v-show,v-for,v-on,v-bind,v-model
Vue 的计算属性
- Vue 工程
- src
- assest 静态
- components 公共组件
- router 路由配置 js 文件
- views 路由相关页面组件
- store Vuex 相关的 js 配置文件
- App.vue 组件
- main.js 主配置文件
- Vue 工程各种组件
- 写法
- template
- script
- style
- 组件传参
- 父传子,标签内设置属性,子组件是指 props 指定接收
- 子传父,通过 $emit 调用父组件中的自定义事件
- Vue 路由
- 路由额外安装插件
- 路由两种方式
- 声明式 写标签 router-link to="/a"
- 编程式 写函数 this.$router.push ("/a")
- 路由规则
- 路由传参
- 路由守卫 (拦截器)
- 重定向
- 元数据
补充:路由模式

补充:路由命名

十七、 Vuex【重点】
17.1 什么是 Vuex
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
说人话:实现多个组件的数据共享,即一个数据需要在多个组件中使用,那么就需要把这个数据存储在 VueX 中,类似 Java 中的 session 机制.
Vuex 五大核心要素
- state:存放状态,比如需要全局共享的数据 (也可以取出数据)
- getters:可以对 state 中的数据进行额外处理,类似计算属性的作用
- mutations:通过提交 mutations 来改变 state 的状态
- actions:异步的 mutations,可以通过 dispatch 调用 mutaitons,从而改变 state
- modules:模块化状态管理,每个模块拥有自己的 state、mutation、action、getter

Vue 组件通过 dispatch 调用 Vuex 中 actions 的方法,进行异步操作。
actions 中的方法中,通过 commit,调用 mutations 中方法,以保证 state 中数据的同步。
如果不需要异步操作,可以直接在组件通过 commit 调用 mutations 中的方法对 state 中共享的数据进行操作。
17.2 安装
如果创建项目时没有勾选 Vuex, 那么可以按照下方再安装一遍
在项目根目录执行如下命令来安装 Vuex
若失败,可使用 cnpm
# 本例使用的vue2,所以安装vuex装的是vuex3!!!
npm install vuex@3

17.3 配置 vuex
在 src 目录下创建一个名为 ** store ** 的目录并新建一个名为 index.js 文件用来配置 Vuex,代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
}
})
修改 main.js 增加刚才配置的 store/index.js,关键代码如下:
import Vue from 'vue'
import App from './App.vue'
// 引入路由模块
import router from './router'
// 引入Vuex模块.注意这里的命名为store
import store from './store'
Vue.config.productionTip = false
// vue2 创建方式
new Vue({
render: h => h(App),
router,
store // 使用Vuex
}).$mount('#app')
17.2 17.3小节中的[安装|配置]也可以不做,因为后续再创建项目时直接选以下依赖,创建好项目直接就配置好了

-
一个是路由Router,一个是状态管理Vuex
17.4 演示 state 存 & 取
- 在 Vuex 中设置数据,在任何组件取出

在任何组件中使用 以下命令取值
this.$store.state.xxx
vue
<template>
<div class="about">
<p>取出Vuex的数据,user: {{this.$store.state.user}}</p>
<p>取出Vuex的数据,token: {{this.$store.state.token}}</p>
<hr>
<p>取出Vuex的数据,user: {{user}}</p>
<p>取出Vuex的数据,token: {{token}}</p>
</div>
</template>
<script>
export default {
name: 'AboutView',
data() {
return {
// 可以在data中直接获取Vuex中的数据
user: this.$store.state.user,
token: this.$store.state.token
}
}
}
</script>
17.5 mutations 修改数据
使用的是 Vuex 中定义的 mutations Mutation | Vuex
具体步骤是
- mutations 中定义一个修改数据的函数
- 定义参数,参数 1 固定是 state 对象,参数 2 是修改的新值
- 其他组件中使用this.$store.commit来调用这个函数进行修改即可

特别注意:发现一个问题
- 直接通过 this.$store.state.xxx 取出的数据,可以随着这次修改而直接改变
- 在 data 中取出的 Vuex 的数据,不会随着这次修改而直接改变
为什么?下回分解.....
17.6 计算属性取出 state 数据 [熟悉]
上面发现,data 中的取出的 Vuex 的数据不会随着修改而改动
该怎么解决?使用计算属性取出 Vuex 的数据
计算属性取值
vue
<template>
<div class="about">
<h2>直接取的</h2>
<!--方式1: 直接取-->
<p>取出Vuex的数据,user: {{this.$store.state.user}}</p>
<p>取出Vuex的数据,token: {{this.$store.state.token}}</p>
<hr>
<h2>data中获取的</h2>
<!--方式2: 在data中获取-->
<p>取出Vuex的数据,user: {{user}}</p>
<p>取出Vuex的数据,token: {{token}}</p>
<hr>
<h2>调用mutations中的方法修改token</h2>
<input type="text" v-model="newToken">
<button @click="changeToken">修改token</button>
<h2>计算属性中获取的</h2>
<p>取出Vuex的数据,token: {{token2}}</p>
</div>
</template>
<script>
export default {
name: 'AboutView',
data() {
return {
// 可以在data中直接获取Vuex中的数据
user: this.$store.state.user,
// token: this.$store.state.token,
newToken:''
}
},
// 建议使用计算属性取出Vuex的数据,而不是data
computed:{
token2(){
return this.$store.state.token
}
}
,
methods: {
changeToken(){
// 调用mutations中的方法,修改token
// this.$store.commit("mutations中定义的方法名", "调用时传递的参数")
this.$store.commit("setToken", this.newToken)
}
}
}
</script>
重要用法:一般从 state 中取出的数据要放在 computed 中而不是 data 中!
为什么?
这是因为 data 中的内容只会在 created 钩子触发前初始化一次。比如说就是 data 中设置 count: this.store.state.token , 则这个 token 的值是 created 钩子函数执行前 this.store.state.token 的值,之后 this.$store.state.token 如何变化均影响不到 token 的取值。
而 computed 则是通过依赖追踪实现的,计算属性在它的相关依赖发生改变时会重新求值。
简而言之就是说,Vuex 存储的数据如果有变化,computed 中的数据就会变化,但是 data 中的不会变化
:::
17.4.3 mapState 函数 [熟悉]
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。
为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们简化计算属性的操作
vue
<template>
<div class="about">
<h2>直接取的</h2>
<!--方式1: 直接取-->
<p>取出Vuex的数据,user: {{this.$store.state.user}}</p>
<p>取出Vuex的数据,token: {{this.$store.state.token}}</p>
<hr>
<h2>data中获取的</h2>
<!--方式2: 在data中获取-->
<p>取出Vuex的数据,user: {{user}}</p>
<p>取出Vuex的数据,token: {{token}}</p>
<hr>
<h2>调用mutations中的方法修改token</h2>
<input type="text" v-model="newToken">
<button @click="changeToken">修改token</button>
<h2>计算属性中获取的</h2>
<p>取出Vuex的数据,token: {{token}}</p>
<p>取出Vuex的数据,avatar: {{avatar}}</p>
</div>
</template>
<script>
// 特殊的,注意 {},是从vuex中引入的同名的方法
import { mapState } from 'vuex'
export default {
name: 'AboutView',
data() {
return {
// 可以在data中直接获取Vuex中的数据
user: this.$store.state.user,
// token: this.$store.state.token,
newToken:''
}
},
// 建议使用计算属性取出Vuex的数据,而不是data
// computed:{
// token2(){
// return this.$store.state.token
// },
// avatar(){
// return this.$store.state.avatar
// }
// }
// 方式3: 使用mapState辅助函数,将state中的数据映射到计算属性中
// token和avatar是vuex中state定义的变量
computed:mapState(['token','avatar'])//获取 state中 token 和 avatar
,
methods: {
changeToken(){
// 调用mutations中的方法,修改token
// this.$store.commit("mutations中定义的方法名", "调用时传递的参数")
this.$store.commit("setToken", this.newToken)
}
}
}
</script>
17.7 解决浏览器刷新后 Vuex 数据消失问题 [了解]
- 问题描述
Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。但是有一个问题就是:vuex 的存储的数据只是在页面的中,相当于我们定义的全局变量,刷新之后,里边的数据就会恢复到初始化状态。但是这个情况有时候并不是我们所希望的。
- 解决方案
采用持久化,VueX 整合插件实现持久化:vuex-persistedstate
- 安装插件

npm install vuex-persistedstate

配置在 /src/store/index.js 中实现配置
import Vue from 'vue'
import Vuex from 'vuex'
// 1引入持久化插件
import vuexPersistedstate from "vuex-persistedstate";
Vue.use(Vuex)
export default new Vuex.Store({
state: {
username:"无名66"
},
mutations: {
updateName(state,v){
state.username = v
}
},
plugins:[vuexPersistedstate()] // 2加入插件
})
- 测试浏览器刷新测试效果
17.8 getters
state 中定义的数据,如果需要处理后再使用且需要重复使用,即就会在计算属性中重复处理,代码冗余.
将对 state 中数据处理的代码抽取到 getter 中,其他组件直接使用即可
使用步骤
- Vuex 中 getter 定义函数
- 组件中使用 this.$store.getter.xxx

也可以使用 mapGetters 在计算属性中简化取值

17.9 actions
调用 mutations 方法,让其以异步的方式执行.
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。

17.10 modules
Vuex 的 index.js 文件中定义太多内容,可以使用 modules 分模块拆分,方便管理维护

十八、Vue 知识总结
- 基本语法
- 插值表达式 {{}}, 用来取出 data 或者 computed 中的数据,也可以在其中做运算,甚至调用函数
- v-if,v-show 用来决定标签是否展现 (场景:分页按钮,权限菜单按钮)
- v-for 用来遍历生成表格数据
- v-model 和 v-bind, 用来绑定变量,实现双向绑定 (数据变化)
- v-on 或者 @ , 用来事件绑定
- 特性
- 计算属性
- 什么时候需要用?当需要对 data 中数据运算且重复使用该运算结果时就可以将运算定义在计算属性
- 侦听器 (自学)
- 组件化
- 一套完整的代码,包含 html,css,js
- 将代码抽取封装,独立,方便使用和维护,可以实现 spa 开发
- 需要特殊语法
- template 里面只能一个根标签,一般都是 div
- js 中需要使用 export 将代码内容导出
- js 中 data 中的数据需要定义在函数的 return 中
- 生命周期函数
- vue 工程化
- 会使用 vue-cli 脚手架创建项目
- 熟悉项目结构
- src
- asstes 静态资源
- components 公共的组件
- router 路由表
- store vuex 管理数据状态
- views 路由的页面
- App.vue 主页面
- main.js 主配置文件
- package.json 定义运行脚本,修改端口
- node_modules 定义了一些项目运行需要的依赖,一般别人的项目都删除了,我们要运行需要先执行 npm install 把这些依赖下载
- 需要掌握核心路由技术 router
- 页面切换就需要路由
- 路由方式 2 种
- 编程式路由
- 声明式路由
- 携带数据路由
- 嵌套路由
- 路由模式
- 路由守卫
- 路由重定向,元信息
- 数据共享技术 Vuex
- 当 存储数据时定义 vuex 中的 state 存储即可
十九、Axios【重点】
19.1 什么是 Axios
Axios 是一个开源的可以用在浏览器端和 NodeJS 的 **<font style="color:#DF2A3F;">异步通信框架</font>**,它的主要作用就是实现 AJAX 异步通信,其功能特点如下:
从浏览器中创建 XMLHttpRequests
从 node.js 创建 http 请求
支持 Promise API
拦截请求和响应
转换请求数据和响应数据
取消请求
自动转换 JSON 数据
客户端支持防御 XSRF(跨站请求伪造)
由于 Vue.js 是一个 视图层框架 并且作者(尤雨溪)严格准守 SoC (关注度分离原则),所以 Vue.js 并不包含 AJAX 的通信功能,为了解决通信问题,作者单独开发了一个名为 vue-resource 的插件,不过在进入 2.0 版本以后停止了对该插件的维护并推荐了 Axios 框架
19.2 Axios 的使用
19.2.1 安装 vue axios
npm install --save axios vue-axios

19.2.2 在需要使用的页面中引入
需要发请求,那就在页面的 script 中引入即可
<font style="background-color:#FBDE28;">import axios from 'axios'</font>
初始化查询数据,一般定义在 created () 函数中
vue
<template>
<div>
<h1>演示axios的应用</h1>
<ul>
<li>当前页: {{ pageNum }}</li>
<li>每页显示条数: {{ pageSize }}</li>
<li>总条数: {{ total }}</li>
<li>总页数: {{ totalPages }}</li>
</ul>
<hr>
<table border="1">
<tr>
<td>id</td>
<td>email</td>
<td>first_name</td>
<td>last_name</td>
<td>avatar</td>
</tr>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.email }}</td>
<td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td>
<td><img :src="user.avatar" alt="avatar" width="50" height="50"></td>
</tr>
</table>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "ShowAxios",
data() {
return {
pageNum: null,
pageSize: null,
total:null,
totalPages: null,
users: []
}
},
methods: {
fetchData() {
// https://reqres.in/api这个网站要求携带x-api-key这个请求头
axios.defaults.headers['x-api-key'] = 'reqres_df5880e088e145d28cee541a6b424b67'
// 发送axios请求获得数据 , 下面这个是获取第2页的用户数据
// res就是该网站的返回的数据
axios.get('https://reqres.in/api/users?page=2').then(res => {
// console.log("获取第2页的用户数据:",res) // 这个res中有6个数据,只关心返回的数据data中
console.log("获取第2页的用户数据:",res.data)
// 从res.data中提取出我们需要的数据
this.pageNum = res.data.page
this.pageSize = res.data.per_page
this.total = res.data.total
this.totalPages = res.data.total_pages
this.users = res.data.data
})
.catch(err => {
console.log(err)
})
}
},
// 组件创建时,发送请求获取数据,使用生命周期函数created
// created函数,会在组件创建完毕自动调用
// 一般初始化数据都会在created函数中发送请求获取数据
created() {
this.fetchData()
}
}
</script>
<style scoped>
</style>

图片上的请求头是该服务器要求,固定写法,照抄
请求地址: 'https://reqres.in/api/users?page=2是固定的
19.2.3 使用 axios 发请求
get 请求:
vue
<template>
<div>
axios测试页面
<table border="1" width="800px" align="center" >
<tr>
<th>序号</th>
<th>id</th>
<th>姓氏</th>
<th>名字</th>
<th>邮箱</th>
<th>头像</th>
</tr>
<tr v-for="(user, index) in users" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ user.id }}</td>
<td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td>
<td>{{ user.email }}</td>
<td><img :src="user.avatar"></td>
<td><button @click="fetchDate">删除</button></td>
<td><button @click="fetchDate">修改</button></td>
</tr>
</table>
</div>
</template>
<script >
import axios from 'axios'//导入 axios对象
export default {
name: 'TableViews',
data() {
return {
users: []
}
},
methods: {
fetchDate(){
console.log('开始请求数据')
// https://reqres.in/api这个网站要求携带x-api-key这个请求头
//axios.defaults.headers['x-api-key'] = 'reqres_df5880e088e145d28cee541a6b424b67'
//开始在这个里面 发送ajax请求 请求一个API(URL) 会返回数据到前端页面中
//https://reqres.in/api/users?page=2 会换成后端的自己的url
//axios.get("https://reqres.in/api/users?page=2").then(res=>{
//axios.get().then().catch() 链式操作
axios.get("/users?page=2").then(res=>{
console.log(res.data.data, "line19行")
this.users = res.data.data
}).catch(err=>{
console.log(err)
})
}
},
created() {
this.fetchDate()
}
}
</script>
<style scoped>
</style>
post 请求:
vue
<template>
<div>
<h1>登录页面</h1>
<!-- 帮我写登录的输入框-->
<input type="text" placeholder="请输入邮箱" v-model="email"> <br>
<input type="password" placeholder="请输入密码" v-model="password"> <br>
<button @click="login">登录</button>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "",
components: {},
data() {
return {
email: "",
password: ""
}
},
methods: {
login() {
// 1. 获取用户输入的邮箱和密码
const email = this.email
const password = this.password
// 2. 发起ajax请求,将用户输入的邮箱和密码发送给服务器
////axios.post("https://reqres.in/api/login")
axios.post("/login", {
email: email,
password: password
}).then(res => {
console.log(res, "line36")
if (res.status == 200) {
//编程式路由 跳转到主页
} else {
}
}).catch(err => {
console.log(err)
})
}
}
};
</script>
<style scoped>
</style>
19.2.5 BUG
在 axios 的回调中,无法使用 this. 数据 获得 vue 的 data 中的数据
造成 axios 取不到值得原因主要是 this 回调问题。
当执行完函数 function 时,this 指向放生变化。导致 this 指向函数体本身。
这时 this.data 里的数据取不到。
简单直接的解决就是把 function 写成箭头函数形式,箭头函数不是构造函数,不会指定 this,而是抓取上下文 this 作为自己的 this

19.3 其他 api 演示
可以查看官网请求配置 | Axios 中文文档 | Axios 中文网 (axios-http.cn)
https://axios-http.com/zh/docs/post_example
http://www.axios-js.com/zh-cn/docs/
也可以看示例
// get请求(1) 路径拼接参数
axios.get('http://localhost:8080/vue/user?id=456').then(function(ret){
console.log(ret.data);
})
// get请求(2) Restful风格参数
// 这么写后端SpringBoot项目需要特殊@PathVariable接收
axios.get('http://localhost:8080/vue/user/123').then(function(ret){
console.log(ret.data);
})
// get请求(3) 使用params传递参数
axios.get('http://localhost:8080/vue/user',{
params:{
id:20001
}
}).then(function(ret){
console.log(ret.data);
})
// post请求,发送的是json,后台需要使用@RequestBody
axios.post('http://localhost:8080/vue/user/json',{
username:'张三',
password:'987654'
}).then(function(ret){
console.log(ret.data);
})
// put请求
axios.put('http://localhost:8080/vue/user/form',{
username:'张三丰',
password:'11111'
}).then(function(ret){
console.log(ret.data);
}).catch(function (err){
console.log(err)
})
19.4 Axios 的响应

响应结果的主要属性:
- <font style="color:#DF2A3F;">data: 服务器响应回的数据</font>
- headers: 响应头信息
- status: 响应状态码
- statusText: 响应状态信息
- config
- request
需要特别注意,我们后台返回的数据在 data 中
19.5 axios 的全局配置
官网: 默认配置 | Axios 中文文档 | Axios 中文网 (axios-http.cn)
我们讲一个特别实用的,我们在项目中调用数十次乃至百次接口是很常见的,后期一旦根域名发生改变,所有接口都需要修改,非常繁琐且容易出错。
axios 提供了设置根域名的方法。在main.js全局入口文件中,设置:
import axios from 'axios'
// 全局配置axios的请求头
axios.defaults.headers['x-api-key'] = 'reqres-free-v1'
// 全局配置axios的baseURL
axios.defaults.baseURL = 'https://reqres.in/api'
在其他 vue 页面中使用 axios 发请求的时候
axios.get('/dept').then((res)=>{
console.log(res)
this.deptList = res.data.data
})
19.6 封装 axios 工具 [重点]
wb 的写法:
1. 创建了 utils/request.js

2. 创建 api/user.js
request 方法的目的: 相当于 axios 发送请求

3. 在组件中 script 中调那个方法正常使用

作用: 就是 在组件中不能出现 axios.get axios.post 等
模仿若依
创建 utils/request.js, 内容如下

// 封装axios请求
import axios from 'axios'
// 统一设置请求头的contentType为json格式
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
axios.defaults.headers['x-api-key'] = 'reqres-free-v1'
// 创建axios实例,这个service就是axios对象
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
// 这个路径是从.env.xxx文件中读取的
baseURL: 'https://reqres.in/api',
// 超时
timeout: 10000
})
// 导出axios实例
export default service
按照项目的模块,创建各自的 api/xxx.js

import request from '@/utils/request'
// 查询全部用户
export function getUserList(query) {
return request({
url: '/users',
method: 'get',
params: query
})
}
// 用户登录
export function login(data) {
return request({
url: '/login',
method: 'post',
data
})
}
页面内导入该方法
// 从api/user.js中导入getUserList,login函数
import { getUserList, login } from '@/api/user'
方法调用使用
methods: {
fetchData() {
getUserList({page: 1}).then(res => {
console.log("获取第1页的用户数据:", res.data)
this.pageNum = res.data.page
this.pageSize = res.data.per_page
this.total = res.data.total
this.totalPages = res.data.total_pages
this.users = res.data.data
}).catch(err => {
console.log("获取用户数据失败:", err)
})
},
login() {
login(this.user).then(res => {
console.log("登录成功:",res.data)
alert("登录成功")
})
.catch(err => {
console.log("登录失败:",err)
})
}
}
删除之前 main.js 中关于 axios 的配置即可
19.7 拦截器
https://www.axios-http.cn/docs/interceptors
在 request.js 文件中添加请求拦截
js
// 添加请求拦截器
service.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
console.log('------- 请求拦截器 -------')
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
在 request.js 中响应拦截
js
// 添加响应拦截器
service.interceptors.response.use(function (response) {
console.log('------- 响应拦截器 -------')
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});
演示接口
接口由该网站提供: https://reqres.in/
每个请求需要设置请求头
yaml
headers:{
"x-api-key":"reqres_df5880e088e145d28cee541a6b424b67"
},
| 方式 | 路径 | 参数 |
|---|---|---|
| GET | https://reqres.in/api/users?page=2 | page=2 |
| POST | https://reqres.in/api/users | plain { "name": "morpheus", "job": "leader" } |
| POST | https://reqres.in/api/login | plain { "email": "eve.holt@reqres.in", "password": "cityslicka" } |
二十、Element UI
Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库
官网: https://element.eleme.cn/#/zh-CN
Element Plus, 基于 Vue 3,面向设计师和开发者的组件库
官网: https://element-plus.gitee.io/zh-CN/
安装 & 入门使用
1. 装 ElementUI
npm install element-ui -S

2. 项目中引入
// main.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
// ...其他以前的东西...
测试
新建文件,Element 官网复制代码,启动访问即可
常用组件
- <font style="color:#DF2A3F;">布局 : </font>布局" target="_blank"><font style="color:#DF2A3F;">布局</font>
- <font style="color:#DF2A3F;">容器 </font>容器" target="_blank"><font style="color:#DF2A3F;">容器</font>
- 单选 单选
- 复选框 多选框
- 输入框 输入
- 下拉框 选择器
- 日期 日期
- 上传 上传
- <font style="color:#DF2A3F;">表单 </font>表单" target="_blank"><font style="color:#DF2A3F;">表单</font>
- <font style="color:#DF2A3F;">表格 </font>表格" target="_blank"><font style="color:#DF2A3F;">表格</font>
- 弹框
- 分页 分页
- 对话框 对话框
补充知识: 对象展开运算符
对象展开运算符
...(也称为扩展运算符)是一种用于展开对象或数组的语法,它允许将一个对象或数组拆分成单独的元素,并在其他对象或数组中使用。在 JavaScript 中,...运算符可以用在多种场景中,下面分别介绍它们的用法。展开对象
可以使用对象展开运算符
<font style="color:rgb(233, 105, 0);background-color:rgb(248, 248, 248);">...</font>来创建一个新对象,并将另一个对象中的属性和值复制到新对象中。这可以用于对象浅拷贝或对象合并。示例:
const obj1 = { name: 'John', age: 30 };
const obj2 = { ...obj1, city: 'New York' };
obj2 =
console.log(obj2);
// 输出:{ name: 'John', age: 30, city: 'New York' }
合并对象
将多个对象合并成一个新对象的简便方法。如果有相同的属性,后面的对象的属性值会覆盖前面的对象的属性值。
示例:
const obj1 = { name: 'John', age: 30 };
const obj2 = { city: 'New York' };
const obj3 = { ...obj1, ...obj2 };
console.log(obj3);
// 输出:{ name: 'John', age: 30, city: 'New York' }
展开数组
在数组中使用
<font style="color:rgb(233, 105, 0);background-color:rgb(248, 248, 248);">...</font>可以将一个数组展开为独立的元素,可以用于创建一个新数组或者在函数调用时传递参数。示例:
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2);
// 输出:[1, 2, 3, 4, 5]
const arr = [1, 2, 3];
someFunction(...arr); // 将数组中的元素作为参数传递给函数
更多推荐










所有评论(0)