Vue学习笔记

vue的特性

  • 数据驱动视图
  • 双向数据绑定

底层原理是MVVM(Model 数据源,view 视图,model view Vue实例)

数据驱动视图

  • 数据的变化会驱动视图自动更新
  • 优点:只需要维护数据,页面结构会被vue自动刷新

Vue学习笔记_第1张图片

双向数据绑定

  • 填写表单时,在开发者不操作DOM的前提下,自动把用户填写的内容同步到数据源中
  • js数据的变化,会被自动渲染到页面上

Vue学习笔记_第2张图片

MVVM

在MVVM概念中:

  • model代表当前页面渲染时所依赖的数据源
  • View 代表当前页面渲染的DOM结构
  • ViewModel 代表vue 的实例(mvvm的核心)

Vue学习笔记_第3张图片

vue的基本使用

   <script>
    //创建 vue 的实例对象
    new Vue({
        //el 属性是固定写法,表示 vm 实例要控制哪个区域 ,接受的值是一个选择器
        //注意:el实例只控制第一个符合条件的标签
        el:'#name',
        //dta 对象就是要渲染到页面的数据
        data:{ name:'wukl',
            username:'11',
            gender:'男',
            info:"你们好啊!"
        }
    })
	script>

vue的指令与过滤器

指令

概念:vue提供的模板语法,辅助开发者渲染页面基本结构

vue中按照不同用途指令分为六大类:

  • 内容渲染指令
  • 属性绑定指令
  • 事件绑定指令
  • 双向绑定指令
  • 条件渲染指令
  • 列表渲染指令
内容渲染指令

渲染DOM元素的文本内容

  • v-text 缺点:会覆盖元素内部原有的结构
  • {{}}:在实际开发中用的最多,只是内容的占位符,不覆盖原有内容!
  • v-html:可以把带有标签的字符串,渲染成真正的HTML内容

vue提供的模板渲染语法中,除了支持绑定简单的数据值,还支持Javascript表达式的运算

<div>1+2 的结果是 {{1+2}} div>


<div>{{ tips}} 反转的结果是:{{tips.split('').reverse().join()}}div>

<!--在使用v-bind绑定属性期间,如果绑定内容需动态拼接,则字符串外面应该包裹单引号--!>
<div :title="'box'+index">只是一个divdiv>
v-text
    
    <p v-text="username">p>

    
    
    <p v-text="gender">性别p>

	<!--一定要放在vue作用域内--!>
{{}}语法

插值表达式,专门解决 v-text 会覆盖默认文本内容的问题

    <p>姓名:{{username}}p>
v-html

v-text只能渲染纯文本内容,若要把包含HTML标签的字符串渲染到页面内,则使用v-html

属性绑定指令

注意:插值表达式只能用在元素的内容节点中,不能用在属性节点中

解决方案: v-blind可以为属性节点绑定值

vue规定 v-bind: 可以简写为 :

    <div id="i">
        <input type="text" v-bind:placeholder="username" >
        
    div>
	
事件绑定指令

vue提供了v-on 事件绑定指令

<p>count的值是{{count}}p>      

<button v-on:click="addCount">+1button>
***************************************************************

<button @click="count +=1" > +1button>

在Vue实例中,methods定义了时间的处理函数

const vm=new Vue({
	el:'#i',
	methods:{
		addCount:function(){}
        // 简写为addCount(){}
	}
})

//vm对象是Vue的实例对象,自带data中的数据,可以通过this(vm.count)调用并修改

再绑定事件处理函数的时候,可以用小括号传递参数:

v-on:click="addCount(1)"

v-on可以被简写成@

@click="addCount(1)"

注意:原生DOM对象 onclick、oninput、onkeyup等原生对象,替换成 vue 的事件绑定形式后分别为v-on:click、v-on:input、v-on:keyup

事件绑定@event
    
	
	<button @click="addCount(1,$event)">+Nbutton>
事件修饰符

为应对事件处理函数中 event.preventDefault() 或event.stopPropagation()等需求,vue提出了事件修饰符的概念,辅助开发者对事件的触发控制。

<a href='http://' @click="show()">a>

methods:{
	show(e){
		e.preventDefault();
		//原生javascript:阻止末尾行为
	}
}
//事件修饰符,加载在事件绑定后
<a href="" @click.prevent="show()">

