Vue3 五天速成

news2024/11/15 12:26:56

文章目录

  • day 1
      • 1. 创建vue3工程
      • 3. 响应式数据
      • 4. 计算属性
  • day 2
      • 5. watch 监视
      • 6. watchEffect
      • 7. 标签的`ref`属性
  • day 3
      • 8. 回顾TS中的接口_泛型_自定义类型
      • 9. `props`的使用
      • 10. 生命周期
      • 11. 自定义Hooks
      • 12. 路由 基本切换效果
      • 13. 路由 两个注意点
      • 14. 路由 路由器的工作模式
      • 15. 路由 to的两种写法
      • 16. 路由 命名路由
      • 17. 路由 嵌套路由
      • 18. 路由 query 参数
      • 19. 路由 params 参数
      • 20. 路由 props配置
      • 21. 路由 replace属性
      • 22. 路由 编程式路由导航
      • 23. 路由 重定向
  • day 4
      • 24. pinia 选项式写法
      • 25. pinia 组合式写法
  • day 5
      • 26. 组件通信_props
      • 27. 组件通信_custom-event
      • 28. 组件通信_mitt
      • 29. 组件通信_v-model
      • 30. 组件通信_`$attrs`
      • 31. 组件通信_`$refs`与`$parent`
      • 32. 组件通信_provide-inject
      • 32. 组件通信_slot默认插槽
      • 33. 组件通信_slot具名插槽
      • 34. 组件通信_slot作用域插槽
      • 35. shallowRef 和 shallowReactive
      • 36. readonly 和 shallowReadonly
      • 37. toRaw 和 markRaw
      • 38. customRef
      • 39. Teleport
      • 40. Suspense
      • 41. 全局api转化为应用对象
      • 42. 其他

day 1

1. 创建vue3工程

相关代码如下:

## 创建vue工程
npm create vue@lastest

## 安装node_modules
npm install // npm i

创建工程完毕之后,进入文件夹可以发现有如下文件,下图是文件介绍:

入口文件介绍:

这里main.ts是与index.html建立联系的;

通过.mount('#app')这样 main.ts就与index.html建立起了联系;

其中main.ts的代码格式如下:

<template>
    <div>
        <h1>你好</h1>
    </div>
</template>

<script setup lang="ts">
    // js或者ts
</script>

<style scoped>
    /* 样式 */
</style>

App.vue的代码格式如下:

<template>
    <div class="app">
        <h1>你好</h1>
    </div>
</template>

<script setup lang="ts">
    // js或者ts
</script>

<style scoped>
    /* 样式 */
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

网页调试vue可以在chrome应用商店search vue.js devtools下载安装就好;

你好

### 2. 选项式API和组合式API

感觉vue2和vue3的主要不同就在于vue文件中script部分;其中vue2是选项式OptionsAPI的,vue3是组合式Composition的;

选项式API相关代码如下

注意,这里并没有setup

<script lang="ts">
    export default {
	    // 定义组件名字
        name: 'Person',
        // 定义数据
        data(){
            return {
                name: '张三',
                age: 18,
                tel: '138888888'
            }
        },
        //  定义方法
        methods:{
            changeName(){
                this.name = 'zhang-san'
            },
            changeAge(){
                this.age += 1
            },
            showTel(){
                alert(this.tel)
            }
        },
    }
</script>

组合式相关代码如下

注意,这里的setup比beforecreate执行得还要早,setup的返回值也可以是渲染函数,data,method,setup可以同时存在,setup不可以读取data中的数据,反之则可以,因为setup是执行得最早的;

<script lang="ts">
    export default {
        name: 'Person',
        setup(){
            // 数据
            let name = '张三' // 此时name,age,tel 不是响应式的    应该改为ref或者reactive
            let age = 18    // 此时name,age,tel 不是响应式的  应该改为ref或者reactive
            let tel = '138888888'   // 此时name,age,tel 不是响应式的  应该改为ref或者reactive
            
            // 方法
            function changeName(){
                name = 'zhang-san'
            }
            function changeAge(){
                age += 1
            }
            function showTel(){
                alert(tel)
            }
            // 把数据和方法交出去
            return {name, age, changeAge, changeName, showTel}
        }
    }
</script>

setup语法糖简化后代码如下:

<script lang="ts">
    export default {
        name: 'Person',
    }
</script>

<script setup lang="ts">
    // 数据
    let name = '张三' // 此时name,age,tel 不是响应式的    应该改为ref或者reactive
    let age = 18    // 此时name,age,tel 不是响应式的  应该改为ref或者reactive
    let tel = '138888888'   // 此时name,age,tel 不是响应式的  应该改为ref或者reactive

    // 方法
    function changeName(){
        name = 'zhang-san'
    }
    function changeAge(){
        age += 1
    }
    function showTel(){
        alert(tel)
    }
    // 此时不需要return 自动提交
</script>

3. 响应式数据

响应式数据创建有两种方法,一种是ref,一种是reactive

ref定义基本类型的数据

使用的相关代码如下:

<template>
    <div class="app">
        <h2>姓名:{{ name }}</h2> // 利用大括号包起来的是不需要.value的
        <h2>年龄:{{ age }}</h2> // 利用大括号包起来的是不需要.value的
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="showTel">查看联系方式</button>
    </div>
</template>

<script setup lang="ts">
    import {ref} from 'vue'

    let name = ref('张三')
    let age = ref(18)   
    let tel = ref('138888888')
	// 在ref包裹住后,需要操作值都需要.value

    function changeName(){
        name.value = 'zhang-san'
    }
    function changeAge(){
        age.value += 1
    }
    function showTel(){
        alert(tel.value)
    }

这里值用ref包裹起来之后,变量会变成一个RefImpl类的数据,这时要修改值直接对变量操作是无意义的,我们需要对.value进行操作;这里要注意的是,ref也可以定义对象类的响应式数据,实现原理是先用ref包裹,再用reactive包裹,即取值还是需要用value

reactive定义对象类型的数据

使用的相关代码如下:

<template>
    <div class="app">
        <h2>姓名:{{ student.name }}</h2>
        <h2>年龄:{{ student.age }}</h2>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person',
    }
</script>

<script setup lang="ts">
    import {reactive} from 'vue'

    let student = reactive({name:'张三', age:18})

    function changeName(){
        student.name = 'zhang-san'
    }
    function changeAge(){
        student.age += 1
    }

</script>

<style scoped>
    /* 样式 */
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
    
    button {
        margin: 10px;
    }
</style>

reactive包起来后,对象变成了一个Proxy的对象,原对象在Proxy里面的target中;这里的reactive是深层次的,只能定义对象类型的响应式数据;

refreactive的对比

这里要注意的是,重新分配reactive的对象时,使用Object.assign,但是如果是利用ref定义的对象类数据,我们是可以直接进行替换的;

对响应式数据进行解构,toReftoRefs

let person = reactive({
	name: '张三',
	age: 18
})

let {name, age} = person
// let name = person.name
// let age = person.age

let {name, age} = toRefs(person) //把reactive对象里面的每一组对象转化为ref
// let name = toRef(person, 'name')
// let age = toRef(person, 'age')

4. 计算属性

v-bind 是 单向绑定,数据流向页面;v-model 是 双向绑定,页面也可以流向数据;

计算属性 computed 是只要响应式变量出现了变化,就随之变化;

使用例子如下:

<template>
    <div class="app">
        姓名:<input type="text" v-model="name"> <br>
        年龄:<input type="text" v-model="age"> <br>
        information: {{ information }}
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person',
    }
</script>

<script setup lang="ts">
    import {ref, computed} from 'vue'

    let name = ref('zhangsan')
    let age = ref(18)

    let information = computed(()=>{
        return name.value + age.value
    })

</script>

<style scoped>
    /* 样式 */
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

computed是用来区别于方法的,使用computed得到的属性是没有缓存的,而且computed得到的属性是不可修改的;如果需要修改,则需要使用以下方法:

<template>
    <div class="app">
        姓名:<input type="text" v-model="name"> <br>
        年龄:<input type="text" v-model="age"> <br>
        information: {{ information }} <br>
        <button @click="changeInformation">修改information为lisi</button>
        
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person',
    }
</script>

<script setup lang="ts">
    import {ref, computed} from 'vue'

    let name = ref('zhangsan')
    let age = ref(18)

    let information = computed({
        get(){
            return name.value + '-' + age.value
        },
        set(val){
            const [s1, s2] = val.split('-')
            name.value = s1
            age.value = parseInt(s2)
        }
    })

    function changeInformation(){
        information.value = 'lisi-18'
    }

