Vue入门到关门之Vue3学习
一、常用API
注意:本文项目均使用脚手架为 Vite
1、setup函数
(1)介绍
如果在项目中使用配置项API,那么写起来就和vue2的写法是一样的;但是如果在项目中写的是组合式API,那么组件中所用到的:数据、方法等等,均要配置在setup中。此外,setup()
钩子也是在组件中使用组合式 API 的入口,通常只在以下情况下使用:
- 需要在非单文件组件中使用组合式 API 时。
- 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。
(2)基本使用
setup函数的返回值:返回一个对象,对象中的属性、方法,在模板中均可以直接使用。setup函数中是默认不带响应式的,需要使用ref或reactive包裹。
<template> <div class="home"> <h2>姓名:{{ name }}</h2> <h2>年龄:{{ age }}</h2> <button @click="handleAdd">点击加年龄</button> <button @click="changeName">点击变彭于晏</button> </div> </template> <script> import {ref} from 'vue' export default { name: 'HomeView', setup() { // 1 插值语法 let name = ref('xiao') // let age=19 // 默认没有响应式 let age = ref(19) // 做成响应式 // 2 方法--》点击年龄+1 function handleAdd() { console.log(age) // age 的类型不是数字了,而是RefImpl age.value += 1 // 让数字加1 ,需要使用 对象.value } function changeName() { name.value = '彭于晏' } // 必须return--》这样setup里面的数据才能在template中使用 return { name, age, handleAdd, changeName } } } </script>
注意:
- 尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methos、computed...)中可以访问到setup暴露的值中的属性、方法
- 但在setup中不能访问到Vue2.x配置(data、methos、computed...)
- 如果有重名, setup优先
2、setup需要注意的地方
(1)setup执行的时机
- 在beforeCreate之前执行(一次),此时组件对象还没有创建;setup函数执行于beforeCreate和created之前,也就是说setup函数里面无法使用data和methods方法中的数据。
- this是undefined,不能通过this来访问data/computed/methods /props;
- 其实所有的组合式API 相关的回调函数中也都不可以。
(2)setup的返回值
- 一般都返回一个对象:为模板提供数据,也就是模板中可以直接使用此对象中的所有属性/方法;
- 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性;
- 返回对象中的方法会与methods中的方法合并成功组件对象的方法;
- 如果有重名,setup优先;
- 注意:一般不要混合使用:methods中可以访问setup提供的属性和方法,但在setup方法中不能访问data和methods;
- setup不能是一个async函数:因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性数据
(3)setup的参数
setup(props, context) / setup(props, (attrs, slots, emiti);
- props: 包含props配置声明且传入了的所有属性的对象;
- attrs: 包含没有在props配置中声明的属性的对象,相当于 this.$attrs;
- slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots;
- emit: 用来分发自定义事件的函数 相当于 this.$emit。
(4)组件的属性
- 只能访问以下四种:props、attrs、slots、emit
3、ref 和 reactive
ref
用来做 基础变量[数字,字符串,布尔]的响应式
reactive
用来做 对象[数组,字典]的响应式
(1)ref
-
语法:
const xxx = ref(initValue) - 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
JS
中操作数据:xxx.value
模板
中读取数据: 不需要.value
,直接:<div>{{xxx}}</div>
; 因为在模板中访问从 setup 返回的 ref 时,它会自动浅层解包,因此你无须再在模板中为它写 .value。
-
备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的 - 对象类型的数据:内部 求助 了Vue3.0中的一个新函数——
reactive
函数
<template> <div class="home"> <h1>setup函数的使用</h1> {{ name }}--{{ age }} <br> <button @click="add">点我年龄+1</button> <br> <button @click="handleChange('彭于晏')">点我变彭于晏</button> </div> </template> <script> import {ref, reactive} from 'vue' export default { name: 'HomeView', setup() { // vue3多的,vue2没有,以后建议vue3的代码全都写在这里,不再写配置项方式了 // 1 定义变量,跟正常写js一样 let name = ref('xiao') // let age = 19 // 没有响应式 let age = ref(19) // 有响应式,变成对象了 // 2 定义一个函数,点击按钮,年龄加一的函数 let add = () => { // alert('111') // 让年龄+1,出问题了,变量确实会变,但是页面不会变化---》vue3定义的变量,默认不是响应式的 // age++ 自增,就不能这么写了 age.value++ //有响应式 console.log(age.value) } let handleChange = (n) => { name.value = n //有响应式 } // 3 必须要有返回值,是个对象,返回的对象,可以在 模板(template)中使用 return {name, age, add, handleChange} }, } </script>
(2)reactive
-
语法:
-
const 代理对象= reactive(源对象) -
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
-
操作数据和读取数据均不需要
.value
-
-
reactive定义的响应式数据是“深层次的”,对象无论多少层,都可以。
-
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作
<template> <div class="home"> <h1>setup函数的使用</h1> <p>用户名:{{ userInfo.name }}</p> <p>年龄:{{ userInfo.age }}</p> <p>爱好:{{ userInfo.hobby }}</p> <button @click="handleAdd">点我年龄+1</button> </div> </template> <script> import {ref, reactive} from 'vue' export default { name: 'HomeView', setup() { let userInfo = reactive({ name: 'xiao', age: 19, hobby: '篮球' }) let handleAdd = () => { userInfo.age++ console.log(userInfo) } return {userInfo, handleAdd} }, } </script>
(3)ref与reactive的对比
- 从定义数据角度对比:
- ref用来定义:基本类型数据
- reactive用来定义:对象(或数组)类型数据
- 从原理角度对比:
- ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。
- reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- 从使用角度对比:
- ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
- reactive定义的数据:操作数据与读取数据:均不需要.value。
4、计算属性-监听属性
(1)计算属性computed
Vue3与Vue2中的计算属性配置功能是一样的,不同的是写法:
- 在Vue2中,computed是通过声明选项的方式书写的,在Vue中,声明选项是指在创建Vue实例时传入的参数,是一个对象。这个对象可以包含多个属性和方法,其中包括data、methods、computed、watch等。这些属性和方法可以用于定义组件的行为和状态。
- 在Vue3中,computed是通过组合式API的方式书写的,Vue中的组合式API是一组新的API,它允许我们使用函数而不是声明选项的方式书写Vue组件。组合式API包括响应式API、生命周期钩子、工具函数等,这些API可以让我们更灵活地组织和复用代码,提高代码的可读性和可维护性 。
所以我们在Vue3中使用computed的时候需要先引入
import {computed} from 'vue'
<template> <h1>计算属性</h1> <p>姓:<input type="text" v-model="person.firstName"></p> <p>名:<input type="text" v-model="person.lastName"></p> <p>全名:{{ person.fullName }}</p> <p>全名修改:<input type="text" v-model="person.fullName"></p> </template> <script> import {ref, reactive} from 'vue' import {computed} from 'vue' export default { name: 'App', setup() { // 3 计算属性 const person = reactive({ firstName: '', lastName: '' }) // 只有 计算属性,不修改值的情况 person.fullName = computed(() => { return person.firstName+person.lastName }) // 支持修改 person.fullName = computed({ get() { return person.firstName + person.lastName }, set(value) { person.firstName = value.slice(0, 1) person.lastName = value.slice(1) }, }) return {person} }, } </script>
(2)监听属性watch
- Vue2和Vue3中的watch属性在功能上是一致的。
- 但是要注意两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
在vue3中watch()方法可以帮助我们监听数据的变化,并按执行一些任务。Vue3中watch接受三个参数,第一个参数是要监听的响应式数据,第二个参数是回调函数,第三个参数是配置项。如果需要监听多个数据,可以在setup函数中使用watch函数多次,每次传入不同的参数即可。不像vue2中的watch是一个配置项,vue3中的watch是一个方法可以多次调用。
情景一:监视ref定义的响应式数据
- 当我们点击按钮的时候,watch可以监听到数据的变化。
<template> <h2>年龄是:{{ age }}</h2> <button @click="age++">点我年龄增加</button> </template> <script> import {ref, watch} from "vue"; export default { name: 'App', setup() { const age = ref(19) // 监听普通 watch(age, (newValue, oldValue) => { console.log('age变化了', '新值',newValue,'旧值', oldValue) }) return {age} } } </script>
情景二:监视多个ref定义的响应式数据
const sum = ref(100) const msg = ref('很好') function changeSum() { sum.value += 1 } const changeMsg = () => { msg.value = 'asdfas' } watch([sum, msg], (newValue, oldValue) => { console.log('sum或msg变化了', '新值',newValue,'旧值', oldValue) })
情景三:监视reactive定义的响应式数据
如果加了{immediate:true}
配置项之后表示立即监听,输入框中的值还没有改变就会触发一次watch方法;
从控制台打印的信息,我们可以清晰地看到oldval的值为undefined。这就是我们需要注意的第一点:若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!
watch(person, (newValue, oldValue) => { console.log('person变化了', '新值', newValue, '旧值', oldValue) }, { immediate: true ,deep:false})
在代码中我并没有写deep:true
,但是依然可以监听到person下的age属性。
而且就算我们在代码中关闭深度监听也是没有用的,所以这里就是我们需要注意的第二点:若watch监视的是reactive定义的响应式数据,则强制开启了深度监视。
情景四:监视reactive定义的响应式数据中的某个属性
- 如果我们想监听一个对象中的某一个属性,我们肯定会轻松到想到这个代码该怎么写。
watch(person.name, (newValue, oldValue) => { console.log('person变化了', '新值',newValue,'旧值', oldValue) }, { immediate: true ,deep:false})
-
但这时控制台会弹出一个警告,简单翻译一下就是 : 监视源只能是getter/effect函数、ref、响应对象或这些类型的数组。通俗的说就是,只能监视一个ref的值或者是reactive对象。
-
所以需要我们这么写,正常的写法是写一个函数,函数有返回值:
const person = reactive({name: 'xiao', age: 14}) // 2 监听对象中的某个属性 watch(() => person.name, (newValue, oldValue) => { console.log('person.name变化了', '新值',newValue,'旧值', oldValue) }, { immediate: true ,deep:false})
情景五:监视reactive定义的响应式数据中的某些属性
- 如果是要监视一个响应式数据的多个属性,也按照上文写的监视多个ref定义的响应式数据那样,将多个属性写在一个数组中,不过每一个属性都要写成函数的形式。
watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{ console.log('person的age变化了', '新值',newValue,'旧值', oldValue) },{immediate:true,deep:true})
特殊情况
- 当我们监视一个reactive定义的对象中的某个属性时,此时deep配置就会生效,然而当我们将deep配置设置为false时,是监听不到person.age的变化的。
watch(() => person.age, (newValue, oldValue) => { console.log('person的age变化了','新值',newValue,'旧值', oldValue) }, { deep: false })
- 不管我们怎么去修改age对应的属性值都是监听不到的。
watchEffect函数
- 当我们使用
watch
监视属性的时候,需要明确的指出需要监视的是哪个属性,也要指明监视的回调函数。 - 而
watchEffect
的工作原理是:不用指定监听谁,只要watchEffect内部用了某个变量,某个变量发送变化,就会触发。
watchEffect(() => { const x1 = sum.value const x2 = person.name console.log('watchEffect配置的回调执行了') })
- 在
watchEffect
中,我们将person的name和sum的值赋值给两个新的变量,证明我们使用了这两个属性,所以修改这两个属性的值是,就会触发监听函数。
(3)总结
-
Computed属性
-
computed 是一个函数,它返回一个值,该值依赖于组件的数据。当依赖的数据发生改变时,computed 返回的值会自动更新。
-
在 Vue.js 中,我们通常使用 computed 来封装复杂的逻辑或计算属性,使得我们能够更加方便地处理这些逻辑,并且保证其响应式的特性。
-
-
Watch属性
-
watch 是一个对象,它允许我们观察 Vue 实例的数据。当数据变化时,我们可以执行一些操作。
-
在某些情况下,我们可能需要等待数据改变后执行某些操作,或者在数据改变时执行异步操作。这种情况下,我们可以使用 watch。
-
5、生命周期
- vue3生命周期流程图
(1)Vue2.X和Vue3.X对比
vue2 -------> vue3配置项 -------> vue3组合式 beforeCreate --------> beforeCreate -------> setup(()=>{}) created --------> created -------> setup(()=>{}) beforeMount --------> beforeMount -------> onBeforeMount(()=>{}) mounted --------> mounted -------> onMounted(()=>{}) beforeUpdate --------> beforeUpdate -------> onBeforeUpdate(()=>{}) updated --------> updated -------> onUpdated(()=>{}) beforeDestroy --------> beforeUnmount -------> onBeforeUnmount(()=>{}) destroyed --------> unmounted -------> onUnmounted(()=>{})
(2)配置项API生命周期
- beforeCreate:beforeCreate钩子用于在实例被创建之前执行逻辑。
- created:created钩子用于在实例创建完成后执行逻辑。
- beforeMount:beforeMount钩子在挂载之前执行。
- mounted:mounted钩子在挂载完成后执行。
- beforeUpdate:beforeUpdate钩子在数据更新之前执行。
- updated:updated钩子在数据更新完成后执行。
- beforeUnmount:beforeUnmount钩子在组件卸载之前执行。
- unmounted:unmounted钩子在组件卸载完成后执行。
(3)组合式API生命周期
- setup() : 开始创建组件,在 beforeCreate 和 created 之前执行,创建的是 data 和 method;
- onBeforeMount() : 组件挂载到节点上之前执行的函数;
- onMounted() : 组件挂载完成后执行的函数;
- onBeforeUpdate(): 组件更新之前执行的函数;
- onUpdated(): 组件更新完成之后执行的函数;
- onBeforeUnmount(): 组件卸载之前执行的函数;
- onUnmounted(): 组件卸载完成后执行的函数
(4)示例
<template> <div class="home"> <h1>生命周期钩子</h1> <h3>年龄是:{{ age }}</h3> <button @click="addAge">点击age+1</button> </div> </template> <script> import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue' export default { name: 'HomeView', setup() { // 生命周期钩子 // 1 写在这里是就是beforeCreate console.log('beforeCreate') const age=ref(19) function addAge(){ age.value++ } //2 写在这里是就是created console.log('created',age.value) //3 beforeMount-->onBeforeMount // onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted onBeforeMount(()=>{ console.log('onBeforeMount','组件挂载前') }) //4 mounted-->onMounted onMounted(()=>{ console.log('onMounted','组件挂载后') }) //5 beforeUpdate-->onBeforeUpdate onBeforeUpdate(()=>{ console.log('onBeforeUpdate','更新之前') }) //6 updated-->onUpdated onUpdated(()=>{ console.log('onUpdated','更新之后') console.log(age.value) }) //7 beforeUnmount-->onBeforeUnmount onBeforeUnmount(()=>{ console.log('onBeforeUnmount','销毁之前') }) //8 unmounted-->onUnmounted onUnmounted(()=>{ console.log('onUnmounted','销毁后') }) return {age,addAge} }, } </script>
6、toRef 和 toRefs
(1)toRef
作用:
创建一个ref对象,其value值指向另一个对象中的某个属性值,与原对象是存在关联关系的。也就是基于响应式对象上的一个属性,创建一个对应的ref,这样创建的ref与它的源属性是保持同步的,与源对象存在引用关系,改变源属性的值将更新ref的值。
语法:
const 变量名 = toRef(源对象,源对象下的某个属性) 如:const name = toRef(person,'name')
使用:
要将响应式对象中的某个属性单独提供给外部使用时,但是不想丢失响应式,把一个prop的ref传递给一个组合式函数也会很有用。
缺点:
toRef()
只能处理一个属性,但是toRefs(源对象)
却可以一次性批量处理
示例:
<template> <div class="home"> <h1>toRef函数</h1> {{data}} <br> {{ name }}---{{ age }} <button @click="handleChangeAttrs">点我看控制台</button> </div> </template> <script> import { ref, toRef, reactive, } from 'vue' export default { name: 'HomeView', setup() { let data = reactive({ name: 'xiao', age: 19, hobby: '篮球' }) // 错误示范 const { name, age} = person; const { web,trade} = person.job; // 这样直接操作数据是无法修改的,因为它不是一个响应式数据,只是一个纯字符串,不具备响应式 function handleChangeAttrs() { name = "itclanCoder"; age = 20; // 正确写法 // 想要修改指定哪个对象具备响应式,那么就使用toRef函数处理,toRef(源对象,源对象下的某个属性) const name = toRef(data, 'name') // 使用ref与toRef对比 const age = ref(data.age) function handleChangeAttrs() { name.value = "刘德华"; age.value = 20; console.log(name) console.log(age) } return {name, age, handleChangeAttrs} }, } </script>
toRef与ref的不同:
如果你用ref
处理数据的话,如下所示,使用ref
处理数据,页面也能实现数据的响应式,更新,但是它与toRef
是不同,有区别的,因为ref修改数据,页面数据会更新,但是源数据不会同步,修改,并无引用关系,ref
相当于是对源对象重新拷贝一份数据 ref()
接收到的是一个纯数值。
(2)toRefs
作用:
toRef()
只能处理源对象指定的某个属性,如果源对象属性很多,一个一个的使用toRef()
处理会显得比较麻烦,那么这个toRefs()
就很有用了,它与toRef()
的功能一致,可以批量创建多个ref
对象,并且能与源对象保持同步,有引用关系
语法:
toRefs(源对象) 如:toRefs(person)
使用:
当从组合式函数中返回响应式对象时,toRefs
是很有用的。使用它,消费者组件可以解构/展开返回的对象而不会失去响应性。
示例:
<template> <div class="home"> <h1>toRefs</h1> <h2>{{ name }}---{{ age }}</h2> <button @click="age++">点击年龄+1</button> <button @click="addAge">点击年龄+2</button> <br> <button @click="handleShow">看控制台</button> </div> </template> <script> import {ref, reactive, toRefs} from 'vue' export default { name: 'HomeView', setup() { // toRefs let person = reactive({name: 'xiao', age: 19}) function addAge() { person.age += 2 console.log(person) } function handleShow() { console.log(person) } // return {name:ref(person.name), age:ref(person.age),addAge, handleShow} return {...toRefs(person),addAge, handleShow} }, } </script>
注意事项:
toRefs
在调用时只会为源对象上可以枚举的属性创建ref
。如果要为可能还不存在的属性创建 ref
,则改用 toRef
。
二、setup写法
1、简单介绍
- 组件,只需要导入,就会自动注册
- setup写法
<script setup>
写原来setup函数中的代码即可</script>
- 生命周期钩子--created
- 监听属性,计算属性
- 组件间通信--父传子
- 组件通信--子传父
- 插槽
- mixin 没了==>直接导入导出用
- 插件也是一样
- toRefs-->把对象中所有变量都做成响应式
- toRef -->只把对象中某一个做成响应式
- ref属性
2、具体使用
(1)App.vue
<script setup> // 以后,只要再 这样写[ <script setup> ] ,script就是setup函数中的 // 定义的变量和函数,不需要return,以后,就不再写配置项了 // 1 组件,只需要导入,就会自动注册 import HelloWorld from './components/HelloWorld.vue' import Child from "./components/Child.vue"; // 2 setup写法 import {ref, reactive, computed, toRefs, toRef} from "vue"; import Child2 from "./components/Child2.vue"; const name = ref('xiao') function changeName() { name.value = '彭于晏' } // 3 生命周期钩子--created console.log('created') // 4 监听属性,计算属性 const newName = computed(() => { return name.value + '_NB' }) // 5 组件间通信 父传子 const message = ref('hello world 组件你好') // 6 组件通信,子传父 const child_name = ref('') function handleEvent(name) { child_name.value = name } // 7 插槽 // 8 mixin 没了-->直接导入导出用 import utils from "./utils/index.js"; let a = utils.add(4, 5) console.log(a) // 9 插件一样 // 10 toRefs-->把对象中所有变量都做成响应式 const person = reactive({name1: 'xiao', age1: 19}) let {name1, age1} = toRefs(person) // 等同于:name:ref(person.name) age:ref(person.age) // let {name1, age1} = person // 等同于: name1=lqz age1=19 console.log(typeof person.name1) console.log(typeof name1) name1.value='sss' // 11 toRef -->只把对象中某一个做成响应式 const person1 = reactive({name2: 'xiao', age2: 19}) //const name=toRefs(person) //{name:ref(name),age:ref(age)} const name2 = toRef(person, 'name2') //name=ref(person.name) function change() { name2.value = 'xxx' } // 12 ref属性-->注意要组件挂载完后才能拿到child3 值 import Child3 from "./components/Child3.vue"; const child3=ref() // 代指 this.$refs.child3 ,这个地方变量名必须跟在组件上定义的名字一致,放在组件上的ref是child3 // created--->还没挂载---》组件还没有 function showLog(){ console.log(child3.value) // child3.value拿到组件对象 child3.value.changeAge() // 使用组件对象的属性和方法---》vue3---》不能直接使用,需要子组件暴露---》子组件中:defineExpose({age,changeAge})---》只能用子组件暴露的 console.log(child3.value.age) } </script> <template> <h1>setup写法</h1> <h2>{{ name }}</h2> <button @click="changeName">点我变名字</button> <h2>计算属性newName:{{ newName }}</h2> <hr> <h1>父传子-自定义属性</h1> <HelloWorld :msg="message"></HelloWorld> <h1>子传父-自定义事件</h1> <h2>子组件传过来的:{{ child_name }}</h2> <Child @myevent="handleEvent"></Child> <h1>插槽</h1> <Child2> <template v-slot:a> <div>我是a</div> </template> <template v-slot:b> <div>我是bbb</div> </template> </Child2> <h1>ref属性-放在组件上</h1> <Child3 ref="child3"></Child3> <button @click="showLog">点我看控制台</button> </template> <style></style>
(2)父子通信父传子==> HelloWorld.vue
<script setup> // 父传子,接受父传入的变量 // 1 数组形式 // defineProps(['msg']) // 2 对象形式 defineProps({ msg: String, }) </script> <template> <h1>{{ msg }}</h1> </template>
(3)父子通信子传父==> Child.vue
<script setup> import {ref} from "vue"; let $emit = defineEmits(['myevent']) // 等同于之前的 this.$emit const name = ref('') function handleSend() { $emit('myevent', name.value) } </script> <template> <input type="text" v-model="name">-->{{ name }}--> <button @click="handleSend">点我,传到父</button> </template> <style scoped> </style>
(4)插槽使用==> Child2.vue
<script setup> </script> <template> <h2>child2</h2> <slot name="a"></slot> <h2>换行</h2> <slot name="b"></slot> </template> <style scoped> </style>
(5)ref属性==> Child3.vue
<script setup> import {ref} from "vue"; const age=ref(0) function changeAge(){ age.value+=10 } defineExpose({age,changeAge}) // 在子组件中暴露 </script> <template> <h1>ref属性使用</h1> </template> <style scoped> </style>
三、axios使用
1、简单介绍
(1)什么是axios?
axios是一个流行的基于Promise的HTTP客户端,可以在浏览器和Node.js环境中使用。它允许您在应用程序中进行HTTP请求,从而与后端服务器进行数据交换。
(2)axios的功能
-
axios的返回结果是一个promise实例对象
-
他的回调不同于promise的value和reason分别叫做response和err
-
axios的成功值是一个axios封装的response对象.服务器返回的真正数据在response.data中
-
axios需要携带query参数的话要写在params中,但是params参数只能写在请求地址中
2、vue3实现加载电影案例
(1)安装
npm install axios -S
(2)导入
import axios from "axios";
(3)使用
// 相当于写在了created中--》页面加载完,就发送请求 axios.get('自己地址').then(res => { console.log(res) })
(4)axios普通使用
<script setup> import axios from "axios"; import {reactive} from "vue"; const filmList = reactive({}) // 相当于写在了created中--》页面加载完,就发送请求 // 普通使用 axios.get('http://127.0.0.1:8000/api/v1/film/').then(res => { console.log(res.data) if(res.data.code==100){ // 加载成功了-->把返回的数据,放到变量中 filmList.result=res.data.results // 能赋值,但是不是响应式 console.log('---',filmList) }else{ alert(res.data.msg) } }) </script> <template> <h1>显示电影案例</h1> <div v-for="item in filmList.result"> <h3>{{ item.name }}</h3> <img :src="item.poster" alt="" height="300px" width="250px"> </div> </template> <style></style>
(5)高级使用
<script setup> import axios from "axios"; import {reactive} from "vue"; const filmList = reactive([]) // 高级使用 Object.assign--》copy-》把一个对象copy到另一个对象身上 axios.get('http://127.0.0.1:8000/api/v1/film/').then(res => { console.log(res.data) if (res.data.code == 100) { // (1) 直接把res.data.results 复制到filmList.result Object.assign(filmList,res.data.results) // (2) 解构赋值 let {data}=res // res={data:{code:100,msg:成功}} Object.assign(filmList,data.results) // (3) 解构赋值 let {data: {results}} = res Object.assign(filmList, results) // (4) 解构赋值 let {data} = res // {code:100,msg:成功,results:[]} Object.assign(filmList, data.results) } else { alert(res.data.msg) } }) </script> <template> <h1>显示电影案例</h1> <div v-for="item in filmList"> <h3>{{ item.name }}</h3> <img :src="item.poster" alt="" height="300px" width="250px"> </div> </template> <style></style>
3、async和await
(1)async/await是什么?
async
关键字用于定义一个异步函数,表示该函数是一个协程(coroutine)。await
关键字用于暂停异步函数的执行,等待另一个异步操作完成。
(2)async和await的基础使用
-
async 表示这是一个async函数, await只能用在async函数里面,不能单独使用;
-
async 返回的是一个Promise对象,await就是等待这个promise的返回结果后,再继续执行;
-
await 等待的是一个Promise对象,后面必须跟一个Promise对象,但是不必写then(),直接就可以得到返回值。
(3)async/await的特点
- Async作为关键字放在函数前面,普通函数变成了异步函数;
- 异步函数async函数调用,跟普通函数调用方式一样。在一个函数前面加上async,变成 async函数,异步函数,return:1,打印返回值;
- 返回的是promise成功的对象;
- Async函数配合await关键字使用。
(4)加载电影案例改写
<script setup> import axios from "axios"; import {reactive} from "vue"; const filmList = reactive({}) async function load() { // response--》就是原来then中的res // let response= await axios.get('http://127.0.0.1:8000/api/v1/films/') // data --》就是原来then中的res.data // 正常返回的then的给了response--》原来catch的会被异常捕获 let {data} = await axios.get('http://127.0.0.1:8000/api/v1/film/') console.log(data) Object.assign(filmList, data.results) } load() </script> <template> <h1>显示电影案例</h1> <div v-for="item in filmList.result"> <h3>{{ item.name }}</h3> <img :src="item.poster" alt="" height="300px" width="250px"> </div> </template> <style></style>
4、axios其它配置项
(1)常用配置项
- GET请求
//完整版写法 const res = axios({ url:'http://localhost:5000/persons',//请求地址 methods:'GET'//请求方式 params:{id:...}//query参数发送方式 }) log(res)//axios返回值是一个promise实例 res.then( response => {log(response.data)} err => {log(err)} ) //精简版写法 axios.get('http:.......',{params:{id:...}}).then( response =>{} err =>{} ) //只要成功的写法 const res = await axios.get('http:/...')
- POST请求
//完整版 axios({ url:'http://...', methods:'POST', data:{name:...,age:...}//json格式的参数 data:`name=..&age=..`//urlencoded格式的参数 }) //精简版 axios.post('http:...',{name:..,age:..}).then( response => {} err => {} )
- 配置默认属性
axios({ url:'地址', method:'post', headers: {'token': 'adsfa.adsfa.adsf',contentType:'application/json'}, params: {name: xiao, age:19}, data: {firstName: 'xxx'}, timeout: 1000, }) // 或者这么写 axios.baseURL = 'http://...' //URL一定是大写 axios.defaults.timeout = 2000 axios.defsults.headers = {'token': 'adsfa.adsfa.adsf',contentType:'application/json'}
(2)其他配置项
// 更多参数 { //1 `url` 是用于请求的服务器 URL url: '/user', //2 `method` 是创建请求时使用的方法 method: 'get', // 默认值 //3 `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。 // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL baseURL: 'https://some-domain.com/api/', //4 `transformRequest` 允许在向服务器发送前,修改请求数据 // 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法 // 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream // 你可以修改请求头。 transformRequest: [function (data, headers) { // 对发送的 data 进行任意转换处理 return data; }], // transformResponse 在传递给 then/catch 前,允许修改响应数据 transformResponse: [function (data) { // 对接收的 data 进行任意转换处理 return data; }], //5 自定义请求头 headers: {'X-Requested-With': 'XMLHttpRequest'}, //6 params` 是与请求一起发送的 URL 参数 // 必须是一个简单对象或 URLSearchParams 对象 params: { ID: 12345 }, // 7 aramsSerializer`是可选方法,主要用于序列化`params` // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/) paramsSerializer: function (params) { return Qs.stringify(params, {arrayFormat: 'brackets'}) }, //8 data` 是作为请求体被发送的数据 // 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法 // 在没有设置 `transformRequest` 时,则必须是以下类型之一: // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams // - 浏览器专属: FormData, File, Blob // - Node 专属: Stream, Buffer data: { firstName: 'Fred' }, // 发送请求体数据的可选语法 // 请求方式 post // 只有 value 会被发送,key 则不会 data: 'Country=Brasil&City=Belo Horizonte', // 0imeout` 指定请求超时的毫秒数。 // 如果请求时间超过 `timeout` 的值,则请求会被中断 timeout: 1000, // 默认值是 `0` (永不超时) // 11 thCredentials` 表示跨域请求时是否需要使用凭证 withCredentials: false, // default // 12 dapter` 允许自定义处理请求,这使测试更加容易。 // 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。 adapter: function (config) { /* ... */ }, // 13 auth` HTTP Basic Auth auth: { username: 'xiao' password: '123‘ }, // 14 `responseType` 表示浏览器将要响应的数据类型 // 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream' // 浏览器专属:'blob' responseType: 'json', // 默认值 // 15 `responseEncoding` 表示用于解码响应的编码 (Node.js 专属) // 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求 // Note: Ignored for `responseType` of 'stream' or client-side requests responseEncoding: 'utf8', // 默认值 // 16 `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称 xsrfCookieName: 'XSRF-TOKEN', // 默认值 // 17 `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称 xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值 // 18 `onUploadProgress` 允许为上传处理进度事件 // 浏览器专属 onUploadProgress: function (progressEvent) { // 处理原生进度事件 }, // 19 `onDownloadProgress` 允许为下载处理进度事件 // 浏览器专属 onDownloadProgress: function (progressEvent) { // 处理原生进度事件 }, // 20 `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数 maxContentLength: 2000, // 21 `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数 maxBodyLength: 2000, // 22 `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。 // 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`), // 则promise 将会 resolved,否则是 rejected。 validateStatus: function (status) { return status >= 200 && status < 300; // 默认值 }, // 23 `maxRedirects` 定义了在node.js中要遵循的最大重定向数。 // 如果设置为0,则不会进行重定向 maxRedirects: 5, // 默认值 // 24 `socketPath` 定义了在node.js中使用的UNIX套接字。 // e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。 // 只能指定 `socketPath` 或 `proxy` 。 // 若都指定,这使用 `socketPath` 。 socketPath: null, // default // 25 `httpAgent` and `httpsAgent` define a custom agent to be used when performing http // and https requests, respectively, in node.js. This allows options to be added like // `keepAlive` that are not enabled by default. httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), // 26 `proxy` 定义了代理服务器的主机名,端口和协议。 // 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。 // 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。 // `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。 // 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。 // 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https` proxy: { protocol: 'https', host: '127.0.0.1', port: 9000, auth: { username: 'xiao', password: '123' } }, // 27 see https://axios-http.com/zh/docs/cancellation cancelToken: new CancelToken(function (cancel) { }), // 28 `decompress` indicates whether or not the response body should be decompressed // automatically. If set to `true` will also remove the 'content-encoding' header // from the responses objects of all decompressed responses // - Node only (XHR cannot turn off decompression) decompress: true // 默认值 }
5、axios请求响应拦截器
axios请求响应拦截器是axios提供的一个重要功能,它可以在我们发送请求或接收响应时进行处理。通过拦截器,我们可以在请求或响应被处理前对其进行修改、日志记录或添加额外的处理逻辑。
在axios中,您可以通过axios.interceptors.request
和axios.interceptors.response
来添加请求和响应拦截器。这两个方法都接受两个回调函数作为参数,一个用于处理成功的情况,另一个用于处理错误的情况。
下面是一个简单的示例,演示了如何使用axios的拦截器:
// 添加请求拦截器 axios.interceptors.request.use(function (config) { // 在发送请求之前做些什么 console.log('请求拦截器被触发'); return config; }, function (error) { // 对请求错误做些什么 return Promise.reject(error); }); // 添加响应拦截器 axios.interceptors.response.use(function (response) { // 对响应数据做点什么 console.log('响应拦截器被触发'); return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); });
在上面的示例中,我们使用axios.interceptors.request.use
添加了一个请求拦截器,它在每次发送请求之前被触发。类似地,使用axios.interceptors.response.use
添加了一个响应拦截器,它在每次接收到响应后被触发。
此外,我们还可以在拦截器中进行各种操作,例如添加请求头、记录日志、对响应数据进行处理等。这使得axios拥有了更高的灵活性和可定制性,能够满足各种复杂的需求。
四、promise语法
1、普通函数和回调函数
(1)普通函数
普通函数是最常见的函数类型,就是可以被正常调用的函数,一般函数执行完毕后才会继续执行下一行代码。普通函数可以接受参数并返回一个值。例如:
<script> let fun1 = () =>{ console.log("fun1 执行了") } // 调用函数 fun1() // 函数执行完毕,继续执行后续代码 console.log("其他代码继续执行") </script>
(2)回调函数
回调函数是作为参数传递给其他函数的函数,表示未来才会执行的一些功能,后续代码不会等待该函数执行完毕就开始执行了,用于在某个操作或事件完成后执行。回调函数通常用于处理异步操作,例如在异步请求完成后执行某些操作。例如:
function fetchData(callback) { // 设置一个2000毫秒后会执行一次的定时任务,基于事件自动调用,console.log先执行 setTimeout(() => { const data = 'Some data'; callback(data); }, 2000); } function processData(data) { console.log('Data received:', data); } fetchData(processData);
在这个例子中,fetchData
函数是一个模拟的异步操作,它接受一个回调函数作为参数,在异步操作完成后调用该回调函数并传递数据。processData
函数作为回调函数传递给fetchData
,当数据准备就绪时会被调用。
回调函数常用于处理事件处理、异步请求、定时器等场景,可以使代码更加灵活和可扩展,但也容易导致回调地狱(callback hell)问题,使代码难以阅读和维护。
总的来说,普通函数和回调函数都是JavaScript中常见的函数类型,普通函数用于一般的函数调用和返回值,而回调函数用于在某个操作完成后执行特定的逻辑。
2、promise基本使用(用来处理回调函数)
在JavaScript中,Promise是一种用于处理异步操作的对象,它代表了一个异步操作的最终完成或失败,并返回结果值。Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
就比如现实生活中你跟你女朋友说,5年后等我赚够500w就结婚 ==> 定义函数
- 进行中(努力赚钱,其他代码继续执行)
- 成功(赚够500w ==> 结婚)
- 失败(没赚够 ==> 分手)
下面是Promise的基本语法:
// 创建一个Promise对象 const myPromise = new Promise((resolve, reject) => { // 异步操作 if (/* 异步操作成功 */) { resolve('成功时的结果'); } else { reject('失败时的原因'); // 主动抛异常,也是执行失败 throw new Error("error message") } }); // 使用Promise对象 myPromise.then((result) => { // 当Promise状态变为fulfilled时调用,result为成功时的结果 console.log(result); }).catch((error) => { // 当Promise状态变为rejected时调用,error为失败时的原因 console.log(error); });
在上面的示例中,我们首先创建了一个Promise对象myPromise
,在Promise的构造函数中传入一个执行器函数,该函数接受两个参数resolve
和reject
,分别用于将Promise的状态从pending改变为fulfilled(成功)或rejected(失败)。
在Promise对象创建后,我们可以使用.then()
方法来处理成功状态下的结果,使用.catch()
方法来处理失败状态下的原因。
Promise的语法使得异步操作的处理变得更加直观和易于管理,避免了回调地狱(callback hell)的问题,使得代码更加清晰和可读。
此外,值得注意的是我在上面提到的axios返回的也是一个promise对象
3、async和await的使用
- 在上面的async和await简单介绍中,了解到async 返回的是一个Promise对象,await就是等待这个promise的返回结果后,再继续执行;所以promise对象肯定支持 async和await 写法
// async 和await 写法 // async标识函数后,async函数的返回值会变成一个promise对象 async function demo01() { let promise = new Promise(function (resolve, reject) { // resolve,reject 是两个函数 console.log("promise 开始执行") // resolve("promise 执行成功") // reject("promise 执行失败") // 主动抛异常,也是执行失败 throw new Error("error message") }) return promise } console.log('11111') // await 关键字,必须写在async修饰的函数中 async function demo02() { try { let res = await demo01() // 正常调用,返回promise 对象,加await 调用--》返回正常then的数据 console.log(res) } catch (err) { console.log('出错了') } } demo02() // 它会等正常执行完成才会调用 console.log('222222')
五、vue3中的vue-router
1、基本使用
(1)安装
- 在vue3中需要安装vue-router4版本的,所以安装的时候需要我们指定版本
npm install -S vue-router@4 cnpm install vue-router@4 --save
(2)注册
import {createRouter, createWebHashHistory, createWebHistory} from "vue-router"; import AboutView from "../view/AboutView.vue"; import HomeView from "../view/HomeView.vue"; import LoginView from "../view/LoginView.vue"; const routes = [ { path: '/', name: 'home', component: HomeView }, { path: '/about/:id', name: 'about', component: AboutView }, { path: '/login', name: 'Login', component: LoginView }, ] const router = createRouter({ history: createWebHistory(), routes, }) export default router
(3)main.js中使用
import {createApp} from 'vue' // 使用vue-router import router from './router' import App from './App.vue' createApp(App).use(router).mount('#app')
(4)补充 链式调用
链式调用是一种编程风格,通常用于方法调用或操作的连续执行。在很多编程语言中,链式调用通过在一个对象上连续调用多个方法来简化代码,并使代码更易读和紧凑。这种方法的返回值通常是一个对象本身,以便可以继续在其上调用其他方法。
以下是一个简单的示例,演示如何在一个对象上进行链式调用:
class Calculator: def __init__(self, value): self.value = value def add(self, x): self.value += x return self # 返回自身以支持链式调用 def multiply(self, x): self.value *= x return self # 返回自身以支持链式调用 # 创建一个 Calculator 实例并进行链式调用 result = Calculator(5).add(3).multiply(4).value print(result) # 输出:32
在上面的示例中,Calculator
类具有 add
和 multiply
两个方法,这两个方法都返回 self
,以支持链式调用。通过在实例化后直接在其上连续调用这些方法,可以在单行代码中实现多个操作。
链式调用在很多库和框架中被广泛应用,例如jQuery中的方法调用、Python中的pandas库等。
2、路由跳转
(1)普通路由跳转(声明式路由)
这种路由实现跳转的话,to中的内容目前是固定的,点击后只能切换/about对象组件(声明式路由)
- 写路径
<router-link to="/about"></router-link>
(2)编程式路由
- 通过useRouter,动态决定向那个组件切换的路由
- 在 Vue 3 和 Vue Router 4 中,你可以使用
useRouter
来实现动态路由(编程式路由) - 这里的
useRouter
方法返回的是一个 router 对象,你可以用它来做如导航到新页面、返回上一页面等操作。
(3)案例
- 通过普通按钮配合事件绑定实现路由页面跳转,不直接使用router-link标签
- HomeView.vue
<script setup> import {useRouter} from 'vue-router' let router = useRouter() function handleTo() { // 编程式路由 // 直接push一个路径 router.push('/about') // push一个带有path属性的对象 router.push({path:'/about'}) } localStorage.setItem('token','asdfa.afda.asdf') </script> <template> <h1>首页</h1> <h1>页面跳转</h1> <router-link to="/about"> <button>跳转到about-html跳</button> </router-link> <button @click="handleTo">跳转到about-js跳</button> <hr> </template> <style scoped> </style>
3、路由传参(useRoute)
(1)请求地址中以 ? 形式携带(键值对参数)
- 类似与get请求通过url传参,数据是键值对形式的
- 例如: 查看数据详情
/showDetail?hid=1
,hid=1
就是要传递的键值对参数 - 在 Vue 3 和 Vue Router 4 中,你可以使用
useRoute
这个函数从 Vue 的组合式 API 中获取路由对象。 useRoute
方法返回的是当前的 route 对象,你可以用它来获取关于当前路由的信息,如当前的路径、查询参数等。
- 例如: 查看数据详情
(2)使用带参数的路径
- 请求地址中携带,例如:/about/数据/
- 在路由配置中,可以定义带参数的路径,通过在路由配置的path中使用
:
来定义参数名称。
(3)案例
需求:切换到ShowDetail.vue组件时,向该组件通过路由传递参数。
- App.vue
<script setup type="module"> import {useRouter} from 'vue-router' //创建动态路由对象 let router = useRouter() //动态路由路径传参方法 let showDetail= (id,language)=>{ // 尝试使用拼接字符串方式传递路径参数 //router.push(`showDetail/${id}/${languange}`) /*路径参数,需要使用params */ router.push({name:"showDetail",params:{id:id,language:language}}) } let showDetail2= (id,language)=>{ /*uri键值对参数,需要使用query */ router.push({path:"/showDetail2",query:{id:id,language:language}}) } </script> <template> <div> <h1>App页面</h1> <hr/> <!-- 路径参数 --> <router-link to="/showDetail/1/JAVA">showDetail路径传参显示JAVA</router-link> <button @click="showDetail(1,'JAVA')">showDetail动态路由路径传参显示JAVA</button> <hr/> <!-- 键值对参数 --> <router-link v-bind:to="{path:'/showDetail2',query:{id:1,language:'Java'}}">showDetail2键值对传参显示JAVA</router-link> <button @click="showDetail2(1,'JAVA')">showDetail2动态路由键值对传参显示JAVA</button> <hr> showDetail视图展示:<router-view name="showDetailView"></router-view> <hr> showDetail2视图展示:<router-view name="showDetailView2"></router-view> </div> </template> <style scoped> </style>
- 修改router/index.js增加路径参数占位符
// 导入路由创建的相关方法 import {createRouter,createWebHashHistory} from 'vue-router' // 导入vue组件 import ShowDetail from '../components/ShowDetail.vue' import ShowDetail2 from '../components/ShowDetail2.vue' // 创建路由对象,声明路由规则 const router = createRouter({ history: createWebHashHistory(), routes:[ { /* 此处:id :language作为路径的占位符 */ path:'/showDetail/:id/:language', /* 动态路由传参时,根据该名字找到该路由 */ name:'showDetail', components:{ showDetailView:ShowDetail } }, { path:'/showDetail2', components:{ showDetailView2:ShowDetail2 } }, ] }) // 对外暴露路由对象 export default router;
- ShowDetail.vue 通过useRoute获取路径参数
<script setup type="module"> import{useRoute} from 'vue-router' import { onUpdated,ref } from 'vue'; // 获取当前的route对象 let route =useRoute() let languageId = ref(0) let languageName = ref('') // 借助更新时生命周期,将数据更新进入响应式对象 onUpdated (()=>{ // 获取对象中的参数 languageId.value=route.params.id languageName.value=route.params.language console.log(languageId.value) console.log(languageName.value) }) </script> <template> <div> <h1>ShowDetail页面</h1> <h3>编号{{route.params.id}}:{{route.params.language}}是世界上最好的语言</h3> <h3>编号{{languageId}}:{{languageName}}是世界上最好的语言</h3> </div> </template> <style scoped> </style>
- ShowDetail2.vue通过useRoute获取键值对参数
<script setup type="module"> import{useRoute} from 'vue-router' import { onUpdated,ref } from 'vue'; // 获取当前的route对象 let route =useRoute() let languageId = ref(0) let languageName = ref('') // 借助更新时生命周期,将数据更新进入响应式对象 onUpdated (()=>{ // 获取对象中的参数(通过query获取参数,此时参数是key-value形式的) console.log(route.query) console.log(languageId.value) console.log(languageName.value) languageId.value=route.query.id languageName.value=route.query.language }) </script> <template> <div> <h1>ShowDetail2页面</h1> <h3>编号{{route.query.id}}:{{route.query.language}}是世界上最好的语言</h3> <h3>编号{{languageId}}:{{languageName}}是世界上最好的语言</h3> </div> </template> <style scoped> </style>
4、 路由重定向
路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。 通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向。
- path 表示需要被重定向的 “原地址” ;
- redirect 表示将要被重定向到的 “新地址”
// 导入路由创建的相关方法 import {createRouter,createWebHashHistory} from 'vue-router' // 导入vue组件 import Home from '../components/Home.vue' import List from '../components/List.vue' // 创建路由对象,声明路由规则 const router = createRouter({ history: createWebHashHistory(), routes:[ { path:'/', components:{ default:Home, homeView:Home } }, { path:'/list', components:{ listView : List } }, { path:'/showAll', // 重定向 redirect :'/list' }, ] }) // 对外暴露路由对象 export default router;
5、路由嵌套--多级路由
(1)配置children属性
语法:
{ path : "/父路径", component : 父组件, children : [{ path : "子路径", component : 子组件 }] }
- 需要我们注意的是:子路径不能带 ' / '
- router/index.js
const routes = [ { path: '/backend', name: 'home', component: HomeView, children: [ //通过children配置子级路由 { path: 'index', //此处一定不要写:/news component: IndexView }, { path: 'order', component: OrderView }, { path: 'goods', component: GoodsView } ] }, { path: '/about/:id', name: 'about', component: AboutView } ]
(2)配置跳转路径
语法:
<router-link to="完整路径">内容</router-link>
-
需要注意的是这里的完整路径是从配置路由的第一层路径开始
-
HomeView.vue
<template> <div class="home"> <div class="left"> <router-link to="/backend/index"><p>首页</p></router-link> <router-link to="/backend/order"><p>订单管理</p></router-link> <router-link to="/backend/goods"><p>商品管理</p></router-link> </div> <div class="right"> <router-view></router-view> </div> </div> </template> <script> export default { name: 'HomeView', methods: {} } </script> <style scoped> .home { display: flex; } .left { height: 500px; width: 20%; background-color: aquamarine; } .right { height: 500px; width: 80%; background-color: gray; } </style>
(3)命名路由(可以简化路由的跳转)
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' //给路由命名 path:'welcome', component:Hello, } ] } ] } <!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link> <!--简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
(4)router-link的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
- 如何开启replace模式:News
6、路由守卫
(1)介绍
在 Vue 3 中,路由守卫是用于在路由切换期间进行一些特定任务的回调函数。路由守卫可以用于许多任务,例如验证用户是否已登录、在路由切换前提供确认提示、请求数据等。Vue 3 为路由守卫提供了全面的支持,并提供了以下几种类型的路由守卫:
- 全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。
- 全局后置守卫:在路由切换之后被调用,可以用于处理数据、操作 DOM 、记录日志等。
- 守卫代码的位置: 在router.js中
//全局前置路由守卫 router.beforeEach( (to,from,next) => { //to 是目标地包装对象 .path属性可以获取地址 //from 是来源地包装对象 .path属性可以获取地址 //next是方法,不调用默认拦截! next() 放行,直接到达目标组件 //next('/地址')可以转发到其他地址,到达目标组件前会再次经过前置路由守卫 console.log(to.path,from.path,next) //需要判断,注意避免无限重定向 if(to.path == '/index'){ next() }else{ next('/index') } } ) //全局后置路由守卫 router.afterEach((to, from) => { console.log(`Navigate from ${from.path} to ${to.path}`); });
(2)案例
登录案例,登录以后才可以进入home,否则必须进入login
- 定义Login.vue
<script setup> import {ref} from 'vue' import {useRouter} from 'vue-router' let username =ref('') let password =ref('') let router = useRouter(); let login = () =>{ console.log(username.value,password.value) if(username.value == 'root' & password.value == '123456'){ router.push({path:'/home',query:{'username':username.value}}) //登录成功利用前端存储机制,存储账号! localStorage.setItem('username',username.value) //sessionStorage.setItem('username',username) }else{ alert('登录失败,账号或者密码错误!'); } } </script> <template> <div> 账号: <input type="text" v-model="username" placeholder="请输入账号!"><br> 密码: <input type="password" v-model="password" placeholder="请输入密码!"><br> <button @click="login()">登录</button> </div> </template> <style scoped> </style>
- 定义Home.vue
<script setup> import {ref} from 'vue' import {useRoute,useRouter} from 'vue-router' let route =useRoute() let router = useRouter() // 并不是每次进入home页时,都有用户名参数传入 //let username = route.query.username let username =window.localStorage.getItem('username'); let logout= ()=>{ // 清除localStorge中的username //window.sessionStorage.removeItem('username') window.localStorage.removeItem('username') // 动态路由到登录页 router.push("/login") } </script> <template> <div> <h1>Home页面</h1> <h3>欢迎{{username}}登录</h3> <button @click="logout">退出登录</button> </div> </template> <style scoped> </style>
- App.vue
<script setup type="module"> </script> <template> <router-view></router-view> </template> <style scoped> </style>
- 定义routers.js
// 导入路由创建的相关方法 import {createRouter,createWebHashHistory} from 'vue-router' // 导入vue组件 import Home from '../components/Home.vue' import Login from '../components/login.vue' // 创建路由对象,声明路由规则 const router = createRouter({ history: createWebHashHistory(), routes:[ { path:'/home', component:Home }, { path:'/', redirect:"/home" }, { path:'/login', component:Login }, ] }) // 设置路由的全局前置守卫 router.beforeEach((to,from,next)=>{ /* to 要去那 from 从哪里来 next 放行路由时需要调用的方法,不调用则不放行 */ console.log(`从哪里来:${from.path},到哪里去:${to.path}`) if(to.path == '/login'){ //放行路由 注意放行不要形成循环 next() }else{ //let username =window.sessionStorage.getItem('username'); let username =window.localStorage.getItem('username'); if(null != username){ next() }else{ next('/login') } } }) // 设置路由的全局后置守卫 router.afterEach((to,from)=>{ console.log(`从哪里来:${from.path},到哪里去:${to.path}`) }) // 对外暴露路由对象 export default router;
- 启动测试
npm run dev
7、路由两种工作模式
在许多现代 JavaScript 框架(如 Vue.js 和 React)中,前端路由器用于管理应用程序的 URL,并在 URL 发生变化时加载不同的组件或页面内容。路由历史对象负责记录用户在应用程序中浏览的历史记录,以便用户可以使用浏览器的前进和后退按钮导航。
路由的工作模式一共有两种:hash模式和history模式。我们可以在创建路由对象的时候对路由的工作模式进行配置,默认是hash模式,下面是vue3中路由工作模式的书写方式:
createWebHashHistory
:hash模式。createWebHashHistory()
是Vue.js
基于 hash 模式创建路由的工厂函数。在使用这种模式下,路由信息保存在 URL 的 hash 中,使用createWebHashHistory()
方法,可以创建一个路由历史记录对象,用于管理应用程序的路由。在Vue.js
应用中,通常使用该方法来创建路由的历史记录对象。createWebHistory
:history模式。createWebHistory
是一个用于创建路由历史对象的函数,通常在 Web 应用程序的前端路由中使用。在 Vue.js 中,createWebHistory
函数通常与createRouter
一起使用,用于创建基于 HTML5 History API 的路由历史对象。
import {createRouter, createWebHashHistory, createWebHistory} from "vue-router"; const router = createRouter({ history: createWebHistory(), routes, })
(1)hash模式
- 对于一个url来说,什么是hash值? ==>
#
及其后面的内容就是hash值。 - hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器
https://192.168.1.1/api/v1/user#login
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 但是兼容性较好。
因为 #
后面的内容不会当做路径传给服务器,有更强的兼容性,不会出现项目部署到服务器上后刷新找不到路径的问题。
(2)history模式
- history模式下的路径什么就是正常访问网站路径
https://192.168.1.1/api/v1/user/login
- 地址干净,美观
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
8、路由懒加载
(1)介绍
路由懒加载是一种将路由组件按需异步加载的方式,只有当路由对应的组件需要使用时,才会动态地加载该组件对应的代码。使用路由懒加载可以优化应用程序的性能。
-
当我们把项目写完过后打包出来的JavaScript包会变得非常大,会影响性能。
-
如果把不同的组件分割成不同的代码块,当路由被访问的时候才加载相应组件,这样就会更加高效。
-
component: ()=> import("组件路径");
注意:我们引入组件的步骤被放到了component配置中,所以不需要再引入组件了。
(2)示例
在Vue Router中使用路由懒加载,我们可以通过使用import()
和动态import()
两种方式来实现
使用import()方式实现懒加载:
const Home = () => import('./views/Home.vue') const About = () => import('./views/About.vue') const routes = [ { path: '/', component: Home }, { path: '/about', component: About } ] const router = createRouter({ history: createWebHistory(), routes })
使用动态import()方式实现懒加载:
const routes = [ { path: '/', component: () => import('./views/Home.vue') }, { path: '/about', component: () => import('./views/About.vue') } ] const router = createRouter({ history: createWebHashHistory(), routes })
六、Vue3状态管理器Pinia
1、什么是Pinia?
Pinia(发音为 /piːnjʌ/
,类似于英语中的“peenya”)是最接近有效包名 piña(西班牙语中的_pineapple_)的词。 Pinia 是 Vue 的存储库,Pinia和Vuex一样都是是vue的全局状态管理器,它允许跨组件/页面共享状态。实际上,其实Pinia就是Vuex5,官网也说过,为了尊重原作者,所以取名 pinia,而没有取名 Vuex,所以大家可以直接将 pinia 比作为 Vue3 的 Vuex。
- 官网文档:Pinia 中文文档
2、对比vuex
- Pinia 同时支持 Vue2 以及 Vue3 ,这让同时使用两个版本的小伙伴更容易上手;
- Pinia 中只存在 State,getter,action,剔除掉了 Vuex 中的 Mutation 及 Module;
- Pinia 中的 action 可同时支持同步任务、异步任务;
- 更友好的支持了 TypeScript ,无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断;
- Pinia 在修改状态的时候不需要通过其他 api,如:vuex 需通过 commit,dispatch 来修改,所以在语法上比 vuex 更容易理解和使用灵活;
- 由于去除掉了 Module ,无需再创建各个模块嵌套了。Vuex 中,如果数据过多,通常会通过划分模块来进行管理,而 Pinia 中,每个 Store 都是独立的,互不影响;
- 支持服务端渲染;
3、使用步骤
(1)安装
npm install pinia
(2)创建js文件
- 在store/counter.js,写入代码,可以定义多个
import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { //1 定义变量 state: () => { return { count: 0, hobby:'篮球' } }, //2 这里面写方法,与后端交互或逻辑判断,再操作数据 actions: { increment(good_id) { // 跟后端交互--》把good_id--》真正加购物车 this.count++ }, changeHobby(hobby){ this.hobby=hobby } }, //3 getter-->获取数据 getters: { getCount(){ return this.count }, }, })
(3)main.js中使用插件
import {createPinia} from 'pinia' const pinia = createPinia() createApp(App).use(router).use(pinia).mount('#app')
(4)组件中使用
- 在组件中使用pinia的数据
import { useCounterStore} from '../store/counter'; let counter= useCounterStore() // 以后通过counter对象--》操作其中state,getter,action的东西 //Pinia 中的state、getter 和 action,我们可以假设这些概念相当于组件中的 data、 computed 和 methods。
(5)注意
-
State (状态) 在大多数情况下,state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。
-
getter函数推荐使用箭头函数,并且它将接收
state
作为第一个参数:
// getter-->获取数据 getters: { getCount:(state)=>{ return state.count }, },
- Action 相当于组件中的 method。它们可以通过
defineStore()
中的actions
属性来定义,并且它们也是定义业务逻辑的完美选择。类似 getter,action 也可通过this
访问整个 store 实例,并支持完整的类型标注(以及自动补全)。不同的是,action
可以是异步的,你可以在它们里面await
调用任何 API,以及其他 action!
七、elementui-plus
1、介绍
本节要叙述的是elementui-plus,是一个基于 Vue 3,面向设计师和开发者的组件库。旨在帮助开发者构建出现代化、美观且高效的 Web 应用程序界面。它是对 Element UI 的进一步发展,专注于提供更好的性能、更丰富的组件以及更好的开发体验。
Element Plus 是 Element UI 的一个分支和进化版本。Element UI 是一个非常受欢迎的 Vue UI 组件库,旨在为开发者提供现代、美观的界面组件。Element Plus 则是在 Element UI 的基础上进一步发展而来,专注于提供更好的性能、更丰富的组件以及更好的开发体验,同时也兼容了 Vue 3 的新特性。因此,可以说 Element Plus 是 Element UI 的下一个版本,是 Element UI 的升级和扩展。
但是另一款组件库也值得我们去学习:Ant Design Vue
2、使用
(1)安装
cnpm install element-plus --save
(2)注册
- main.js中注册
//导入element-plus相关内容 import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' createApp(App).use(router).use(pinia).use(ElementPlus).mount('#app')
(3)在组件中使用
<script setup> import { ElMessage } from 'element-plus' const open2 = () => { ElMessage({ message: '恭喜您成功了', type: 'success', }) } </script> <template> <div class="mb-4"> <el-button>Default</el-button> <el-button type="primary">Primary</el-button> <el-button type="success">Success</el-button> <el-button type="info">Info</el-button> <el-button type="warning">Warning</el-button> <el-button type="danger">Danger</el-button> </div> <div> <el-card style="max-width: 480px"> <template #header>Yummy hamburger</template> <img src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png" style="width: 100%" /> </el-card> </div> <div> <el-button :plain="true" @click="open2">Message</el-button> </div> </template> <style scoped> </style>
八、补充 代理模式
在 Python 中,代理模式是一种结构型设计模式,其目的是通过引入一个代理对象来控制对另一个对象的访问。代理通常充当客户端和实际对象之间的中间人,从而可以在访问实际对象时添加额外的功能,如权限控制、缓存、延迟加载等。
以下是一个简单的示例,演示了如何在 Python 中实现代理模式:
# 实际对象 class RealSubject: def request(self): print("RealSubject: Handling request") # 代理对象 class Proxy: def __init__(self, real_subject): self.real_subject = real_subject def request(self): if self.check_access(): self.real_subject.request() self.log_access() def check_access(self): # 检查访问权限 print("Proxy: Checking access") return True def log_access(self): # 记录访问日志 print("Proxy: Logging the time of request") # 客户端代码 real_subject = RealSubject() proxy = Proxy(real_subject) # 通过代理对象访问实际对象 proxy.request()
在这个示例中,RealSubject
是实际的对象,而 Proxy
是代理对象。代理对象在调用 request
方法时会先检查访问权限,然后再调用实际对象的 request
方法,并记录访问日志。
代理模式的优点包括:
- 安全控制:代理可以控制客户端对对象的访问权限。
- 延迟加载:代理可以延迟加载实际对象,直到客户端真正需要访问它。
- 缓存:代理可以缓存实际对象的结果,避免重复计算。
- 简化客户端:客户端可以与代理对象交互,而无需直接与实际对象交互。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?