常见的事件修饰符:

Vue学习笔记_第4张图片

冒泡:当外层与内层部件同时绑定相同事件,触发内部事件时,会冒泡至外层部件。

按键修饰符

监听键盘事件时,可以为键盘相关的事件添加键盘修饰符


<input @keyup.enter="vm.submit()">

<p>{{username}}p>

<input type="text" v-model="username">

<input	type="text" :value="username"> 

<select v-model="city">
     <option value="">请选择城市option>
     <option value="1">北京option>
     <option value="2">上海option>
select>
v-model指令的修饰符

.unmber :对于 input 中的数据,当内容仅为一位数字时,才会被当成number类型,多位数字默认为字符串。

.tirm : 绑定数据时自动去除两端字符串。

.lazy : 懒惰模式,忽略变化的中间过程。

Vue学习笔记_第5张图片

条件渲染指令

辅助开开发者按需控制DOM的显示与隐藏。

  • v-if
  • v-show

<p v-if="flag">被 v-if 控制p>

<p v-show="flag">被 v-show 控制p>

v-if可以配合v-else-if、v-else使用

<div v-if="type === 'A'">优秀div>
<div v-else-if="type === 'B'">良好div>
<div v-else="type === 'c'">一般div>
列表渲染指令

vue提供了循环v-for指令,辅助开发者基于一个数组来循环渲染一个列表结构,v-for使用的是 item in items 形式的特殊语法。

还有第二个可选参数,代表当前索引**(item,index) in items**

  • items是待循环数组
  • items是被循环的每一项
data:{
     items:[
          {id:1,username:'kl'},
          {id:2,username:'wu'}
           ]
}
-----------------------------------------------------------
<table class="table table-bordered table-hover table-striped">
        <thead>
            <th>索引th>
            <th>名称th>
        thead>
    
    
        <tbody v-for="item in items":key="item.id">
            <tr>
            	<td>{{item.id}}td>
            	<td>{{item.username}}td>
            tr>    
    tbody>
table>

key的注意事项

Vue学习笔记_第6张图片

使标签的状态和数据对应起来,防止标签状态紊乱

过滤器(vue3.0已删除功能)

过滤器功能:文本的格式化,常用于:插值表达式和v-bind属性绑定


<p>{{ message | capitalize }}p>


过滤器本质是个函数,定义到filters:{} Vue实例属性中
而且一定要有返回值,message的值可以被过滤器函数直接接收到
**********************************************************

new Vue({
	filter:{
//val接收到的就是message的值
		capitable(val){
			const first=val.charAt(0).toUpperCase //charAt接受一个索引,表示从字符串把索引对应的字符获取过来
			const other = val.slice(1) //slice方法可以截取字符串,从索引位置开始
			return first+other
	}
}
})
私有过滤器和全局过滤器

!!!!!全局过滤器应绑定在Vue实例之前!!!!!

定义在filter中的是私有过滤器,不能被其他区域的 Vue 实例访问

在多个 vue 实例之间共享过滤器,要定义为全局过滤器

//第一个参数是全局过滤器的“名字”
//第二个参数是全局过滤器的“处理函数”
//当全局过滤器和私有过滤器重名时,会优先调用私有过滤器(就近原则)
Vue.filter('capitalize',(str) => {
		const first=val.charAt(0).toUpperCase 
		const other = val.slice(1) 
		return first+other
})

过滤器的其他特性

1.过滤器可以连续调用

<p>
    {{ data | filter_1 | filter_2}}
p>

2.过滤器函数可以传递参数(因为其本质是函数)

<p> {{ message | filter(arg_1,arg_2)}}p>

****************************************************
Vue.filter('filter',(val,arg_1,arg_2){})

Vue基本用法

侦听器

Watch侦听器

Watch侦听器:监测数据变化,依据数据变化做出反应

new Vue({
	//所有的侦听器都应该被定义到 watch 节点下
	watch:{
        //侦听器本质是个函数,要监听哪个数据的变化,就把哪个数据名作为方法名
        //第一个是newVal 新值 后面是 oldVal 旧值
		username(newval,oldval){
		
		}
}
})

方法格式的侦听器:因为如果值没有被改变,侦听器不会立即执行