</script>

<style scoped>
    /* 样式 */
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

day 2

5. watch 监视

情况1:监视ref定义的基本类型数据

停止监视只需要调用watch函数的返回值就可以;

<template>
    <div>
        <h5>情况1:监视【ref】定义的值是【基本类型】的数据</h5>
        sum : {{ sum }} <br>
        <button @click="changeSum"> sum + 1</button> <br>
        <hr>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {ref, watch} from 'vue'

    let sum = ref(1)

    function changeSum(){
        sum.value += 1
    }

    // 这里的sum是不需要添加.value的,返回值就是停止监视的函数
    const stopwatch = watch(sum, (newVal, oldVal)=>{
        console.log('sum变化了', newVal, oldVal)
        // 如果sum的最新值大于等于10则停止监视
        if(newVal >= 10 ){
            stopwatch()
        }
    })
</script>

<style scoped>
</style>

情况2:监视ref定义的对象类型数据

这里要注意的是:若修改的是ref定义的对象中的属性,newVal和oldVal都是新值,因为是同一个对象;若修改的是ref定义的整个对象,newVal是新值,oldVal是旧值,因为不是同一个对象;

<template>
    <div>
        <h5>情况2:监视【ref】定义的值是【对象类型】的数据</h5>
        姓名: {{ person.name }} <br>
        年龄: {{ person.age }} <br>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="changeFull">修改整个人</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {ref, watch} from 'vue'

    let person = ref({
        name: '张三',
        age: 18
    })

    function changeName (){
        person.value.name = '李四'
    }

    function changeAge (){
        person.value.age = 19
    }

    function changeFull (){
        person.value = {name: '王五', age: 20}
    }

    // 监视【ref】定义的【对象类型数据】,监视的是对象的地址值,若想要监视
    // 对象的内部属性的变化,需要手动开启深度监视deep

    // 若修改的是ref定义的对象中的属性,newVal和oldVal都是新值,因为是同一个对象
    // 若修改的是ref定义的整个对象,newVal是新值,oldVal是旧值,因为不是同一个对象

    // watch的第一个参数是:被监视的数据
    // watch的第二个参数是:监视的回调
    // watch的第三个参数是:配置对象(deep, immediate ... )
    watch(person, (newVal, oldVal)=>{
        console.log('person变化了', newVal, oldVal)
    }, {deep: true})
</script>

<style scoped>
    button {
        margin: 10px;
    }
</style>

情况3:监视reactive定义的对象类型数据

很简单,只需要把ref定义的对象改为reactive定义的对象,然后在修改整个对象的时候使用Object.assign替换就可以;

<template>
    <div>
        <h5>情况3:监视【reactive】定义的值是【对象类型】的数据</h5>
        姓名: {{ person.name }} <br>
        年龄: {{ person.age }} <br>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="changeFull">修改人</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {reactive, watch} from 'vue'

    let person = reactive({
        name: '张三',
        age: 18
    })

    function changeName (){
        person.name = '李四'
    }

    function changeAge (){
        person.age = 19
    }

    function changeFull (){
        // 并没有修改地址值;
        Object.assign(person, {name: '王五', age: 20})
    }

    // 监视【reactive】定义的对象类型数据,默认是开启深度监视的,而且深度是关不掉的
    watch(person, (newVal, oldVal)=>{
        console.log('person变化了', newVal, oldVal)
    })

    
</script>

<style scoped>
    button {
        margin: 10px;
    }
</style>

情况4:监视ref或者reactive定义的对象类型中的某个属性

<template>
    <div>
        <h5>情况4:监视【ref或reactive】定义的【对象类型】某个属性</h5>
        姓名: {{ person.name }} <br>
        年龄: {{ person.age }} <br>
        车辆:{{ person.car.c1 }}, {{ person.car.c2 }} <br>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="changeC1">修改c1</button>
        <button @click="changeC2">修改c2</button>
        <button @click="changeCar">修改car</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {reactive, watch} from 'vue'

    let person = reactive({
        name: '张三',
        age: 18,
        car:{
            c1: 'asd',
            c2: 'das'
        }
    })

    function changeName (){
        person.name = '李四'
    }

    function changeAge (){
        person.age = 19
    }

    function changeC1(){
        person.car.c1 = 'qqq'
    }
    function changeC2(){
        person.car.c2 = 'www'
    }
    function changeCar(){
        person.car = {c1:'yyy', c2:'jjj'}
    }

    // 监视响应式对象中的某个属性,且该变量是基本类型的,要写成函数式
    /* watch(()=>person.name, (newVal, oldVal)=>{
        console.log('person.name变化了', newVal, oldVal)
    }) */

    // 监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,推荐写函数
    watch(()=>person.car, (newVal, oldVal)=>{
        console.log('person.car变化了', newVal, oldVal)
    }, {deep:true})

    
</script>

<style scoped>
    button {
        margin: 10px;
    }
</style>

情况5:监视多个数据

<template>
    <div>
        <h5>情况4:监视【ref】定义的值是【对象类型】的数据</h5>
        姓名: {{ person.name }} <br>
        年龄: {{ person.age }} <br>
        车辆:{{ person.car.c1 }}, {{ person.car.c2 }} <br>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="changeC1">修改c1</button>
        <button @click="changeC2">修改c2</button>
        <button @click="changeCar">修改car</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {reactive, watch} from 'vue'

    let person = reactive({
        name: '张三',
        age: 18,
        car:{
            c1: 'asd',
            c2: 'das'
        }
    })

    function changeName (){
        person.name = '李四'
    }

    function changeAge (){
        person.age = 19
    }

    function changeC1(){
        person.car.c1 = 'qqq'
    }
    function changeC2(){
        person.car.c2 = 'www'
    }
    function changeCar(){
        person.car = {c1:'yyy', c2:'jjj'}
    }

    // 此时newVal和oldVal是数组与前面的对应
    watch([()=>person.name, ()=>person.age], (newVal, oldVal)=>{
        console.log('变化了', newVal, oldVal)
    })

    
</script>

<style scoped>
    button {
        margin: 10px;
    }
</style>

6. watchEffect

相较于watch,watchEffect不需要指定监视对象,而是响应式的追踪对象;

<template>
    <div>
        <h5>情况4:监视【ref】定义的值是【对象类型】的数据</h5>
        姓名: {{ person.name }} <br>
        年龄: {{ person.age }} <br>
        车辆:{{ person.car.c1 }}, {{ person.car.c2 }} <br>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="changeC1">修改c1</button>
        <button @click="changeC2">修改c2</button>
        <button @click="changeCar">修改car</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {reactive, watch, watchEffect} from 'vue'

    let person = reactive({
        name: '张三',
        age: 18,
        car:{
            c1: 'asd',
            c2: 'das'
        }
    })

    function changeName (){
        person.name = '李四'
    }

    function changeAge (){
        person.age += 1
    }

    function changeC1(){
        person.car.c1 = 'qqq'
    }
    function changeC2(){
        person.car.c2 = 'www'
    }
    function changeCar(){
        person.car = {c1:'yyy', c2:'jjj'}
    }

    // 此时newVal和oldVal是数组与前面的对应
    /* watch([()=>person.name, ()=>person.age], (newVal, oldVal)=>{
        let [newname, newage] = newVal
        if( newage > 23 ){
            console.log('发送请求')
        }
    }) */

    // 如果采用watchEffect,全自动的watch;
    watchEffect(()=>{
        if( person.age > 23 ){
            console.log('发送请求')
        }
    })

</script>

<style scoped>
    button {
        margin: 10px;
    }
</style>

7. 标签的ref属性

利用refdocument.getElementById('')的区别在于,前者是局部的,不会受到整体的干扰;

<template>
    <h2> 北京 </h2>
    <h2 ref="title"> 师范 </h2>
    <h2> 大学 </h2>
    <button @click="output"> 点击一下输出样式 </button>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {ref, defineExpose} from 'vue'

    // 这里变量名和template中的ref中的变量名对应上了
    let title = ref()

    function output(){
        console.log(title.value)
    }
    // 在这里可以使调用该模块的模块得到该模块的内容
    defineExpose({title})
</script>

<style scoped>
    /* 这里的scoped是局部样式,防止和子文件样式出现重复而全部修改
    无脑加上就好 */
</style>

day 3

8. 回顾TS中的接口_泛型_自定义类型

