Vite创建Vue3项目以及Vue3相关基础知识
1.创建Vue3项目
1.运行创建项目命令
# 使用 npm
npm create vite@latest
2、填写项目名称
3、选择前端框架
4、选择语法类型
5、按提示运行代码
不出意外的话,运行之后应该会出现 下边这个页面
6.延伸学习:对比webpack和vite(这个是面试必考题哦)
1、Webpack:会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。
将所有的模块提前编译、打包进 bundle 中,不管这个模块是否被用到,随着项目越来越大,打包启动的速度自然越来越慢。
使用 webpack 打包模式如下图:
2、Vite:直接启动开发服务器,请求哪个模块再对该模块进行实时编译。
瞬间开启一个服务,并不会先编译所有文件,当浏览器用到某个文件时,Vite 服务会收到请求然后编译后响应到客户端。
使用 Vite 打包模式如下图:
3、vite 优点:
vite 服务器启动速度比 webpack 快;
由于vite启动的时候不需要打包,也就无需分析模块依赖、编译,所以启动速度非常快。当浏览器请求需要的模块时,再对模块进行编译,这种按需动态编译的模式,极大缩短了编译时间,当项目越大,文件越多时,vite的开发时优势越明显;
vite热更新比webpack快;vite在HRM方面,当某个模块内容改变时,让浏览器去重新请求该模块即可,而不是像webpack重新将该模块的所有依赖重新编译;
vite使用esbuild(Go 编写) 预构建依赖,而webpack基于nodejs, 比node快 10-100 倍;
4、vite 缺点:
生态不及webpack,加载器、插件不够丰富;
没被大规模重度使用,会隐藏一些问题;
2.初识项目目录以及main.js(这个是最重要的文件欧)
目录
import { createApp } from 'vue'
// 全局样式
import './style.css'
import App from './App.vue'// 用create()方法把APP组件创建成一个项目挂载到#app的标签上
createApp(App).mount('#app')
看一下下边的index.html文件
<!doctype html>
<html lang="en"><head><meta charset="UTF-8" /><link rel="icon" type="image/svg+xml" href="/vite.svg" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Vite + Vue</title></head><body><div id="app"></div><script type="module" src="/src/main.js"></script></body>
</html>
App.vue第一个组件
// 组件三大要素
// 结构
<template><div class="firstApp">hello world</div>
</template>
// 交互
<script lang="ts">
// js or ts都是可以的 export default{name: 'App'}
</script>
// 样式
<style scoped>
.firstApp{font-size: 30px;height: 600;text-align: center;margin-top: 100px;
}
</style>
3.Vue3核心语法
3.1optionsAPI 与 compositionAPI
我们先看一下Vue3组件的代码
import { ref, computed } from 'vue';export default {setup() {const count = ref(0);const doubleCount = computed(() => count.value * 2);function increment() {count.value++;}return {count,doubleCount,increment};}
};
发现vue3的逻辑都在一块,而vue2,数据,方法都是分开的。
下边是组合式api的优势
- 可复用性:
Composition API
更适合实现代码的复用和逻辑的封装,而Options API
的代码复用性相对较差。 - 逻辑组织:
Composition API
将相关逻辑集中在一起,更易于理解和维护,而Options API
将相关逻辑分散在不同的选项中,不够清晰。 - 代码重用:
Composition API
可以更灵活地重用逻辑代码,而Options API
的重用性较低。 - 学习曲线:
Composition API
相对Options API
来说,有一定的学习曲线,但一旦掌握,会发现其强大的灵活性和可复用性。
3.2.setup
setup是vue3中的一个新的配置项,值是一个函数。他是compositionAPI表演的舞台,组件中所用到的:数据/方法/计算属性/监视。。。等,均配置在setup中
特点如下:
- setup函数返回对象中的内容,可以直接在模板中使用。
- setup中访问this是undefined
- setup函数会在beforeCreate之前调用,他是领先所有的钩子进行执行的。
<template><!-- 使用 --><div>{{a}}</div><button @click="changeMsg">点击</button>
</template>
<script lang="ts">
// ts or js 都是可以的
export default{name : 'HelloWorld',beforeCreate(){console.log('beforeCreate');},setup(){console.log('setup',this); // setup 函数中的this是undefined,Vue3已经弱化this了// 声明let msg = '新中国成立103周年' // 此时的数据不是响应式的function changeMsg(){console.log('我要改变',msg);alert(msg)}// 把变量交出去return {a:msg,changeMsg}}
}
</script><style scoped></style>
setup的返回值 可以是对象,也可以是函数
<template><!-- 使用 --><div>{{a}}</div><button @click="changeMsg">点击</button>
</template>
<script lang="ts">
// ts or js 都是可以的
export default{name : 'HelloWorld',beforeCreate(){console.log('beforeCreate');},setup(){console.log('setup',this);// 声明let msg = '新中国成立103周年' // 此时的数据不是响应式的function changeMsg(){console.log('我要改变',msg);alert(msg)}// 把变量交出去// return {a:msg,changeMsg}// 渲染函数写法1// return function(){// return '哈哈'// }// 渲染函数写法2return () => '哈哈'}
}
</script><style scoped></style>
data(){},methods可以与setup共存
<template><!-- 使用 --><div>{{ a }}</div><div>{{ b }}</div><button @click="changeMsg">点击</button><button @click="changeB">点击2</button>
</template>
<script lang="ts">
// ts or js 都是可以的
export default{name : 'HelloWorld',beforeCreate(){console.log('beforeCreate');},data() {return {b : 100,}},methods:{changeB(){this.b +=1console.log('cccccc');}},setup(){console.log('setup',this);// 声明let msg = '新中国成立103周年' // 此时的数据不是响应式的function changeMsg(){console.log('我要改变',msg);alert(msg)}// 把变量交出去return {a:msg,changeMsg}// 渲染函数写法1// return function(){// return '哈哈'// }// 渲染函数写法2// return () => '哈哈'}
}
</script><style scoped></style>
setup语法糖
<template><!-- 使用 --><div>{{ msg }}</div><button @click="changeMsg">点击</button>
</template>
<script setup lang="ts">// 声明let msg = '新中国成立103周年' // 此时的数据不是响应式的function changeMsg(){console.log('我要改变',msg);alert(msg)}
</script><style scoped></style>
使用 vite-plugin-vue-setup-extend (给组件起名字)
第一步:下载依赖vite-plugin-vue-setup-extend
npm i vite-plugin-vue-setup-extend -D
第二步:项目引入然后使用此插件
代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 引入插件
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
// 使用插件
// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),VueSetupExtend()],
})
3.3.ref创建:基本类型的响应式数据
<template><!-- 使用 --><div>{{ msg }}</div><button @click="changeMsg">点击</button>
</template>
<script setup lang="ts" name="HelloWorld">
import {ref} from 'vue'// 声明let msg = ref('新中国成立103周年') // 此时的数据是响应式的,返回的是refImpl的实例对象function changeMsg(){msg.value = '新中国成立103周年,生日快乐'console.log('我要改变',msg.value);alert(msg.value)}
</script><style scoped></style>
3.4.reactive创建:对象类型的响应式数据
<template><!-- 使用 --><div>{{ msg.content }}</div><button @click="changeMsg">点击</button><ul><!-- :key其实相当于把后边的内容当成表达式去解析 --><li v-for="item in arrList" :key="item.id">{{ item.title }}</li></ul>
</template>
<script setup lang="ts" name="HelloWorld">
import {reactive} from 'vue'// 声明let msg = reactive({content:'新中国成立103周年'}) // 此时的数据是响应式的,返回的是Proxy的实例对象let arrList = reactive([{id:'1',title:'中国银行'},{id:'2',title:'工商银行'},{id:'3',title:'建设银行'}])function changeMsg(){msg.content = '新中国成立103周年,生日快乐'arrList[1].title = '我的银行'console.log('我要改变',msg,msg.content);alert(msg.content)}
</script><style scoped></style></style>
3.5.ref创建:对象类型的响应式数据
<template><!-- 使用 --><div>{{ msg.content }}</div><button @click="changeMsg">点击</button><ul><!-- :key其实相当于把后边的内容当成表达式去解析 --><li v-for="item in arrList" :key="item.id">{{ item.title }}</li></ul>
</template>
<script setup lang="ts" name="HelloWorld">
import {ref} from 'vue'// 声明let msg = ref({content:'新中国成立103周年'}) // 此时的数据是响应式的,返回的是Proxy的实例对象let arrList = ref([{id:'1',title:'中国银行'},{id:'2',title:'工商银行'},{id:'3',title:'建设银行'}])function changeMsg(){msg.value.content = '新中国成立103周年,生日快乐'arrList.value[1].title = '我的银行'// console.log('我要改变',msg,msg.content);// alert(msg.value.content)}
</script>
<style scoped></style>
这个说明ref可以定义响应式对象,底层也是在使用Reactive,也就是proxy生成的.
3.6.ref与reactive
宏观角度
1.ref定义:基本数据类型,对象类型数据
2.reactive定义:对象类型数据。
区别:
1.ref创建的变量必须使用.value(也可以使用volar插件自动添加.value)
现在是Vue-official插件,设置在下图。
使用原则
1.若需要一个基本类型的响应式数据,必须使用ref.
2.若需要一个响应式对象,层级不深,ref,reactive都可以。
3.若需要一个响应式对象,层级比较深,推荐使用reactive。
注意点: 当reactive定义的复杂数据类型时,一个属性是ref类型时,就不用value取值了,会自动拆包了。
3.7.torefs与toref
toRefs把对象中多个属性转变成新的ref响应式对象
toRefs把对象中单个属性转变成新的ref响应式对象
<template><div>名:{{name}}</div><div>年龄:{{age}}</div><div>toRef新名字{{ newName }}</div><button @click="changeName">修改名字</button><button @click="changeAge">修改名字</button>
</template>
<script setup lang="ts" name="HelloWorld">import {reactive,toRefs,toRef} from 'vue'let person = reactive({name:'zhangsan',age:18})let {name,age} = toRefs(person) // 把person里边的属性给领出来重新给搞成响应式,我们现在有新的响应式数据name,age,在下边的方法中我们可以发现只要改name,person.name也会跟着改let newName = toRef(person,'name')function changeName(){name.value += '~'}function changeAge(){age.value += 10}
</script>
<style scoped></style>
3.8.computed
页面使用6次,但是只调用一次。这就是使用它的优势。
数据发生改变才进行调用。
<template>姓:<input type="text" v-model="firstName"><br/>名:<input type="text" v-model="secondName"><br/>姓名:<span>{{fullName}}</span><br/>姓名:<span>{{fullName}}</span><br/>姓名:<span>{{fullName}}</span><br/>姓名:<span>{{fullName}}</span><br/>姓名:<span>{{fullName}}</span><br/>姓名:<span>{{fullName}}</span><br/>
</template>
<script setup lang="ts" name="HelloWorld">
import {ref,computed} from 'vue'
let firstName = ref('zhang')
let secondName = ref('san')let fullName = computed(() => {console.log('使用');return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + secondName.value
})
</script>
<style scoped></style>
3.9.watch
只能监听以下四种
- ref定义的数据。
- reactive定义的数据。
- 函数返回一个值(getter函数)。
- 一个包含以上内容的数组。
3.9.1 ref定义的基本数据类型
<template><div>ref定义基本类型的数字:{{ num }}</div><div>名:{{person.name}}</div><div>年龄:{{person.age}}</div><button @click="changeName">修改名字</button><button @click="changeAge">修改名字</button><button @click="changeRef">修改名字</button>
</template>
<script setup lang="ts" name="HelloWorld">import {reactive,watch,ref} from 'vue'let person = reactive({name:'zhangsan',age:18})let num = ref(0)// let numObj = ref({// num:10// })function changeName(){person.name += '~'}function changeAge(){person.age += 10}function changeRef(){num.value += 1}const stopWatch = watch(num,(val,oldVal) => {console.log('获取新的值是',val,oldVal);if(val >=3 ){stopWatch()}})console.log('1991',stopWatch);</script>
<style scoped></style>
3.9.2 ref定义的复杂数据类型
<template><div>ref定义复杂类型的数字:{{ numObj.num }}</div><div>ref定义复杂类型的数字:{{ numObj.name }}</div><button @click="changeRefname">修改名字</button><button @click="changeRefnum">修改数据</button><button @click="changeRefObj">修改整个</button>
</template>
<script setup lang="ts" name="HelloWorld">import {watch,ref} from 'vue'let numObj = ref({num:0,name:'123'})function changeRefname(){numObj.value.name += '~~~~'}function changeRefnum(){numObj.value.num += 2}function changeRefObj(){numObj.value = {num:1,name:'456'}}// 关心整个值,如果想要监听某一个属性,deep:true 另外immediate:true 指的是一上来就开始监听watch(numObj,(val,oldVal) => {console.log('获取新的值是',val,oldVal); // 除非整个对象被改不然新旧值是一样的,因为地址没有改变},{deep:true,immediate:true})</script>
<style scoped></style>
3.9.3 reactive定义的复杂数据类型
<template><div>reactive定义复杂类型的数字:{{ numObj.num }}</div><div>reactive定义复杂类型的数字:{{ numObj.name }}</div><div>车牌子:{{numObj.car.brand}} id:{{ numObj.car.id }}</div>
<button @click="changeRefbrand">修改牌子</button><button @click="changeRefname">修改名字</button><button @click="changeRefnum">修改数据</button><button @click="changeRefObj">修改整个车</button>
</template>
<script setup lang="ts" name="HelloWorld">import {watch,reactive} from 'vue'let numObj = reactive({num:0,name:'123',car:{id:1,brand:'奥迪'}})function changeRefname(){numObj.name += '~~~~'}function changeRefnum(){numObj.num += 2}function changeRefbrand(){numObj.car.brand = '长安'}function changeRefObj(){// reactive定义的对象不可以整体修改numObj.car = {brand:'长安',id:456}// Object.assign(numObj.car,{brand:'456',id:100})}// 监听某一个属性用getter函数(拥有返回值的函数)// watch(numObj.car ,(val,oldVal) => {// console.log('获取新的值是',val,oldVal); // })watch(() => numObj.car ,(val,oldVal) => {console.log('获取新的值是',val,oldVal); },{deep:true})</script>
<style scoped></style>
// reactive定义的对象不可以整体修改 ,可以通过Object.assign()这个是重点欧
// reactive自动深度监听,而且不能关闭
<template><div>reactive定义复杂类型的数字:{{ numObj.num }}</div><div>reactive定义复杂类型的数字:{{ numObj.name }}</div><button @click="changeRefname">修改名字</button><button @click="changeRefnum">修改数据</button><button @click="changeRefObj">修改整个</button>
</template>
<script setup lang="ts" name="HelloWorld">import {watch,reactive} from 'vue'let numObj = reactive({num:0,name:'123'})function changeRefname(){numObj.name += '~~~~'}function changeRefnum(){numObj.num += 2}function changeRefObj(){// reactive定义的对象不可以整体修改// numObj = {num:1,name:'456'}Object.assign(numObj,{name:'456',num:1}) // 没有改变地址}// reactive定义的数据直接深度监听watch(numObj,(val,oldVal) => {console.log('获取新的值是',val,oldVal); })</script>
<style scoped></style>
3.9.4 监听复杂数据中的某一个属性,为基本数据类型
<template><div>reactive定义复杂类型的数字:{{ numObj.num }}</div><div>reactive定义复杂类型的数字:{{ numObj.name }}</div><button @click="changeRefname">修改名字</button><button @click="changeRefnum">修改数据</button><button @click="changeRefObj">修改整个</button>
</template>
<script setup lang="ts" name="HelloWorld">import {watch,reactive} from 'vue'let numObj = reactive({num:0,name:'123'})function changeRefname(){numObj.name += '~~~~'}function changeRefnum(){numObj.num += 2}function changeRefObj(){// reactive定义的对象不可以整体修改// numObj = {num:1,name:'456'}Object.assign(numObj,{name:'456',num:1})}// 监听某一个属性用getter函数(拥有返回值的函数)watch(() => numObj.name ,(val,oldVal) => {console.log('获取新的值是',val,oldVal); })</script>
3.9.5 监听复杂数据中的某一个属性,为复杂数据类型
<template><div>reactive定义复杂类型的数字:{{ numObj.num }}</div><div>reactive定义复杂类型的数字:{{ numObj.name }}</div><div>车牌子:{{numObj.car.brand}} id:{{ numObj.car.id }}</div>
<button @click="changeRefbrand">修改牌子</button><button @click="changeRefname">修改名字</button><button @click="changeRefnum">修改数据</button><button @click="changeRefObj">修改整个车</button>
</template>
<script setup lang="ts" name="HelloWorld">import {watch,reactive} from 'vue'let numObj = reactive({num:0,name:'123',car:{id:1,brand:'奥迪'}})function changeRefname(){numObj.name += '~~~~'}function changeRefnum(){numObj.num += 2}function changeRefbrand(){numObj.car.brand = '长安'}function changeRefObj(){// reactive定义的对象不可以整体修改numObj.car = {brand:'长安',id:456}// Object.assign(numObj.car,{brand:'456',id:100})}// 监听某一个属性用getter函数(拥有返回值的函数)// watch(numObj.car ,(val,oldVal) => {// console.log('获取新的值是',val,oldVal); // })watch(() => numObj.car ,(val,oldVal) => {console.log('获取新的值是',val,oldVal); },{deep:true})</script>
<style scoped></style>
3.9.6 监听一个数组
<template><div>reactive定义复杂类型的数字:{{ numObj.num }}</div><div>reactive定义复杂类型的数字:{{ numObj.name }}</div><div>车牌子:{{numObj.car.brand}} id:{{ numObj.car.id }}</div>
<button @click="changeRefbrand">修改牌子</button><button @click="changeRefname">修改名字</button><button @click="changeRefnum">修改数据</button><button @click="changeRefObj">修改整个车</button>
</template>
<script setup lang="ts" name="HelloWorld">import {watch,reactive} from 'vue'let numObj = reactive({num:0,name:'123',car:{id:1,brand:'奥迪'}})function changeRefname(){numObj.name += '~~~~'}function changeRefnum(){numObj.num += 2}function changeRefbrand(){numObj.car.brand = '长安'}function changeRefObj(){// reactive定义的对象不可以整体修改numObj.car = {brand:'长安',id:456}// Object.assign(numObj.car,{brand:'456',id:100})}// 监听某一个属性用getter函数(拥有返回值的函数)// watch(numObj.car ,(val,oldVal) => {// console.log('获取新的值是',val,oldVal); // })watch([() => numObj.car,() => numObj.name] ,(val,oldVal) => {console.log('获取新的值是',val,oldVal); },{deep:true})</script>
<style scoped></style>
3.10.watchEffect
- watch和watchEffect都能监听,只不过监听数据变化方式不一样
- watch需要明确监听的数据
- watchEffect:不用明确指出监视的数据(函数中用到哪些,就监听哪些属性)
<template><div>reactive定义复杂类型的数字:{{ numObj.num }}</div><div>reactive定义复杂类型的数字:{{ numObj.name }}</div><div>车牌子:{{numObj.car.brand}} id:{{ numObj.car.id }}</div>
<button @click="changeRefbrand">修改牌子</button><button @click="changeRefname">修改名字</button><button @click="changeRefnum">修改数据</button><button @click="changeRefObj">修改整个车</button><button @click="changeAge1">修改Age1</button><button @click="changeAge2">修改Age2</button><div>age1:{{ age1 }}</div><div>age2:{{ age2 }}</div>
</template>
<script setup lang="ts" name="HelloWorld">import {watch,reactive,watchEffect,ref} from 'vue'let age1 = ref(2)let age2 = ref(10)function changeAge1(){age1.value += 10}function changeAge2(){age2.value += 20}let numObj = reactive({num:0,name:'123',car:{id:1,brand:'奥迪'}})function changeRefname(){numObj.name += '~~~~'}function changeRefnum(){numObj.num += 2}function changeRefbrand(){numObj.car.brand = '长安'}function changeRefObj(){// reactive定义的对象不可以整体修改numObj.car = {brand:'长安',id:456}// Object.assign(numObj.car,{brand:'456',id:100})}// 监听某一个属性用getter函数(拥有返回值的函数)// watch(numObj.car ,(val,oldVal) => {// console.log('获取新的值是',val,oldVal); // })watchEffect(() => {console.log('获取新的值是',age1.value,age2.value); })</script>
<style scoped></style>
3.11.标签ref属性
作用:用于注册模板引用。
- 用于普通Dom标签上,获取的是Dom节点。
- 用于组件标签上,获取的是组件实例对象。
子组件
<template><div>我是ref</div><h2 ref="titles">北京</h2><button @click="getDom">点击获取Dom</button>
</template>
<script setup lang="ts" name="HelloWorld">import {ref,defineExpose} from 'vue'let aaa = ref(100)let bbb = ref(200)let titles = ref()function getDom(){console.log('获取Dom',titles.value);}defineExpose({aaa,bbb})
</script>
<style scoped></style>
父组件
// 组件三大要素
// 结构
<template><h2>asdasd</h2><button @click="getDom">获取ref</button><HelloWorld ref="hellos"></HelloWorld>
</template>
// 交互
<script setup lang="ts" name="App">
import HelloWorld from './components/HelloWorld.vue'
import {ref} from 'vue'
let hellos = ref()
function getDom(){console.log('父亲获取子组件',hellos.value);}
</script>
// 样式
<style scoped>
.firstApp{font-size: 30px;height: 600;text-align: center;margin-top: 100px;
}
</style>
3.12.props
父传子,通过属性传输,子通过defineProps接收
父组件
// 组件三大要素
// 结构
<template><HelloWorld ref="hellos" a="haha" b="heihei" :list="personList"></HelloWorld>
</template>
// 交互
<script setup lang="ts" name="App">
import HelloWorld from './components/HelloWorld.vue'
import { reactive } from 'vue';
import {type Persons} from './types/index'
// reactive直接用泛型形式写比较优雅 reactive<Person>,如果可有可无的属性y需要在接口中用?:去控制
let personList = reactive<Persons>([{id:1,name:'张三',age:18},{id:2,name:'张四',age:19},{id:3,name:'张五',age:20},
])
</script>
// 样式
<style scoped>
.firstApp{font-size: 30px;height: 600;text-align: center;margin-top: 100px;
}
</style>
子组件
<template>
<!-- <div>子组件{{ a }}</div> -->
<ul><li v-for = 'item in list' :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script setup lang="ts" name="HelloWorld">
// defineProps 是宏函数 不用非得引入
import { defineProps,withDefaults } from 'vue';
import { type Persons } from '../types';
// 只接受
// defineProps(['list'])// 接受list并限制类型
// defineProps<{list:Persons}>()// 接受list 并限制类型 + 限制必要性 + 指定默认值 ?指的是是否可传withDefaults(defineProps<{list?:Persons}>(),{list:()=> [{id:5,name:'wang',age:40}]
})// 接受并保存
// let x = defineProps(['a','b','list'])
// console.log(x);</script>
<style scoped></style>
types
// 定义接口 限制person的具体属性
export interface PersonInter {name:string,age:number,id:number,// y?:number
}// export type Persons = Array<PersonInter>
export type Persons = PersonInter[]
3.13.生命周期
组件的生命周期:创建,挂载,更新,销毁
Vue2
beforeCreate created
beforeMounte mounted
beforeUpdate updated
beforeDestroy destroyed v-show 与 v-if 在这里有本质的区别
Vue3
setup 创建阶段
onBeforeMount(() => {})
onMounted
onBeforeUpdate
onUpdated
onBeforeUnmount
onUnmounted
挂载顺序是先父后子再是子然后是父,父子子父的顺序
<template>
<!-- <div>子组件{{ a }}</div> -->
<ul><li v-for = 'item in list' :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script setup lang="ts" name="HelloWorld">
// defineProps 是宏函数 不用非得引入
import { defineProps,withDefaults,onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';
import { type Persons } from '../types';
// 只接受
// defineProps(['list'])// 接受list并限制类型
// defineProps<{list:Persons}>()// 接受list 并限制类型 + 限制必要性 + 指定默认值 ?指的是是否可传withDefaults(defineProps<{list?:Persons}>(),{list:()=> [{id:5,name:'wang',age:40}]
})onBeforeMount(() => {console.log('挂载前');})onMounted(() => {console.log('挂载后');
})onBeforeUpdate(() => {console.log('更新前');})onUpdated(() =>{console.log('更新后');
})onBeforeUnmount(() => {console.log('卸载前');})onUnmounted(()=>{console.log('卸载后');
})
// 接受并保存
// let x = defineProps(['a','b','list'])
// console.log(x);</script>
<style scoped></style>
3.14.自定义hooks
useDog.ts
// defineProps 是宏函数 不用非得引入
import { reactive } from 'vue';
import axios from 'axios';
export default function(){let dogList = reactive<string[]>(['https://images.dog.ceo/breeds/pembroke/n02113023_3337.jpg'])// function getDog(){// // axios.get('https://images.dog.ceo/breeds/pembroke/n02113023_3337.jpg').then(// // response => {// // console.log(response);// // },// // error => {// // console.log(error);// // }// // )async function getDog(){try { let response = await axios.get('https://dog.ceo/api/breed/pembroke/images/random'); // 假设响应体有一个属性(比如 'message')包含图片 URL // 注意:这里的属性名应该根据您的实际 API 响应来调整 let dogUrl = response.data.message; console.log('123',dogUrl);dogList.push(dogUrl); } catch (error) { console.error('Error fetching dog image URL:', error); } }return {getDog,dogList}
}
useSum
// defineProps 是宏函数 不用非得引入
import { ref,onMounted,computed } from 'vue';export default function(){let sum = ref(0)let bigSum = computed(() => {return sum.value*10}) function add(){sum.value += 1}onMounted(()=>{console.log('加载完成');})return {sum,add,bigSum}
}// 图片接口 https://dog.ceo/api/breed/pembroke/images/random
3.15. 回顾Ts
接口:
// 定义接口 限制person的具体属性
export interface PersonInter {name:string,age:number,id:number
}// export type Persons = Array<PersonInter>
export type Persons = PersonInter[]
页面使用接口:
<template>
<div>Ts</div>
<div>{{ person.name }}</div>
<button @click="changeName">改名字</button>
</template>
<script setup lang="ts" name="HelloWorld">
import { reactive } from 'vue';
// import { type PersonInter,type Persons } from '../types'; // 下边写法更好
import type { PersonInter,Persons } from '../types';let person : PersonInter = reactive({id:1,name:'wang',age:60}) // ts
// 第一种写法
// let personList:Array<PersonInter> = reactive([
// {id:1,name:'wang',age:60},
// {id:2,name:'wang1',age:60},
// {id:3,name:'wang2',age:60},
// ])
// 第二种写法
// let personList:PersonInter[] = reactive([
// {id:1,name:'wang',age:60},
// {id:2,name:'wang1',age:60},
// {id:3,name:'wang2',age:60},
// ])
// 第三种写法
let personList:Persons = reactive([{id:1,name:'wang',age:60},{id:2,name:'wang1',age:60},{id:3,name:'wang2',age:60},
])
function changeName(){person.name = 'aaa'console.log(personList[0]);}</script>
<style scoped></style>
4.路由
4.1初体验
1.导航区,展示区布局好。
2.生成路由器。
3.制定路由规则。
4.形成一个个的Vue文件。
router/index
// 创建路由器// 第一步:引入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:createWebHistory(),// 路由规则routes:[{path:'/home',component:Home},{path:'/about',component:About},{path:'/news',component:News}]
})// 暴露
export default router
main.ts
import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'// 引入路由器
import router from './router/index'let app = createApp(App)
app.use(router)
app.mount('#app')
App.vue
// 组件三大要素
// 结构
<template>
<div><h2>Vue路由测试</h2><div class="navigate"><RouterLink to="/home" class="item" active-class="jihuoshi">首页</RouterLink><RouterLink to="/news" class="item" active-class="jihuoshi">新闻</RouterLink><RouterLink to="/about" class="item" active-class="jihuoshi">关于</RouterLink></div><div class="main-content">此处以后需要展示各种内容<RouterView></RouterView></div>
</div>
</template>
// 交互
<script setup lang="ts" name="App">
import { RouterView,RouterLink } from 'vue-router';
</script>
// 样式
<style scoped>
.navigate{.item{margin-left: 30px;}.jihuoshi{color: red;}
}
.main-content{height: 200px;background: rgb(213, 210, 210);/* opacity: 0.2; */
}
</style>
4.2注意点(工程化)
1.路由组件通常放在pages或者views文件下,一般组件放在components
2.通过点击导航,视觉效果上消失了的路由,默认是销毁的,需要的时候需要再去挂载。
4.3to的两种写法
<RouterLink to="/home" class="item" active-class="jihuoshi">首页</RouterLink><RouterLink to="/news" class="item" active-class="jihuoshi">新闻</RouterLink><RouterLink to="/about" class="item" active-class="jihuoshi">关于</RouterLink><RouterLink :to="{name:'news'}" class="item" active-class="jihuoshi">新闻</RouterLink><RouterLink :to="{path:'/home'}" class="item" active-class="jihuoshi">首页</RouterLink><RouterLink :to="{name:'about'}" class="item" active-class="jihuoshi">关于</RouterLink>
// 创建路由器// 第一步:引入createRouter
import { createRouter,createWebHistory,createWebHashHistory } 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:createWebHashHistory(),// 路由规则routes:[{name:'home',path:'/home',component:Home},{name:'about',path:'/about',component:About},{name:'news',path:'/news',component:News}]
})// 暴露
export default router
4.4路由的工作模式
History模式
url更加美观,无#,更贴近传统的网站URL,缺点需要服务器配合处理路径问题,否则刷新会有404
Vue2:mode:'history'
Vue3:history:createWebHistory()
React:BrowserRouter
Hash模式
兼容性更好,不需要服务器处理路径
Vue2:mode:'hash'
Vue3:history:createWebHashHistory()
4.5嵌套路由
// 创建路由器// 第一步:引入createRouter
import { createRouter,createWebHistory,createWebHashHistory } from "vue-router";
// 引入一个个组件
import Home from '../components/Home.vue'
import About from '../components/About.vue'
import News from '../components/News.vue'import Details from '../components/Details.vue'// 第二步 创建路由器
const router = createRouter({// 路由模式history:createWebHashHistory(),// 路由规则routes:[{name:'home',path:'/home',component:Home},{name:'about',path:'/about',component:About},{name:'news',path:'/news',component:News,children:[{path:'details',component:Details},]}]
})// 暴露
export default router
news.vue
<template><div><!-- 导航区 --><ul><li v-for="item in newsList" :key="item.id"><RouterLink :to="{path:'/news/details'}">{{ item.title }}</RouterLink></li></ul></div><div>新闻内容<RouterView></RouterView></div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterLink, RouterView } from 'vue-router';const newsList = reactive([{id:1,title:'sadasda1',content:'西瓜1'},{id:2,title:'sadasda2',content:'西瓜2'},{id:3,title:'sadasda3',content:'西瓜3'},{id:4,title:'sadasda4',content:'西瓜4'},
])
</script>
4.6query传参
<template><div><!-- 导航区 --><ul><li v-for="item in newsList" :key="item.id"><!-- 第一种写法 --><!-- <RouterLink :to="`/news/details?item=${item.content}`">{{ item.title }}</RouterLink> --><!-- 第二种写法 --><RouterLink :to="{path:'/news/details',query:{item:item.content}}">{{ item.title }}</RouterLink></li></ul></div><div>新闻内容<RouterView></RouterView></div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterLink, RouterView } from 'vue-router';const newsList = reactive([{id:1,title:'sadasda1',content:'西瓜1'},{id:2,title:'sadasda2',content:'西瓜2'},{id:3,title:'sadasda3',content:'西瓜3'},{id:4,title:'sadasda4',content:'西瓜4'},
])
</script>
<template><div>{{ query.item }}</div>
</template>
<script setup lang="ts" name="Details">
import { toRefs } from 'vue';
import { useRoute } from 'vue-router';
let route = useRoute()
// 必须使用toRefs ,否则失去了响应式
let {query} = toRefs(route)
console.log('使用路由',route.query.item);</script>
4.6params传参
<template><div><!-- 导航区 --><ul><li v-for="item in newsList" :key="item.id"><!-- 第一种写法 --><!-- <RouterLink :to =" `/news/details/${item.content}`">{{ item.title }}</RouterLink> --><!-- 第一种写法 --><RouterLink :to ="{name:'details',params:{item:item.content}}">{{ item.title }}</RouterLink></li></ul></div><div>新闻内容<RouterView></RouterView></div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterLink, RouterView } from 'vue-router';const newsList = reactive([{id:1,title:'sadasda1',content:'西瓜1'},{id:2,title:'sadasda2',content:'西瓜2'},{id:3,title:'sadasda3',content:'西瓜3'},{id:4,title:'sadasda4',content:'西瓜4'},
])
</script>
<template><div>{{ route.params.item }}</div>
</template>
<script setup lang="ts" name="Details">
// import { toRefs } from 'vue';
import { useRoute } from 'vue-router';
let route = useRoute()
// 必须使用toRefs ,否则失去了响应式
// let {params} = toRefs(route)
// console.log('使用路由',route.query.item);</script>
备注1:传递params参数时,使用to,必须使用name,不能使用path
备注2:传递params参数时候,需要提前在路由规则处占位
{name:'news',path:'/news',component:News,children:[{path:'details/:item',component:Details,name:'details'}]}
4.7路由的props传参
4.7.1路由加上props为true,只能处理params参数
{name:'news',path:'/news',component:News,children:[{path:'details/:item',component:Details,name:'details',props:true}]}
页面不再使用useRoute了
<template><div>{{ item }}</div>
</template>
<script setup lang="ts" name="Details">
// import { toRefs } from 'vue';
// import { useRoute } from 'vue-router';
// import { toRefs } from 'vue';
// let route = useRoute()
// let {params} = toRefs(route)
// 一步搞定 其实我们可以认为路由收到params参数的时候,如果props为true,那么就会放在Vue组件上,有意思
defineProps(['item'])
// 必须使用toRefs ,否则失去了响应式
// let {params} = toRefs(route)
// console.log('使用路由',route.query.item);</script>
4.7.2处理query参数
{name:'news',path:'/news',component:News,children:[{path:'details',component:Details,name:'details',props(route){return route.query}}]}
<template><div>{{ item }}</div>
</template>
<script setup lang="ts" name="Details">
// import { toRefs } from 'vue';
// import { useRoute } from 'vue-router';
// // import { toRefs } from 'vue';
// let route = useRoute()
// let {query} = toRefs(route)
defineProps(['item'])
// 必须使用toRefs ,否则失去了响应式
// let {params} = toRefs(route)
// console.log('使用路由',route.query.item);</script>
4.7.3 自己决定传什么参数
{name:'news',path:'/news',component:News,children:[{path:'details',component:Details,name:'details',props(route){return {item:'自己定义'}}}]}
总结:一般组件可以自己写标签,属性自己加,props自己传,路由组件呢,就是在上边加上喽。本质就是这样的。
4.8 replace属性
replace其实就是替换,push就是加入栈了,方便用户在页面上进行页面切换。
<RouterLink replace to="/home" class="item" active-class="jihuoshi">首页</RouterLink><RouterLink replace to="/news" class="item" active-class="jihuoshi">新闻</RouterLink><RouterLink replace to="/about" class="item" active-class="jihuoshi">关于</RouterLink>
4.9 编程式导航
import {useRouter} from 'vue-route'
let router = useRouter()
router.push('/news')
router.replace('/news')
传参就按照to属性去写
<template><div><!-- 导航区 --><ul><li v-for="item in newsList" :key="item.id"><button @click="toNews(item)">点我带你编程式导航并传参</button><!-- 第一种写法 --><!-- <RouterLink :to =" `/news/details/${item.content}`">{{ item.title }}</RouterLink> --><!-- 第一种写法 --><RouterLink :to ="{name:'details',query:{item:item.content,}}">{{ item.title }}</RouterLink></li></ul></div><div>新闻内容<RouterView></RouterView></div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterLink, RouterView,useRouter } from 'vue-router';interface NewsInter{content:string
}const newsList = reactive([{id:1,title:'sadasda1',content:'西瓜1'},{id:2,title:'sadasda2',content:'西瓜2'},{id:3,title:'sadasda3',content:'西瓜3'},{id:4,title:'sadasda4',content:'西瓜4'},
])const router = useRouter()
function toNews(item:NewsInter){router.push({name:'details',query:{item:item.content,}})
}
</script>
4.10 路由的重定向
// 创建路由器// 第一步:引入createRouter
import { createRouter,createWebHistory,createWebHashHistory } from "vue-router";
// 引入一个个组件
import Home from '../components/Home.vue'
import About from '../components/About.vue'
import News from '../components/News.vue'import Details from '../components/Details.vue'// 第二步 创建路由器
const router = createRouter({// 路由模式history:createWebHashHistory(),// 路由规则routes:[{name:'home',path:'/home',component:Home},{name:'about',path:'/about',component:About},{path:'/',redirect:'/news'},{name:'news',path:'/news',component:News,children:[{path:'details',component:Details,name:'details',props(route){return {item:route.query.item}}}]},]
})// 暴露
export default router
5.pinia
符合直觉的Vue.js状态管理库,大白话就是项目帮助你全局存数据
准备一个页面,写的的比较简单。
<template><div><div>我是数字:{{num}}</div><button @click="getWords">获取撩妹情话</button><div>{{ wordss }}</div></div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import axios from 'axios';
let num = ref(110)
let wordss = ref<string>('')async function getWords(){// 两次结构赋值再加重命名let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')wordss.value = title}
5.1 搭建pinia环境
第一步 npm i pinia
第二步 main.js
import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'// 引入路由器
import router from './router/index'// 引入pinia
import {createPinia} from 'pinia'let app = createApp(App)// 创建pinia
const pinia = createPinia()
// 项目使用pinia
app.use(pinia)app.use(router)
app.mount('#app')
5.2 简单存储加上读取
import { defineStore } from "pinia";export const useCountStore = defineStore('count',{// 真正存储的地方state(){return {sum:6}}
})
<template><div><div>我是数字:{{countStore.sum}}</div><button @click="getWords">获取撩妹情话</button><div>{{ wordss }}</div></div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'// 使用countStore
let countStore = useCountStore()
// reactive 内部 有ref ,自动拆包不用加value
console.log('获取内容', countStore.sum);let wordss = ref<string>('')async function getWords(){// 两次结构赋值再加重命名let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')wordss.value = title}</script>
5.3 修改数据的三种方式
import { defineStore } from "pinia";export const useCountStore = defineStore('count',{actions:{editSum(value:number){this.$state.sum += valuethis.$state.country = '中国'this.$state.name = '张三'}},// 真正存储的地方state(){return {sum:6,country:'china',name:'zhansan'}}
})
<template><div><div>我是数字:{{countStore.sum}}</div><div>国家是:{{countStore.country}}</div><div>名字是:{{countStore.name}}</div><button @click="addSum">sum+10</button><br><button @click="getWords">获取撩妹情话</button><div>{{ wordss }}</div></div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'// 使用countStore
let countStore = useCountStore()function addSum(){// 第一种修改方式// countStore.sum += 10// countStore.country = '中国'// countStore.name = '张三'// 第二种批量修改 数量多的时候需要用,效能更高一些// countStore.$patch({// sum:countStore.sum += 10,// country:'中国',// name:'张三'// })// 第三种 actioncountStore.editSum(10)
}
// reactive 内部 有ref ,自动拆包不用加value
console.log('获取内容', countStore.sum);let wordss = ref<string>('')async function getWords(){// 两次结构赋值再加重命名let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')wordss.value = title}</script>
actions复用最舒服
5.4 storeToRefs
<template><div><div>我是数字:{{sum}}</div><div>国家是:{{country}}</div><div>名字是:{{name}}</div><button @click="addSum">sum+10</button><br><button @click="getWords">获取撩妹情话</button><div>{{ wordss }}</div></div>
</template>
<script setup lang="ts">
import {ref,toRefs } from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'
import { storeToRefs } from 'pinia';// 使用countStore
let countStore = useCountStore()
// 千万别用,这样太累了
// let {sum,country,name} = toRefs(countStore)// 需要过滤一下
let {sum,country,name} = storeToRefs(countStore)function addSum(){// 第一种修改方式// countStore.sum += 10// countStore.country = '中国'// countStore.name = '张三'// 第二种批量修改 数量多的时候需要用,效能更高一些// countStore.$patch({// sum:countStore.sum += 10,// country:'中国',// name:'张三'// })// 第三种 actioncountStore.editSum(10)
}
// reactive 内部 有ref ,自动拆包不用加value
console.log('获取内容', countStore.sum);let wordss = ref<string>('')async function getWords(){// 两次结构赋值再加重命名let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')wordss.value = title}</script>
5.5 getters 的 使用
本质就是修饰一下state的数据形成一个新的state,从而进行使用修饰过后的store数据。
import { defineStore } from "pinia";export const useCountStore = defineStore('count',{actions:{editSum(value:number){this.$state.sum += valuethis.$state.country = '中国'this.$state.name = '张三'}},// 真正存储的地方state(){return {sum:6,country:'china',name:'zhansan'}},getters:{bigSum:(state) => state.sum*10,// bigSum(state){// return state.sum*10// },daXie():string{return this.country.toUpperCase()}}
})
<template><div><div>getters:{{ bigSum }}</div><div>我是数字:{{sum}}</div><div>国家是:{{country}}</div><div>国家名:{{daXie}}</div><div>名字是:{{name}}</div><button @click="addSum">sum+10</button><br><button @click="getWords">获取撩妹情话</button><div>{{ wordss }}</div></div>
</template>
<script setup lang="ts">
import {ref,toRefs } from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'
import { storeToRefs } from 'pinia';// 使用countStore
let countStore = useCountStore()
// 千万别用,这样太累了
// let {sum,country,name} = toRefs(countStore)// 需要过滤一下
let {sum,country,name,bigSum,daXie} = storeToRefs(countStore)function addSum(){// 第一种修改方式// countStore.sum += 10// countStore.country = '中国'// countStore.name = '张三'// 第二种批量修改 数量多的时候需要用,效能更高一些// countStore.$patch({// sum:countStore.sum += 10,// country:'中国',// name:'张三'// })// 第三种 actioncountStore.editSum(10)
}
// reactive 内部 有ref ,自动拆包不用加value
console.log('获取内容', countStore.sum);let wordss = ref<string>('')async function getWords(){// 两次结构赋值再加重命名let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')wordss.value = title}</script>
5.5 $subscribe 监听 store里边的数据修改
其实本质就是watch,监听数据变化的
<template><div><div>getters:{{ bigSum }}</div><div>我是数字:{{sum}}</div><div>国家是:{{country}}</div><div>国家名:{{daXie}}</div><div>名字是:{{name}}</div><button @click="addSum">sum+10</button><br><button @click="getWords">获取撩妹情话</button><div>{{ wordss }}</div></div>
</template>
<script setup lang="ts">
import {ref,toRefs } from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'
import { storeToRefs } from 'pinia';// 使用countStore
let countStore = useCountStore()
// 订阅,监听
countStore.$subscribe((mutate,state)=>{console.log('数据发生变化了',mutate,state);})
5.6 store 的 组合式写法
import { defineStore } from "pinia";// export const useCountStore = defineStore('count',{
// actions:{
// editSum(value:number){
// this.$state.sum += value
// this.$state.country = '中国'
// this.$state.name = '张三'
// }
// },
// // 真正存储的地方
// state(){
// return {
// sum:6,
// country:'china',
// name:'zhansan'
// }
// },
// getters:{
// bigSum:(state) => state.sum*10,
// // bigSum(state){
// // return state.sum*10
// // },
// daXie():string{
// return this.country.toUpperCase()
// }
// }
// }) import {ref} from 'vue'// 相当于setup
export const useCountStore = defineStore('count',() => {// 相当于stateconst sum = ref(6)const country = ref('china')const name = ref('zhansan')const bigSum = ref(0)bigSum.value = sum.value * 10const daXie = ref('')daXie.value = country.value.toUpperCase()// 相当于actionsfunction editSum(value:number){sum.value += valuecountry.value = '中国'name.value = '张三'}return {sum,country,name,editSum,bigSum,daXie}})
6.组件通信
6.1 props 父传子 --非函数传值
1.父组件 传值 <HomeChild a="father"/>
2.子组件 收值 defineProps(['a'])
6.2 props子传父 -- 函数传值
1.父组件 <HomeChild a="father" :sendToy="getToy"/>
function getToy(value:string){
console.log(value);
childToy.value = value
}
2.子组件 <button @click="sendToy(toy)">告诉父亲</button>
6.3 自定义事件 (专门用来子传父)
1.子组件定义自定义事件并在合适的时机触发
const emit = defineEmits(['childEvent'])
function sendBook(){
emit('childEvent','数学')
}
2.父组件调用此自定义函数并且拿着一个回调函数接收这个值
<HomeChild a="father" :sendToy="getToy" @childEvent="qwe"/>
function qwe(val:string){
console.log('111111111111',val);
}
6.4 mitt
pubsub;$bus;mitt
提前绑好事件(提前订阅事件)
提供数据(合适时机触发事件,发布消息)
第一步 npm i mitt
第二步 引入
// 引入
import mitt from 'mitt'// 创造
let emitter:any = mitt()
console.log('获取2m',emitter);emitter.on('test1',(value:number)=>{console.log('asd','被调用',value);
})
setInterval(() => {emitter.emit('test1',10)
},1000)
setTimeout(() => {emitter.all.clear()
},3000)
// 暴漏
export default emitter
import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'// 引入路由器
import router from './router/index'import emitter from '../src/utils/emitter'// 引入pinia
import {createPinia} from 'pinia'let app = createApp(App)// 创建pinia
const pinia = createPinia()
// 项目使用pinia
app.use(pinia)
app.use(emitter)
app.use(router)
app.mount('#app')
另外记得在在组件卸载的时候把emitter事件取消掉 emitter.off('text1') // 避免内存泄漏
6.5 v-model
父组件
<template><div>about页面<!-- v-model用在html标签上 --><!-- <input type="text" v-model="name"/> --><!-- (<HTMLInputElement>$event.target) <HTMLInputElement> 断言是html元素--><!-- <input type="text" :value="name" @input="name = (<HTMLInputElement>$event.target).value"/> --><!-- v-model 用在组件上 --><div>父组件呈现{{name}}</div><!-- <MyInput v-model='name' /> --><!-- <MyInput :modelValue="name" @update:modelValue="name = $event"/> --><!-- 一定记得input --><!-- 自定义写法 --><MyInput v-model:ZIDINGYI='name' /></div>
</template>
<script lang="ts" setup name="About">
import {onUpdated, ref} from 'vue'import MyInput from '../components/mynput.vue';let name=ref('张三')
onUpdated(()=>{console.log('12121', name.value);
})
</script>
子组件
<!-- <template><br>子组件<input type="text" :value="modelValue"@input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)"/>
</template>
<script setup lang="ts" name="MyInput">
defineProps(['modelValue'])
let emit = defineEmits(['update:modelValue'])
</script> --><template><br>子组件<input type="text" :value="ZIDINGYI"@input="emit('update:ZIDINGYI',(<HTMLInputElement>$event.target).value)"/>
</template>
<script setup lang="ts" name="MyInput">
defineProps(['ZIDINGYI'])
let emit = defineEmits(['update:ZIDINGYI'])
</script>
注意看自定义的写法
其实Ui底层大量使用了:modelValue 以及@update-modelValude
对于原生事件,$event就是事件对象 需要target
对于自定义事件,$event就是触发事件时,所传递的数据 不需要target
本质使用了父传子子传父
6.6 $attrs
祖孙传值
<MyInput v-model:ZIDINGYI='name' a="haha" v-bind="{b:'100',c:'200'}"/>
看一下v-bind,相当于传了多了属性
<GrandChild v-bind="$attrs"/>
<template><div>孙{{ $attrs }}</div>
</template>
<script lang="ts" name="Sun"></script>
6.7 $refs,$parents
$refs 父传子
<template><div>孙{{ $attrs }}<div>密码是:{{password}}</div></div>
</template>
<script setup lang="ts" name="Sun">
import {ref} from 'vue'
let password = ref(123)
defineExpose({password})
</script>
<GrandChild ref="grandChild" v-bind="$attrs"/>import { onMounted,ref } from 'vue';
import GrandChild from './SunZi.vue'
let grandChild = ref()
onMounted(() => {console.log('555555555555',grandChild.value.password = '456');})
在父组件可以获取多了子组件,进行值的修改
$parents 子传父
1.defineExpose({parentNum}) 父组件暴漏数据
2.$parent.parentNum 子组件直接使用
6.8 provide ,inject
本质不打扰子组件
祖先组件
import {onUpdated, ref,provide,reactive} from 'vue'
let car = reactive({brand:'奥迪',num:10
})
provide('car',car)
孙组件
import {onMounted, ref,inject} from 'vue'
// {brand:'宝贝',num:0} 给默认值解决爆红问题
let car = inject('car',{brand:'宝贝',num:0})
祖先组件
import {onUpdated, ref,provide,reactive} from 'vue'import MyInput from '../components/mynput.vue';
let money = ref(10013)
function updateMoney(value:number){money.value += value
}
provide('money',{money,updateMoney})
孙组件
<template><div>孙<button @click="updateMoney(6)">修改爷爷的钱</button></div>
</template>
<script setup lang="ts" name="Sun">
import {onMounted, ref,inject} from 'vue'
let {money,updateMoney} = inject('money',{money:0,updateMoney:(params:number)=>{console.log(params)}})
6.9 slot
默认插槽
<template><GrandChild><span>你好</span></GrandChild><GrandChild><span>你好a啊</span></GrandChild><GrandChild><span>天气好啊好a啊</span></GrandChild>
</template>
<script setup lang="ts" name="MyInput">
import GrandChild from './SunZi.vue'
</script>
<template><div>孙<slot></slot></div>
</template>
<script setup lang="ts" name="Sun"></script>
具名插槽
其实默认插槽 name='default'
<template><!-- 第一种写法 --><!-- <GrandChild ><template v-slot:s2><span>你好</span></template><template v-slot:s1><span>,好啊</span></template></GrandChild> --><GrandChild ><template #s2><span>你好</span></template><template #s1><span>,好啊</span></template></GrandChild>
</template>
<script setup lang="ts" name="MyInput">
import GrandChild from './SunZi.vue'
</script>
<template><div>孙<slot name="s2"></slot><slot name="s1"></slot><slot name="s3"></slot></div>
</template>
<script setup lang="ts" name="Sun"></script>
作用域插槽
数据在子组件上,怎么显示由父亲决定
<template><!-- 第一种写法 --><!-- <GrandChild ><template v-slot="params"><span>{{ params.age }}</span></template></GrandChild> --><GrandChild ><template v-slot:qwe="{age}" ><span>{{ age }}</span></template></GrandChild><!-- 当无name,那就是默认名default --><!-- <GrandChild ><template v-slot:default="{age}" ><span>{{ age }}</span></template></GrandChild><GrandChild ><template #default="{age}" ><span>{{ age }}</span></template></GrandChild> -->
</template>
<script setup lang="ts" name="MyInput">
import GrandChild from './SunZi.vue'
</script>
<template><div>孙<slot age="18" name="qwe"></slot></div>
</template>
<script setup lang="ts" name="Sun"></script>
组件关系 | 传递方式 |
父传子 | 1.props 2.v-model 3.$refs 4.默认插槽,具名插槽 |
子传父 | 1.props 2.自定义事件 3.v-model 4.$parent 5.作用域插槽 |
祖传孙,孙传祖 | 1.$attrs 2.provide,inject |
兄弟之间传输,任意组件传输 | 1.mitt 2.pinia |
7.其他API
7.1 shallowRef与shallowReactive
shallowRef
作用:创建一个响应式数据,但只对顶层属性进行响应式处理。
用法:let myVal = shallowRef(initialValue)
特点:只是跟踪引用值的变化,不关心值内部的属性变化。
shallowReactive
作用:创建一个响应式数据,但只对顶层属性进行响应式处理,对象内部嵌套属性则不会变成响应式。
用法:let myVal = shallowReactive({......})
特点:只是跟踪引用值的变化,不关心值内部的属性变化。
7.2 readonly与shallowReadonly
readonly
作用:用于创建一个对象的深只读副本。
用法:const original = reactive({...})
const readOnlyCopy = readonly(original)
特点:
对象的所有嵌套属性都将变成只读状态。
任何修改对象被阻止
应用场景:
创建不可变的状态快照。
保护全局状态或者配置不被修改。
shallowReadonly
浅层只读,深层可以改
7.3 toRow与markRaw
toRow
作用:用于获取一个响应式的数据对象的原始对象,返回的也不再是响应式,不触发视图更新。
场景:不想让别人改。
markRaw
作用:标记一个对象,使其永远不会变成响应式的。
例如:使用mock.js时,为了防止误把mock.js变成响应式对象。可以使用markRaw去标记mock.js.
7.4 customRef
后续内容继续补充
相关文章:

Vite创建Vue3项目以及Vue3相关基础知识
1.创建Vue3项目 1.运行创建项目命令 # 使用 npm npm create vitelatest2、填写项目名称 3、选择前端框架 4、选择语法类型 5、按提示运行代码 不出意外的话,运行之后应该会出现 下边这个页面 6.延伸学习:对比webpack和vite(这个是面试必考…...

Elasticsearch封装公共索引增删改查
什么是索引? 定义:索引是 Elasticsearch 中用于存储数据的逻辑命名空间。它由多个文档组成,每个文档是一个 JSON 格式的结构化数据对应关系:在关系数据库中,索引类似于表;而在 Elasticsearch 中࿰…...

Python异常检测:Isolation Forest与局部异常因子(LOF)详解
这里写目录标题 Python异常检测:Isolation Forest与局部异常因子(LOF)详解引言一、异常检测的基本原理1.1 什么是异常检测?1.2 异常检测的应用场景 二、Isolation Forest2.1 Isolation Forest的原理2.1.1 算法步骤 2.2 Python实现…...

Git的原理和使用(二)
1. git的版本回退 之前我们也提到过,Git 能够管理⽂件的历史版本,这也是版本控制器重要的能⼒。如果有⼀天你发现 之前前的⼯作做的出现了很⼤的问题,需要在某个特定的历史版本重新开始,这个时候,就需要版本 回退的功能…...

docker 发布镜像
如果要推广自己的软件,势必要自己制作 image 文件。 1 制作自己的 Docker 容器 基于 centos 镜像构建自己的 centos 镜像,可以在 centos 镜像基础上,安装相关的软件,之后进行构建新的镜像。 1.1 dockerfile 文件编写 首先&…...

投了15亿美元,芯片创新公司Ampere为何成了Oracle真爱?
【科技明说 | 科技热点关注】 一个数据库软件公司却想要操控一家芯片厂商,这样的想法不错。也真大胆。 目前,全球数据库巨头甲骨文Oracle已经持有Ampere Computing LLC 29%的股份,并有可能通过未来的投资选择权获得对这家芯片制造…...

vue 报告标题时间来自 elementUI的 el-date-picker 有开始时间和结束时间
要在Vue中使用 Element UI 的 el-date-picker 来选择开始时间和结束时间,并将其展示在报告中,以下是详细的实现步骤。 实现思路: 使用 Element UI 的 el-date-picker 组件,让用户选择时间范围(开始时间和结束时间&am…...

简单几何问题的通解
来,这道题怎么做?边长为2的正方形内,2个扇形的交集面积是多少?这道题一定要画辅助线,因为要用到两个扇形的交点,如果不画辅助线,这个交点相关的4个子图一个都无法求出面积,只能求出子…...

DBeaver导出数据表结构和数据,导入到另一个环境数据库进行数据更新
在工作中,我们会进行不同环境之间数据库的数据更新,这里使用DBeaver导出新的数据表结构和数据,并执行脚本,覆盖另一个环境的数据库中对应数据表,完成数据表的更新。 一、导出 右键点击选中想要导出的数据表࿰…...

【Golang】合理运用泛型,简化开发流程
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...

OpenCV单目相机内参标定C++
基于OpenCV 实现单目相机内参标定: a.使用OpenCV库实现内参标定过程。通过角点检测、亚像素角点定位、角点存储与三维坐标生成和摄像机标定分别获取左右相机的内参。 b.具体地,使用库函数检测两组图像(左右相机拍摄图像)中棋盘格…...

基于MATLAB(DCT DWT)
第三章 图像数字水印的方案 3.1 图像数字水印的技术方案 在数据库中存储在国际互联网上传输的水印图像一般会被压缩,有时达到很高的压缩比。因此,数字水印算法所面临的第一个考验就是压缩。JPEG和EZW(Embedded Zero-Tree Wavelet࿰…...

渗透基础-rcube_webmail版本探测
简介 本文介绍了开源产品RoundCube webmail邮件系统的版本探测思路,并用go语言实现工具化、自动化探测。 正文 0x01 探测思路研究 探测系统版本,最理想的方法就是系统主页html代码中有特定的字符串,比如特定版本对应的hash在主页的html代…...

linux下编译鸿蒙版boost库
我在上一篇文章中介绍了curl和openssl的编译方式(linux下编译鸿蒙版curl、openssl-CSDN博客),这篇再介绍一下boost库的编译。 未经许可,请勿转载! 一.环境准备 1.鸿蒙NDK 下载安装方式可以参考上篇文章,…...

滚雪球学Redis[6.3讲]:Redis分布式锁的实战指南:从基础到Redlock算法
全文目录: 🎉前言🚦Redis分布式锁的概念与应用场景🍃1.1 什么是分布式锁?🍂1.2 应用场景 ⚙️使用Redis实现分布式锁🌼2.1 基本思路🌻2.2 示例代码🥀2.3 代码解析 &#…...

springboot二手汽车交易平台-计算机毕业设计源码82053
目录 1 绪论 1.1研究背景 1.2研究意义 1.3国内外研究现状 2 二手汽车交易平台系统分析 2.1 可行性分析 2.2 系统流程分析 2.3 功能需求分析 2.4 性能需求分析 3 二手汽车交易平台概要设计 3.1 系统体系结构设计 3.2总体功设计 3.3子模块设计设计 3.4 数据库设计 …...

typescript 中的类型推断
在 TypeScript 中,类型推断(Type Inference)是一种编译器自动确定变量或表达式类型的能力。这大大减少了需要显式声明类型的代码量,使得代码更加简洁和易读。TypeScript 的类型推断机制非常强大,可以在很多情况下自动推…...

linux 隐藏文件
在Linux中,隐藏文件以点(.)开头的文件或文件夹被认为是隐藏文件。隐藏文件通常用于存储系统配置文件或敏感文件。 以下是几种不同的方法来隐藏文件或文件夹: 方法1:在文件或文件夹名字前面加上点(.&#…...

【网络协议栈】Tcp协议(上)结构的解析 和 Tcp中的滑动窗口(32位确认序号、32位序号、4位首部长度、6位标记位、16为窗口大小、16位紧急指针)
绪论 “没有那么多天赋异禀,优秀的人总是努力翻山越岭。”本章主要讲到了再五层网络协议从上到下的第二层传输层中使用非常广泛的Tcp协议他的协议字段结构,通过这些字段去认识其Tcp协议运行的原理底层逻辑和基础。后面将会再写一篇Tcp到底是通过什么调…...

手表玻璃盖板视觉贴合
在手表生产过程中,贴合加工是一个至关重要的环节,它涉及将手表的盖板与LCM模组或各种功能片进行精准贴合。这一过程不仅要求高度的精度,还追求效率与稳定性,以满足现代可穿戴设备日益增长的市场需求。然而,当前行业在这…...

电信和互联网行业数据安全评估师CCRC-DSA人才强基计划
“电信和互联网行业数据安全人才强基计划”(以下简称“强基计划”)自 2022 年 4 月启动伊始,始终秉持以人才需求为导向,以体系化能力建设为重点,扎实铸就数据安全人才培养品牌,力促行业数据安全人才培养工作…...

MQTTnet 4.3.7.1207 (最新版)使用体验,做成在线客服聊天功能,实现Cefsharp的物联的功能(如远程打开新网址)
一、MQTTnet 4.3.x版本客户端 将客户端集成到 cefsharp 定制浏览器中,实现物联网功能 网上很多代码是3.x版本代码,和4.x版本差异性较大,介绍较为简单或不系统 二、部分代码说明 初始化,初始化》连接服务端》发布上线信息(遗嘱)ConnectAsync等 订阅主题:SubscribeAsync 接…...

将java项目jar包打包成exe服务
1.结构展示 2.注意事项 前提: 环境准备:jdk8 和 .net支持 { 1.控制面板》程序和功能》启用和关闭windows功能》.net的勾选》2.jdk8自行百度安装环境3.其他项目必须的软件环境安装等(数据库...) }第一次准备: 1.将打包好的jar包放到premiumServices.exe…...

Django请求响应对象
在 Django 中,请求(request)和响应(response)对象是处理 HTTP 请求和返回 HTTP 响应的核心。它们分别由 Django 的 HttpRequest 和 HttpResponse 类表示。 HttpRequest 对象 HttpRequest 对象包含了客户端发送的所有…...

DevExpress中文教程 - 如何在静态SSR模式下使用Blazor Drawer组件?
Microsoft的 .NET 8 UI框架引入了静态服务器端呈现模式(静态SSR)——组件在服务器端呈现,然后返回到客户端,没有任何交互,DevExpress Blazor Drawer组件需要交互式呈现模式来动态地改变其IsOpen状态。 在本文中&#…...

商汤科技十周年公布新战略,将无缝集成算力、模型及应用
10月18日,恰逢商汤科技十周年庆典,“2024商汤十周年国际论坛:迈向AI 2.0共融新时代”在香港科学园成功举办。 据「TMT星球」了解,来自全球的行业领袖、政府代表、AI专家共聚于此,共同探讨AI行业的未来。 活动上&…...

【如何获取股票数据07】Python、Java等多种主流语言实例演示获取股票行情api接口之沪深A股历史分时MA数据获取实例演示及接口API说明文档
最最近一两年内,股票量化分析逐渐成为热门话题。而从事这一领域工作的第一步,就是获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息,这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的主要…...

Rust语法基础
注释 所有的开发者都在努力使他们的代码容易理解,但有时需要额外的解释。在这种情况下,开发者在他们的源码中留下注释,编译器将会忽略掉这些内容,但阅读源码的人可能会发现有用。 和大多数的编程语言一样,主要有一下两种: 单行注释 // 多行注释 /* */ 基本数据类型 Ru…...

AWS WAF实现API安全防护
在当今的互联网环境中,API安全防护变得越来越重要。本文将介绍如何使用AWS WAF(Web Application Firewall)来实现有效的API安全防护策略。 背景 我们有一个API服务,其URL模式如下: https://dev.example.com/bff-app/sec/v1/module-a/feature-a/sub-feature-a我们需要使用AWS…...

vue将table转换为pdf导出
安装依赖: 首先,你需要安装 jspdf 和 html2canvas 这两个库。 npm install jspdf html2canvas创建Vue组件: 创建一个Vue组件,用于显示表格并提供导出PDF的功能。 <template> <div> <div id"table-contain…...