对象格式的侦听器:可以通过 immediate 选项,让侦听器自动触发

watch:{
    //定义对象格式的侦听器
    username:{
        handler:function(newVal,oldVal){},
        //true代表一进入页面就会触发,默认值是false,控制侦听器是否会自动触发一次
        immediate:true
    }
}
深度侦听
data:{
	info:{
		username:5
	}
}

此时username不直接挂载在data中,不能通过info属性侦听到其值的改变

解决方法:在对象类型侦听器中,设置 deep:true ,只要对象中任何数据变化,就会导致侦听方法被调用

watch:{
    //定义对象格式的侦听器
    username:{
        handler:function(newVal,oldVal){},
       	//默认为false
       	deep:true
    }
}
**********************************************************
    //如果要侦听对象的子属性的变化,则必须包裹一层单引号
    'info.username'(){
    
}

计算属性

通过一系列运算最终得到一个属性值,这个属性值可以直接被模板结构或methods 方法使用



<div class="box" :style="{ backgroundColor :`rgb(${r},${g},${b})`}">
div>


**************************************************************

computed:{
	//这个方法要返回一个生成好的rgb字符串
	rgb:function(){
		return `rgb($(this.r),$(this.g),$(this.b))`
}
}

优点:

  • 实现了代码的复用
  • 只要它所依赖的数据源改变,它会被重新求值

axios

axios 只专注于网络请求。

基本语法(axios 在导入后就会在创建一个全局可用的函数,用来发起网络请求)

axios({
    method:'请求类型',
    url:'URL地址',
    //params:{},  GET传参
    data:{}		//POST传参
}).then((result) => {
    //.then 用来指定请求成功后的回调函数
})
//result 拿到的数据中的result.data 才是服务器真实的数据
//	因为axios 在真正的数据外,套了一层壳

axios 在真正的数据外,套了一层壳

{
	config:{},
	
	data:{真实的数据},
	
	headers:{},
	request:{},
	status:xxx,
	statusText:""
}
结合async和await调用 axios
document.querySelector('#btn').addEventListener('click',async function(){
        //如果调用某个方法的返回值时 Promise 实例,则前面可以添加 await !
        // await 只能用在被 async “修饰”的方法中
        // 加await 后,result从一个Promise对象变成数据对象
            await axios({
            method:'GET',
            url:'http://www.liulongbin.top:3006/api/getbooks',
            params:{
                id:2
            }
        }).then(function(result){
            console.log(result)
        })
        })
解构赋值
//结合上层代码result 对象中有 config、data、status等属性
//    如果我们只关心 data 类型,可以使用解构赋值,提取我们期许的数据
//    解构赋值中使用冒号进行重命名
const { data:res }=await axios()
axios.get()和axios.post()
const {data:res}=await axios.get('URL'
(,{
    //get参数
    params:{}
})
)
***************************************************************
const {data:res}=await axios.post('URL',
(,{
 //直接在花括号里写数据即可
 })
          )

vue-cli

单页面应用程序

单页面应用程序(Single Page Application)简称 SPA,即一个Web网站中只有一个HTML页面。

vue-cli

vue-cli 是Vue.js开发的标准工具,简化了Webpack创建工程化Vue项目的过程。

安装

npm i -g @vue/cli

创建项目

vue create 项目名称

vue项目中 src 目录的构成

assets 文件夹:存放项目中用到的静态资源,eg:css样式,图片资源
compoents 文件夹:程序员封装的、可复用的组件,都要放到 conponents 目录下
mian.js 是项目的入口文件,整个项目的运行,s要执行 main.js
App.vue 是项目的根组件

vue项目运行逻辑

main.js把app.vue渲染到index.html的页面中

//导入vue 这个包,得到vue 构造函数
import Vue from 'vue'
//导入 app.vue 根组件,将来要把 app.vue 中的模板结构,渲染到 html 页面中
import App from './App.vue'

Vue.config.productionTip = false

//创建 Vue 的实例对象
new Vue({
  //把render 函数指定组件,渲染到 html 页面中
  render: h => h(App),
  //.$mount 或者 el 都可以指定 Vue 的作用区域
  //el:'#app'  div#app 就是个占位符,把其替换为模板内容
      //.$mount 与el 作用完全一样
}).$mount('#app')