一般在文件目录src下面有一个types文件夹,types中有一个index.ts文件可以直接导入(导入过程不需要写文件)

这里文件表示泛型和接口的意思,一般是结合后端使用,避免缺失参数;

// 定义一个接口,用于限制person对象的具体属性 string,number小写
// 使用export暴露
export interface PersonInter {
    id: string,
    name: string,
    // ?问号表示可有可无
    age?: number
}


// 一个自定义类型
export type Persons = Array<PersonInter>
// 也可以这样写 export type Persons = PersonInter[]
<template>
    <div class="app">

    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    // PersonInter是类型,必须使用type前置
    import {type PersonInter, type Persons} from '@/types'
	import {reactive} from 'vue'
	
    // 单个
    let person:PersonInter = {id: 'asdasdas01', name: '张三', age: 18}

    // 数组 第一种形式   尖括号表示的是泛型:即符合规范
    let persons:Array<PersonInter> = [
        {id: 'asdasdas01', name: '张三', age: 18},
        {id: 'asdasdas02', name: '李四', age: 20},
        {id: 'asdasdas03', name: '王五', age: 33}
    ]
	// 数组 第二种形式 这里结合index.ts文件
    let personList:Persons = [
        {id: 'asdasdas01', name: '张三', age: 18},
        {id: 'asdasdas02', name: '李四', age: 20},
        {id: 'asdasdas03', name: '王五', age: 33}
    ]

	let personsReactive = reactive<Persons>([
        {id: 'asdasdas01', name: '张三', age: 18},
        {id: 'asdasdas02', name: '李四', age: 20},
        {id: 'asdasdas03', name: '王五', age: 33}
    ])
    
</script>

<style scoped>
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

9. props的使用

将父组件中的数据传给子组件

vuehtml标签属性前面加冒号表示表达式

<h2 a="1+1" :b="1+1"></h2>

父组件如下:

<template>
    <Person a="哈哈" :list="persons"/>
</template>

<script lang="ts">
    export default {
        name: 'App'
    }
</script>

<script lang="ts" setup>
    import Person from './components/Person.vue'
    import {reactive} from 'vue'
    import {type Persons} from '@/types'

    let persons = reactive<Persons>([
        {id: 'asdasdas01', name: '张三', age: 18},
        {id: 'asdasdas02', name: '李四', age: 20},
        {id: 'asdasdas03', name: '王五', age: 33}
    ])

</script>

<style scoped>
</style>

子组件如下:

<template>
    <div class="app">
        <ul>
            <li v-for="a in x.list" :key="a.id">{{ a.name }} - {{ a.age }}</li>
        </ul>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {type Persons } from '@/types';
    // 下面可以不用引入defineProps,withDefaults
    import {defineProps, withDefaults} from 'vue'

    // 接收a
    // defineProps(['a'])

    // 接收a和list,同时将props保存起来
    // let x = defineProps(['a', 'list'])
    // x = {a:'哈哈'}

    // 接收list+限制类型
    // let x = defineProps<{list:Persons}>()

    // 接收list + 限制类型 + 限制必要性 + 指定默认值
    // list后面加?表示父组件可传可不传, 第二个list后面需要跟函数
    let x = withDefaults(defineProps<{list?:Persons}>(),{
        list:()=>[{id:'adasdasdaszxc01', name:'kang', age:20}]
    })
    
    
</script>

<style scoped>
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

10. 生命周期

组件的生命周期有四个阶段:创建,挂载,更新,销毁;

钩子:生命周期函数 (阶段前,阶段完毕) 钩子一共有8个

<template>
    <div class="app">
        {{ message }}
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import {onBeforeMount, onMounted, onBeforeUpdate,
         onUpdated, onBeforeUnmount, onUnmounted} from 'vue'

    let message = '哈哈'
    
    onBeforeMount(()=>{
        console.log('ok')
    })

    onUnmounted(()=>{
        console.log('一般来说需要搭配v-if')
    })
    
</script>

<style scoped>
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

11. 自定义Hooks

Hooks是组合式功能的体现,其创建过程类似于8节中在文件目录src下创建一个hooks的文件夹,里面存放useXxxx.ts文件,实例如下:

useAdd.ts

import { computed } from '@vue/reactivity'
import {ref, onMounted} from 'vue'

export default function(){
    let sum = ref(0)
    let bigSum = computed(()=>{
        return sum.value * 10
    })

    function add(){
        sum.value += 1
    }

    onMounted(()=>{
        sum.value += 1 
    })
    return {sum, add, bigSum}
}

useDog.ts

import {reactive, onMounted} from 'vue'
import axios from 'axios'

export default function(){
    let dogList = reactive([
        'https://images.dog.ceo/breeds/pembroke/n02113023_4136.jpg'
    ])
    
    // axios 的 实例用法1
    /* function addDog(){
        axios.get('https://dog.ceo/api/breed/pembroke/images/random').then(
            response => {dogList.push(response.data.message)},
            error => {console.log(error)}
        )
    } */

    // axios 的 实例用法2
    async function addDog() {
        try {
            let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
            dogList.push(result.data.message)
        } catch (error) {
            console.log(error)
        }
    }

    onMounted(()=>{
        addDog()
    })

    return {dogList, addDog}
}

这样组件中可以直接应用hooks文件下的内容:

<template>
    <div class="app">
        <h2> sum: {{ sum }};    bigSum: {{ bigSum }}</h2>
        <button @click="add"> 点我sum+1</button>
        <hr>
        <img v-for="(dog,index) in dogList" :src="dog" :key="index"> <br>
        
        <button @click="addDog"> 点我再添加一只狗 </button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

<script setup lang="ts">
    import useAdd from '@/hooks/useAdd'
    import useDog from '@/hooks/useDog'

    const {sum, add, bigSum} = useAdd()
    const {dogList, addDog} = useDog()
</script>

<style scoped>
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }

    img {
        height: 150px;
        margin-right: 10px;
    }
</style>

一般在文件目录src下面有一个types文件夹,types中有一个index.ts文件可以直接导入(导入过程不需要写文件)

12. 路由 基本切换效果

路由和路由器不一样

路由类似于键值对,route

路由器类似于管理路由,router

路由的创建与Hooks和TS接口类似,其创建过程类似于8节中在文件目录src下创建一个router的文件夹,里面存放index.ts文件(无需导入文件名),实例如下:

index.ts 实例如下:

// 创建一个路由器,并暴露出去


// 第一步:引入createRouter
import {createRouter, createWebHistory} from 'vue-router'

// 第二步:引入可能要呈现的组件
import Home from '@/components/Home.vue'
import About from '@/components/About.vue'
import News from '@/components/News.vue'


// 第三步:创建路由器
const router = createRouter({
    // history表示路由的工作模式
    history: createWebHistory(),
    routes:[// 一个个路由规则
        {
            path: '/home',
            component: Home
        },
        {
            path: '/news',
            component: News
        },
        {
            path: '/about',
            component: About
        }
    ]
})

export default router

在这里我们只是创建了路由,于此同时,我们需要把路由加载到项目中,所以接下来我们需要在main.ts文件中操作;

// 引入createApp 用于创建应用
import { createApp } from 'vue'
// 引入app根组件
import App from './App.vue'
// 引入路由器
import router from './router'
// 创建一个应用
const app = createApp(App)
// 使用路由器
app.use(router)
// 挂载整个应用到app容器中
app.mount('#app')

为了在App.vue引入组件,我们还需要做一些操作,使用RouterView, RouterLink

<template>
    <h2> Vue 路由测试 </h2>
    <!-- 导航区 -->
    <div class="navigate">
        <RouterLink to="/home">主页</RouterLink>
        <RouterLink to="/news">新闻</RouterLink>
        <RouterLink to="/about">关于</RouterLink>
    </div>
    <!-- 展示区 -->
    <div class="main_content">
        <RouterView/>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'App'
    }
</script>

<script lang="ts" setup>
    import {RouterView, RouterLink} from 'vue-router'

</script>

<style scoped>
    h2 {
        background: skyblue;
        text-align: center;
    }

    div.navigate {
        text-align: center;
    }
    div.navigate a{
        text-align: center;
        margin: 50px;
    }

    div.main_content{
        text-align: center;
        height: 500px;
    }
</style>

就完毕了,这里要用RouterLink替换a标签,并把href改成to,用RouterView表示component的插入位置;

13. 路由 两个注意点

路由组件一般放在pages或者views文件夹,一般组件通常放在components文件夹;

