首页 > 其他分享 >Vue入门到关门之Vue3学习

Vue入门到关门之Vue3学习

时间:2024-05-09 21:24:11浏览次数:23  
标签:Vue name 对象 vue Vue3 import ref 路由 入门

一、常用API

注意:本文项目均使用脚手架为 Vite

1、setup函数

(1)介绍

如果在项目中使用配置项API,那么写起来就和vue2的写法是一样的;但是如果在项目中写的是组合式API,那么组件中所用到的:数据、方法等等,均要配置在setup中。此外,setup() 钩子也是在组件中使用组合式 API 的入口,通常只在以下情况下使用:

  1. 需要在非单文件组件中使用组合式 API 时。
  2. 需要在基于选项式 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()getset完成的
    • 对象类型的数据:内部 求助 了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生命周期流程图

image

(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()接收到的是一个纯数值。
image

(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.requestaxios.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的构造函数中传入一个执行器函数,该函数接受两个参数resolvereject,分别用于将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 类具有 addmultiply 两个方法,这两个方法都返回 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 为路由守卫提供了全面的支持,并提供了以下几种类型的路由守卫:

  1. 全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。
  2. 全局后置守卫:在路由切换之后被调用,可以用于处理数据、操作 DOM 、记录日志等。
  3. 守卫代码的位置: 在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("组件路径");

image

注意:我们引入组件的步骤被放到了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。

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 方法,并记录访问日志。

代理模式的优点包括:

  1. 安全控制:代理可以控制客户端对对象的访问权限。
  2. 延迟加载:代理可以延迟加载实际对象,直到客户端真正需要访问它。
  3. 缓存:代理可以缓存实际对象的结果,避免重复计算。
  4. 简化客户端:客户端可以与代理对象交互,而无需直接与实际对象交互。

标签:Vue,name,对象,vue,Vue3,import,ref,路由,入门
From: https://www.cnblogs.com/xiao01/p/18183075

相关文章

  • Vue3——Vue Router
    安装vue-router依赖包npminstallvue-router@4创建router文件夹,然后在里面创建一个index.ts文件,用于定义你的路由配置//index.tsimport{createRouter,createWebHashHistory}from"vue-router";import{routes}from"./routes";//创建一个路由实例co......
  • Vue入门到关门之Vue2高级用法
    一、在vue项目中使用ref属性ref属性是Vue.js中用于获取对DOM元素或组件实例的引用的属性。通过在普通标签上或组件上添加ref属性,我们可以在JavaScript代码中使用this.$refs.xxx来访问对应的DOM元素或组件实例。放在普通标签上,通过this.$refs.名字---》取到的是do......
  • Vue入门到关门之Vue3项目创建
    一、vue3介绍1、为什么要学习vue3?vue3的变化:首先vue3完全兼容vue2,但是vue3不建议用vue2的写法;其次,vue3拥抱TypeScript,之前vue2使用的JavaScript,ts完全兼容js最后之前学的vue2是配置项api,而vue3是组合式apioptionsAPI(旧)=>compositionAPI(新),效果:代码组织更方便了,逻辑......
  • 高度自适应iframe(Vue3)
    https://juejin.cn/post/7283306395913617427<scriptsetup>import{ref,onBeforeUnmount}from'vue'defineProps({srcdoc:{type:String,default:''}})constiframe=ref()constsetIframeHeight=(......
  • springboot+vue项目
    1MyBatisPlus的分页插件是怎么生效的?体现在哪里?PaginationInnerInterceptor是通过拦截数据库操作来实现分页功能的。 MyBatisPlus的分页插件PaginationInnerInterceptor是通过拦截数据库操作来实现分页功能的。它的工作原理如下:配置分页插件:在你的SpringBoot应用......
  • 01--JS01--入门
    JavaScript01:基础入门JavaScript是一门能够运行在浏览器上的脚本语言,简称JS。首先,Javascript这个名字的由来就很有意思,不少人认为Javascript和Java貌似很像,容易想象成Java的脚本但其实两者之间没有任何关系,纯粹是商业碰瓷。既然JS是可以运行在浏览器上的脚本,并且本质上......
  • Linux从入门到精通——Centos 7.9.2009 配置国内yum源及epel源
    Centos7.9.2009配置国内yum源及epel源一、备份原有的yum源配置文件在进行任何更改之前,建议先备份原有的yum源配置文件,以防止配置过程中出现问题。可以使用以下命令备份CentOS-Base.repo文件:[root@localhost~]#cat/etc/redhat-releaseCentOSLinuxrelease7.9.2009(Cor......
  • 《最新出炉》系列入门篇-Python+Playwright自动化测试-45-鼠标操作-下篇
    1.简介鼠标为我们使用电脑提供了很多方便,我们看到的东西就可以将鼠标移动过去进行点击就可以打开或者访问内容,当页面内容过长时,我们也可以使用鼠标滚轮来实现对整个页面内容的查看,其实playwright也有鼠标操作的方法。上一篇文章中已经讲解过鼠标的部分操作了,今天宏哥在这里将剩下......
  • kettle从入门到精通 第五十七课 ETL之kettle调用存储过程
    1、之前有个同学说他使用kettle执行一坨sql语句时,kettle直接卡死掉了。我给出的建议是使用存储过程,果不其然使用存储过程顺利解决问题。今天我们一起来学习下kettle如何调用存储过程,这里基于mysql进行演示。调用存储过程有两种方法:使用步骤【调用DB存储过程】或者步骤【执行SQL脚......
  • Pacemaker入门之---了解配置故障转移
    案例说明:以下流程介绍了创建运行服务的一个Pacemaker集群,当节点上的服务变为不可用时,将其从一个节点切换到另一个节点上。通过这个步骤,您可以了解如何在双节点集群中创建服务,并可以查看在运行该服务的节点出现问题时会出现什么情况。这个示例步骤配置一个运行ApacheHTTP服......