vue组件

组件化开发

就是对UI结构的封装和复用

vue中的组件化开发

App.vue本质都是就是一个vue的组件

Vue组件的组成部分
  • template->组件的模板结构
  • script->组件的Javescript行为
  • style->组件的样式
<template>
    <div>
        <h1>test {{username}}</h1>
    </div>
</template>

<script>


export default ({
    //data信号源
    //注意:vue组件中的 data 不能像之前一样,不能指向对象
    //注意:组件中data 必须是一个函数
        data(){
            //这个 return 出去的{ },可以定义数据
            return {
                username:'keli'
            }
        }
        
})
</script>

<style>
    h1{
        background:pink
    }
</style>
唯一根节点以及less预处理器

template只能包含一个根节点

解决方案:模板整体使用一个

整体包裹


组件之间的父子关系

Vue学习笔记_第7张图片

组件的使用
//1.使用import 语法导入需要的组件
import Left from "./components/Left.vue"
//2.使用 components 节点注册组件
export default{
    components:{
        Left
    }
}
//3.以标签形式使用刚才注册的组件
<div>
	<Left></Left>
</div>
通过 components 注册的是私有子组件

私有组件只能使用在已在 components 注册过的组件内,未声明则无法使用。

注册全局组件

全局注册一次,不需重复注册

方法:在vue项目的 main.js 入口文件中,通过 Vue.comonent()方法,可以注册全局组件。

//导入需要全局注册的组件
import Count from'@/components/Count.vue'

//参数一:字符串格式,表示组件的“注册名称”
//参数二:需要被全局注册的组件
Vue.component('MyCount',Count)
组件的props

对于全局组件,可能有不同的初始值需求,而props 作为自定义属性,在封装组件时,可以极大的提高组件的复用性!

//这里是全局声明组件Count 中的vue.component 对象挂载的内容
export default{
    //这里是数组
    //自定义的属性的名字,是封装者自定义的(只要名称合法即可)
    props:['init_vue1','init_vue2']
	data(){
        return count=init_vue1+init_vue2
    }
}

***************************************************************
//在使用组件时,需要传递props 中声明的属性值
//这里的 9 是字符串
<count init_vue1='9'></count>
//这里的 9 是数字类型,因为 :(v-bind) 默认括号中的为表达式
<count :init_vue1='9'></count>

这时 init 值可以结合 v-bind 使用,传递javascript 中的属性值.

props中的数据,可以直接在模板结构中被使用,但是,props中的值是只读的,不能直接修改props 的值

解决方案:可以把 props 的值转存在 data 中,因为 data 中的数据都是可读可写的

props中的默认值

用户有可能不传入props中要求属性的默认值,所以要设置初始值。

//注意:数组格式的props不能定义初始值,所以要使用对象格式的 props
export default{
    props:{
        init:{
            //如果外界使用 Count 组件的时候,没有传递 init 属性,初始值就会生效
            default:0
        }
    }
}
props的type值类型

在声明自定义属性时,可以通过 type 来定义属性的值类型。

export default{
    props:{
        init:{
            //用 default 属性定义属性的默认值
            default:0,
            //用 type 属性定义属性的值类型
            //如果传递过来的值不符合此类型,则会在终端里报错
            type:Number
        }
    }
}
props中的 required 必填项
export default{
    props:{
        init:{
            //当调用该组件时,如果没有传递init 属性值,则会强制报错
            required:true
        }
    }
}
组件之间的样式冲突

写在.vue 组件中的样式会全局生效,所以容易造成样式冲突(因为样式都会在一个 html 页面中呈现,样式会共用)

解决方法

使用 css 中的属性选择器 [ ],为组件添加统一的自定义属性(不同属性之间应该不同)



高级解决方法

在 style 标签中 添加 scoped属性,vue中自动生成并填充自定义属性。


/deep/样式穿透

要在父组件修改子组件中的值,而父组件已添加 scoped 属性,所以不能直接选中子属性,需要加 /deep/ 使用了后代选择器。


//样式不加 /deep/ 时: h5[data-v-3c83f0b7]
//样式修改为: [data-v-3c83f0b7] h5 后代选择器
//可以选中子元素中的 h5 标签