通过点击导航,视觉上效果消失了的组件,默认是被销毁掉的,需要的时候再去挂载;

14. 路由 路由器的工作模式

路由器有两种工作模式,history和hash,前端一般用history,后端一般用hash;

15. 路由 to的两种写法


%% 第一种: %%
<RouterLink to="/home" active-class="active">主页</RouterLink>

%% 第二种: %%:
<RouterLink :to="{path='/home'}" active-class="active">主页</RouterLink>

这里再介绍一下 active-class ,由于RouterLink在渲染的时候会变成a标签,所以样式可以类似于设置如下:


    a.active {
        text-align: center;
        margin: 50px;
        color: red;
    }

16. 路由 命名路由

可以在路由的index.ts文件中添加添加名字:

// 创建一个路由器,并暴露出去


// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'

// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'


// 第三步:创建路由器
const router = createRouter({
    // 路由的工作模式:history模式 不带#号 优先
    history: createWebHistory(),
    // 路由的工作模式:hash模式 带#号
    // history: createWebHashHistory(),
    routes:[// 一个个路由规则
        {
            name: 'zhuye',
            path: '/home',
            component: Home
        },
        {
            name: 'xinwen',
            path: '/news',
            component: News
        },
        {
            name: 'guanyu',
            path: '/about',
            component: About
        }
    ]
})

export default router

17. 路由 嵌套路由

子级不用加斜杠,但是to的时候需要完整的使用\news\detail

// 创建一个路由器,并暴露出去

// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'

// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'
import Detail from '@/views/Detail.vue'

// 第三步:创建路由器
const router = createRouter({
    // 路由的工作模式:history模式 不带#号 优先
    history: createWebHistory(),
    // 路由的工作模式:hash模式 带#号
    // history: createWebHashHistory(),
    routes:[// 一个个路由规则
        {
            name: 'zhuye',
            path: '/home',
            component: Home
        },
        {
            name: 'xinwen',
            path: '/news',
            component: News,
            children:[
                {
                    path: 'detail', //子级不用写斜杠
                    component: Detail,
                }
            ]
        },
        {
            name: 'guanyu',
            path: '/about',
            component: About
        }
    ]
})

export default router

18. 路由 query 参数

在传参数给组件之前,我们需要给接收参数的组件添加一个Hooks:useRoute

<template>
    <ul class="news-list">
        <li>编号:{{ route.query.id }}</li>
        <li>标题:{{ route.query.title }}</li>
        <li>内容:{{ route.query.content }}</li>
    </ul>
</template>

<script setup lang="ts">
    import {useRoute} from 'vue-router'
    import {toRefs} from 'vue'
    
    let route = useRoute()
    
	// 如果需要解构
	let {query} = toRefs(route)

</script>

<style scoped>
</style>

这里在RouterLink中添加query参数:

        <ul>
            <li v-for="news in NewsList" :key="news.id">
                <!-- 第一种写法 -->
                <!-- <RouterLink :to="`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`">{{ news.title }}</RouterLink> -->
                <!-- 第二种写法 -->
                <RouterLink :to="{
                    path: '/news/detail',
                    query: {
                        id: news.id,
                        title: news.title,
                        content: news.content
                    }
                }">
                    {{ news.title }}
                </RouterLink>

            </li>
        </ul>

19. 路由 params 参数

首先需要在router中设置占位:就是在path中添加/:

// 创建一个路由器,并暴露出去

// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'

// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'
import Detail from '@/views/Detail.vue'


// 第三步:创建路由器
const router = createRouter({
    // 路由的工作模式:history模式 不带#号 优先
    history: createWebHistory(),
    // 路由的工作模式:hash模式 带#号
    // history: createWebHashHistory(),
    routes:[// 一个个路由规则
        {
            name: 'zhuye',
            path: '/home',
            component: Home
        },
        {
            name: 'xinwen',
            path: '/news',
            component: News,
            children:[
                {
                    name: 'xiangqing',
                    path: 'detail/:id/:title/:content?', // 需要占位 ?代表可传可不传
                    component: Detail,
                }
            ]
        },
        {
            name: 'guanyu',
            path: '/about',
            component: About
        }
    ]
})

export default router

params只能使用routename,而不能使用path,同时传参数只能使用基本类型,而不能使用数组和对象;

        <ul>
            <li v-for="news in NewsList" :key="news.id">
                <!-- 第一种写法 -->
                <!-- <RouterLink :to="`/news/detail/${news.id}/${news.title}/${news.content}`">{{ news.title }}</RouterLink> -->
                <!-- 第二种写法 -->
                <RouterLink :to="{
                    name: 'xiangqing',
                    params: {// 只能传基本类型,不能是对象和数组
                        id: news.id,
                        title: news.title,
                        content:news.content,
                    }
                }">
                    {{ news.title }}
                </RouterLink>

            </li>
        </ul>

20. 路由 props配置

props:true 布尔值写法,只能搭配params使用,path需要占位

props(route){return route.query} 函数式写法,其中route可以操作params和query

props:{id:xx,title:xx,content:xx}

其原理就相当于在<Detail/> 中 添加了 <Detail id=xx title=xx content=xx/>

21. 路由 replace属性

类似于浏览器的前进后退机制;

    <div class="navigate">
        <RouterLink replace to="/home" active-class="active">主页</RouterLink>
        <RouterLink replace :to="{name:'xinwen'}" active-class="active">新闻</RouterLink>
        <RouterLink replace to="/about" active-class="active">关于</RouterLink>
    </div>

直接在RouterLink后面添加replace,不会留下记录;

22. 路由 编程式路由导航

可以做到不点标签直接跳转的效果,脱离RouterLink实现跳转;router.push() router.replace() 括号中to能怎么写,括号就能怎么写;

setTimeout(()=>{
	// 在此处执行跳转
}, 3000) // 3秒后执行函数

实例代码如下:

import {onMounted} from 'vue'
import { useRouter } from 'vue-router';

const router = useRouter()

onMounted(()=>{
	setTimeout(()=>{
		router.push('/news')
	}, 3000)
})

按钮点击实例代码如下:

<template>
    <div class="news">
        <!-- 导航区 -->
        <ul>
            <li v-for="news in NewsList" :key="news.id">
                <button @click="showContent(news)">点击获取详情</button>
                <RouterLink :to="{
                    name: 'xiangqing',
                    params: {// 只能传基本类型,不能是对象和数组
                        id: news.id,
                        title: news.title,
                        content:news.content,
                    }
                }">
                    {{ news.title }}
                </RouterLink>

            </li>
        </ul>
        <!-- 展示区 -->
        <div class="news-content">
            <RouterView></RouterView>
        </div>
    </div>
</template>

<script setup lang="ts">
    import type path from 'path';
    import {reactive} from 'vue'
    import {RouterView, RouterLink, useRouter} from 'vue-router'

    interface News{
        id: string,
        title: string,
        content: string,
    }

    const NewsList = reactive<News[]>([
        {id: 'asdasd01', title:'新闻阿斯达所多01', content: '在成长速度还01'},
        {id: 'asdasd02', title:'新闻阿斯达所多02', content: '在成长速度还02'},
        {id: 'asdasd03', title:'新闻阿斯达所多03', content: '在成长速度还03'},
        {id: 'asdasd04', title:'新闻阿斯达所多04', content: '在成长速度还04'},
    ])

    const router = useRouter()

    function showContent(news:News){
        router.push(
            {
                name: 'xiangqing',
                params: {// 只能传基本类型,不能是对象和数组
                    id: news.id,
                    title: news.title,
                    content:news.content,
                }
            })
    }
</script>

<style scoped>
    div.news {
        background-color: yellow;
        height: 500px;
    }

    div.news-content {
        background-color: aqua;
        height: 300px;
        width: 200px;
    }
</style>

23. 路由 重定向

自动切换url,在router文件中补充规则:

{
	path: '/',
	redirect: '/home'
}

day 4

24. pinia 选项式写法

pinia环境首先需要安装

npm install pinia

安装之后,pinia的创建与Hooks和TS接口类似,其创建过程类似于8节中在文件目录src下创建一个store的文件夹,里面存放对应components中的文件实例如下:

在main.ts文件中需要引入pinia

import {createApp} from 'vue'
import App from './App.vue'
import {createPinia} from 'pinia'
import router from './router'

const app = createApp(App)
const pinia = createPinia()

app.use(pinia)
app.use(router)

app.mount('#app')

Count.vue如下:

