Vue2学习记录
前言
这篇笔记,是根据B站尚硅谷的Vue2网课学习整理的,用来学习的
如果有错误,还请大佬指正
Vue核心
Vue简介
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。
它基于标准 HTML、CSS 和 JavaScript 构建,
并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面
官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)
初识Vue
- Vue进行工作,需要提供 Vue实例 和 模版
- 通过 new Vue() 创建出Vue实例对象
- 在Vue实例对象中,需要绑定模板
<!--准备容器-->
<div id="test01">
Hello {{word}}!!
</div>
<script>
//创建Vue实例
new Vue({
el: '#test01',//指定当前实例为那个模版服务
data: {
word: 'world'
}//提供数据,供指定模版使用
})
</script>
MVVM模型
Vue 仿照MVVM来进行设计
分为模型(Model),视图(View)和视图模型(ViewModel)三部分
- M:模型(Model) -> data中的数据
- V:视图(View) -> 模版,即 DOM
- VM:视图模型(ViewModel) -> Vue 实例对象
模版绑定 与 data 写法
模版绑定
方法一:
使用 el 来绑定
语法 : el : ' css选择器 '
new Vue({
el: '#test01',
data: {
word: 'world'
}
})
方法二:
语法:实例对象 . $mount ( ' css选择器 ' )
const vm = new Vue({
data: {
word: 'world!!!'
}
})
vm.$mount('.test01')
data写法
写法一:对象式
语法:data : { 数据对象 }
data: {
word: 'world!!!'
}
写法二:函数式
必须返回一个数据对象
不要写箭头函数
data: function(){
return{
word:'world!!!'
}
}
模版语法
Vue 使用一种基于 HTML 的模板语法,
使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上
简单来说:
将Vue实例中的数据,呈现到 HTML 上
插值语法
可以分析标签体内容(标签包裹内容)
语法:
{{JS表达式}}
示例:
<div id="test01">
Hello {{word}}
<!-- {{}} 中的内容会被替换为Vue实例中的数据-->
</div>
<script>
new Vue({
el:'#test01',
data:{
word:'world!!!'
}
})
</script>
{{ }} 中的内容会被替换为Vue实例中的数据
指令语法
可以解析标签(标签属性、标签体内容),并可以绑定事件
指令有很多,以 v- 为开头
例:v-bind: (可简写为 : )
v-bind : 标签属性 = 'JS表达式' 或 : 标签属性 = 'JS表达式'
<div id="test02">
<a v-bind:href="Prohibited">未满18不要打开</a>
<!-- "" 中的内容会被替换为Vue实例中的数据-->
</div>
<script>
new Vue({
el: '#test02',
data: {
Prohibited: 'https://store.steampowered.com/app/2358720/_/'
}
})
</script>
数据绑定
单向数据绑定
语法:
v-bind : 标签属性 = 'JS表达式' 或简写为 : 标签属性 = 'JS表达式'
数据只能从data流向页面
即 修改data中的数据,同时改变页面数据
双向数据绑定
语法:
v-model:value="JS表达式" 或简写为 v-model="JS表达式"
用于有value的标签
数据不仅能从 data 流向页面,还能从页面流向data
即 修改data中的数据,同时改变页面数据 ; 修改页面中的数据,同时改变data数据
数据代理
通过一个对象,代理对另一个对象中的属性的操作(读/写)
Object . defineProperty()
Object . defineProperty()可以为对象添加属性
语法:
Object . defineProperty ( 对象名 , ' 添加属性 ' , {
get函数 ,
set函数
})
get函数需要有返回值,作为添加属性的值
当读取所添加属性时,get函数会被调用
当修改所添加属性时,set函数会被调用
let num = 18
let Person = {
name: 'zhao',
school: 'sn'
}
Object.defineProperty(Person, 'age', {
//读取age时,调用get函数,返回值为age的值
get() {
return num
},
//修改age时,调用set函数,修改值作为参数
set(value) {
num = value//将参数,也就是修改值,传给num
}
})
使用Object . defineProperty(),实现了Person对num的数据代理,
修改Person的属性值,就可以修改num
Vue中的数据代理
在Vue实例对象中,data的数据存放在Vue._data中
为了更方便地操作data中的数据,Vue通过Object . defineProperty(),
对data中数据进行了数据代理
事件处理
事件基本使用
使用指令 v-on 来进行事件绑定
语法:v-on : 事件 = " 函数名 "
简写:@事件 = " 函数名 "
对应函数需要配置到Vue实例里,methods对象中
<div>
<button v-on:click="test01">普通按钮01</button>
</div>
<script>
const vm=new Vue({
el:'div',
methods:{
test01(){
console.log(11111)
}
}
})
</script>
函数参数
<div>
<button v-on:click="test02">普通按钮02</button>
<button v-on:click="test03(3)">普通按钮03</button>
<button v-on:click="test04($event,4,44)">普通按钮04</button>
</div>
<script>
const vm=new Vue({
el:'div',
methods:{
test02(e){
console.log(e)
console.log(2222)
},
test03(a){
console.log(a)
console.log(3333)
},
test04(e,a,b){
console.log(e,a,b)
console.log(4444)
}
}
})
</script>
以上代码为例:
- 没有参数时,传入事件对象(test02)
- 有参数时,没有事件对象(test03)
- 可以用$enevt,来配置事件对象(test04)
注:
- 不要用箭头函数,因为this将不再指向vm
事件修饰符
用于修饰事件
语法:v-on : 事件 . 事件修饰符 = " 函数名 "
简写:@事件 . 事件修饰符 = " 函数名 "
事件修饰符 | 作用 |
prevent | 阻止事件的默认行为 |
stop | 停止事件冒泡 |
once | 事件仅触发一次 |
capture | 使用事件捕获 |
self | e.target为当前元素时触发 |
passive | 默认行为立即执行,再进行回调函数 |
示例:
<div class="out" @click="work">
out
<div class="in" @click.stop="work">
<!--阻止事件冒泡-->
in
</div>
</div>
修饰符可以连写
<div class="in" @click.stop.prevent="work" >
按键修饰符
Vue提供了一些触发键盘事件时使用的按键修饰符
用于在按下某个按键时,触发事件
语法:v-on : 键盘事件 . 按键别名 = " 函数名 "
简写:@键盘事件 . 按键别名 = " 函数名 "
按键别名为Vue提供,即 按键码的别名
<!--在按下回车键时触发事件-->
<input type="text" @keyup.enter="work">
一些常用键盘别名
- 回车键 ==》enter
- 删除键 ==》delete
- 退出键 ==》esc
- 空格键 ==》space
- 换行键 ==》tab(最好与keyup使用)
- 上键 ==》up
- 下键 ==》down
- 左键 ==》left
- 右键 ==》right
未提供的键盘别名可以用按键Key值,使用短横线命名法
系统修饰键
如ctrl,alt,shift,meta(win键)有特殊用法
配合keyup使用:
按下系统修饰键,再按下并放开其他键时,触发事件
配合keydown使用:
按下系统修饰键即触发事件
定制按键别名
语法:
Vue.config.keyCodes.自定义按键名= 键码
计算属性
概念
属性:Vue实例中,data中的都认定为属性
计算属性:所需要的属性不存在,需要通过已有属性来计算获得
计算属性需要配置到Vue实例里,computed对象中
语法:
const vm=new Vue({
el:'.root',
data:{
属性
},
computed:{
计算属性名:{
get函数
}
}
})
通过get()函数来进行计算,需要有return返回值,作为计算属性的值
get(调用时机):
- 在初次读取计算属性时,get()调用
- 计算属性所依赖属性数据发生变化时,也会调用
备注:
- 也可以调用set()函数,进行数据改动
- get()和set()函数的this均指向Vue实例
- 计算属性会最终会出现在Vue实例上,直接调用即可
案例:
<div class="root">
<input type="text" v-model:value="xing"><br>
<input type="text" v-model:value="ming"><br>
<p>
全名:{{quanming}}
</p>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
xing: '张',
ming: '三'
},
computed: {
quanming: {
get() {
return this.xing + this.ming
}
}
}
})
</script>
简写
在没有写set()函数时,可以使用简写
语法:
computed: {
计算属性名() {
函数体
}
}
computed: {
quanming() {
return this.xing + this.ming
}
}
监视属性
概念与语法
可以设置被监视的属性,当被监视属性发生改变时,会自行调用函数
写法1:
const vm = new Vue({
el: 'div',
watch: {
监视属性名: {
immediate: 布尔值,//默认为false,是否初始化时调用
deep: 布尔值,//默认为false,是否开启深度监视
handler(newValue, oldValue) {
//newValue为新值,oldValue为旧值
函数体
}
}
}
})
写法2:
vm.$watch('监视属性名', {
immediate: 布尔值,//默认为false,是否初始化时调用
deep: 布尔值,//默认为false,是否开启深度监视
handler(newValue, oldValue) {
//newValue为新值,oldValue为旧值
函数体
}
})
在监视属性改变时,会调用handler()函数
深度监视
配置deep: true时开启深度监视
Vue中watch默认不检测对象内部值的改变
开启深度监视可以检测对象内部值的改变
简写
当deep和immediate均没有配置时,可以简写
简写1:
const vm = new Vue({
el: 'div',
watch: {
监视属性名(newValue, oldValue): {
//newValue为新值,oldValue为旧值
函数体
}
}
})
简写2:
vm.$watch('监视属性名', function(newValue, oldValue){
//newValue为新值,oldValue为旧值
函数体
})
计算属性与监听属性区别
- computed相当于watch更加简单
- computed能完成的,watch均能完成
- watch能完成的,computed不一定能完成(watch可进行异步操作)
绑定样式
绑定class
1.字符串写法
直接使用v-bind来绑定样式
可以绑定事件,来动态改变类名
<div class="main" :class="mood">{{text}}</div>
<head>
<style>
.main {
width: 100px;
height: 100px;
border: 1px solid black;
}
.test01 {
background-color: aqua;
}
</style>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<div class="main" :class="mood">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '111',
mood: 'test01',
}
})
</script>
</body>
2.数组写法
用于绑定多个样式
可以通过动态操作数组来控制类
<head>
<style>
.work01 {
font-size: 20px;
}
.work02 {
color: aqua;
}
.work03 {
font-style: italic;
}
</style>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<div class="main" :class="classArr">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '111',
classArr: ['work01', 'work02', 'work03']
}
})
</script>
</body>
3.对象写法
用于绑定多个样式,并动态决定用与不用
控制对象中的布尔值,动态决定类的用与不用
<head>
<style>
.work01 {
font-size: 20px;
}
.work02 {
color: aqua;
}
.work03 {
font-style: italic;
}
</style>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<div class="main" :class="classObj">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '111',
classObj: {
work01: true,
work02: true,
work03: true,
}
}
})
</script>
</body>
绑定style
1.直接绑定
直接使用v-bind来绑定样式
语法:
: style = " { 属性(驼峰命名法) : xxx } "
xxx为Vue示例中的data数据
<div id="root">
<div class="main" :style="{fontSize:fs}">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '1111',
fs:'20px'
}
})
</script>
2.对象写法
<div id="root">
<div class="main" :style="styleObj">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '1111',
styleObj: {
fontSize: '40px',
backgroundColor: 'aqua'
}
}
})
</script>
3.数组写法
需要在数组中配置对象
<div id="root">
<div class="main" :style="styleArr">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '1111',
styleArr: [
{
fontSize: '40px',
backgroundColor: 'aqua'
},
{
color: 'blue'
}
]
}
})
</script>
条件渲染
v-show
语法:
v-show = “ 表达式 ”
表达式值为布尔值
表达式值为false,元素被隐藏,display值为none
v-if
语法:
v-if = “ 表达式 ”
后可接v-else-if和v-else
表达式值为布尔值
表达式值为false时,元素直接被删除,替换为空注释
<div id="root">
<div v-show="false">{{test01}}</div>
<div v-show="true">{{test01}}</div><br>
<div v-if="false">{{test02}}</div>
<div v-if="true">{{test02}}</div>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
test01:'111',
test02:'222'
}
})
</script>
列表渲染
v-for遍历
遍历对象
语法:
v-for = " ( value , key) in 对象名 " 或
v-for = " value in 对象名 "
value获取属性值,key获取属性名
<div id="root">
<li v-for="(value,key) in Person">
{{value}}----{{key}}
</li>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
Person:{
name:'Tom',
age:18,
shool:'sdau'
}
}
})
</script>
遍历数组
语法:
v-for = " ( value , item) in 数组名 " 或
v-for = " value in 对象名 "
value获取值,item获取索引
<div id="root">
<li v-for="(value,item) in Arr">
{{value}}----{{item}}
</li>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
Arr: ['A','B','C']
}
})
</script>
key的使用与作用
key的使用
在 v-for遍历 中要添加一个 key属性
key值的选择,最好使用每条数据的唯一标识
例如ID,身份证,学号等等
如果不添加 key属性 则key值默认为数组或对象的序列号
<div id="root">
<li v-for="(p,item) in Person" :key="p.id">
{{p.name}}---{{p.age}}
</li>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
Person:[
{id:'001',name:'wu',age:18},
{id:'002',name:'liu',age:20},
{id:'003',name:'zhao',age:19},
]
}
})
</script>
key的作用和虚拟DOM
虚拟DOM是Vue根据数据生成的
key是虚拟DOM对象的标识,可以理解成虚拟DOM对象的唯一ID
当数据发生改变时,Vue会根据新数据生成新虚拟DOM
新旧DOM会进行对比:
1.若有相同的key值:
- 若虚拟DOM内容没有变,直接使用真实DOM生成页面
- 若虚拟DOM内容变化,生成新真实DOM替换之前的
2.若没有相同的key值:
- 直接建立真实DOM,生成页面
Vue监视数据原理
基本
Vue会监视data中所有有层次的数据
被监视的对象,会添加get()和set()函数,以实现响应式
( get()和set()函数用自Object . defineProperty())
示例:
data: {
Person: {
name: 'Tom',
age: 18,
shool: 'sdau'
},
Arr: ['A','B','C']
}
注:数组中的数据没有进行监视,但数组本身被监视
如上图,Arr数组有get()和set()函数
如下图,Arr[0]以及其他数据没有get()和set()函数
关于对象
对象中的数据通过 get()和set()函数 监视
对象中后追加的属性,Vue不会进行监视,即不会有get()和set()函数
如果需要监视追加的属性,则需要用到 特定API
语法:
Vue.set(对象名,属性名,属性值)
另一种写法
this.$set(对象名,属性名,属性值)
对象名指被添加属性的对象,注意 ' '
示例:给Person对象添加sex属性,属性值为 男
addSex() {
//Vue.set(this.Person,'sex','男') 另一种写法
this.$set(this.Person, 'sex', '男')
}
用此API添加的属性,拥有get()和set()函数,会被监视
注:
该API,不能给vm或vm的根数据对象(data等)添加属性
关于数组
因为Vue中数组没有get()和set()函数
所有Vue通过其他方式,来实现数组数据的响应式
在数组中修改数据,必须使用以下方法:
- 使用API:push(),pop(),shift(),unshift(),splice(),sort(),reverse()
- 使用Vue.set() 或 vm.$set().
Vue.set(数组名,序列,修改值)
另一种写法
this.$set(数组名,序列,修改值)
只有使用以上方法数组数据才可以实现响应式
当然,也可以直接修改数组本身,因为数组本身有get()和set()函数
过滤器
定义:
对要显示的数据进行特定格式化后再显示(用于一些简单的逻辑处理)
注册过滤器(两种方式):
Vue.filters(过滤器名称,function(){
函数体
})//全局过滤器
或
new Vue({
filters:{
过滤器名称(){
函数体
}
}
})//局部过滤器
使用过滤器:
过滤器可以用于插值语法 和 v-bind指令
- {{ xxx | 过滤器名称 }}
- v-bind:属性 = " xxx | 过滤器名称 "
常见内置指令
之前接触过的指令
指令 | 作用 | 简写 |
v-bind | 单向绑定解析表达式 | :xx |
v-model | 双向数据绑定 | v-model="xx" |
v-for | 遍历数组/对象/字符串 | 无 |
v-on | 绑定事件监听 | @ |
v-if / v-else | 条件渲染(控制节点是否存在) | 无 |
v-show | 条件渲染(控制节点是否展示) | 无 |
v-text指令
作用:
向所在节点渲染文本
<div id="root">
<p>{{name}}</p>
<p v-text="name"></p>
<p v-text="name">1111</p>
</div>
<script>
new Vue({
el:'#root',
data:{
name:'zhao'
}
})
</script>
注:
v-text会替换掉节点中的内容
v-html指令
作用:
向指定节点中渲染包含html结构的内容
可以识别html结构
<div id="root">
<div v-html="test"></div>
</div>
<script>
new Vue({
el:'#root',
data:{
test:'<p>123456</p>'
}
})
</script>
注(v-html有安全性问题):
- 在网站上动态渲染任意HTML非常危险,容易导致xss攻击
- 在可信内容上使用v-html,不用用在用户提交内容上
v-cloak指令
在网速非常慢的时候,可能会展示未经过Vue渲染的页面
因为网速慢,Vue会加载不出来
可以使用v-cloak指令配合CSS来解决
- v-cloak没有值
- v-cloak本质是一个属性
- v-cloak在Vue加载完成后删除
<style>
[v-cloak]{
display: none;
}
</style>
<div id="root">
<div v-cloak>111</div>
</div>
v-once指令
- v-once没有值
- v-once所在节点只进行初次动态渲染
- 初次动态渲染完成后,变为静态内容
- 以后数据变化不会引起所在结构改变
v-pre指令
用于跳过其所在节点的编译过程
<p v-pre>{{name}}</p>
自定义指令
函数式
自定义指令可以通过配置到Vue实例里,directives对象中来实现
语法:
new Vue({
directives:{
指令名(element,binding){
函数体
}
}
})
其中传入的参数:
element为绑定自定义指令标签的DOM对象
binding为DOM对象的相关属性
绑定自定义指令时要加v-
<div class="root">
<p v-test01>1111</p>
</div>
<script>
const vm=new Vue({
el:'.root',
directives:{
test01(element,binding){
console.log(element,binding)
}
}
})
</script>
自定义指令中函数调用时机:
- 指令与元素成功绑定时函数调用
- 指令所在模板重新解析时函数调用
注:命名如果有多个单词,使用 - 链接
对象式
对象式自定义指令函数调用时机更加细致,会比函数时多一次调用
语法:
new Vue({
directives:{
指令名:{
bind(element,binding){
函数体
},
inserted(element,binding){
函数体
},
update(element,binding){
函数体
}
}
}
})
传入的参数同函数式
函数调用时机:
- bind函数:指令与元素绑定成功时调用
- inserted函数:指令所在元素被插入页面时调用
- update函数:指令指令所在模板重新解析时调用
全局自定义指令
通过在Vue实例中配置directives对象所设置的为局部指令
全局自定义指令语法:
Vue.directive(指令名,配置对象)
Vue.directive(指令名,回调函数)
<div class="root">
<div v-test03>33</div>
<div v-test04>44</div>
</div>
<script>
Vue.directive('test03', {
bind(element, binding) {
console.log(element, binding)
},
inserted(element, binding) {
console.log(element, binding)
},
update(element, binding) {
console.log(element, binding)
}
})
Vue.directive('test04',function(element, binding){
console.log(element, binding)
})
</script>
生命周期
概念
生命周期 又名:生命周期回调函数,生命周期函数,生命周期钩子
生命周期本质上就是函数
Vue会在特殊时刻帮我们调用的一些特殊名称的函数
生命周期函数名字不可更改,其配置在Vue实例中,其中this指向Vue示例
示例(以mounted为例):
<script>
const vm = new Vue({
el: '#root',
mounted() {
console.log(this)
},
})
</script>
“Vue的一生”
创建(出生)
Vue刚刚创建,仅完成初始化,此时数据监测和数据代理创建还未开始;
初始化完成后,进行数据监测和数据代理创建时,会调用两个生命周期函数(不是Vue创建时)
此时 模板未解析
1.数据监测和数据代理之前,初始化后:
- 调用beforeCreate函数
- 此时无法通过vm访问data中的数据,methods中的方法
- 因为数据监测和数据代理未完成
2.数据监测和数据代理之后,解析模板之前:
- 调用created函数
- 此时可以通过vm访问data中的数据,methods中的方法
- 因为模板未解析,页面不会显示解析内容
挂载(生活)
挂载即把初始的真实DOM元素放到页面
在Vue解析完模板,生成虚拟DOM后,进行虚拟DOM转真实DOM
此时调用两个生命周期函数
1.解析完模板后,挂载完成前:
- 调用beforeMount函数
- 页面此时呈现未未经Vue编译的DOM
- 所有对DOM的操作,最终都不会奏效
2.挂载完成后:
- 调用mounted函数
- 对DOM的操作有效,但尽量避免
- 此时可以进行一些初始化操作,比如开启定时器,发送网络需求等
更新(颠覆三观)
在数据更新时,会调用两个生命周期函数
1.新虚拟DOM生成前:
- 调用beforeUpdate函数
- 此时Data数据是新的,但是页面没有进行更新
2.页面更新后:
- 调用updated函数
- 此时Data数据和页面都是新的
销毁(逝世)
Vue可以通过Vue.$destroy()来销毁
销毁时会调用两个生命周期函数
1.销毁前(回光返照):
- 调用beforeDestroy函数
- 此时Vue的所有配置都还可以使用,即将销毁
- 一般在此阶段进行关闭定时器,取消订阅的收尾操作
2.销毁后(已寄):
- 可以调用destroyed函数,但Vue以及没有
注意事项
常用生命周期钩子:
- mounted:初始化操作
- beforeDestroy:收尾操作
关于销毁Vue:
- 销毁后自定义事件失效,但原生DOM事件依然有效
- 一般不会使用beforeDestroy操作Data数据,因为用不到了
Vue组件化编程
组件
实现应用中局部功能代码和资源的集合
扒一张官网的图(下图):
在一个大盒子中可以分三个中盒子(灰色) ,中盒子又可以分成几个小盒子(深灰色)
这几个盒子中代码和资源的集合可以视为一个组件
作用:组件可以复用编码,简化项目编码
当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用
非单文件组件
特点:一个文件中包含多个组件
Vue使用组件有三步:
- 定义组件(创建组件)
- 注册组件
- 使用组件
定义组件(创建组件)
定义组件需要使用Vue.extend()在全局中进行定义
其中()中传入的内容与 new Vue()需要传入的几乎一模一样
区别:
- 不写el配置:最终所有组件由vm中的el来决定服务于那个容器
- data必须写出函数式:避免数据存在引用关系
- 使用template配置组件结构
示例:
//定义组件
const Person = Vue.extend({
template: `
<div>
<p>{{name}}--{{age}}</p>
</div>
`,
data() {
return {
name: 'zhao',
age: '18'
}
}
})
注册组件
1.局部注册
在Vue实例中配置components对象
const vm = new Vue({
el: '#Root',
//注册局部组件
components: {
Person: Person
//简写:Person
}
})
2.全局注册
语法:
Vue.component('组件名', 组件)
//注册全局组件
Vue.component('School', School)
使用组件
在HTML中编写组件标签即可
<div id="Root">
<!--使用组件-->
<Person></Person>
<br>
<School></School>
</div>
示例代码
<body>
<div id="Root">
<!--使用组件-->
<Person></Person>
<br>
<School></School>
</div>
<script>
//定义组件
const Person = Vue.extend({
template: `
<div>
<p>{{name}}--{{age}}</p>
</div>
`,
data() {
return {
name: 'zhao',
age: '18'
}
}
})
const School = Vue.extend({
template: `
<div>
<p>{{name}}--{{address}}</p>
</div>
`,
data() {
return {
name: 'sdau',
address: '泰安'
}
}
})
//注册全局组件
Vue.component('School', School)
const vm = new Vue({
el: '#Root',
//注册局部组件
components: {
Person: Person
//简写:Person
}
})
</script>
</body>
注意事项
组件名
- 组件名如果由多个单词组成,使用kebab-case命名法:my-school
- 也可以使用CamelCase命名:MySchool,但是需要Vue脚手架
- 可以使用name配置指定组件在开发者工具中呈现的名字
const School = Vue.extend({
name:'xxx',
template: `
<div>
<p>{{name}}--{{address}}</p>
</div>
`,
data() {
return {
name: 'sdau',
address: '泰安'
}
}
})
简写
const School = Vue.extend(options)
简写为:
const School = options
组件其他事项
组件的嵌套
组件可以注册在组件中,实现组件的嵌套
示例:
<body>
<div id="Root">
<App></App>
</div>
<script>
const School = Vue.extend({
template: `
<div>{{name}}--{{address}}</div>
`,
data() {
return {
name: 'sdau',
address: '泰安'
}
}
})
const Person = Vue.extend({
template: `
<div>{{name}}--{{age}}</div>
`,
data() {
return {
name: 'zhao',
age: 18
}
}
})
const App = Vue.extend({
template: `
<div>
<Person></Person>
<School></School>
</div>
`,
components: {
School,
Person
}
})
const vm = new Vue({
el: '#Root',
components: {
App
}
})
</script>
</body>
VueComponent
组件的本质是一个名为VueComponent的构造函数,由Vue.extend生成
在我们使用组件时,Vue会解析并创建出组件的实例对象
即Vue会帮我们调用 new VueComponent(options)
另外,每次调用Vue.extend,返回值都是一个全新的VueComponent
关于this指向:
- 组件配置中,this指向均是【VueComponent实例对象】
- new Vue()配置中,this指向均是【Vue实例对象】
一个内置关系
在Vue中,组件原型对象 的 原型 为Vue示例的 原型对象
即:VueComponent.prototype.__proto__===Vue.prototype
可以让组件实例对象可以访问到Vue原型上的属性和方法
单文件组件
特点:一个文件中只包含1个组件
将非单文件组件中的多个组件进行拆分,写到多个文件中,每个文件就是一个组件
每个组件文件的后缀为 .vue
通过ES6模块化语法进行导出和引入
导出
组件文件写法:
其中使用 export default { } 来导出组件
<template>
<div>
模板
</div>
</template>
<script>
//导出
export default {
Vue配置项
};
</script>
<style>
样式 /*可省略*/
</style>
示例:
<template>
<div class="test">
<h2>{{ name }}--{{ address }}</h2>
<button @click="show">123456</button>
</div>
</template>
<script>
export default {
name: "SchoolName",
data() {
return {
name: "sdau",
address: "山东泰安",
};
},
methods: {
show() {
alert("123456");
},
},
};
</script>
<style>
.test {
background-color: aqua;
}
</style>
引入
引入可以使用 import···from··· 来引入
import 组件名 from "组件地址";
示例:
<template>
<div>
<PersonName></PersonName>
<SchoolName></SchoolName>
</div>
</template>
<script>
import PersonName from "./components/PersonName.vue";
import SchoolName from "./components/SchoolName.vue";
export default {
name: "App",
components: {
PersonName,
SchoolName,
},
};
</script>
ref属性
ref属性用来给元素或子组件注册引用信息(id属性的替代者)
语法:
- 标记(示例):<h1 ref="xxx">···<h1> 或 <组件名 ref="xxx">···</组件名>
- 获取:this.$refs.xxx
关于获取值:
- 应用在html标签上,获取真实DOM元素
- 应用在组件标签上,获取组件示例对象
<template>
<div id="app">
<img ref="img" alt="Vue logo" src="./assets/logo.png" />
<HelloWorld ref="hello" msg="Welcome to Your Vue.js App" />
<button @click="show">按钮</button>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
components: {
HelloWorld,
},
methods: {
show() {
console.log(this.$refs);//返回对象
console.log(this.$refs.img);//返回真实DOM
console.log(this.$refs.hello);//返回组件示例对象
},
},
};
</script>
配置项props
配置项props可以让组件接受外部传来的数据
当父组件引入其他组件,父组件所需数据子组件没有时,
可以使用配置项props,通过父组件给子组件数据赋值
是一种组件间的通信方式,将数据从父组件传递给子组件
传递与接收
1.传递方(父组件):
<子组件名 数据名=" 值 "/>
将传入的值传递给子组件
2.接收方(子组件):
props:['数据名']
接收到父组件传递来的数据
示例:
- App.vue(传递方,部分代码):
<template>
<div id="app">
<MyPerson name="zhao"/><!--传递-->
</div>
</template>
- MyPerson(接收方,部分代码):
<template>
<div>
<h1>{{ name }}--{{ age }}</h1>
</div>
</template>
<script>
export default {
name: "MyPerson",
data() {
return {
age: 18,
};
},
props:['name']//接收
};
</script>
接收的三种方法
接收数据共有三种方法:
1.只接收
props:['name']
2.限制类型
props:{
name:String//限制name为字符串
}
3.限制类型与必要性,指定默认值
props: {
name: {
type: String, //限制name为字符串
required: true, //必要性
default: "zhao", //默认值
},
},
必要性:父组件必须传值
必要性与默认值一般不在一起使用,有矛盾
mixin(混入)
功能:将多个组件共用的配置,进行提取,成为一个混入对象,进行使用
定义混入
混入可以定义在另一个JS文件中
即多个组件都有的配置,提取到一个JS文件中
export const test={
methods:{
show(){
console.log(this.name);
}
}
}
通过导出,以便于组件使用
使用混入
局部混入
在引入混入后,可以通过mixins配置项来使用混入
<template>
<div>
<h1>
{{name}}--{{age}}
</h1>
<button @click="show">123</button>
</div>
</template>
<script>
import {test} from './mixin.js'
export default {
name:'MyPerson',
data(){
return{
name:'zhao',
age:18
}
},
mixins:[test]
}
</script>
全局混入
可以直接在App组件中引入混入,使用Vue.mixin(xxx)来进行全局混入
import {test} from './components/mixin'
Vue.mixin(test)
插件
功能:用于增强Vue
本质:插件本质上是一个对象,其中含有一个install方法
install的第一个参数是Vue,所有插件可以实现各种功能
定义插件
对象名.install = function(Vue){
函数体
}
使用插件
Vue.use(对象名)
scoped样式
在Vue组件化编程中,每个组件的样式也会影响到其他的组件,产生冲突
作用:
可以让样式在局部生效,防止冲突
写法:
<style scoped>
组件自定义事件
组件自定义事件是一种组件间的通信方式,将数据从子组件传递给父组件
绑定
绑定自定义事件都是在父组件中
方法一
直接在父组件中,给子组件绑定事件
<MyPerson @gainP="getPersonName" />
如上,为子组件MyPerosn绑定名为gainP的事件
触发事件调用getPersonName回调函数
方法二
在生命周期钩子中绑定,用到ref属性
<MySchool ref="dome" />
......
mounted() {
this.$refs.dome.$on("gainS", this.getSchoolName);
},
如上,为子组件MySchool绑定名为gainS的事件
触发事件调用getSchoolName回调函数
整体代码示例(父组件):
<template>
<div id="app">
<p>{{ PersonName }}//{{ SchoolName }}</p>
<MyPerson @gainP="getPersonName" />
<MySchool ref="dome" />
</div>
</template>
<script>
import MyPerson from "./components/MyPerson.vue";
import MySchool from "./components/MySchool.vue";
export default {
name: "App",
components: {
MyPerson,
MySchool,
},
data() {
return {
PersonName: "",
SchoolName: "",
};
},
methods: {
getPersonName(test) {
this.PersonName = test;
},
getSchoolName(test) {
this.SchoolName = test;
},
},
mounted() {
this.$refs.dome.$on("gainS", this.getSchoolName);
},
};
</script>
触发
触发自定义事件都是在子组件中
通过触发原生事件来触发自定义事件
语法:
this.$emit("自定义事件名", 传入数据);
示例:
<button @click="givePerosnName">触发事件</button>
......
methods: {
givePerosnName() {
this.$emit("gainP", this.Name);
},
},
如上,触发按钮点击事件,调用givePerosnName函数
在givePerosnName函数中触发gainP自定义事件
并且传入数据this.Name
解绑
解绑事件也是在子组件中
语法:
this.$off("自定义事件名");
示例:
<button @click="relieve">解绑事件</button>
......
methods: {
relieve() {
this.$off("gainP");
},
},
如上,触发按钮点击事件,调用relieve函数
在relieve函数中解绑gainP自定义事件
其他
- 组件可以绑定原生DOM事件,但是需要 native 修饰符
- 通过方法二来绑定事件时,回调函数要配置在methods中,或用箭头函数,这样this指向正常,为父组件。使用普通函数,this指向子组件。
全局事件总线
全局事件总线是一种组件之间的通信方式,可以用于任意组件间通信
其并非Vue所提供的方法,而是一种思路,所以写法不唯一
核心思想是找到一个各个组件均可进行通信的中介,来进行任意组件间的通信
虽然写法不唯一,但只记一种
安装全局事件总线
中介可以创建在Vue实例对象的原型上
通过生命周期钩子在Vue实例对象创建完成前安装
在main.js中
new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
}
}).$mount('#app')
如上:创建名为$bus的中介
使用全局事件总线
接收
接收数据要在接收方组件中,给中介$bus绑定自定义事件
事件的回调配置在接收方组件中
methods: {
getPersonName(test) {
this.PersonName = test;
},
},
mounted() {
this.$bus.$on('gainP',this.getPersonName)
},
如上:
为$bus绑定gainP的自定义事件,函数回调在组件methods配置中
另外,最好在beforeDestroy生命周期钩子中,用$off解绑当前组件所用事件
提供
提供数据只需要在提供方触发$bus的自定义事件即可
this.$bus.$emit("gainP", this.Name);
消息订阅与发布
消息订阅与发布是一种组件之间的通信方式,可以用于任意组件间通信
1.安装
需要安装pubsub
npm i pubsub-js
2.引入
import pubsub from 'pubsub-js'
3.接收数据
methods: {
dome(){
......
}
},
mounted(){
this.pid=pubsub.subscribe('xxx',this.dome)//订阅消息
}
4.提供数据
pubsub.publish('xxx',数据)
5.取消订阅
PubSub.unsubscribe(pid)
插槽
作用:是一种组件间的通信方式,可以让父组件向子组件指定位置插入HTML结构
是一种将数据从父组件传递给子组件的方式
默认插槽
父组件(传递方):
设置要传给子组件的HTML结构,需要在使用组件时,在组建标签内写入
<MyTest>
<div>Hello World</div>
</MyTest>
<!--MyTest为组件名-->
子组件(接收方):
在子组件中需要进行定义插槽,确定传入HTML的插入位置
HTML结构会插入在<slot>标签的位置
<template>
<div>
<slot>默认内容</slot>
<!--如果没有传入HTML,则渲染slot标签内的默认内容-->
</div>
</template>
具名插槽
当需要插入多个插槽时,可以用具名插槽
子组件(接收方):
子组件定义多个插槽,并配置name
<template>
<div>
<slot name=dome1>默认内容</slot>
<slot name=dome2>默认内容</slot>
</div>
</template>
父组件(传递方):
给传递的HTML结构配置solt,值为插槽的name值
<MyDome>
<div slot='dome1'>Hello World</div>
<div slot='dome2'>Hello World</div>
</MyDome>
作用域插槽
当需要使用的data数据不在传递方父组件自身,而在子组件里时
子组件(接收方):
子组件在定义插槽时,将数据传入插槽中
<template>
<div>
<slot :word='word'>默认内容</slot>
</div>
</template>
<script>
export default {
name: "TheDome",
data() {
return {
word: "Hello World",
};
},
};
</script>
父组件(传递方):
必须使用<template>标签,并在标签内配置scope
<TheDome>
<template scope="scopeData">
<div>{{ scopeData.word }}</div>
</template>
</TheDome>
如上, 子组件传递的数据,以对象形式,给了scopeData
nextTick
语法:
this.$nextTick(()=>{
回调函数
})
作用:
在下一次DOM更新后执行回调
用于改变数据后,基于更新后的新DOM进行操作时
Vue封装的过渡与动画
如图所示:
Vue封装的过渡与动画分为进入(enter)和离开(leave)
进入和离开又分为起点,过程中(active)和终点(to)
类名
- 元素进入:
- v-enter:进入起点
- v-enter-active:进入过程
- v-leave-to:进入终点
- 元素离开:
- v-leave:离开起点
- v-leave-active:离开过程
- v-leave-to:离开终点
在Vue中添加过渡与动画,需要给Vue所配置的类添加样式
过渡/动画
过渡的添加,需要六种样式全部配置
.v-enter,
.v-leave-to {
transform: translateX(-100%);
}
.v-enter-to,
.v-leave {
transform: translateX(0);
}
.v-enter-active,
.v-leave-active {
transition: 1s;
}
使用 <transition>标签将要进行过度的元素进行包裹
<transition>
<div class="dome" v-show="xxx">111</div>
</transition>
被包裹的元素会自行执行样式中所写的过渡
动画的添加,只需要配置v-enter-active和v-leave-active即可,其他同过渡
.v-enter-active {
animation: 1s test01;
}
.v-leave-active {
animation: 1s test01 reverse;
}
@keyframes test01 {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
多个元素过渡
相同样式:
多个元素,共用一个过渡,需要用 <transition-group>标签包裹
并且每个元素都要指定key值
<transition-group>
<div class="dome" v-show="xxx" key="1">111</div>
<div class="dome" v-show="xxx" key="2">222</div>
</transition-group>
不同样式:
多个元素,不共用一个过渡
要为<transition>标签配置name属性
<transition name='test01'>
<div class="dome" v-show="xxx">111</div>
</transition>
在写样式时,类名中的v换成name值
例:
.test01-enter,
.test01-leave-to {
transform: translateX(-100%);
}
Vue中的Ajax
Vue脚手架配置代理
配置代理可以解决开发环境 Ajax 跨域问题
简
在vue.config.js中添加配置
devServer:{
proxy:'xxx'//xxx为服务器基础路径
}
优点:
- 配置简单,请求资源时直接发给前端
缺点:
- 不能配置多个代理
- 不能控制请求是否走代理
详
在vue.config.js中添加配置
devServer:{
proxy:{
'/dome':{
//匹配所有以'/dome'为开头的请求路径
target:'xxx',//代理目标的基础路径
changeOrigin:true,
pathRewrite:{'^/dome':''}
}
}
}
changeOrigin值为true时,服务器收到请求头中host为服务器基础路径
changeOrigin值为flase时,服务器收到请求头中host为本地基础路径
默认值为true
Vuex
Vuex简介
概念
专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且适用于任意组件间通信。
简而言之,当多个组件共享数据时,使用Vuex来管理这些共享数据
原理
如图,在Vuex中,封装有actions,mutations,state三个对象,由store统一管理(图未显示)
其中,actions用于响应组件,mutations用于修改数据,state用于存储数据
当组件调用dispatch时,会经过相当于门卫的actions,进行一些逻辑处理
当然也可以绕过actions,调用commit直接到mutations,进行相应数据的修改
在action中调用commit,会到mutations中进行相应数据的修改
搭建Vuex环境
安装Vuex
搭建Vuex环境首先需要在终端中安装Vuex
npm i vuex
创建store文件
创建路径:src/store/index.js
文件内容:
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象————响应组件中用户动作
const actions = {}
//准备mutations对象————修改state中的数据
const mutations = {}
//准备state对象————存储数据
const state = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
传入store配置
在main.js中创建vm时,传入store配置
import Vue from 'vue'
import App from './App.vue'
//引入store
import store from './store'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
基本使用
组件
在组件中,通过调用dispatch,来触发action
语法:
this.$store.dispatch('函数名', 数据);
示例:
methods: {
add() {
this.$store.dispatch('add', this.number);
//将会触发actions中的add函数,并把this.number传给actions
},
},
也可以通过调用commit,直接到 mutations中
methods: {
add() {
this.$store.commit('add', this.number);
//将会触发mutations中的add函数,并把this.number传给mutations
},
},
actions
在actions中,可以进行一些逻辑处理(如if判断等),然后调用commit
//准备action对象————响应组件中用户动作
const actions = {
add(context, value) {
context.commit('ADD', value)
},
}
在组件中,触发actions的add函数,函数用两个参数
- context封装有dispatch函数,commit函数,state对象等
- context中dispatch函数用于调用其他actions中的函数
- context中commit函数用于触发mutations
- context中state数据对象,用于进行逻辑处理
- value则是组件传入的数据
关于commit函数
context.commit('ADD', value)
将会触发mutations中的ADD函数,并把tvalue传给mutations
mutations
在mutations,进行state的数据处理
//准备mutations对象————修改state中的数据
const mutations = {
ADD(state, value) {
state.sum += value
},
}
其中的ADD函数传入两个参数:state数据对象和actions传入的value
state
state用于存储数据
//准备state对象————存储数据
const state = {
sum: 0,
}
回到组件
在组件中,使用 $store.state 即可使用Vuex中的数据
<div>和为:{{ $store.state.sum }}</div>
getters的使用
概念:
当state中的数据需要经过加工后再使用时,可以使用getters进行加工
类似于计算属性,可以当作Vuex中的计算属性
使用:
在index.js中追加getters配置
//准备getters对象————加工数据
const getters = {
multiple(state) {
return state.sum * 10
}
}
并暴露出去
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
在组件中,使用 $store.getters 即可使用Vuex中加工后的数据
<div>10倍和为:{{ $store.getters.multiple }}</div>
map方法
mapState方法
用于映射state中的数据,将其映射为组件中的计算属性
如下传统方法,在组件中,一直使用 $store.state获取数据显得十分麻烦
<div>和为:{{ $store.state.sum }}</div>
<div>学校:{{ $store.state.school }}</div>
<div>姓名:{{ $store.state.name }}</div>
可以直接借助mapState生成计算属性
语法(两种写法)
//对象写法
mapState({ sum: "sum", school: "school", name: "name" }),
//或
//数组写法
mapState(["sum", "school", "name"]),
内容
mounted(){
console.log(mapState({sum:'sum',school:'school',name:'name'}));
}
其输出为一个对象,且都是函数,故需要放在计算属性中
使用
import { mapState } from "vuex";
computed: {
//对象写法
...mapState({ sum: "sum", school: "school", name: "name" }),
},
computed: {
//数组写法
...mapState(["sum", "school", "name"]),
},
<div>和为:{{ sum }}</div>
<div>学校:{{ school }}</div>
<div>姓名:{{ name }}</div>
mapGetters方法
用于映射getters中的数据,将其映射为组件中的计算属性
用法与mapState方法无区别
import { mapGetters } from "vuex";
computed: {
//对象写法
...mapGetters({ multiple: "multiple" }),
},
computed: {
//数组写法
...mapGetters(["multiple"]),
},
<div>10倍和为:{{ multiple }}</div>
mapActions方法
如下传统方法,使用$store.dispatch与actions对话,显得特**烦人
methods: {
add() {
this.$store.dispatch("add", this.number);
},
sub() {
this.$store.dispatch("sub", this.number);
},
addOdd() {
this.$store.dispatch("addOdd", this.number);
},
addWait() {
this.$store.dispatch("addWait", this.number);
},
},
可以靠mapActions方法来解决,省略$store.dispatc,实现组件与actions对话
其语法与mapState无异
内容
mounted() {
console.log(mapActions({ add: "add", sub: "sub" }));
},
其输出也为一个对象,且都是函数,需要放在methods配置中
使用
注意:需要手动在绑定事件时传参!
import { mapActions } from "vuex";
methods: {
//对象写法
...mapActions({
add: "add",
sub: "sub",
addOdd: "addOdd",
addWait: "addWait",
}),
},
methods: {
//数组写法
...mapActions(["add", "sub", "addOdd", "addWait"]),
},
<button @click="add(number)">+</button>
<button @click="sub(number)">-</button>
<button @click="addOdd(number)">为奇数时加</button>
<button @click="addWait(number)">一秒钟后加</button>
mapMutations方法
省略$store.commit,实现组件与actions对话
用法与mapActions方法无异
import { mapMutations } from "vuex";
methods: {
//对象写法
...mapMutations({
add: "add",
sub: "sub",
addOdd: "addOdd",
addWait: "addWait",
}),
},
methods: {
//数组写法
...mapMutations(["add", "sub", "addOdd", "addWait"]),
},
模块化和命名空间
让代码更好维护,让多种数据分类更加明确
修改store
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
const Count01 = {
namespaced: true,//开启命名空间
actions: {...},
mutations: {...},
state: {...},
getters: {...}
}
const Count02 = {
namespaced: true,//开启命名空间
actions: {...},
mutations: {...},
state: {...},
getters: {...}
}
//创建并暴露store
export default new Vuex.Store({
modules: {
Count01,
Count02
},
})
不使用map方法
<template>
<div>
<!--读取state数据-->
<p>数:{{ $store.state.Count01.number }}</p>
<br />
<!--读取getters数据-->
<p>10倍为:{{ $store.getters['Count01/multiple'] }}</p>
<br />
<button @click="add(1)">+1</button><br /><br />
<button @click="addIf(1)">为奇数+1</button>
</div>
</template>
<script>
export default {
name: "TheCount01",
methods: {
add(value) {
//调用commit
this.$store.commit("Count01/ADD", value);
},
addIf(value) {
//调用dispatch
this.$store.dispatch("Count01/addIf", value);
},
},
};
</script>
使用map方法
<script>
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
name: "TheCount02",
computed: {
//读取state数据
...mapState("Count02", ["number"]),
//读取getters数据
...mapGetters("Count02", ["multiple"]),
},
methods: {
//调用dispatch
...mapActions("Count02", ["addIf"]),
//调用commit
...mapMutations("Count02", ["ADD"]),
},
};
</script>
路由
路由的理解
路由就是一组 key-value 的对应关系
多个路由由路由器进行管理
Vue中通过vue-router插件来实现路由
基本路由
创建router文件
创建路径:src/router/index.js
示例:
//引入VueRouter
import VueRouter from "vue-router";
//引入组件
import TheTest01 from '../pages/TheTest01'
import TheTest02 from '../pages/TheTest02'
//创建并暴露router实例对象
export default new VueRouter({
routes: [
{
path: '/test01',
component: TheTest01
},
{
path: '/test02',
component: TheTest02
},
]
})
创建的router示例对象中,需要配置一个routers数组,数组存储对象
path值为路由中的key值,component值为组件
组件需要进行引入
安装与应用vue-router插件
搭建vue-router环境首先需要在终端中安装vue-router
npm i vue-router
另外,需要在main.js中应用插件
import Vue from 'vue'
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//应用VueRouter
Vue.use(VueRouter)
//引入路由器
import router from './router/index'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router: router
}).$mount('#app')
注意在main.js中引入路由器,即router文件
实现组件切换
<div>
<div class="a">
<router-link active-class="active" to="/test01">test01</router-link>
</div>
<div class="a">
<router-link active-class="active" to="/test02">test02</router-link>
</div>
</div>
<div>
<router-view></router-view>
</div>
</div>
使用<router-link>标签,实现切换,必需配置to以确定切换的页面
active-class可以配置类名
<router-view>标签,用于指定切换页面的展示位置
<router-view>标签本质是<a>标签
注意点
- 路由组件一般存放在pages文件夹中,一般组件存放在components文件夹中
- 切换后,未显示的组件默认被销毁,展示时在进行挂载
- 每个组件都有独属于自身的$router属性,存储自己的路由信息
- 整个应用只有一个routers,可以通过组件的$router属性获取
多级路由
多级路由就是实现路由的嵌套
在router文件中,使用children配置项
routes: [
{
path: '/dome01',
component: TheDome01
},
{
path: '/dome02',
component: TheDome02,
//通过children配置子级路由
children:[
{
path:'childdome01',//注意不要写/
component:ChildDome01
},
{
path:'childdome02',//注意不要写/
component:ChildDome02
},
]
},
]
在组件(子组件)中实现跳转要写完整路径
<div>
<router-link active-class="active" to="/dome02/childdome01"
>child01</router-link
>
</div>
<div>
<router-link active-class="active" to="/dome02/childdome02"
>child02</router-link
>
</div>
路由中的query参数
query参数是以路由为媒介,给路由组件传递数据的
传递方
<router-link
active-class="active"
:to="{
//注意引号!!
path: '/test02',
query: {
words: 'test02',
},
}"
>test02</router-link
>
在<router-link>标签中,to配置项写成对象形式,里面写有query对象
query对象中的参数既是传递的数据
另外,也可以使用to的字符串写法,无需配置query,同样可以达到目的
<router-link active-class="active" to="/test01?words=test01"
>test01</router-link
>
<router-link active-class="active" :to="`/test01?words=${test01}`"
>test01</router-link
>
接收方(路由组件)
使用$route.query.xxx来接收数据
<div>
<p>{{$route.query.words}}</p>
</div>
虽说是路由中的query参数,但是路由只是一个媒介
传递方组件的数据通过路由传递给路由组件
在此过程中,router文件不需要进行任何修改
命名路由
作用:用于简化路由的跳转
主要在多级路由中展现其作用
在router文件中,使用name配置项
children:[
{
path:'childdome01',//注意不要写/
component:ChildDome01,
name:'child1'//命名
},
]
组件中,在<router-link>标签中,to配置项同添加query时一样,需要写成对象形式
<router-link active-class="active" :to="{
name:'child1'
}"
>child01</router-link
>
可以配合query配置传递参数
路由中的params参数
params参数和query参数一样,是以路由为媒介,给路由组件传递数据的
路由器修改
在router文件中,要事先使用占位符声明接收参数
children:[
{
path:'childdome01/:words',//使用占位符什么接收params参数
component:ChildDome01,
name:'child1'
},
]
如上,意为会传递一个名为words的参数
传递方
字符串写法:
直接使用 / 进行拼接
<router-link active-class="active" :to="`/dome02/childdome02/text2`"
>child02</router-link
>
对象写法:
配置params,并且不可以用path,而是用name
<router-link active-class="active" :to="{
name:'child1',
params:{
words:'text1'
}
}"
>child01</router-link
>
接收方(路由组件)
使用$route.params.xxx来接收数据
<div class="dome">
<p>{{$route.params.words}}</p>
</div>
路由中的props配置
作用:配合query和params参数,让路由组件更方便收到参数
在接收方路由组件中,以$route.params.xxx或$route.query.xxx来接收数据显得特别麻烦
可以在router文件中配置props解决
方法一:
{
path:'childdome01/:words',
component:ChildDome01,
name:'child1',
//值为对象,对象中所有key-value组合通过props传给路由组件
props:{name:'test'}
},
方法二:
{
path:'childdome01/:words',//使用占位符什么接收params参数
component:ChildDome01,
name:'child1',
//值为布尔值,为true时,把路由收到的所有params参数传给路由组件
props:true
},
方法三:
{
path:'childdome01/:words',//使用占位符什么接收params参数
component:ChildDome01,
name:'child1',
//为函数,返回值传给路由组件
props(route){
return{
test:route.query.words
}
}
},
replace属性
浏览器的历史记录有两种写入方式:
- push:追加历史记录
- replace:覆盖历史记录
追加上的历史记录可以通过点击上图 ← 返回
历史记录被覆盖后就无法返回
路由跳转默认为push,即可以返回
在<router-link>标签中添加replace属性,可以改为replace
<router-link active-class="active" to="/dome02" replace>dome02</router-link>
编程式路由
作用:无需使用<router-link>标签,即可实现路由的跳转
methods:{
show01(){
this.$router.push({
path:'/test01'
})
},
show02(){
this.$router.replace({
path:'/test02'
})
},
}
通过this.$router.push和this.$router.replace两个API来实现
分别以追加历史记录和覆盖历史记录的方式进行跳转路由
其里面的配置同to的对象写法
另外,如果配置name,则name为路由组件的name
其他API:
- this.$router.forward() 前进
- this.$router.back() 后退
- this.$router.go() 传入一个整数,正值则前进,负值后退,跳转数为传入数
缓存路由组件
路由组件在切走后会被销毁
缓存路由组件可以使路由组件在切走后保持挂载,不被销毁
将<router-view>标签包裹在<keep-alive>标签中即可
<keep-alive include="test01">
<router-view></router-view>
</keep-alive>
<keep-alive>标签中的include属性为保持挂载的路由组件
不配置默认全部保持挂载,有多个时写成对象的形式
路由中特有的生命周期钩子
路由中有两个特有的生命周期钩子
-
activated() 路由组件激活时触发
-
deactivated() 路由组件切走后触发
注意:只有在<keep-alive>标签包裹下有用!
路由守卫
作用:对路由进行权限控制
分类:全局守卫,独享守卫,组件内守卫
全局守卫
全局守卫分为前置守卫和后置守卫
router文件更改
使用全局守卫,需要用到router的API,故不可用以下方式暴露
export default new VueRouter({
......
})
应改为如下代码
const router = new VueRouter({
......
})
export default router
meta配置
每个路由可以配置meta,这是一个可以让程序员自由传值的配置,meta是一个对象
使用路由守卫需要判断路由是否需要进行权限控制
可以在meta中添加一个布尔值,用于判断
const router = new VueRouter({
routes: [
{
path: '/Person',
component: ThePerson,
meta: { isAuth: true }
},
]
})
全局守卫用法
1.全局前置守卫
初始化时执行,以及每次路由切换前执行
router.beforeEach((to, from, next) => {
if (to.meta.isAuth) {//判断路由是否需要进行权限控制
if (localStorage.getItem('test') === 'xxx') {//判断权限,权限控制的具体规则
next()//放行
} else {
console.log('无权限');
}
} else {
next()//放行
}
})
全局前置守卫使用router.beforeEach(),传值为一个函数
函数传三个参数:to, from, next
to, from为对象,分别存储着 切换到的路由组件信息 和 切换走的路由组件信息
next为一个函数,调用时放行,进行路由的切换
2.全局后置守卫
初始化时执行,以及每次路由切换后执行
router.afterEach((to, from)=>{
console.log(to, from);
})
一般情况下不用,只需要传递to和from两个参数
因为以及切换完,无需使用next
独享守卫
独享守卫是单独配置在某个路由中的
用于判断某个路由是否需要权限控制
除使用的API外,其他与前置守卫无区别
const router = new VueRouter({
routes: [
{
path: '/Person',
component: ThePerson,
meta: { isAuth: true },
beforeEnter(to, from, next) {
if (to.meta.isAuth) {//判断路由是否需要进行权限控制
if (localStorage.getItem('test') === 'xxx') {
//判断权限,权限控制的具体规则
next()//放行
} else {
console.log('无权限');
}
} else {
next()//放行
}
}
},
]
})
组件内守卫
组件内守卫分为进入守卫和离开守卫
进入守卫,在通过路由进入某个路由组件时调用
离开守卫,在通过路由离开某个路由组件时调用
需要写在路由组件中,用法同全局守卫
//进入守卫
beforeRouteEnter(to, from, next){
console.log(to, from, next);
},
//离开守卫
beforeRouteLeave (to, from, next) {
console.log(to, from, next);
}
路由器两种工作模式
对于一个url来说,#后面的内容就是其hash值
hash值不会包含着http请求中,即hash值不会传递给服务器
路由器有两种工作模式:
1.hash模式
- 地址中永远带着 #
- 地址可能被第三方App标记为不合法
- 兼容性高
2.history模式
- 地址中不带着 #
- 兼容性比hash模式差
- 部署后需要后端修404问题
路由默认为hash模式
修改为history模式如下代码:
const router = new VueRouter({
mode:'history',
.......
})