使用外部 UI 库时,不应修改 UI 库的源码,可以使用 /deep/ 选择子代组件,并修改样式。

组件的生命周期

生命周期:创建、运行、销毁的过程

为了在组件生命周期中关键的时间点,准确地执行某些操作,vue提供了生命周期函数,这些函数会被依次执行。

Vue学习笔记_第8张图片

vue官方给出的生命周期流程图:

Vue学习笔记_第9张图片

created

因为此时 vue 实例中的,peops、methods、data已经初始化,但是没有渲染,所以经常在此发送 ajax 请求(调用methods 方法),添加到 data 中,供模板使用。

注意:此时模板结构尚未生成到网页中,所以不能调用DOM操作

befroeMount

将要把生成好的模板结构渲染到 html 页面中,这时还不能操作DOM结构

mounted

这时 el 结构已被模板结构替换,成功渲染到 html 页面中,完成了DOM结构的渲染,可以获取并且操作DOM

beforeUpdate

将要根据数据变化后、最新的数据重新渲染组建的模板结构

此时,数据是最新的,UI是旧的

document.elementSelect.innerhtml

updated

数据与UI 结构都是最新的

组件之间的数据共享

组件之间的关系

  • 父子关系
  • 兄弟关系(除了父子关系,剩下的都是兄弟关系)
父子之间的数据共享

父 -> 子: 自定义属性 props

子 -> 父:子组件向父组件共享数据使用自定义事件。

//子组件
methods:{
	add(){
		this.count+=1
		this.$emit('numchange',this.count)
}
}
***************************************************************
//父组件
    //只要 $emit 被调用,numChange就会被触发
    <Son @numchange='getNewCount'></son>
	export default{
        data(){
            return {this.countFromSon}
        }
        methods:{
        	// val 即子组件传来的参数值
            getNewCount(val){
                this.countFromSon = val
            }
        }
    }
兄弟组件之间的数据共享

vue2.x 中,兄弟组件之间数据共享方案是EventBus

Vue学习笔记_第10张图片

EventBus的使用步骤

  • 创建 EventBus.js 模块,并向外共享一个 Vue 的实例对象
  • 在数据发送方,调用 bus.$emit (‘事件名称’,要发送的数据)方法触发自定义事件。
  • 在数据接收方,调用 bus.$on (‘事件名称’,事件处理函数)方法注册一个自定义事件
ref 引用 DOM

在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用

Vue 组件实例对象中都有一个 refs空对象

可以在组件中不同的标签内添加不同的 ref ,这个属性值会被添加到 refs 中,通过 this.refs.ref 值,便可以直接拿到这个 DOM 元素。







this.$nextTick(cb)

组件的 $nextTick(cb) 方法,把回调函数中的内容延迟执行,等DOM渲染完毕后执行

this.$nextTick(() =>{
      this.$refs.btnref.focus()
})
***************************************************************
//不能代替this.$nextTick() 因为一旦数据改变就会调用该函数
//而功能要求是: 每次 input 出现时调用,这样会导致按钮出现时也会调用 focus功能 ,导致报错
updated(){
    this.$refs.btnref.focus()
}
动态组件
component标签

为了实现动态组件渲染:vue 提供了一个内置的 组件,专门用来实现动态组件的渲染。

//变量 left_val 用来存储组件名称
<component :is="left_val"></components>

data(){
    return {
        left_val:'left'
    }
}
使用 keep-alive 保持状态

因为在使用 component 标签动态创建、销毁组件时,其中的data 数据会被重置,所以要使用 keep-alive 组件保持数据状态。



    