<template>
    <div class="count">
        <h2>当前求和为: {{ sum }}, bigsum:{{ bigSum }}, smallsum:{{ smallSum }}</h2>
        <!-- 这里.number表示value数值后面的符号为单位 -->
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="add">加</button>
        <button @click="minus">减</button>
    </div>
</template>

<script  lang="ts">
    export default {
        name: 'Count'
    }
</script>

<script setup lang="ts">
    import {useCountStore} from '@/store/Count'
    import {storeToRefs} from 'pinia'

    let countStore = useCountStore()
    // storeToRefs只会关注store中的数据
    let {sum, n, bigSum, smallSum} = storeToRefs(countStore)

    // 以下两种方式都能拿到state中的数据
    // console.log(countStore.sum)
    // console.log(countStore.$state.sum)

    // 方法
    function add(){
        // 第一种修改方式
        // countStore.sum += countStore.n

        // 第二种修改方式
        // countStore.$patch({
        //     sum: 100
        // })

        // 第三种修改方式 复杂内容可以放到store中的actions中
        countStore.increment()
    }
    function minus(){
        sum.value -= n.value
    }

</script>

<style scoped>
    .count {
        background-color: skyblue;
        padding: 10px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
        margin-bottom: 20px;
    }
    select, button {
        margin: 0 10px;
        height: 25px;
    }
</style>

Count.ts 如下:

import {defineStore} from 'pinia'
import {ref} from 'vue'


export const useCountStore = defineStore('Count', {
	// 写函数
    actions:{
        increment(){
            this.sum += this.n
        }
    },
    // 真正存储数据的地方
    state(){
        return {
            sum: ref(0),
            n: ref(1),
        }
    },
    // 类似于vue中的computed属性
    getters:{
        bigSum: state=>state.sum*10,
        smallSum():number{
            return this.sum - 1000
        }
    }
})

LoveTalk.vue如下:

<template>
    <div class="talk">
        <button @click="getMessage"> 点击获取一句土味情话</button>
        <ul>
            <li v-for="talk in talkList" :key="talk.id">{{ talk.content }}</li>
        </ul>
    </div>
</template>

<script  lang="ts">
    export default {
        name: 'LoveTalk'
    }
</script>

<script setup lang="ts">
    import {useLoveStore} from '@/store/LoveTalk'
    import {storeToRefs} from 'pinia'

    let loveStore = useLoveStore()
    let {talkList} = storeToRefs(loveStore)

    loveStore.$subscribe((mutate, state)=>{
        // mutate是变化的东西,state是后面的状态
        console.log('lovestore保存的数据发生了变化!', mutate, state)
        // 存储在本地的talkList变量中,由于talkList只能接受字符串数据,所以需要JSON.stringify
        localStorage.setItem('talkList', JSON.stringify(state.talkList))
    })

    async function getMessage(){
        loveStore.getTalk()
    }

</script>

<style scoped>
    .talk {
        background-color: orange;
        padding: 10px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }

</style>

LoveTalk.ts如下:

import {defineStore} from 'pinia'
import {nanoid} from 'nanoid' // 生成id
import axios from 'axios'

export const useLoveStore = defineStore('LoveTalk', {
    actions:{
        async getTalk(){
            let {data:{content}} = await axios.get('https://api.uomg.com/api/rand.qinghua')
            let obj = {id: nanoid(), content: content}
            this.talkList.unshift(obj)
        }
    },
    // 真正存储数据的地方
    state(){
        return {
            talkList: JSON.parse(localStorage.getItem('talkList') as string ) || []
        }
    }
})

25. pinia 组合式写法

这里相对于选项式,我们需要把state中的部分转化为响应式才有效,然后添加return返回:

LoveTalk.ts如下:

import {defineStore} from 'pinia'
import {nanoid} from 'nanoid'
import axios from 'axios'
import {reactive} from 'vue'

export const useLoveStore = defineStore('LoveTalk', ()=>{

    // 这里写state中的部分
    let talkList = reactive(JSON.parse(localStorage.getItem('talkList') as string ) || [])

    async function getTalk(){
        console.log('获取一句')
        try {
            let {data:{content}} = await axios.get('https://api.uomg.com/api/rand.qinghua')
            let obj = {id: nanoid(), content: content}
            talkList.unshift(obj)
        } catch (error) {
            console.log('获取失败!')
        }

    }

    return {talkList, getTalk}
})

day 5

26. 组件通信_props

Father.vue 如下:

<template>
    <div class="father">
        <h2>这是父组件</h2>
        <h4>汽车:{{car}}</h4>
        <h4 v-if="toy">获取到儿子的玩具:{{ toy }}</h4>
        <Child :car="car" :sendToy="getToy"></Child>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Props'
    }
</script>

<script setup lang="ts">
    import Child from './Child.vue'
    import {ref} from 'vue'

    // 数据
    let car = ref('benz')
    let toy = ref('')

    // 方法
    function getToy(val:string){
        toy.value = val
    }

</script>

<style scoped>
    div.father {
        margin: 30px;
        background-color: yellow;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Child.vue如下:

<template>
    <div class="child">
        <h2>这是子组件</h2>
        <h4>玩具:{{ toy }}</h4>
        <h4>父亲给的车:{{ car }}</h4>
        <button @click="sendToy(toy)">点击把玩具交给父亲</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Child'
    }
</script>

<script setup lang="ts">
    import { ref } from 'vue'

    // 数据
    let toy = ref('atm')

    defineProps(['car', 'sendToy'])
</script>

