内容来源于蓝桥杯竞赛,自己根据这个题纲重新巩固下前端的知识
第五章 Vue.js
认识VUE
因为自己开发框架就是vue所以对vue有了一个基本的使用了解,这部分就不详述了,有需要可以到官网看下。看文档才是最高效的学习方法。
模板语法
双大括号表达式
插值
但是通过使用 v-once
指令你也能执行一次性地插值,当数据 改变时,插值处的内容 不会更新。但是你需要注意一下,该元素节点下面其他数据的绑定,数据改变,内容也不会更新,所以,注意代码块的划分。
<p v-once>msg:{{msg}}</p>
那就是因为双大括号表达式会将数据解释为普通文本。有的同学会问,有的需求就是要把标签渲染出来,那么我们就需要认识另外一个指令 v-html
,使用它我们就能将它正确渲染。
<!-- v-html 渲染 html 元素-->
<div id="app" v-html="msg"></div>
<script>
var app = new Vue({
el: "#app", // el: 挂载点
data: {
// data: 数据选项
msg: "<h1>hello syl</h1>",
},
});
</script>
们只进行了绑定简单的属性键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持,感受强大的模板语法力量吧!
<!-- javascript 表达式-->
<div id="app">
<!-- 运算符 -->
<p>num + 24 = {{num + 24}}</p>
<!-- 三元表达式 -->
<p>Are you ok? {{ok ? 'I am ok !':'no'}}</p>
<!-- 对象方法直接调用 -->
<p>名字倒过来写:{{name.split('').reverse().join('')}}</p>
<!-- 属性值运算操作 -->
<p v-bind:class="'col'+colNum">syl</p>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
num: 20,
ok: true,
name: "实验楼",
colNum: "12",
},
});
</script>
v-bind 指令
值的绑定
v-on 指令
事件的绑定,如下动态操作的方法没有用过
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue</title>
<!--引入 vue.js-->
<script src="vue.min.js"></script>
</head>
<body>
<!-- 指令 动态参数-->
<div id="app">
<p>我叫:{{name}}</p>
<button v-on:[event]="handleClick">点我</button> // 动态参数
</div>
<script>
var app = new Vue({
el: "#app",
data: {
name: "实验楼",
event: "click",
},
methods: {
handleClick: function () {
this.name = this.name.split("").reverse().join("");
},
},
});
</script>
</body>
</html>
修饰符
修饰符是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定,大致分为三类,后面课程我们会一一接触到:
- 事件修饰符
- 按键修饰符
- 系统修饰符
例如,事件修饰符中的 .prevent
修饰符和原生 event.preventDefault()
效果一样,可以阻止事件默认行为,在表单中点击提交按钮,就会发生页面跳转,但是使用了 .prevent
就不会发生跳转,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue</title>
<!--引入 vue.js-->
<script src="vue.min.js"></script>
</head>
<body>
<!-- 指令 修饰符-->
<div id="app">
<form action="/" v-on:submit.prevent="submit">
<button type="submit">提交</button>
</form>
</div>
<script>
var app = new Vue({
el: "#app",
data: {},
methods: {
submit: function () {
console.log("成功提交!");
},
},
});
</script>
</body>
</html>
指令缩写
v-bind
使用:
替换,例如v-bind:class="className"
可以简写为 :class="className"
,v-bind:value="myValue"
可以简写为 :value
。
v-on
使用@
替换,例如@click="handleClick"``v-on:click="handleClick"
。
计算、侦听属性与过滤器
计算属性
例如:购物车,平常开发数据与数据关联计算。在实例的 computed
选项中定义你的计算属性,直接使用 {{}}
向页面输出。计算属性是 惰性的,只有当依赖数据发生改变时,才会触发计算,否则,它的值是上一次触发计算的缓存值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<p>我名字正着写:{{name}}</p>
<!-- reverseName 计算属性 可以像绑定普通属性一样在模板中绑定计算属性-->
<p>计算出我名字倒着写:{{reverseName}}</p>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
name: "实验楼",
},
computed: {
// reverseName 是一个计算属性
reverseName: function () {
return this.name.split("").reverse().join("");
},
},
});
</script>
</body>
</html>
setter 和 gette
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<p>firstName:{{firstName}}</p>
<p>lastName:{{lastName}}</p>
<p>全名是:{{fullName}}</p>
<button v-on:click="changeName">改姓</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
firstName: "王",
lastName: "花花",
},
methods: {
// changeName 定义一个方法改变 计算属性 fullName 的值
changeName: function () {
// 修改计算属性 fullName 等于李花花
this.fullName = "李花花";
// 上面一句等于触发了 fullName 属性的 setter
},
},
computed: {
fullName: {
// getter
get: function () {
return this.firstName + this.lastName;
},
// setter 直接改变计算属性 fullName 的值就可以触发 setter this.fullName='XX'
set: function (newName) {
var name = newName;
this.firstName = name.slice(0, 1); // 取新值的第一个字符
this.lastName = name.slice(1); // 从新值的第二个字符开始取值
},
},
},
});
</script>
</body>
</html>
侦听属性
在开发我们需要监听数据的变化,Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动,侦听属性。在实例 watch
选项中确定监听项。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<p>{{msg}}</p>
<!-- v-on:click 简写为 @click -->
<button @click="handleClick('hello syl')">改变msg</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
msg: "hello",
},
methods: {
// 改变 msg 的值
handleClick: function (val) {
this.msg = val;
},
},
// watch 监听属性
watch: {
// 监听新旧值 监听属性有两个参数,第一个新值,第二个旧值
msg: function (newVal, oldVal) {
alert("新值" + newVal + "----" + "旧值" + oldVal);
},
},
});
</script>
</body>
</html>
计算属性和侦听属性两者在很多场景都是共同,都可以实现同样的需求。
过滤器 **
计算属性和侦听属性,在数据处理环节中很有作用,但是用它来处理数据过滤不是最优的
<p>{{msg2|getString}}</p>
<p v-bind:class="msg2|getString"></p>
在双花括号插值和 v-bind 表达式中把需要过滤的数据用 | 与过滤器分割 (data|filter)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- toUpperCase getString 为自定义的过滤器-->
<p>小写转换大写:过滤前:{{msg}} 过滤后: {{msg|toUpperCase}}</p>
<p>去除数字:过滤前:{{msg2}} 过滤后: {{msg2|getString}}</p>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
msg: "hello",
msg2: "1s2y3l",
},
// filters 过滤器选项
filters: {
// toUpperCase 定义一个字符串转大写的过滤器
toUpperCase: function (val) {
return val.toUpperCase();
},
// getString 定义一个获取去除数字的过滤器
getString: function (val) {
let newVal = "";
val.split("").map(function (item) {
if (9 >= item && item >= 0) {
return;
} else {
return (newVal += item);
}
});
return newVal;
},
},
});
</script>
</body>
</html>
过滤器应用场景
我们请求到的数据并没有 ¥,在开发中直接去操作数据源是不推荐的,此时我们的过滤器就派上用场,定义一个拼接 ¥ 的过滤器,只是在视图层面实现了效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- joint 为自定义的过滤器-->
<p>不要¥899,只要{{price|joint}}</p>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
// 后台价格数据
price: 199,
},
// filters 过滤器选项
filters: {
// joint 定义 ¥ 拼接过滤器
joint: function (price) {
return "¥" + price;
},
},
});
</script>
</body>
</html>
实验:简易购物车
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
<style>
* {
padding: 0;
margin: 0;
}
.price {
font-size: 22px;
color: brown;
}
</style>
</head>
<body>
<div id="app">
<!-- joint 为自定义的过滤器-->
<p>单价<span class="price">{{price|joint}}</span></p>
数量:<input type="number" v-model="goodsNum" />
<p>总价:<span class="price">{{allPrice|joint}}</span></p>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
goodsNum: 0,
price: 199,
},
computed: {
allPrice: function () {
return this.goodsNum * this.price;
},
},
// filters 过滤器选项
filters: {
// joint 定义 ¥ 拼接过滤器
joint: function (price) {
return "¥" + price;
},
},
});
</script>
</body>
</html>
class 与 style 绑定**
元素的 class 绑定
有三种,准确说不能说三种,是三个用法
对象语法
给 v-bind:class
一个对象,以动态地切换 class,语法表示 active
这个 class 存在与否将取决于数据属性 isActive
的 Boolean 值,大致语法 {className:Boolean}
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
<style>
.active {
color: pink;
font-size: 22px;
}
</style>
</head>
<body>
<div id="app">
<!-- 数组语法绑定 class 当 isActive 为 true 时,active 就成 span 标签的 class -->
<span v-bind:class="{'active':isActive}">syl</span>
<!-- isActive 为 true 渲染结果 <span class="active">syl</span> -->
</div>
<script>
var app = new Vue({
el: "#app",
data: {
isActive: true,
},
});
</script>
</body>
</html>
多对象语法
你可以在对象中传入更多属性来动态切换多个 class,v-bind:class
指令也可让普通的 class 并存。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
<style>
.active {
color: pink;
font-size: 22px;
}
.red-bg {
background: red;
}
</style>
</head>
<body>
<div id="app">
<!-- 数组语法绑定 class 当 isActive 为 true 时,active 就成 span 标签的 class -->
<span class="static" v-bind:class="{'active':isActive,'red-bg':isRed}"
>syl</span
>
<!-- isActive 为 true 渲染后 <span class="static active red-bg">syl</span> -->
</div>
<script>
var app = new Vue({
el: "#app",
data: {
isActive: true,
isRed: true,
},
});
</script>
</body>
</html>
数组语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
<style>
.active {
color: pink;
font-size: 22px;
}
.red-bg {
background: red;
}
</style>
</head>
<body>
<div id="app">
<!-- 数组语法绑定 class -->
<span v-bind:class="[activeClass,bgColorClass]">syl</span>
<!-- 渲染后 <span class="active red-bg">syl</span> -->
</div>
<script>
var app = new Vue({
el: "#app",
data: {
activeClass: "active",
bgColorClass: "red-bg",
},
});
</script>
</body>
</html>
元素 style 绑定
在前端开发中,内联样式经常被使用到,Vue 中内联样式绑定语法灵活。CSS 属性名可以用驼峰式 (camelCase) 或 短横线分隔 (kebab-case,记得用单引号括起来) 来命名。
方案一:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<p v-bind:style="{fontSize:size,backgroundColor:bgColor}">你好,实验楼</p>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
size: "26px",
bgColor: "pink",
},
});
</script>
</body>
</html>
方案二:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<p v-bind:style="styleObject">你好,实验楼</p>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
styleObject: {
fontSize: "26px",
backgroundColor: "pink",
},
},
});
</script>
</body>
</html>
方案三:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<p v-bind:style="[styleObject1,styleObject2]">你好,实验楼</p>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
//样式一
styleObject1: {
fontSize: "26px",
backgroundColor: "pink",
},
//样式二
styleObject2: {
marginTop: "200px",
textAlign: "center",
},
},
});
</script>
</body>
</html>
条件与循环渲染
条件渲染
v-if
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回除了 false
,0
,""
,null
,undefined
和 NaN
外的值的时候被渲染。
v-else
使用 v-else
指令来表示 v-if
的“else 块”,当 v-if 返回除了 false
,0
,""
,null
,undefined
和 NaN
外的值的时候被渲染,否则,就渲染 v-else
块元素。注意:v-else
元素必须紧跟在带 v-if
或者 v-else-if
的元素的后面,否则它将不会被识别
v-show
另一个用于根据条件展示元素的选项是 v-show
指令。不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS 属性 display
。而 v-if
是从虚拟 DOM 的层面操作。
一般来说,v-if
会牵涉到虚拟 DOM diff 算法,有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
循环渲染
循环渲染经常会使用到,在开发中经常会遇到 DOM 结构一样的块代码,那么我们就可以使用循环渲染来一步到位。
v-for 将数组数据渲染成元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
<style>
* {
padding: 0;
margin: 0;
}
ul {
width: 100%;
height: 40px;
list-style: none;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background: yellowgreen;
}
ul li {
width: 20%;
height: 100%;
color: white;
line-height: 40px;
text-align: center;
text-transform: uppercase; /*大写转换*/
}
</style>
</head>
<body>
<div id="app">
<ul class="nav">
<li v-for="navItem in nav">{{navItem}}</li>
</ul>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
nav: ["home", "shop", "contact", "about", "name", "mroe", "histroy"],
},
});
</script>
</body>
</html>
key 属性
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
数组更新检测
对数组的有些操作不会直接被页面响应,此时如果需求为响应式需要使用vue方法。
app.items[1] = "x"; // 不是响应性的
app.items.length = 2; // 不是响应性的
//以下为响应式
// Vue.set
Vue.set(vm.items, indexOfItem, newValue);
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue);
显示过滤
显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。
方案一:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<span v-for="number in oddNumber">{{number}}</span>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
numberArray: [1, 2, 3, 4, 5, 6, 7, 8],
},
computed: {
// 计算 numberArray 中为奇数的 oddNumber 奇数数组
oddNumber: function () {
return this.numberArray.filter(function (number) {
return number % 2 === 1;
});
},
},
});
</script>
</body>
</html>
方案二:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- v-for 内直接调用方法 -->
<span v-for="number in getOddNumber()">{{number}}</span>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
numberArray: [1, 2, 3, 4, 5, 6, 7, 8],
},
methods: {
// 定一个一个获取数组内奇数的方法 filter 数组对象的过滤方法
getOddNumber: function () {
return this.numberArray.filter(function (number) {
return number % 2 === 1;
});
},
},
});
</script>
</body>
</html>
v-for 循环一段值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- :style 绑定样式 -->
<span v-for="number in 10" :style="styleObject">{{number}}</span>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
// 指定分页样式
styleObject: {
fontSize: "14px",
color: "#fff",
background: "green",
padding: "5px 10px",
border: "1px solid #fff",
},
},
});
</script>
</body>
</html>
综合实验
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<style>
* {
padding: 0;
margin: 0;
}
a {
text-decoration: none;
color: #fff;
}
ul {
list-style: none;
}
nav,
ul {
width: 100%;
display: flex; /* 开启弹性盒模型 布局方式*/
flex-direction: row;
justify-content: center;
background: yellowgreen;
}
nav > ul > li {
width: 20%;
height: 100%;
text-align: center;
line-height: 50px;
}
nav > ul > li:hover {
box-shadow: 1px 0px 10px #fff;
}
nav > ul > li > ul {
display: flex;
flex-direction: column;
}
nav > ul > li > ul > li {
box-shadow: 1px 0px 10px #fff;
}
nav > ul > li > a {
text-transform: uppercase;
}
</style>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<nav>
<ul>
<!-- 循环渲染一级菜单 -->
<!-- 鼠标移入触发 currentIndex(index) 函数,更正 current 是当前菜单的 index,鼠标移出重置 current 为空,事件回调方法在 methods 中实现-->
<li
v-for="(nav,index) in navbar"
:key="index"
@mouseover="currentIndex(index)"
@mouseout="changeIndex"
>
<!-- nav.name 一级菜单名字 -->
<a href="javascript:;">{{nav.name}}</a>
<!-- 如果 nav.child 存在,说明有子菜单,再次循环渲染子菜单 -->
<!-- 子菜单 v-show 如果当前菜单的 index 等于 鼠标移入那个菜单的下标我们就展示出子菜单-->
<ul v-if="nav.child" v-show="current===index">
<li v-for="item in nav.child">
<a href="javascript:;">{{item}}</a>
</li>
</ul>
</li>
</ul>
</nav>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
// navbar 模拟后台获取到的菜单列表
navbar: [
{
name: "home",
child: ["homeItem", "homeItem"],
},
{
name: "contact",
child: ["contactItem", "contactItem"],
},
{
name: "about",
},
],
// current 当前鼠标在那个菜单上,初始时没有值
current: null,
},
methods: {
// 更正 当前鼠标移入的是哪个菜单的 index
currentIndex: function (index) {
this.current = index;
},
// 鼠标移出 重置 current 值
changeIndex: function () {
this.current = null;
},
},
});
</script>
</body>
</html>
事件处理
事件处理方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
<style>
button {
width: 150px;
height: 40px;
border-radius: 10px;
background: green;
outline: none;
color: #fff;
}
</style>
</head>
<body>
<div id="app">
<!-- 绑定点击监听 -->
<button v-on:click="say">点击</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
counter: 0,
},
methods: {
// 声明事件点击监听 say 方法
say: function (event) {
// 监听事件回调处理 event.type 触发事件类型 说明:`${}` 为 es6 模板字符串,拼接字符串的
alert(`小楼提醒:你触发了${event.type}事件`);
},
},
});
</script>
</body>
</html>
修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求,阻止事件冒泡或捕获或者事件默认行为。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
.stop
:阻止单击事件继续传播。
.stop
修饰符的应用。
未添加 .stop
修饰符,事件会触发冒泡行为,点击子元素也会触发父元素的相同事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
<style>
/* 居中 */
.super,
.child {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
}
.super {
width: 300px;
height: 300px;
background: pink;
}
.super .child {
width: 100px;
height: 100px;
background: green;
}
</style>
</head>
<body>
<div id="app">
<div class="super" v-on:click="handleClick('super')">
父
<div class="child" v-on:click="handleClick('child')">子</div>
</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {},
methods: {
// 声明事件点击监听 handleClick
handleClick: function (name) {
alert(`我是${name}`);
},
},
});
</script>
</body>
</html>
添加后
<div id="app">
<div class="super" v-on:click.stop="handleClick('super')">
父
<div class="child" v-on:click.stop="handleClick('child')">子</div>
</div>
</div>
.prevent
:阻止事件默认行为。.capture
:添加事件监听器时使用事件捕获模式。.self
:只当在 event.target 是当前元素自身时触发处理函数。.once
:点击事件将只会触发一次。.passive
:滚动事件的默认行为 (即滚动行为) 将会立即触发。
按键修饰符
.enter
- `.tab``
- ``.delete`
.esc
- ``.space`
- .up.down.left.right`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<input
type="text"
v-on:keyup.enter="alert('你按了enter,确定输入完毕?')"
/>
</div>
<script>
var app = new Vue({
el: "#app",
});
</script>
</body>
</html>
按键 | 键码 |
---|---|
Enter | 13 |
Shift | 16 |
Alt | 18 |
Spacebar | 32 |
Page Up | 33 |
Page Down | 34 |
系统修饰符
.ctrl
.alt
.shift
.meta
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- 同时鼠标左击和按 ctrl 弹出提示 -->
<div @click.ctrl="alert('你同时按了鼠标点击和ctrl')">Do something</div>
</div>
<script>
var app = new Vue({
el: "#app",
});
</script>
</body>
</html>
精确按钮修饰符
.exact
精确按键修饰符,允许你控制由精确的系统修饰符组合触发的事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button
@click.ctrl="alert('你不单单只按了鼠标左键和 Ctrl键,同时按其他键我也可以触发')"
>
A
</button>
<!-- 有且只有 ctrl 键 + 鼠标左键 被按下的时候才触发 -->
<button @click.ctrl.exact="alert('你只按ctrl键+鼠标左键,才能触发我')">
A
</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="alert('没有按任何系统修饰符')">A</button>
</div>
<script>
var app = new Vue({
el: "#app",
});
</script>
</body>
</html>
鼠标按钮修饰符
.left
.right
.middle
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<button @click.left="alert('你按了鼠标左击键')">按钮</button>
<button @click.middle="alert('你按了鼠标滚轮')">按钮</button>
<button @click.right="alert('你按了鼠标右击键')">按钮</button>
</div>
<script>
var app = new Vue({
el: "#app",
});
</script>
</body>
</html>
综合小练习
小游戏(500px),一直鼠标左键,将小球向右移 500px,拼手速的哦:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<!-- 引入 vue.js -->
<script src="vue.min.js"></script>
<style>
* {
padding: 0;
margin: 0;
}
#app {
position: relative;
width: 500px;
height: 100px;
background: black;
}
.box {
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 100px;
border-radius: 50%;
background: red;
transition: all 0.3s ease-in;
}
</style>
</head>
<body>
<div id="app" @click.left="changePosition">
<div class="box" :style="{left:left+'px'}"></div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
left: 0,
},
methods: {
changePosition: function () {
this.left += 25;
if (this.left >= 500) {
alert("走了500px了");
this.left = 0;
}
},
},
});
</script>
</body>
</html>
表单处理
复选框用发:当选中值位yes未选中值位false。
在日常开发中,复选框的值很多情况是特定的值,那么我们可以这样做,在标签中声明 true-value="yes"
和 false-value="no"
这两个属性,当选中时就是 true-value 属性指定的值,当未选中时就是 false-value 属性值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no"
/>
<p>toggle:{{toggle}}</p>
</div>
<script>
// 通过 true-value="yes" false-value="no" 属性控制,选中时 toggle 值为 yes,未选中时为 no
var vue = new Vue({
el: "#app",
data() {
return {
toggle: "",
};
},
});
</script>
</body>
</html>
如果你只想要选中有值,你可以这样做,true-value 属性指定值,false-value 属性设为空值。
<input type="checkbox" v-model="toggle" true-value="name" false-value="" />
修饰符
.lazy
开始介绍表单处理时,我们说了几点注意,不同的元素,使用的值不同,抛出的事件也不同。可能开发中,我们不需要数据实时更新,那么,我们怎么将 input 事件与 change 事件替换,可以使用 .lazy 修饰符,可以将抛出事件由 input 改为 change,使表单元素惰性更新,不实时更新。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<!--使用 .lazy 修饰符将文本框 抛出的事件改为 change 事件,不再实时更新,只有文本框失去焦点才更新数据 惰性更新 -->
<input v-model.lazy="msg" />
<p>{{msg}}</p>
</div>
<script>
var vue = new Vue({
el: "#app",
data: {
msg: "hello",
},
});
</script>
</body>
</html>
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model
添加 number
修饰符:
这通常很有用,因为即使在 type="number"
时,HTML 输入元素的值返回字符串(默认),需要自己进行类型转换。如果这个值无法被 parseFloat()
解析,则会返回原始的值。 给 v-model
添加 number
修饰符,用户即使输入的是非数值类型,也会进行转换,无法转换时,会返回原始的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<p>没有使用 .number 修饰符</p>
<input v-model="number1" type="number" />
<!-- 使用 typeof 对值类型检测 -->
<p>{{typeof(number1)}}</p>
<p>使用 .number 修饰符</p>
<input v-model.number="number2" type="number" />
<!-- 使用 typeof 对值类型检测 -->
<p>{{typeof(number2)}}</p>
</div>
<script>
var vue = new Vue({
el: "#app",
data: {
number1: "",
number2: "",
},
});
</script>
</body>
</html>
.trim
表单元素值首尾空格,自动过滤。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<input v-model.trim="msg" type="text" />
<p>首尾空格被过滤了:{{msg}}</p>
</div>
<script>
var vue = new Vue({
el: "#app",
data: {
msg: "",
},
});
</script>
</body>
</html>
综合小练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
<style>
* {
padding: 0;
margin: 0;
}
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
#app {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
width: 400px;
height: 400px;
}
</style>
</head>
<body>
<div id="app">
<form class="app-form">
<span>name: </span>
<input type="text" v-model="username" />
<br />
<span>password:</span><input type="password" v-model="password" />
<br />
<span
>sex: </span
>
<input type="radio" id="man" value="man" v-model="sex" />
<label for="man">man</label>
<input type="radio" id="woman" value="woman" v-model="sex" />
<label for="woman">women</label>
<br />
<span>hobby:</span>
<input type="checkbox" id="game" value="game" v-model="hobby" />
<label for="game">game</label>
<input
type="checkbox"
id="basketball"
value="basketball"
v-model="hobby"
/>
<label for="basketball">basketball</label>
<input type="checkbox" id="study" value="study" v-model="hobby" />
<label for="study">study</label>
<br />
<br />
<p>名字:{{username}}</p>
<p>密码:{{password}}</p>
<p>性别:{{sex}}</p>
<p>爱好:{{hobby}}</p>
</form>
</div>
<script>
var vue = new Vue({
el: "#app",
data() {
return {
username: "",
password: "",
sex: "man", // 性别单选默认勾选男
hobby: [],
};
},
});
</script>
</body>
</html>
组件
组件注册
全局组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<syl></syl>
<syl></syl>
<syl></syl>
</div>
<script>
// Vue.component(组件名字,template:{元素标签})
Vue.component("syl", {
template: "<h1>实验楼全局组件</h1>",
});
var app = new Vue({
el: "#app",
});
</script>
</body>
</html>
局部组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="header">
<syl-header></syl-header>
</div>
<div id="mid">
<syl-mid></syl-mid>
</div>
<script>
// 头部组件
var childComponent = {
template: "<h2>我是实验楼局部组件header,只有我们父级才能调用</h2>",
};
// 中间部分组件
var childComponent2 = {
template: "<h2>我是实验楼局部组件mid,只有我们父级才能调用</h2>",
};
// header vm
var header = new Vue({
el: "#header",
// 子组件必须声明后使用,不然不能起效
components: {
"syl-header": childComponent,
},
});
var mid = new Vue({
el: "#mid",
// 子组件必须声明后使用,不然不能起效
components: {
"syl-mid": childComponent2,
},
});
</script>
</body>
</html>
组件复用
复用组件内的 data 必须是一个函数,如果是一个对象(引用类型),组件与组件间会相互影响,组件数据不能独立管理。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script>
// 注册一个全局可复用组件
Vue.component("button-counter", {
// data 必须是一个函数不然会影响其他组件
data() {
return {
counter: 0,
};
},
template: '<button @click="counter++">{{counter}}</button>',
});
var app = new Vue({
el: "#app",
});
</script>
</body>
</html>
组件间通信
父子组件之 props
props 是一个单向的数据流,只允许父组件向子组件传值,值类型可以是一个数值、字符、布尔值、数值、对象,子组件需要显式地用 props 选项声明 “prop”。
注意:HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 需要使用其等价的 kebab-case (短横线分隔命名) 命名,
通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式列出 prop,这些属性的名称和值分别是 prop 各自的名称和类型:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<title-component post-title="syl1"></title-component>
<title-component post-title="syl2"></title-component>
<title-component post-title="syl3"></title-component>
</div>
<script>
// 注册一个 title 组件,通过传入不同的 title 值,渲染不同的东西
// 组件上 传递的 props 属性名为 kebab-case(短横线分隔命名)的要转换为驼峰命名
Vue.component("title-component", {
props: ["postTitle"], // post-title 转换为驼峰命名
template: "<p>{{postTitle}}</p>",
});
var app = new Vue({
el: "#app",
});
</script>
</body>
</html>
子父组件通信之 emit
上面提到 props 实现父向子组件传递数据是单向流的,那么,如何实现子组件向父组件通信呢?这里要使用自定义事件 emit
方法,通过自定义事件来由下到上的数据流动。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<child-component v-on:send-msg="getMsg"></child-component>
</div>
<script>
// 定义一个子组件,template 绑定 click 事件
// 当 click 事件触发就使用 emit 自定义一个事件 send-msg,传入参数 “我是子组件请求与你通信”
// $emit('send-msg','我是子组件请求与你通信')
// 子组件标签上绑定自定义事件 send-msg,并绑定上父级的方法 getMsg,即可完成了子父组件通信
// <child-component v-on:send-msg="getMsg"></child-component>
Vue.component("child-component", {
template: `
<button v-on:click="$emit('send-msg','我是子组件请求与你通信')">
Click me
</button>
`,
});
var app = new Vue({
el: "#app",
methods: {
getMsg: function (msg) {
// 弹出子组件传递的信息
alert(msg);
},
},
});
</script>
</body>
</html>
子组件向父组件数据传递套路:
第一步:子组件绑定事件。
第二步:子组件绑定事件触发,使用 $emit 创建自定义事件并传入需要传值给父组件的数据。
第三步:在子组件标签上 用 v-on 绑定自定义事件,在父组件中声明自定义事件处理的方法。
第四步:父组件方法,接受自定义事件传的参数,就完成了整个由下到上的数据流。
动态组件
上面例子我们传值都是直接传的固定值,其实动态传值我们也支持,生成动态组件,使用 v-bind 动态绑定 props 值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- 使用 v-bind 简写模式 动态绑定 props 值 -->
<child-component
:name="name"
:age="age"
:height="height"
></child-component>
<child-component
:name="name+'2'"
:age="age+1"
:height="height"
></child-component>
</div>
<script>
// 定义一个子组件
Vue.component("child-component", {
// 使用属性类型检测
props: {
name: String,
age: Number,
height: String,
},
template: `
<ul>
<li>{{name}}</li>
<li>{{age}}</li>
<li>{{height}}</li>
</ul>
`,
});
var app = new Vue({
el: "#app",
data() {
return {
name: "syl",
age: 20,
height: "180cm",
};
},
});
</script>
</body>
</html>
生命周期函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue</title>
<script src="vue.min.js"></script>
</head>
<body>
<div id="app">
<button @click="handleClick">{{name}}</button>
</div>
<script>
var app = new Vue({
el: "#app",
data() {
return {
name: "syl",
};
},
methods: {
handleClick: function () {
this.name = "syl syl";
},
},
beforeCreate() {
alert(
"在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用"
);
},
created() {
"created 钩子函数内我们可以进行异步数据请求。"
alert(
"在实例创建完成后被立即调用,挂载阶段还没开始,$el 属性目前不可见"
);
},
beforeMount() {
alert("在挂载开始之前被调用:相关的 render 函数首次被调用");
},
mounted() {
"mounted 我们可以直接操作元素 DOM 了,但是并不推荐这样做,不利于性能提升“
alert("el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子");
},
beforeUpdate() {
alert("数据更新时调用");
},
updated() {
alert("组件 DOM 已经更新");
},
beforeDestroy() {},
destroyed() {},
});
</script>
</body>
</html>
Axios 的使用
- 可以从浏览器中创建 XMLHttpRequest。
- 能从 Node.js 创建 HTTP 请求。
- 支持 Promise API。
- 能够拦截请求和响应。
- 可以转换请求和响应数据。
- 也可取消请求。
- 可以自动转换 JSON 数据。
- 在客户端支持防止 CSRF/XSRF 攻击。
Axios简介
其中需要注意的是,get
和 post
请求中向后端传递参数的配置项名字不同:get
请求的需要使用 params
,post
请求用于发送数据的为 data
。
axios.get('url',{
params:{
id:'接口配置参数(相当于url?id=xxxx)',
},
}).then(function(res){
console.log(res); // 处理成功的函数 相当于 success
}).catch(function(error){
console.log(error) // 错误处理 相当于 error
})
axios
.post(
"url",
{ data: {} },
{
headers: "xxxx", // 头部配置
}
)
.then(function (res) {
console.log(res); // 处理成功的函数 相当于 success
})
.catch(function (error) {
console.log(error); // 错误处理 相当于 error
});
Axios的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>syl-vue-test</title>
<script src="vue.min.js"></script>
<script src="axios.min.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="item in dataList">{{item}}</li>
</ul>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
dataList: [],
},
});
// 可以放在外边,但为了方便理解一般会放在自定义函数中
axios.get("dataList.json").then((res) => {
console.log(res.data);
app.dataList = res.data;
});
</script>
</body>
</html>
Vue-Router
关于前端路由的 hash 和 history 两种模式的实现,感兴趣的同学可以通过这篇《前端路由实现方法》文章去了解。
前端路由的优点如下:
- 页面刷新速度快:由于不需要向服务器发送请求,所以这个过程不会受到网络延迟的影响,实际上只是完成部分组件间的切换,因此页面的刷新速度会比较快,用户体验也更好些。
- 复用性强:由于使用前端路由的应用为单页面应用,所以代码中很多 CSS、JS 都可以共用,避免了过多的重复加载,大大提升了性能。
- 页面状态可记录:如果不使用前端路由,仅通过 Ajax 在页面进行局部切换的应用,由于页面 URL 始终保持不变,因此页面的状态是无法记录的,而前端路由很好的解决了这个问题。例如,使用了前端路由的应用中访问
https://www.lanqiao.cn/a
这个链接,再打开后会直接触发/a
匹配的路由页面中的事件。
当然,前端路由也有一些缺点,比如使用浏览器的前进、后退键的时候,会重新发送请求,没有合理地利用缓存。
功能
- 嵌套路由映射
- 动态路由选择
- 模块化、基于组件的路由配置
- 路由参数、查询、通配符
- 展示由 Vue 的过渡系统提供的过渡效果
- 细致的导航控制
- 自动激活 CSS 类的链接
- HTML5 history 模式或 hash 模式
- 可定制的滚动行为
- URL 的正确编码
基本使用
我们通过一个单页面应用来看看 Vue-Router 的使用,其基本步骤如下所示:
- 使用
router-link
组件来导航,其通过to
属性来指定跳转链接(这相当于 HTML 中的 a 标签)。 - 使用
router-view
组件定义路由出口,路由匹配到的组件将会渲染到此处。 - 使用
const routes = [{ path, component }]
来定义路由(路径和组件名)。 - 使用
const router = new VueRouter({})
来创建路由实例,在其中传入上一步定义的路由配置routes
。 - 创建和挂载根实例,在
new Vue
中挂载上一步创建的路由实例router
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="vue.min.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<h1>路由的使用</h1>
<p>
<!-- 使用 router-link 组件来导航 -->
<router-link to="/home">首页</router-link>
<router-link to="/hot">热门</router-link>
<router-link to="/class">分类</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
<script>
const Home = { template: "<div>首页</div>" };
const Hot = { template: "<div>热门</div>" };
const Class = { template: "<div>分类</div>" };
// 定义路由
const routes = [
{ path: "/home", component: Home },
{ path: "/hot", component: Hot },
{ path: "/class", component: Class },
];
// 创建 router 实例,然后传 routes 配置
const router = new VueRouter({
routes,
});
// 创建和挂载根实例
const app = new Vue({
router,
}).$mount("#app");
</script>
</body>
</html>
Vuex 状态管理器
Vuex 的简介
Vuex 是一个专门为 Vue 应用程序开发的状态管理模式。通过它可以将 Vue 应用中所有组件的共享状态储存管理起来,并以一定的规则保证这些状态以一种可预测的方式发生变化。
Vuex 的核心概念
在 Vuex 中有五个核心概念,它们分别是 State、Getters、Mutations、Actions 和 Modules。
State
import Vue from "vue";
import App from "./App.vue";
import Vuex from "vuex"; // 导入 Vuex
Vue.use(Vuex); // 使用 Vuex,让 Vuex 可以访问到 Vue
Vue.config.productionTip = false;
// 创建 Store 实例
const store = new Vuex.Store({
state: {
count: 0, // 计数器的初始值
},
});
new Vue({
store, // 注入 Store
render: (h) => h(App),
}).$mount("#app");
Mutations
// 在 App.vue 文件中定义一个按钮,新增代码如下:
<!--绑定一个点击事件,用 increment 来执行 count++ 的逻辑-->
<button @click="$store.commit('increment')">++</button>
// 我们在 main.js 文件中增加 mutations,代码如下:
const store = new Vuex.Store({
// 此处省略 ...
mutations: {
increment(state) {
state.count++; // 执行 count++ 的操作
},
},
});
Actions
有时候我们需要向后台发出一些异步请求,我们不能直接在 mutations
里进行操作,这时就可以在 actions
中定义一些异步操作。
在页面上新增一个按钮,触发 count--
的操作。在 App.vue
中新增以下代码:注意哦!!! Actions 是通过 store.dispatch
方法来触发 actions
更新 state
状态。
<button @click="$store.dispatch('decrement')">--</button>
在 main.js
文件中新增以下内容。
const store = new Vuex.Store({
mutations: {
decrement(state) {
state.count--;
},
},
actions: {
decrement({ commit }) {
setTimeout(() => {
// 通过 commit 交给 mutations 去处理
commit("decrement");
}, 500);
},
},
});
actions 与 mutations 的区别。
actions
类似于 mutations
,不同的是:
actions
中的更新函数最终仍是通过调用mutations
中的函数来更新状态的,不能通过它直接变更状态。- 与
mutations
不同,actions
中可以包含任意异步操作。
Getters
getters
可以帮助我们缓存数据。
// main.js
getters: {
doubleCount(state) {
return state.count * 2
}
}
// app文件
{{$store.getters.doubleCount}}
Vuex规则
在实际开发中 Vuex 并不会限制我们的代码结构。但是,它规定了一些需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。
不过随着业务的增多,我们可能会面临这样一个问题:由于 state
、mutations
、getters
、actions
存储的内容越来越多,会导致 store 文件及其庞大,开发和维护起来变得困难。没关系,学习过 ES6+ 的同学都知道模块化的概念,这里我们可以将它们作为单独的文件从 store 中分割出去。这样做对于大型应用开发来说在合适不过。😊
我们可以将 store 分割为如下所示的结构:
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── state.js # 根级别的 state
├── getters.js # 根级别的 getters
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
Module
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── state.js # 根级别的 state
├── getters.js # 根级别的 getters
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块