被keep-alive 包裹的组件不会被销毁,只会被隐藏。( inactive 被缓存状态

keep-alive 对应的生命周期

当组件被缓存时,会自动触发组件的 deactived 生命周期,(一开始会触发created,之后不会触发,因为之后都是被隐藏)

当组件被激活时,会自动触发组件的 activated 生命周期

keep-alive 的include 和 exclude属性

include:只有名称匹配的组件会被缓存,多个组件间用英文的逗号分隔。

<keep-alive include="MyLeft,MyRight>
	<component :is="comName"></component>
</keep-alive>

exclude: 只有名称匹配的组件不被缓存,它和 include 只能存在一个

组件声明的名称和注册时的名称

如果在“声明组件”的时候,没有为组件指定 name 名称,则组件的名称就是“注册时候的名称”。

而当声明过 name 之后,就会把组件的注册名称覆盖。(include 、exclude 等属性中要使用 name 的名称。)

1.组件中的“注册名称”的主要应用场景:以标签的形式,把注册好的组件,渲染和使用到页面结构中

2.组件声明的时候,“name” 名称的主要应用:结合 标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称。

插槽

插槽(slot )是vue 为组件封装者提供的能力。允许开发者在封装组件时,把不确定、希望由用户指定的部分定义为插槽

Vue学习笔记_第11张图片

// change.vue
//vue 官方规定:每一个 slot 插槽都有一个 name 名称
//如果忽略了 solt 的 name 属性,则有一个默认的名称叫做 default

<slot name="default"> </slot>

//此时引用该组件时:会将内容渲染到插槽中

<change>
    //默认渲染到 default 中,而要指定插槽,需要用 template 包裹,然后用 v-slot 指定插槽, v-slot 不能直接用在元素标签身上。
	<template v-slot:default>
		<p>这是插槽的内容</p>
	<template>
</change>
***************************************************************
 <template #default>

v-slot 简写是 #

后备内容

在 标签中声明一些内容,如果用户没有传入,则插槽部分会被后备内容所填充。

数据传递



***************************************************************
这样可以实现数据之间的传递

自定义指令
  • 私有自定义指令
  • 全局自定义指令
私有自定义指令

directives 节点声明私有自定义属性。

//调用时,使用 v-color 来调用自定义属性。

directives:{
	color:{
		bind(el){
			
			el.style.clor = 'red'
		}
	}
}
//当指令第一次被绑定到元素上的时候,会立即触发 bind 指令,形参中的el 表示绑定到的那个 DOM 对象
自定义指令传递值
<p v-color="'red'"></p>

directives:{
	color(){
		bind(el,binding)
			//obj 或者其他名字都可以
			//binding.value 对象中包含了自定义属性中传递的值
			//binding.expression 是表达式的值 "'red'"
			el.style.color = binding.value
	}
}
update 函数

bind 函数只会在创建时调用一次,当数据更新时,不会被触发。而 update 函数会在每次数据更新时调用。

directives:{
    color:{
        update(el,binding){
            el.style.color=binding.value
        }
    }
}

bind 和 update 都要设置,bind在第一次绑定时被执行, update 在数据刷新时被执行。

函数简写

如果 bind 和 update 函数中的逻辑完全形同,则对象格式可以写为函数格式。

directives:{
color(el,binding){
    el.style.color = binding.value
	}
}
//相当于在 bind 和 update 中都写了一份
全局自定义指令

全局自定义指令需要通过“Vue.directive()”声明(放到 main.js 中):

Vue.directive('color',(el,binding)=>{
        el.style.color=binding.value
    }
})

axios全局挂载

把 axios 挂载到 vue 组件 原型对象上,这样不需要组件重复导入。同时,可以为 axios 对象设置 defaults.baseURL 属性,确立默认根目录地址。

// 全局配置 axios 的请求根路径
axios.defaults.baseURL = 'http://www.liulongbin.top:3006'
// 把请求根路径挂载到 axios中

Vue.prototype.$http = axios
// 今后在每个 .vue组件中发起请求,只需要 this.$http.get()即可

但是,这种方法不利于实现 api 的复用,不建议使用

前端路由

前端路由:Hash 地址和组件之间的对应关系

工作方式:

  1. 用户点击页面中不同的路由链接。
  2. 导致了 URL 地址栏中的 Hash 值发生了变化。
  3. 前端路由监听到了 Hash 地址的变化
  4. 前端路由把当前的 Hash 地址对应的组件渲染到浏览器中
    Vue学习笔记_第12张图片

可以结合动态组件使用

// 为了监听 hash值的变化
window.onHashChange = () => {
	// 当hash 值变化时,便会调用该函数
	//location.hash 可以获取当前的hash 值
	switch (locatin.hash){
        case '#/home':
            this.comName = 'Home'
    		break
        case...
            ...
    }	
}

vue-router

vue-router 是 vue.js 官方给出的路由解决方案。

安装
// 注意,由于版本之间兼容问题,vue@2.需要安装 vue-router 的@3.系列版本
npm i vue-router 
创建路由模块