<style scoped>
    div.child {
        background-color: gray;
        width: 80%;
        height: 70%;
        margin-left: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

27. 组件通信_custom-event

标签中使用@开头的props表示的是事件,比如@click是点击事件,如果我们需要打印事件,我们可以使用$event当做参数放入函数中获取事件;

事件一般用串串的形式,而回调函数使用小驼峰

Father.vue 代码如下:

<template>
    <div class="father">
        <h2>这是父组件</h2>
        <h3 v-if="toy">儿子给的玩具{{ toy }}</h3>

        <!-- 这里send-toy表示时间的名字,getToy表示事件触发时使用的函数回调 -->
        <!-- 事件一般用串串的形式,而回调函数使用小驼峰 -->
        <Child @send-toy="getToy"></Child>

        <!-- 这里可以使用$event 提取出事件进行打印 -->
        <!-- <Child @send-toy="getToy(param1, param2, $event)"></Child> -->

    </div>
</template>

<script lang="ts">
    export default {
        name: 'Event'
    }
</script>

<script setup lang="ts">
    import Child from './Child.vue'
    import {ref} from 'vue'

    let toy = ref('')

    function getToy(val:string){
        toy.value = val
    }

</script>

<style scoped>
    div.father {
        margin: 30px;
        background-color: yellow;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Child.vue 代码如下:

<template>
    <div class="child">
        <h2>这是子组件</h2>
        <h3>儿子的玩具{{ toy }}</h3>
        <button @click="emit('send-toy', toy)"> 发送给父亲</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Child'
    }
</script>

<script setup lang="ts">
    import {ref} from 'vue'
    let toy = ref('atm')

    let emit = defineEmits(['send-toy'])

</script>

<style scoped>
    div.child {
        background-color: gray;
        width: 80%;
        height: 70%;
        margin-left: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

28. 组件通信_mitt

类似于总线bus,收数据的:在公共区域提前绑定好事件;提供数据的:在合适的时候触发事件;

首先需要安装mitt

npm i mitt

由于mitt属于一个工具,所以在src目录下创建一个utils文件夹,创建一个emitter.ts文件;

emitter.ts 文件内容如下:

// 引入 mitt
import mitt from 'mitt'

// 调用mitt得到emitter,emitter可以绑定事件和触发事件
const emitter = mitt()

// 绑定事件
emitter.on('test1', ()=>{
    console.log('test1被调用了')
})
emitter.on('test2', ()=>{
    console.log('test2被调用了')
})

// 触发事件
setInterval(()=>{
    emitter.emit('test1')
    emitter.emit('test2')
}, 2000)

// 解绑事件
setTimeout(()=>{
    emitter.off('test1')

    // 全部解绑
    // emitter.all.clear()
}, 3000)

// 暴露emitter
export default emitter

同时,需要在mian.ts文件中进行引入:

import {createApp} from 'vue'
import App from './App.vue'
import {createPinia} from 'pinia'
import router from './router'
import emitter from './utils/emitter'

const app = createApp(App)
const pinia = createPinia()

app.use(pinia)
app.use(router)

app.mount('#app')

注意,这里不需要app.use;

Child1.vue代码如下:

<template>
    <div class="child">
        <h2>这是子1</h2>
        <h3> 玩具 {{toy}}</h3>
        <button @click="emitter.emit('send-toy', toy)">点击发送玩具</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Child1'
    }
</script>

<script setup lang="ts">
    import emitter from '@/utils/emitter';
    import {ref} from 'vue'


    let toy = ref('atm')

</script>

<style scoped>
    div.child {
        background-color: gray;
        width: 80%;
        height: 70%;
        margin-left: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Child2.vue代码如下:

<template>
    <div class="child">
        <h2>这是子2</h2>
        <h3> 电脑 {{computer}}</h3>
        <h3 v-if="toy"> 玩具 {{toy}}</h3>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Child2'
    }
</script>

<script setup lang="ts">
    import {ref, onUnmounted} from 'vue'
    import emitter from '@/utils/emitter';
    

    let computer = ref('wxr')
    let toy = ref('')

    emitter.on('send-toy', (val:any)=>{
        toy.value = val
    })

    // 防止被卸载后占用内存
    onUnmounted(()=>{
        emitter.off('send-toy')
    })


</script>

<style scoped>
    div.child {
        background-color: gray;
        width: 80%;
        height: 70%;
        margin-left: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

29. 组件通信_v-model

UI组件库底层大量使用v-model进行通信;

e v e n t :对于原生事件, event: 对于原生事件, event:对于原生事件,event就是事件对象; 能==> .target
对于自定义事件,$event就是触发事件时,所传递的数据; 不能==> .target

Father.vue如下:

<template>
    <div>
        <h2>父组件</h2>
        <h4>value : {{ username }}</h4>
        <!-- v-model用在html标签上 下面两种方法等价-->
        <!-- <input type="text" v-model="username"> -->
        <!-- <input type="text" :value="username" @input="username=(<HTMLInputElement>$event.target).value"> -->
    
        <!-- v-model用在组件标签上 下面两种方法等价-->
        <!-- <MyCustomInput v-model="username"></MyCustomInput> -->
        <!-- <MyCustomInput :modelValue="username" @update:modelValue="username = $event"></MyCustomInput> -->

        <!-- 自定义modelValue 需要把UI组件中所有的modelValue改成qwe --> 
        <MyCustomInput v-model:qwe="username"></MyCustomInput>


    </div>
</template>

<script lang="ts">
    export default {
        name: 'Model'
    }
</script>

<script setup lang="ts">
    import {ref} from 'vue'
    import MyCustomInput from './MyCustomInput.vue';

    let username = ref('zhangsan')

</script>

<style scoped>

</style>

自定义组件MyCustomInput.vue如下:

<!-- 要是用v-model,必须要modelValue和update:modelValue -->
<template>
    <div>
        <input 
            type="text" 
            :value="modelValue"
            @input="emit('update:modelValue', (<HTMLInputElement>$event.target).value)"
        >
    </div>
</template>

<script lang="ts">
    export default {
        name: 'MyCustomInput'
    }
</script>

<script setup lang="ts">
    defineProps(['modelValue'])
    const emit =  defineEmits(['update:modelValue'])

</script>

<style scoped>
    input {
        border: 2px solid black;
        background-image: linear-gradient(45deg, red, yellow, green);
        height: 30px;
        font-size: 20px;
        color: white;
    }

</style>

30. 组件通信_$attrs

使用props,未用defineProps的数据,子组件会使用attrs接收;

Father.vue

<template>
    <div class="father">
        <h2> 父组件 </h2>
        <h4> {{ data }} </h4>
        <!-- v-bind="{x: 100, y:200}" 相当于 :x="100" :y="200" -->
        <Child v-bind="data"></Child>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'AttrsListeners'
    }
</script>

<script setup lang="ts">
    import Child from './Child.vue'
    import {reactive} from 'vue'

    let data = reactive({
        a: 100,
        b: 200,
        c: 300
    })


</script>

<style scoped>
    div.father {
        margin: 30px;
        background-color: yellow;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Child.vue

<template>
    <div class="child">
        <h2> 子组件 </h2>
        <Grandson v-bind="$attrs"> </Grandson>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Child'
    }
</script>

<script setup lang="ts">
    import Grandson from './Grandson.vue'
</script>

<style scoped>
    div.child {
        margin: 30px;
        background-color: red;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Grandson.vue

<template>
    <div class="grandson">
        <h2> 孙组件 </h2>
        <h4> {{ $attrs }}</h4>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Grandson'
    }
</script>

<script setup lang="ts">


</script>

<style scoped>
    div.grandson {
        margin: 30px;
        background-color: green;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

31. 组件通信_$refs$parent

$refs可以获取html下所有的ref组件,$parent可以获取父组件中的数据;都要结合defineExpose()

Father.vue 代码如下:

<template>
    <div>
        <h2> 父组件 </h2>
        <h4>房产:{{house}}</h4>
        <button @click="changeToys">改变c1的toys</button>
        <button @click="changeComputer">改变c2的computer</button>
        <button @click="getAllChild($refs)">获取所有的子组件实例对象</button>
        <Child1 ref="c1"/>
        <Child2 ref="c2"/>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'RefChildrenParent'
    }
</script>

<script setup lang="ts">
    import Child1 from './Child1.vue'
    import Child2 from './Child2.vue'
    import {ref} from 'vue'

    let house = ref(3)
    let c1 = ref()
    let c2 = ref()

    function changeToys(){
        c1.value.toys = '修改后的玩具'
    }
    function changeComputer(){
        c2.value.computer = '修改后的电脑'
    }

    // refs:{[key:string]:any} 或者 refs:any
    function getAllChild(refs:{[key:string]:any}){
        for(let index in refs){
            refs[index].books += 3
        }
    }

    defineExpose({house})
</script>

<style scoped>

</style>

Child1.vue 代码如下:

<template>
    <div>
        <h2> 子组件1 </h2>
        <h4>玩具:{{ toys }}</h4>
        <h4>书籍:{{ books }}</h4>
        <button @click="minusHouse($parent)">干掉父亲的一套房产</button>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Child1'
    }
</script>

<script setup lang="ts">
    import {ref} from 'vue'

    let toys = ref('asdsdsaxzczxc')
    let books = ref(3)

    function minusHouse(parent:any){
        parent.house -= 1
    }

    defineExpose({toys, books})
</script>

<style scoped>

</style>

Child2.vue 代码如下:

<template>
    <div>
        <h2> 子组件1 </h2>
        <h4>电脑:{{ computer }}</h4>
        <h4>书籍:{{ books }}</h4>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Child2'
    }
</script>

<script setup lang="ts">
    import {ref} from 'vue'

    let computer = ref('asdas')
    let books = ref(3)

    defineExpose({computer, books})
</script>

<style scoped>

</style>

32. 组件通信_provide-inject

常用于祖孙之间越过父亲直接通信;

祖使用provide传送,孙使用inject接收,inject第二个参数是默认值

Father.vue

<template>
    <div class="father">
        <h2> 父组件 </h2>
        <h4>钱:{{ money }}</h4>
        <h4 v-for="car in cars">车:{{ car.brand }} 价值 :{{ car.price }}</h4>
        <Child></Child>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'ProvidedInject'
    }
</script>

<script setup lang="ts">
    import Child from './Child.vue'
    import {ref, reactive, provide} from 'vue'

    let money = ref(100)
    let cars = reactive([{
        brand: 'benz',
        price: 1000,
    }])

    // 向后代提取数据
    provide('qian', money)
    provide('che', cars)

</script>

<style scoped>
    div.father {
        margin: 30px;
        background-color: yellow;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Child.vue

<template>
    <div class="grandson">
        <h2> 孙组件 </h2>
        <h4> 钱:{{ money }}</h4>
        <h4 v-for="car in cars">车:{{ car.brand }} 价值 :{{ car.price }}</h4>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Grandson'
    }
</script>

<script setup lang="ts">
    import {inject} from 'vue'

    //  这里inject第二个参数是默认值
    let money = inject('qian', '我是默认值')
    let cars = inject('che', {})

</script>

<style scoped>
    div.grandson {
        margin: 30px;
        background-color: green;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

32. 组件通信_slot默认插槽

slot有三种:默认插槽,具名插槽,作用域插槽;

默认插槽就是在组件中间使用html标签,然后在组件中加入<slot></slot>

Father.vue 如下:

<template>
    <div class="father">
        <Category>
            <h2> 热门游戏列表</h2>
            <ul>
                <li v-for="game in games" :key="game.id">{{ game.name }}</li>
            </ul>
        </Category>
        <Category>
            <template v-slot:default>
                <h2> 热门美食列表</h2>
                <img :src="imgUrl" alt="">
            </template>
        </Category>
        <Category>
            <h2> 热门美食列表</h2>
            <video :src="videoUrl" controls> </video>
        </Category>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Slot'
    }
