文章目录
- 1.什么是vue
- 2.vue的特点
- 3.el与data两种写法
- 4.MVVM模型
- 5.Object.defineProperty使用
- 6.数据代理
- 7.事件使用
- 8.计算属性
- 9.监视属性
- 10.class、style样式
- 11. 条件渲染
- 11. 列表渲染
- 11. vue中数据监测
- 11. 内置指令
- 12. 自定义指令
- 13. 生命周期
- 14. 非单文件组件
- 15.vue.config.js文件
- 16.ref使用
- 17.props使用
- 18.mixin混入
- 19.scoped样式
- 20.todo-list案例
- 20.webStorage
- 21.组件的自定义事件
- 22.全局事件总线
- 23.$nextTick
- 24.配置代理
- 25.插槽
- 26.vuex
- 27. 路由vue-router
1.什么是vue
一套用于构建用户界面的渐进式javascript框架。
补充:构建用户界面(将数据转化为界面) 渐进式:(简单应用:只需引入轻量小巧的vue核心库。复杂应用:可引入各式各样的vue插件)
2.vue的特点
①组件化,提高代码复用率和可维护性
②声明式编程,无需直接操作DOM,提高开发效率
③使用虚拟DOM+diff算法,尽量复用DOM节点
3.el与data两种写法
① el:"#root"
② v.$mount("#root")
data:对象或者函数形式
4.MVVM模型
M:模型 (model):对应data中的数据
V:视图(view):模板
VM:视图模型 :Vue实例 :用于数据绑定,DOM监听
5.Object.defineProperty使用
(1)不使用Object.defineproperty
let person={
name:"zhangsan",
sex:"男",
age:18
}
打印结果:
(2)使用Object.defineproperty
let person={
name:"zhangsan",
sex:"男",
// age:18
}
Object.defineProperty(person,"age",{
value:18
})
打印结果:
补充:此时age不可枚举
Object.defineproperty基本配置项
Object.defineProperty(person,"age",{
value:18,
enumerable: true, //控制属性是否可枚举,默认是false
writable: true, //控制属性是否可以被修改,默认是false
configurable: true //控制属性是否可以被删除,默认是false
})
let number = 18
let person = {
name:"zhangsan",
sex:"男",
// age:18
}
Object.defineProperty(person,"age",{
// 当读取age属性时,get函数被调用
get(){
return number
},
// 当修改age属性时,set函数被调用
set(value){
number = value
}
})
6.数据代理
(1)定义:通过一个对象代理另一个对象中属性的操作(读/写)
简单数据代理相关代码:
let obj1 = {
x:100
}
let obj2 = {
y:200
}
Object.defineProperty(obj2,"x",{
get() {
return obj1.x
},
set(value) {
obj1.x = value
}
})
打印结果:
(2)vue中的数据代理
7.事件使用
7.1 点击事件
(1)基础理解
<body>
<button v-on:click="showInfo1($event,number)">点我出现提示信息1</button>
<button @click="showInfo2">点我出现提示信息1</button>
</body>
<script>
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示
new Vue({
el:"#root",
methods: {
showInfo1(event,number){
console.log(event,number);
},
showInfo2(event){
console.log(event)
}
},
})
</script>
补充:
- 用指令v-on:click = " 事件名 ",简写@click = " 事件名 "来写。
- 可以传参,可不传参,不传参时事件调用可以不添加括号。
- methods中配置的函数不要用箭头函数,this为vue实例,否则不为vm了
- 本身自带参数event,可在methods函数中直接使用。既想用到event参数,又想传其它参数时,必须在触发事件函数时写出参数为$event和其它参数,且调用顺序和定义顺序一致。
(2)事件修饰符
.prevent :阻止事件默认行为
.stop: 阻止事件冒泡
.once:事件只触发一次
.capture:事件捕获
.self:当event.tagert是当前元素时才调用
补充:事件默认是冒泡
7.2 键盘事件
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:
<input v-on:keyup.enter="submit">
补充:
①为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:
- .enter
- .tab(只能绑定到keydown事件上)
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
②capsLock修饰符写成caps-lock
③ 系统修饰键
- .ctrl
- .alt
- .shift
- .meta
<input v-on:keyup.alt.67="clear">
注意:在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发
keyup.ctrl。而单单释放 ctrl 也不会触发事件。
8.计算属性
(1)基础理解
data(){
return {
firstName:"xiao",
lastName:"li"
}
},
computed:{
fullName:{
get() {
return this.firstName + this.lastName
},
set(value){
let arr = value.split("-")
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
- 定义:通过已有的属性计算出来
- 原理:底层借助了Object.defineProperty的getter和setter
- 优势:内部有缓存机制,效率更高,调试方便
- 备注:计算属性最终都会出现在vm身上,它的值为getter中返回的值
(2)简写
computed:{
fullName(){
return this.firstName + this.lastName
}
}
注意:只考虑读取,不考虑修改时才可使用
9.监视属性
(1)基础理解
data(){
isHot:true
}
watch:{
isHot:{
immediate:true, //初始化时让handler立即执行
handler(newValue,oldValue){
console.log(newValue,oldValue)
}
}
}
结果:
补充:①当监视属性变化时,会自动调用回调函数handler ②监视属性必须存在,才能进行监听。监视属性可以为computed中的属性。
(2)深度监视
data(){
numbers:{
a:100,
b:200
}
}
watch:{
numbers:{
deep:true, //深度监视
immediate:true, //初始化时让handler立即执行
handler(newValue,oldValue){
console.log(newValue,oldValue)
}
}
}
vue提供的watch默认不检测对象内部值的改变,配置deep:true才可以(vue自身是可以检测对象内部值的改变
(3)简写形式
watch:{
isHot(newValue,oldValue){
console.log(newValue,oldValue)
}
}
当配置项只有handler时,可以使用简写形式
(4)computed与watch对比
- computd能实现的,watch一定能实现,watch能实现的,computed不一定能实现。例如watch能实现异步操作
- vue所管理的函数写成一般函数,不是vue所管理的函数写成箭头函数,例如setTimeout。这样写是为了保证this指向为vue实例
10.class、style样式
10.1 class样式
(1)一般绑定
<div class="basic" :class="mood"></div>
new Vue({
el:"#root",
data(){
return {
mood:"normal"
}
}
})
显示结果为:
<div class="basic mood"></div>
适用场景:样式的类名不确定
(2)数组形式绑定
<div class="basic" :class="classArr"></div>
new Vue({
el:"#root",
data(){
return {
classArr:["atguigu1","atguigu2","atguigu3"]
}
}
})
补充:适用场景:样式个数和名字不确定的情况下
(3)对象形式绑定
<div class="basic" :class="classObj"></div>
new Vue({
el:"#root",
data(){
return {
classObj:{
atguigu1:true,
atguigu2:true
}
}
}
})
适用场景:class个数和名字确定,但是不确定是否使用
10.2 style样式
<div class="basic" :style="styleObj"></div>
<div class="basic" :style="styleArr"></div>
new Vue({
el:"#root",
data(){
return {
styleObj:{
fontSize:"40px",
color:"blue"
},
styleobj1:{
backgroundColor:"green"
}
}
}
})
提示:style动态样式可写成对象或者是数组形式,数组形式很少用到,注意对象属性需写成小驼峰形式
11. 条件渲染
(1)v-if
写法:v-if=“表达式”,v-else-if=“表达式”,v-else=“表达式”
适用场景:切换频率较低
特点:不展示的DOM元素直接被剔除 ,不能再获取到DOM节点
注意:v-if与v-else-if,v-esle一起使用时,结构不能被打断
可以与template搭配使用
(2)v-show
写法:v-show=“表达式”
适用场景:切换频率较高
特点:不展示的DOM元素不是被剔除,而是用css样式display:none隐藏掉
11. 列表渲染
(1)基本列表
语法:v-for="(item,index) in object"
<div id="root">
<!-- 遍历数组 -->
<li v-for="(p,index) in person" :key="index">
{{p.name}}--{{p.age}}
</li>
<hr>
<!-- 遍历对象 -->
<p v-for="(value,key) in cars" :key="key">
{{value}}--{{key}}
</p>
<hr>
<!-- 遍历数字 -->
<p v-for="(number,index) in 3">
{{number}}--{{index}}
</p>
</div>
<script >
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示
new Vue({
el:"#root",
data(){
return {
person:[
{id:001,name:"zhang",age:18},
{id:002,name:"jiang",age:19},
{id:001,name:"liang",age:20}
],
cars:{
name:"benchi",
price:123456
}
}
}
})
(2)key的作用
- 以id作为key的值
- 以index作为key的值
小结:vue中key的作用? - key是虚拟DOM对象的标志,当数据发生变化时,vue会根据新数据生成新的虚拟DOM
- vue进行新旧虚拟DOM对比
- 若旧虚拟DOM中找到了与新虚拟DOM中相同的key,若虚拟DOM中内容没变,则直接使用之前的真实DOM,若内容改变,则生成新的真实DOM
- 若旧虚拟DOM中未找到与新虚拟DOM中相同的key,则直接生成新的真实DOM
用index作为key可能引发的问题?
若对数据进行逆序添加删除等破坏顺序的操作,会产生没有必要的DOM更新,界面效果没变,但效率低
若结构中包括输入框类的DOM,可能产生错误的DOM更新,并且界面会出现问题
11. vue中数据监测
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据?
通过setter实现监视,且要在newVue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue . set(target, propertyName/index, value) 或
Vm . $set(target, propertyName/ index, value)
3.如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定 要用如下方法:
1.使用这些API:push()、pop()、shift()、 unshift()、splice()、 sort()、reverse()
2.Vue .set()或vm. $set()
特别注意: Vue.set() 和vm.$set()不能给vm或vm的根数据对象添加属性! !
11. 内置指令
11. 1 v-text
文本指令
<div id="root">
<p>你好,{{text}}</p>
<p v-text="text">你好,</p>
</div>>
new Vue({
el:"#root",
data:{
text:"hello,world"
}
})
显示结果为:
11. 2 v-html
<p v-html="str"></p>
data:{
text:"hello,world",
str:"<h3>你好啊</h3>"
}
显示结果:
小结:向指定节点中渲染包含html结构的内容,会替换掉节点中的所有内容,插值语法{{xx}}不会,存在安全性问题,容易导致XSS攻击
11. 2 v-cloak
- 本质是个特 殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
- 配合css使用可解决网络慢而导致页面显示不正确的问题:css中: [ v-cloak ] { disp;ay:none}
11. 3 v-once
- v-once所在节点在初次动态渲染后,就视为静态内容了
- 以后数据改变不会引起页面的更新
11. 3 v-pre
- 可以让vue跳过所在节点的编译过程
- 可利用它跳过没有使用插值语法,指令语法的节点,会加快编译
12. 自定义指令
<div id="root">
<p>{{n}}</p>
<p >n扩大十倍为:<span v-big="n"></span></p>
</div>
directives:{
big(element,binding){
element.innerText = binding.value*10
}
}
big何时被调用?指令与元素成功绑定时,指令所在的模板重新解析时
13. 生命周期
14. 非单文件组件
14.1 基本用法
非单文件组件:一个文件中有n个组件
<body>
<div id="root">
<hello></hello>
<school></school>
<hr>
<student></student>
</div>
<hr>
<div id="root1">
<hello></hello>
</div>
</body>
<script>
const school=Vue.extend({
data(){
return {
schoolName:"尚硅谷",
address:"北京"
}
},
template:`
<div>
<h2>学校:{{schoolName}}</h2>
<h2>地址:{{address}}</h2>
</div>
`
})
const student=Vue.extend({
data(){
return {
name:"小小",
sex:"女"
}
},
template:`
<div>
<h2>姓名:{{name}}</h2>
<h2>性别:{{sex}}</h2>
</div>
`
})
const hello=Vue.extend({
data(){
return {
hello:"hello,world"
}
},
template:`
<div>
<h2>{{hello}}</h2>
</div>
`
})
// 全局注册
Vue.component("hello",hello)
new Vue({
el:"#root",
// 局部注册组件
components:{
school,
student
}
})
new Vue({
el:"#root1",
// 局部注册组件
components:{
school,
student
}
})
</script>
- 总结:定义组件 ➤ 注册组件 ➤ 使用组件
- 使用Vue.extend({})创建组件,其中options和new Vue时传入的options基本相同,区别:el不要写,因为,最终的组件都会通过vm管理。data需要写成函数形式,避免组件复用时,数据之间存在引用关系。
- 注册组件分为全局注册和局部注册,全局注册通过Vue.component(“组件名”,组件)。局部注册通过在vm中写配置项components:{“组件名”,组件}
- 使用组件,通过在DOM中写标签元素
14.2 组件使用注意点
- 一个单词组成:可小写school,也可大写School
- 多个单词组成:可用kebab-case命名:my-school;可用CamelCase命名:MySchool
- 组件名尽可能回避html中已有的元素名称,例如H2,h2都不行
- 可使用name配置项配置指定组件在开发工具中呈现的名字
- 组件标签和都可以,但不使用脚手架时,会导致后续不能正常渲染
- 一个简写方式:const school=Vue.extend({options})可简写为const school=options
14.3 组件的嵌套
<script>
let student=Vue.extend({
data(){
return {
name:"小小",
sex:"女"
}
},
template:`
<div>
<h2>姓名:{{name}}</h2>
<h2>性别:{{sex}}</h2>
</div>
`
})
let school=Vue.extend({
data(){
return {
schoolName:"尚硅谷",
address:"北京"
}
},
template:`
<div>
<h2>学校:{{schoolName}}</h2>
<h2>地址:{{address}}</h2>
<student></student>
</div>
`,
components:{student}
})
new Vue({
el:"#root",
// 局部注册组件
components:{
school
}
})
</script>
开发者工具中结果为:
组件的嵌套需要在父组件创建时的配置项中添加components选项,子组件使用在父组件template配置项中使用
14.4 一个重要的内置关系
VueComponent.prototype.proto===Vue.prototype
这样可以让组件实例对象(vc)可以访问到Vue原型上的属性和方法
15.vue.config.js文件
vue.config.js文件可以修改vue-cli的默认配置参数,文件位置与package.json在同一级位置
详请使用请点击链接查找:https://cli.vuejs.org/zh/config/#vue-config-js
例如使用lintOnSave:
export default = {
lintOnSave:false //关闭语法检查
}
16.ref使用
应用在html标签上是获取DOM元素,应用在组件标签上是该组件实例对象(vc)
<template>
<div>
<h2 ref="msg">hello,world!</h2>
<TheSchool ref="sch"></TheSchool>
<button @click="showDOM">点我展示信息</button>
</div>
</template>
<script>
import TheSchool from "./components/TheSchool"
export default {
name:"App",
components:{
TheSchool,
},
methods:{
showDOM(){
console.log(this.$refs.msg,this.$refs.sch);
}
}
}
</script>
控制台打印结果为:
17.props使用
功能:让组件接收外部传进来的数据
父组件App中代码:
<template>
<div>
<my-student name="姜小小" age="18" sex="女"></my-student>
</div>
</template>
<script>
import MyStudent from "./components/MyStudent"
export default {
name:"App",
components:{
MyStudent,
},
}
</script>
子组件MyStudent:
<template>
<div class="">
<h2>姓名:{{ name }}</h2>
<h2>性别:{{ sex }}</h2>
<h2>年龄:{{ age }}</h2>
</div>
</template>
<script>
export default {
name:"MyStudent",
data() {
return {
};
},
props:["name","sex","age"]
};
</script>
浏览器展示结果:
传递数据:父组件通过在组件标签中添加属性,子组件通过props接收数据进行展示
props接收数据几种方式:
- 只接收:
props:[ "name","age" ]
- 限制类型:
props:{ name:String, age:Number }
- 限制类型,限制必要性,指定默认值:
props: {
name: {type:String, require:true} ,
age: {type:Number, default:99}
}
注意事项:
- 当父组件通过
<my-student name="姜小小" age="18" sex="女"></my-student>
传入数据时,子组件接收到的值为字符串,若想传入的值为数值类型,可将属性绑定为动态值,此时代码可改写为<my-studentname="姜小小" :age="18" sex="女"></my-student>
,接收到的年龄就为数值类型了- props接收到的数据最好不要在子组件中进行修改,若有修改数据这个需求,可将props中的数据复制一份到data中,然后修改data中的数据
18.mixin混入
功能:可以把多个组件公用的配置提取成一个混入对象在单独的一个文件中
使用方式:
①定义混合,例如:
{
data(){......},
methods:(){......},
......
}
②引入混合文件
③使用混合:若是全局混入,则在main.js文件中写入Vue.mixin(xxx)
;若是局部混入,配置项中mixins:[xxx]
注意事项:若是组件本身配置项中的数据和混入的数据发生冲突,则会使用组件本身带有的数据。若是混入的数据,组件本身不带有,则直接添加。生命周期中无论是在导入的混入还是本身组件带有,都会执行
19.scoped样式
在单文件组件style标签中添加scoped属性时,该样式只对本组件有效。
20.todo-list案例
详情查看:todo-list案例
20.webStorage
-
存储内容大小一般5M左右(不同浏览器可能不一样)
-
浏览器通过window.localStorage和window.sessionStorage实现本地存储机制
-
相关API
①localStorage.setItem(“key”,“value”)
该方法接收一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则对应更新相应的值
②localStorage.getItem(“key”)
该方法接收一个键名作为参数,返回值为键值,如果对应的值获取不到,则返回null
③localStorage.removeItem(“key”)
把键名从该存储中删除
④localStorage.clear()
该方法清空存储中的所有数据 -
localStorage和sessionStorage区别:
localStorage中的数据需要手动清除才会消失,sessionStorage随着浏览器的关闭而消失
21.组件的自定义事件
22.全局事件总线
- 适用性:适用于所有组件间的通信
- 安装全局事件总线:
new Vue({
.....
beforeCreate(){
Vue.prototypt.$bus=this
......
}
})
- 使用事件总线
接收数据
mounted(){
this.$bus.$on("xxxx",this.demo)
}
提供数据:
this.$bus.$emit("xxxxx","数据")
- 最好在beforeDestroy钩子中,用$off解绑当前组件所用到的组件
beforeDestroy(){
this.$bus.$off("xxxx")
}
23.$nextTick
语法:this.$nextTick(回调函数)
作用:在下一次OM更新结束后执行指定的回调函数
什么时候用:当改变数据后,基于更新后的DOM进行操作时使用
24.配置代理
跨域的几种方式:
①cors
②jsonp,利用script的src属性不受同源策略影响
③配置代理:
前端地址:8080,服务器:5000,5001
方式一:
devServer:{
proxy:{
target:"http://localhost:5000"
}
}
方式二:
编写vue.config,js配置代码规则:
优点:可以配置多个代理,且可以灵活的控制请求是否走代理
缺点:配置略为繁琐,请求资源必须加前缀
25.插槽
25.1默认插槽
父组件:App.vue
在组件标签中写入html元素
<template>
<div id="root">
<TheCategory title="食物">
<img src="https://img2.baidu.com/it/u=2794071807,1165923010&fm=253&fmt=auto&app=138&f=JPEG?w=680&h=453" alt="">
</TheCategory>
<TheCategory title="游戏">
<ul>
<li v-for="(item,index) in games" :key="index">{{item}}</li>
</ul>
</TheCategory>
<TheCategory title="电影"></TheCategory>
</div>
</template>
<script>
import TheCategory from '@/components/TheCategory'
export default {
name:"App",
data(){
return {
games:["王者荣耀","和平精英"]
}
},
components:{
TheCategory
}
}
</script>
<style>
#root{
display: flex;
justify-content: space-around;
}
img{
width: 300px;
height: 200px;
}
</style>
子组件:TheCategory.vue
通过slot占据位置,父组件的html填到这个位置
<template>
<div id="container">
<h3>{{title}}</h3>
<slot>没有传的话,就展示这个哦</slot>
</div>
</template>
<script>
export default {
name:"TheCategory",
props:["title"]
}
</script>
<style>
#container{
background-color: cornflowerblue;
width:300px;
height: 400px;
}
h3{
background-color: orange;
text-align:center;
}
</style>
效果图:
25.2具名插槽
父组件:App.vue
若是使用在template上,则使用v-slot:name属性,一般则使用slot=“name”,后面这个在vue2.6.0中已经弃用
<template>
<div id="root">
<TheCategory title="食物">
<img slot="center" src="https://img2.baidu.com/it/u=2794071807,1165923010&fm=253&fmt=auto&app=138&f=JPEG?w=680&h=453" alt="">
<p slot="footer">跟我一起品尝食物吧!</p>
</TheCategory>
<TheCategory title="游戏">
<ul slot="center">
<li v-for="(item,index) in games" :key="index">{{item}}</li>
</ul>
<template v-slot:footer>
<a href="">单机游戏</a>
<a href="">联网游戏</a>
</template>
</TheCategory>
<TheCategory title="电影"></TheCategory>
</div>
</template>
<script>
import TheCategory from '@/components/TheCategory'
export default {
name:"App",
data(){
return {
games:["王者荣耀","和平精英"]
}
},
components:{
TheCategory
}
}
</script>
<style>
#root{
display: flex;
justify-content: space-around;
}
img{
width: 300px;
height: 200px;
}
</style>
TheCategory.vue
在slot标签中添加属性name=“name”
<template>
<div id="container">
<h3>{{title}}</h3>
<slot name="center">没有传的话,就展示这个哦</slot>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
name:"TheCategory",
props:["title"]
}
</script>
<style>
#container{
background-color: cornflowerblue;
width:300px;
height: 400px;
}
h3{
background-color: orange;
text-align:center;
}
</style>
浏览器展示:
25.3作用域插槽
父组件App.vue中
在template中使用v-slot="{数据}"接收数据,可结合具名插槽和默认插槽使用
<template>
<div id="root">
<TheCategory title="游戏">
<template v-slot:center="{games}">
<ul>
<li v-for="(item,index) in games" :key="index">{{item}}</li>
</ul>
</template>
<template v-slot:footer>
<a href="">单机游戏</a>
<a href="">联网游戏</a>
</template>
</TheCategory>
<TheCategory title="电影">
</TheCategory>
</div>
</template>
<script>
import TheCategory from '@/components/TheCategory'
export default {
name:"App",
components:{
TheCategory
}
}
</script>
<style>
#root{
display: flex;
justify-content: space-around;
}
img{
width: 300px;
height: 200px;
}
</style>
子组件TheCategory.vue中
通过在slot标签中属性传值传入数据给父组件
<template>
<div id="container">
<h3>{{title}}</h3>
<slot name="center" :games="games">没有传的话,就展示这个哦</slot>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
name:"TheCategory",
props:["title"],
data(){
return {
games:["王者荣耀","和平精英"]
}
},
}
</script>
<style>
#container{
background-color: cornflowerblue;
width:300px;
height: 400px;
}
h3{
background-color: orange;
text-align:center;
}
</style>
浏览器展示:
26.vuex
- 概念:在vue中实现集中式状态(数据)管理的一个vue插件,对vue中多个组件的共享状态进行集中式管理,也是一种组件间通信的方式,可以实现任意组件间的通信
- 适用场景:(共享)
多个组件依赖同一状态
来自不同组件的行为需要改变同一状态 - 下面是vuex关系图:
- 关于mapState(),mapGetters(),mapMutations(),mapActions()在模块化中的使用,在使用模块化时,使用上述几个函数需要在每个模块中开启命名空间nameSpaced:true;
27. 路由vue-router
-
定义:vue的一个插件库,专门用来实现SPA单应用
-
对SPA理解:
①单个web应用;②整个应用只有一个完整的页面;③点击页面中的导航链接不会刷新页面,只会做页面的局部更新;④数据需要通过ajax请求获取; -
对路由的理解:
①一个路由就是一组映射关系 (key-value)
②key为路径,value可能是函数(后端)或者组件(前端)
27.1 路由的基本使用
①安装vue-router,命令npm i vue-router@3(如果使用的是vue2框架);
②在main.js中引入vue-router插件,并且应用Vue.use(VueRouter),在vm实例中写router配置项;
③新建router文件夹,在index.js中写router配置规则:
import VueRouter from "vue-router"
import About from "../components/MyAbout.vue"
import Home from "../components/MyHome.vue"
export default new VueRouter({
routes:[
{
path:"/about",
component:About
},
{
path:"/home",
component:Home
}
]
})
④实现切换: active-class实现高亮效果,激活后的样式,to:跳转路径
<router-link to="/about" active-class="active">点我跳转about组件</router-link> <br>
⑤指定展示位置:<router-view></router-view>
注意事项:
- 路由组件一般放在page文件夹下,一般组件放在components组件下
- 通过切换,隐藏的路由组件默认是被销毁了的,再出现时重新挂
- 每个组件都有自己的$route属性,里面存储着自己的路由信息
- 整个应用只有一个 r o u t e r , 可 以 通 过 组 件 的 router,可以通过组件的 router,可以通过组件的router获取到
27.2 路由的嵌套
- 配置路由规则
export default new VueRouter({
routes:[
{
path:"/about",
component:About
},
{
path:"/home",
component:Home,
children:[
{
path:"message",
component:MyMessage
},
{
path:"news",
component:MyNews
}
]
}
]
})
- 跳转
<router-link to="/home/message" active-class="active">展示message</router-link><br>
<router-link to="/home/news" active-class="active">展示news</router-link>
注意事项:
- 通过children配置子路由时,子路由path不要写"/"
- 跳转的时候to的值需要写完整路径 ,要带上父级路由
27. 3 路由的query传参
- 传递参数
<template>
<div>
<ul>
<li v-for="item in news" :key=item.id>
<!-- <router-link :to="`/home/news/detail?id=${item.id}&title=${item.title}`">消息{{item.id}}</router-link> -->
<router-link :to='{path:"/home/news/detail",query:{title:item.title,id:item.id}}'>消息{{item.id}}</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:"MyNews",
data(){
return {
news:[
{id:1,title:"我是消息1"},
{id:2,title:"我是消息2"},
{id:3,title:"我是消息3"}
]
}
}
}
</script>
- 接收参数:
$route.query.id
,$route.query.title
补充:query传参可以有两种形式,一种是to的字符串写法,另一种是对象写法
27. 4 命名路由
首先在router配置项中可以添加一个name属性:
{
name:"detail",
path:"detail",
component:MyDetail
}
当to写成对象形式时可以将:to='{path:"/home/news/detail",query:{title:item.title,id:item.id}}'
改写为:to='{name:"detail",query:{title:item.title,id:item.id}}'
,这样写起来更方便
27. 5 路由的params参数
- 路由配置:
{
name:"detail",
path:"detail/:id/:title",
component:MyDetail
}
- 传递参数
<!-- <router-link :to="`/home/news/detail/${item.id}/${item.title}`">消息{{item.id}}</router-link> -->
<router-link :to='{name:"detail",params:{title:item.title,id:item.id}}'>消息{{item.id}}</router-link>
接收参数:$route.params.id
,$route.params.title
注意事项:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
27. 6 路由的props配置
作用:让路由更方便的接收到参数
{
name:"detail",
path:"detail/:id/:title",
component:MyDetail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件,组件通过props接收
//props:{a:900}
//第二种写法,props为布尔值,若为true,则把路由收到的params参数通过props传给DEtail组件
//props:true
//第三种写法,props值为函数,且自身带有参数route,该函数返回的对象中每一组key-value都会通过props传给Detal组件
props(route){
return {
id:route.query.id
}
}
27. 6 router-link的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器历史记录有两种写入方式:分别为push和replace,push是追加李四记录,replace是替换当前记录,路由跳转时默认为push
- 如何开启replace模式?
<router-link replace......>News</router-link>
27. 7 编程式路由导航
- 作用:不借助实现路由跳转,让路由跳转更灵活
- 具体编码
//可用push,也可用repalce this.$router.push({ name:"detail", params:{ id:xxx, title:xxx } }) this.$router.foward();前进一步记录 this.$router.back(); 后退 this.$router.go(2)//数字为正数,前进
27.8 缓存路由组件
- 作用:让不展示的路由组件保持挂载,不被销毁
- 具体编码:(includes后写组件名)
<keep-alive include="MyNews"> <router-view></router-view> </keep-alive> //缓存多个 <keep-alive :include=["MyNews","MyMessage"]> <router-view></router-view> </keep-alive>
27.9 两个新的生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕获路由的激活状态
- activated路由组件被激活时触发
- deactivated路由组件被销毁时触发
27.10 路由守卫
27.10.1 全局路由守卫
在导出路由实例之前,进行全局路由守卫
相关代码:
//全局前置路由守卫
router.beforeEach(to,from,next){
if(to.meta.isAuth){ //判断当前路由是否需要权限控制,meta路由元可在配置路由时设置
if(localStorage.getItem("school")=="atguigu"){
next()
} //权限控制具体规则
}else{
next()
}
}
//后置路由守卫
router.afterEach(to,from ,next){
if(to.meta.title){
ducument.title=to.meta.title
}else{
documnt.title="vue_test"
}
}
27.10.2 独享路由守卫
在对应的router配置项中写:
routerEnter:(to,from,next){
if(to.meta.isAuth){ //判断是否有权限
if(localStorage.getItem("school")=="atguigu"){
next()
}else{
alert("学校名不对,无权限查看!")
} //权限控制具体规则
}else{
next()
}
}
27.10.3 组件内路由守卫
在组件内写,与mounted同级
//通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from.next){
if(to.meta.isAuth){ //判断是否有权限
if(localStorage.getItem("school")=="atguigu"){
next()
}else{
alert("学校名不对,无权限查看!")
} //权限控制具体规则
}else{
next()
}
}
//通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from){
}
27.11 路由懒加载
- 定义:整个网页默认是刚打开就去加载所有页面,路由懒加载就是只加载你当前点击的那个模块。按需去加载路由对应的资源,提高首屏加载速度(tip:首页不用设置懒加载,而且一个页面加载过后再次访问不会重复加载)。
- 实现原理:将路由相关的组件,不再直接导入了,而是改写成异步组件的写法,只有当函数被调用的时候,才去加载对应的组件内容。
路由懒加载写法:
import Vue from 'vue'
import VueRouter from 'vue-router'
//const Login = ()=> {
// return import('@/views/login/index.vue')
//}
//const Home = ()=> {
// return import('@/views/home/home.vue')
//}
//有return且函数体只有一行,所以省略后为
const Login = ()=> import('@/views/login/index.vue')
const Home = ()=> import('@/views/home/home.vue')
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/login', component: Login },
{ path: '/home', component: Home }
]
export default router
再次简化(省去定义变量):
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/login', component: () => import('@/views/login/index.vue') },
{ path: '/home', component: () => import('@/views/home/home.vue') }
]
export default router