在 src 源代码目录下,新建 router/inex.js 路由模块:

// src/router/index.js 就是到项目的路由模块
// 导入 VUE 和vue-router 的包
import Vue from 'vue'
import VueRouter from 'vue-router'

// 使用 Vue.use() 函数,把VueRouter 安装为 Vue 的插件
Vue.use(VueRouter)

// 创建路由的实例对象
const router = new VueRouter()

export default router

在main.js 中挂载路由模块;

import router from './router/index.js'
new Vue({
  render: (h) => h(App),
  // 在Vue 项目中,想要把路由用起来,必须把路由实例对象,通过以下方式挂载
  // router: router 属性名和属性值相同,可以简写
  router
}).$mount('#app')

在进行模块化导入时,如果给定的是文件夹,则默认导入这个文件夹下,名字叫做 index.js 的文件

应用
    
    
    

***************************************************************
const router = new VueRouter({
  // routes 是一个数组,定义“hash 地址”和“组件”之间的对应关系
  routes: [
    // 这里要省略‘#’
    { path: '/left', component: left },
    { path: '/right', component: right }
  ]
})
router-link 代替 a
//#在 router-link中不需要添加

redirect 重定向

路由重定向:用户在访问地址 A 时,强制用户跳转到地址C

{ path:'/',redirect:'/home'}
嵌套路由

通过路由实现组件的嵌套展示,叫做嵌套路由

Vue学习笔记_第13张图片

子组件嵌套实现
 // 在子组件 left 中嵌套声明 router-link ,接下来只剩下 router/index.js 中的路径配置了
 <router-link to="/right/tab1"> tab1 </router-link>
 <router-link to="/right/tab2"> tab2 </router-link>
 <hr />

 <router-view></router-view>
通过 children 属性声明子路由规则

注意:子路由规则不要加斜线