</script>

<script setup lang="ts">
    import Category from './Category.vue';
    import {ref, reactive} from 'vue'

    let games = reactive([
        {id: 'zxzczxczxc01', name: '爱谁谁大所多'},
        {id: 'zxzczxczxc02', name: '爱阿萨德按所多'},
        {id: 'zxzczxczxc03', name: '爱自行车在线所多'},
        {id: 'zxzczxczxc04', name: '爱谁周传雄所多'},
    ])

    let imgUrl = ref('https://z1.ax1x.com/2023/11/19/piNxLo4.jpg')
    let videoUrl = ref('http://vjs.zencdn.net/v/oceans.mp4')

</script>

<style scoped>
    .father {
        display: flex;
        background-color: rosybrown;
        padding: 20px;
        border-radius: 10px;
        justify-content: space-evenly;
        width: 100%;
    }
    img, video {
        width: 100%;
    }

</style>

Child.vue 如下:

<template>
    <div class="category">
        <slot></slot>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Category'
    }
</script>

<script setup lang="ts">

</script>

<style scoped>
    .category {
        background-color: skyblue;
        border-radius: 10px;
        box-shadow: 0 0 10px;
        padding: 10px;
        width: 200px;
        height: 300px;
    }
</style>

33. 组件通信_slot具名插槽

具名插槽意思就是具有名字的插槽,这里默认插槽的名字为default,多个slot可以倒序;

Category.vue

<template>
    <div class="category">
        <slot name="s1"></slot>
        <slot name="s2"></slot>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Category'
    }
</script>

<script setup lang="ts">

</script>

<style scoped>
    .category {
        background-color: skyblue;
        border-radius: 10px;
        box-shadow: 0 0 10px;
        padding: 10px;
        width: 200px;
        height: 300px;
    }
</style>

Father.vue

<template>
    <div class="father">
        <Category>
            <template #s1>
                <h2> 热门游戏列表 </h2>
            </template>
            <template v-slot:s2>
                <ul>
                    <li v-for="game in games" :key="game.id">{{ game.name }}</li>
                </ul>
            </template>
        </Category>
        <Category>
            <template #s2>
                <img :src="imgUrl" alt="">
            </template>
            <template #s1>
                <h2> 热门美食列表 </h2>
            </template>
        </Category>
        <Category>
            <template #s2>
                <video :src="videoUrl" controls> </video>
            </template>
            <template #s1>
                <h2> 热门美食列表 </h2>
            </template>
        </Category>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Slot'
    }
</script>

<script setup lang="ts">
    import Category from './Category.vue';
    import {ref, reactive} from 'vue'

    let games = reactive([
        {id: 'zxzczxczxc01', name: '爱谁谁大所多'},
        {id: 'zxzczxczxc02', name: '爱阿萨德按所多'},
        {id: 'zxzczxczxc03', name: '爱自行车在线所多'},
        {id: 'zxzczxczxc04', name: '爱谁周传雄所多'},
    ])

    let imgUrl = ref('https://z1.ax1x.com/2023/11/19/piNxLo4.jpg')
    let videoUrl = ref('http://vjs.zencdn.net/v/oceans.mp4')

</script>

<style scoped>
    .father {
        display: flex;
        background-color: rosybrown;
        padding: 20px;
        border-radius: 10px;
        justify-content: space-evenly;
        width: 100%;
    }
    img, video {
        width: 100%;
    }

</style>

34. 组件通信_slot作用域插槽

主要用在子里面有数据,但是父组件想以不同形式进行表示;

Game.vue

<template>
    <div class="game">
        <h2>游戏列表</h2>
        <slot :youxi="games"></slot>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Category'
    }
</script>

<script setup lang="ts">
    import {reactive} from 'vue'

    let games = reactive([
        {id: 'zxzczxczxc01', name: '爱谁谁大所多'},
        {id: 'zxzczxczxc02', name: '爱阿萨德按所多'},
        {id: 'zxzczxczxc03', name: '爱自行车在线所多'},
        {id: 'zxzczxczxc04', name: '爱谁周传雄所多'},
    ])
</script>

<style scoped>
    .game {
        background-color: skyblue;
        border-radius: 10px;
        box-shadow: 0 0 10px;
        padding: 10px;
        width: 200px;
        height: 300px;
    }
</style>

Father.vue

<template>
    <div class="father">
        <Game>
            <!-- 子组件中slot传的props中所有的数据都在a的手里-->
            <template v-slot="{youxi}">
                <ul>
                    <li v-for="y in youxi" :key="y.id"> {{ y.name }}</li>
                </ul>
            </template>
        </Game>
        <Game>
            <!-- 子组件中slot传的props中所有的数据都在a的手里-->
            <template v-slot="{youxi}">
                <ol>
                    <li v-for="y in youxi" :key="y.id"> {{ y.name }}</li>
                </ol>
            </template>
        </Game>
        <Game>
            <!-- 子组件中slot传的props中所有的数据都在a的手里-->
            <template v-slot="{youxi}">
                <h3 v-for="y in youxi" :key="y.id"> {{ y.name }}</h3>
            </template>
        </Game>
    </div>
</template>

<script lang="ts">
    export default {
        name: 'Slot'
    }
</script>

<script setup lang="ts">
    import Game from './Game.vue';


</script>

<style scoped>
    .father {
        display: flex;
        background-color: rosybrown;
        padding: 20px;
        border-radius: 10px;
        justify-content: space-evenly;
        width: 100%;
    }
    img, video {
        width: 100%;
    }

</style>

35. shallowRef 和 shallowReactive

36. readonly 和 shallowReadonly

37. toRaw 和 markRaw

38. customRef

customRef() 预期接收一个工厂函数作为参数,这个工厂函数接受 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set 方法的对象。

一般来说,track() 应该在 get() 方法中调用,而 trigger() 应该在 set() 中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。

示例 创建一个防抖 ref,即只在最近一次 set 调用后的一段固定间隔后再调用:

import { customRef } from 'vue'

export function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
	  // 数据被读取时,调用get
      get() {
        track() // 告诉vue数据value很重要,一旦value变化就去更新
        return value
      },
      // 数据被修改时,调用set
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger() // 通知一下vue数据value变化了
        }, delay)
      }
    }
  })
}

39. Teleport

这个组件中有一个 <button> 按钮来触发打开模态框,和一个 class 名为 .modal 的 <div>,它包含了模态框的内容和一个用来关闭的按钮。

当在初始 HTML 结构中使用这个组件时,会有一些潜在的问题:

  • position: fixed 能够相对于浏览器窗口放置有一个条件,那就是不能有任何祖先元素设置了 transformperspective 或者 filter 样式属性。也就是说如果我们想要用 CSS transform 为祖先节点 <div class="outer"> 设置动画,就会不小心破坏模态框的布局!

  • 这个模态框的 z-index 受限于它的容器元素。如果有其他元素与 <div class="outer"> 重叠并有更高的 z-index,则它会覆盖住我们的模态框。

<Teleport> 提供了一个更简单的方式来解决此类问题,让我们不需要再顾虑 DOM 结构的问题。让我们用 <Teleport> 改写一下 <MyModal>

<button @click="open = true">Open Modal</button>

<Teleport to="body">
  <div v-if="open" class="modal">
    <p>Hello from the modal!</p>
    <button @click="open = false">Close</button>
  </div>
</Teleport>

40. Suspense

在这个组件树中有多个嵌套组件,要渲染出它们,首先得解析一些异步资源。如果没有 <Suspense>,则它们每个都需要处理自己的加载、报错和完成状态。在最坏的情况下,我们可能会在页面上看到三个旋转的加载态,在不同的时间显示出内容。

有了 <Suspense> 组件后,我们就可以在等待整个多层级组件树中的各个异步依赖获取结果时,在顶层展示出加载中或加载失败的状态。

<Suspense> 可以等待的异步依赖有两种:

  1. 带有异步 setup() 钩子的组件。这也包含了使用 <script setup> 时有顶层 await 表达式的组件。
  2. 异步组件。
<Suspense>
  <!-- 具有深层异步依赖的组件 -->
  <Dashboard />

  <!-- 在 #fallback 插槽中显示 “正在加载中” -->
  <template #fallback>
    Loading...
  </template>
</Suspense>

41. 全局api转化为应用对象

import {createApp} from 'vue'
import App from './App.vue'
import Hello from './Hello.vue'
//创建应用
const app = createApp(App)

// 定义全局组件
app.component('Hello',Hello)

// 定义全局属性
app.config.globalProperties.x = 99
declare module 'vue'{
    interface ComponentCustomProperties{
        x: number
    }
}

// 定义样式 对组件使用v-beauty改变样式
app.directive('beauty',(element,{value})=>{
    element.innerText += value
    element.style.color = 'green'
    element.style.backgroundColor = 'yellow'
})

// app挂载
app.mount('#app')

// app卸载
setTimeout(()=>{
    app.unmount()
}, 3000)

42. 其他

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1491157.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙 Stage模型-AbilityStage、Context、Want

前提&#xff1a;基于官网3.1/4.0文档。参考官网文档 基于Android开发体系来进行比较和思考。&#xff08;或有偏颇&#xff0c;自行斟酌&#xff09; 一、 AbilityStage 1.概念 AbilityStage是一个Module级别的组件容器&#xff0c;应用的HAP在首次加载时会创建一个AbilitySt…

步进电机驱动器接法

实物 参数 共阳极&#xff1a; 使能给高电平有效 共阴极&#xff1a; 使能给低电平有效 整体接线 参考内容 B站UP范辉

Google Play上架:2024年五月份政策自查之敏感信息访问权限和 API(适用于上架被拒和因AI扫荡被下架或封号)

本文根据谷歌上架政策协议截止日期归纳而出&#xff0c;提供上架遇到问题的开发者们&#xff0c;感谢支持&#xff0c;如有不足&#xff0c;欢迎补充。 关于敏感信息访问权限和 API的相关政策 政策公布日期政策相关内容相关补充内容政策截止日期 政策公布日期 公布日期&#x…

【QT】窗口的大小标题图标设置

窗口的大小标题图标设置 添加一个新的类 创建完成&#xff0c;根据上一节最后的在总结&#xff0c;做个测试&#xff1a; #include "mybutton.h" #include <QDebug>//打印&#xff0c;标准输出 MyButton::MyButton(QWidget *parent) : QPushButton(parent) { …

INFINI Labs 产品更新 | Easysearch 1.7.1发布

INFINI Labs 产品又更新啦~&#xff0c;包括 Console&#xff0c;Gateway&#xff0c;Agent 1.23.0 和 Easysearch 1.7.1。此次版本重点修复历史遗留 Bug 、网友们提的一些需求等。以下是本次更新的详细说明。 INFINI Console v1.23.0 INFINI Console 是一款非常轻量级的多集…

ATFX汇市:油价回落之际加元币值走弱,USDCAD有望刷新年内新高

ATFX汇市&#xff1a;加元是商品货币&#xff0c;币值受到国际油价和精炼石油出口的显著影响。2022年3月份&#xff0c;国际油价达到130美元的峰值水平&#xff0c;随后开启回落走势&#xff0c;时至今日&#xff0c;最新报价在80美元下方&#xff0c;累计跌幅近40%。疲弱的油价…

根据用户名称实现单点登录

一、参数格式 二、后端实现 Controller层 public class IAccessTokenLoginController extends BaseController {Autowiredprivate ISysUserService sysUserService;Autowiredprivate ISingleTokenServiceImpl tokenService;/*** 登录方法** return 结果*/PostMapping("/l…

小家电压力锅WIFI模块供电AC-DC电源芯片特点与SM6035解析

WIFI模块供电的是一种将AC-DC电源芯片交流电转换为直流电的芯片&#xff0c;通常用于为WIFI模块提供电源。这种芯片具有高效、紧凑和可靠等特点&#xff0c;能够满足各种应用场景的需求。 其中&#xff0c;离线开关电源芯片是一种常见的AC-DCLED电源芯片&#xff0c;它通常包括…

JimuReport积木报表 v1.7.1 版本发布,低代码报表工具

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

JavaScript基础知识(三)

JavaScript基础知识&#xff08;三&#xff09; 一、事件1. 事件绑定2.事件流2.1 事件捕获与事件冒泡 3.事件对象扩展3.1 阻止事件冒泡3.2 事件默认行为 4.事件委托5.事件类型5.1 鼠标事件5.2 键盘事件5.3 触屏事件 二、计时器方法1. setInterval 与 clearInterval2. setTimeou…

吴恩达机器学习笔记:第5周-9 神经网络的学习1(Neural Networks: Learning)

目录 9.1 代价函数9.2 反向传播算法9.3 反向传播算法的直观理解 9.1 代价函数 首先引入一些便于稍后讨论的新标记方法&#xff1a; 假设神经网络的训练样本有&#x1d45a;个&#xff0c;每个包含一组输入&#x1d465;和一组输出信号&#x1d466;&#xff0c;&#x1d43f;…

【数据存储】大端存储||小端存储(超详细解析,小白一看就懂!!!)

目录 一、前言 二、什么是低地址、高地址 &#xff1f; 三、什么是数据的高位和低位 &#xff1f; 四、什么是大小端存储&#xff1f; &#x1f349; 小端存储详解 &#x1f352; 大端存储详解 五、为什么会有大小端存储&#xff1f; &#x1f34d;大端存储的优点 &#…

保护模式笔记九 中断门和IDT(中断描述符表)

段选择子&#xff1a; 先直观认识一下GDT和段选择子在逻辑地址转换为线性地址中的作用&#xff0c;例如&#xff1a; 给出逻辑地址&#xff1a;21h:12345678h&#xff0c;需要将其转换为线性地址 a. 选择子SEL21h0000000000100 0 01b&#xff0c;他代表的意思是&#xff1a…

H3C 路由过滤路由引入实验

H3C 路由过滤&路由引入实验 实验拓扑 ​​ 实验需求 按照图示配置 IP 地址&#xff0c;R1&#xff0c;R3&#xff0c;R4 上使用 loopback 口模拟业务网段R1 和 R2 运行 RIPv2&#xff0c;R2&#xff0c;R3 和 R4 运行 OSPF&#xff0c;各自协议内部互通在 RIP 和 OSPF …

OpenAI划时代大模型——文本生成视频模型Sora作品欣赏(十五)

Sora介绍 Sora是一个能以文本描述生成视频的人工智能模型&#xff0c;由美国人工智能研究机构OpenAI开发。 Sora这一名称源于日文“空”&#xff08;そら sora&#xff09;&#xff0c;即天空之意&#xff0c;以示其无限的创造潜力。其背后的技术是在OpenAI的文本到图像生成模…

Gemma模型一些细节讲解

Gemma模型报告中提到的几个点进行代码细节解读一下&#xff1a; &#xff08;1&#xff09;Embedding层共享参数 &#xff08;2&#xff09;输入输出层均进行RMSNorm Embedding层共享参数 共享embedding的权重给最后的llm_head层。是词嵌入层的共享&#xff0c;与旋转位置编码…

低密度奇偶校验码LDPC(七)——SPA和积译码算法的简化

往期博文 低密度奇偶校验码LDPC&#xff08;一&#xff09;——概述_什么是gallager构造-CSDN博客 低密度奇偶校验码LDPC&#xff08;二&#xff09;——LDPC编码方法-CSDN博客 低密度奇偶校验码LDPC&#xff08;三&#xff09;——QC-LDPC码概述-CSDN博客 低密度奇偶校验码…

CNAN知识图谱辅助推荐系统

CNAN知识图谱辅助推荐系统 文章介绍了一个基于KG的推荐系统模型&#xff0c;代码也已开源&#xff0c;可以看出主要follow了KGNN-LS 。算法流程大致如下&#xff1a; 1. 算法介绍 算法除去attention机制外&#xff0c;主要的思想在于&#xff1a;user由交互过的item来表示、i…

前端小案例——登录界面(正则验证, 附源码)

一、前言 实现功能&#xff1a; 提供用户名和密码输入框。当用户提交表单时&#xff0c;阻止默认提交行为。使用正则表达式验证用户输入的内容&#xff0c;判断输入的是有效的邮箱地址还是身份证号码。根据验证结果&#xff0c;在输入框下方显示相应的提示信息。 实现逻辑&a…

【C++庖丁解牛】初始化列表 | Static对象 | 友元函数

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1. 再谈构造函数1.1 …