const router = new VueRouter({
  // routes 是一个数组,定义“hash 地址”和“组件”之间的对应关系
  routes: [
    // 这里要省略‘#’
    {
      path: '/left',
      component: left
    },
    {
      path: '/right',
      component: right,
       //重定向为 /right/tab1 使切换到 right 时,就会默认出现 tab1 组件
      redirect: '/right/tab1'
      children: [
        { path: 'tab1', component: Tab1 },
        { path: 'tab2', component: Tab2 }
      ]
    },
    // redirect 重定向
    { path: '/', redirect: '/left' }
  ]
默认子路由
    {
      path: '/right',
      component: right,
      // redirect: '/right/tab1',
      children: [
        // 默认子路由:如果 children 数组中,某个路由规则的 path 值为
        // 空字符串,则这条路由规则,叫做“ 默认子路由”
        { path: '', component: Tab1 },
        { path: 'tab2', component: Tab2 }
      ]
    },
***************************************************************
    //此时要在默认组件对应的 router-link 中
动态路由匹配

把 Hash 地址中的可变的部分定义为参数项,从而提高路由规则的复用性

在 vue-router 中使用英文的冒号)来定义路由的参数项。

//动态路由以 : 进行声明,冒号后面是动态参数的名称
{ path:'/movie/:id',component : Movie}

现在的需求是,希望根据 id 的值来展示对应电影的详情信息

解决方法一:

在组件实例对象中,有一个 router 属性,里面的 params 记录了路径中的 id 值

Vue学习笔记_第14张图片

this.$route.params.id //取值即可

this.$route 是路由的“参数对象”

this.$router 是路由的“导航对象”

解决方法二

为当前路由开启 props 传参

//在当前路由中开启 props 传参
{ path: ':id', component: movie, props: true }
***************************************************************
 //在组件中接收
 //在数组中,属性名都以字符串的形式表示
   props:['id']
扩展 query 和 fullquery
  • 在 hash 地址中,/ 后面的参数项,叫做“路径参数”
  • 在路由“参数对象”中,需要使用 this.$route.params 访问路径参数

eg:

/right/:id
  • 在 hash 地址中,?后面的参数项,叫做“ 查询参数 ”
  • 在路由“参数对象”中,需要使用 this.$route.query 来获取查询参数

eg:

/right?id='1'&age=20

在 this.$route.fullpath 中,包括了之后的查询参数,而同级的 path 属性不包括查询参数。

声明式导航 & 编程式导航

在浏览器中,点击链接实现导航的方式,叫做声明式导航。

eg:

  • 标签,vue 项目中点击 都属于声明式导航。

在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航

eg:

  • 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航。
vue-router中的编程式导航 API
  • this.$router.push(‘hash 地址’)

跳转到指定的 hash 地址,并创造一条历史记录

  • this.$router.replace(‘hash 地址’)

跳转到指定的 hash 地址,并替换到当前的历史记录

  • this.$router.go(数值 n)

调用 $router.go() 方法,可以在浏览历史中前进和后退。

// go(-1) 
//表示后退一层,如果后退的层数超过上限,则原地不动
this.$router.go(-1)  //后退到之前的组件页面
$router.go 的简化用法

在实际开发过程中,一般只会用到前进和后退一层页面,所以 vue-router 提供了如下两个方法:

  • $router.back() 后退一个页面
  • $router.forward() 前进一个页面
导航守卫

导航守卫可以控制路由的访问权限

Vue学习笔记_第15张图片

全局前置守卫

每次发生路由的导航跳转时,都会触发全局前置守卫,从而实现对每个路由进行访问权限的控制。

// 创建路由实例对象
const router = new VueRouter({ ... })
// 调用路由实例对象的 beforeEach 方法,即可声明“全局前置守卫”
// 每次发生路由导航跳转的时候,都会自动触发 fn 这个“回调函数”
router.beforeEach(fn)
守卫方法的三个形参

全局前置守卫的回调函数中接收 3 个形参,格式为:

const router = new VueRouter({ ... })

router.beforeEach((to,from,next) =>{
    // to 时即将要访问的路由信息对象
    // from 时将要离开的路由信息对象
    // next 是一个函数,调用 next() 表示放行,允许此次导航
})
next函数的三种调用方式

Vue学习笔记_第16张图片

控制后台主页的访问权限

router.beforeEach((to,from,next) =>{
    if(to.path === '/main'){
        //获取本地储存的 token 值
        const token = localStorage.getItem('token')
        if(token) {
            next
        }else {
            next('/login')
        }
    } else {
            next()
        }
})

在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航

eg:

  • 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航。
vue-router中的编程式导航 API
  • this.$router.push(‘hash 地址’)

跳转到指定的 hash 地址,并创造一条历史记录

  • this.$router.replace(‘hash 地址’)

跳转到指定的 hash 地址,并替换到当前的历史记录

  • this.$router.go(数值 n)

调用 $router.go() 方法,可以在浏览历史中前进和后退。

// go(-1) 
//表示后退一层,如果后退的层数超过上限,则原地不动
this.$router.go(-1)  //后退到之前的组件页面
$router.go 的简化用法

在实际开发过程中,一般只会用到前进和后退一层页面,所以 vue-router 提供了如下两个方法:

  • $router.back() 后退一个页面
  • $router.forward() 前进一个页面
导航守卫

导航守卫可以控制路由的访问权限

[外链图片转存中…(img-58R2B2lg-1665064366052)]

全局前置守卫

每次发生路由的导航跳转时,都会触发全局前置守卫,从而实现对每个路由进行访问权限的控制。

// 创建路由实例对象
const router = new VueRouter({ ... })
// 调用路由实例对象的 beforeEach 方法,即可声明“全局前置守卫”
// 每次发生路由导航跳转的时候,都会自动触发 fn 这个“回调函数”
router.beforeEach(fn)
守卫方法的三个形参

全局前置守卫的回调函数中接收 3 个形参,格式为:

const router = new VueRouter({ ... })

router.beforeEach((to,from,next) =>{
    // to 时即将要访问的路由信息对象
    // from 时将要离开的路由信息对象
    // next 是一个函数,调用 next() 表示放行,允许此次导航
})
next函数的三种调用方式

Vue学习笔记_第17张图片

控制后台主页的访问权限

router.beforeEach((to,from,next) =>{
    if(to.path === '/main'){
        //获取本地储存的 token 值
        const token = localStorage.getItem('token')
        if(token) {
            next
        }else {
            next('/login')
        }
    } else {
            next()
        }
})

你可能感兴趣的:(vue.js,学习,前端)