1,setup
vue3中一个新的配置项,值为函数。组件中所用到的数据,方法,生命周期,监视属性,计算属性等都要配置在setup中。
setup函数的两种返回值
1,若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。
2,若返回一个渲染函数:则可以自定义渲染内容。
注意点
1,尽量不要与Vue2.x配置混用
2,setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。
3,vue3不需要最外层的div,可以直接在template里面写
<template>
<h1>vue3</h1>
<h2>{{name}}</h2>
<h2>{{age}}</h2>
<h2>{{sex}}</h2>
<button @click="introduce">vue3入门</button>
</template>
<script>
export default{
setup(){
//数据
let name="张三"
let age=18
let sex="男"
//方法
function introduce(){
console.log(`我的名字叫${name},我今天${age}岁,我的性别是${sex}`)
}
//返回一个对象
return {
name,
age,
sex,
introduce
}
}
}
</script>
2,ref函数
作用:定义一个响应式数据。创建一个包含响应式数据的引用对象(处理对象数据)。
<template>
<h1>vue3</h1>
<h2>{{name}}</h2>
<h2>{{age}}</h2>
<button @click="modify">动态数据-基本数据类型</button>
<h3>工作:{{obj.work}}</h3>
<h3>工资:{{obj.money}}</h3>
<button @click="GoToWork">动态数据-对象引用类型</button>
</template>
<script>
//需要先引入ref
import {ref} from 'vue'
export default{
setup(){
//基本数据类型
let name=ref("张三")
let age=ref(18)
//修改普通数据
function modify(){
//修改namd和age
name.value="李四"
age.value=28
}
//对象类型
let obj=ref({
work:"前端开发",
money:1000
})
function GoToWork(){
obj.value.work="测试"
obj.value.money=2000
}
//返回一个对象
return {
name,
age,
modify,
obj,
GoToWork
}
}
}
</script>
3,reactive函数
作用:定义一个对象类型的响应式数据,基本类型还是用ref
语法:const 代理对象=reactive(源对象)接收一个对象或数据,返回一个代理对象Proxy的实例对象,内部是基于ES6 Proxy来进行实现的,通过代码理对象操作源对象内部数据进行操作。
<template>
<h1>vue3</h1>
<h2>姓名:{{Obj.name}}</h2>
<h2>年纪:{{Obj.age}}</h2>
<h2>工作:{{Obj.job.work}} 我的工资是{{Obj.job.money}}</h2>
<h2>其它:{{Obj.other}}</h2>
<button @click="reactiveTest">reactive测试</button>
</template>
<script>
//要先引入reactive
import {reactive} from 'vue'
export default{
setup(){
//reactive的使用
let Obj=reactive({
name:"李四",
age:18,
job:{
work:"前端开发工程师",
money:1000
},
other:['看书','运动','打球']
})
function reactiveTest(){
Obj.name="王五"
Obj.age=28
Obj.job.work="java开发"
Obj.job.money=1200
Obj.other[2]='唱歌'
}
return {
Obj,
reactiveTest
}
}
}
</script>
如果需要修改源对象,需要使用Object.assign
<template>
<button @click="midifyName">修改名称</button>
<button @click="midifyAge">修改年龄</button>
<button @click="midifyAll">修改整个</button>
<h2>姓名:{{student.name}}</h2>
<h2>年龄:{{student.age}}</h2>
</template>
<script setup>
import {ref,reactive,watch} from 'vue'
let student=reactive({
name:"张三",
age:18
})
const midifyName=(()=>{
student.name='李四'
})
const midifyAge=(()=>{
student.age='100'
})
//进行整体修改
const midifyAll=(()=>{
//方式一 无效
//student={name:"王五",age:200}
//方式二 无效
// student=reactive({name:"王五",age:200})
//方式三 有效 原理就是批量更换了这个student里面的属性
Object.assign(student,{name:"王五",age:200})
})
//监视reactive里面定义的对象类型数据 且默认是开启深度监视
watch(student,(newValue,oldValue)=>{
console.log("值发生改变",newValue,oldValue)
})
</script>
<style>
</style>
4,reactive对比ref
从定义数据角度
1,ref用来定义基本类型数据(ref也可以用来定义对象或数组类型数据,它内部会自动通过reactive转为代码对象)
2,reactive用来定义对象或数组类型数据
从原理对比角度
1,ref是通过Object.defineProperty()的访问器属性get和set来实现响应的
2,reactive是通过ES6中的proxy来实现响应,并通过Relect操作源对象内部的数据
5,setup的两个注意点
1,set的执行时机:在beforeCreate之前只执行一次,this是undefined。(setup的执行比beforeCreate要早)
2,setup的参数(一共有两个props和context)
(1) props:值为对象,包含:组件外部传递过来,且组件内部声明接收 了的属性。
(2) context上下文对象
--1-- attrs:值为对象,包含:组件外部传递过来,但没有props配置中声明的属性,相当于this.$attrs
--2-- slots:收到的插槽内容, 相当于 this.$slots。
--3-- emit:分发自定义的事件函数,相当于this.$emit。
6,setup的简写
如果每次都需要return进行返回,数据多了之后难免忘记,这里有它的简写形式,只需要在script标签中加入setup即可。(如果是ts则加上lang="ts")
<template>
<div class="greetings">
<h1 class="green">{{ name }}</h1>
<h1 class="green">{{ age }}</h1>
<button @click="btnClick">点击</button>
</div>
</template>
<script setup lang="ts">
let name="小张"
let age="18"
const btnClick=(()=>{
alert(name)
})
</script>
这种写法就要省事不少。
7,计算属性computed与监视属性watch
1,computed函数,用之前需要先引用。
<template>
<h1>sutup的注意点</h1>
<input type="text" v-model="Obj.name">
<br/>
<input type="text" v-model="Obj.age">
<span>{{computerResult2}}</span>
</template>
<script>
//vue3中的计算属性是一个组合的api,在使用之前需要先引入
import {computed, reactive} from 'vue'
export default{
beforeCreate(){
console.log("beforeCreate")
},
setup(){
let Obj = reactive({
name:"小赵",
age:18
})
//1,计算属性简写形式(正常情况下的使用)
let computerResult1=computed(()=>{
return Obj.name+'-'+Obj.age
})
//2,计算属性完整写法(如果是直读,且需要修改属性的情况下使用)
let computerResult2=computed({
get(){
return Obj.name+'-'+Obj.age
},
set(value){
const newResult = value.split('-')
Obj.name=newResult[0]
Obj.age=newResult[1]
}
})
return{
Obj,
computerResult1,
computerResult2
}
}
}
</script>
2,watch函数
一,两种常用的监视用法
<template>
<h1>监视属性的使用</h1>
<h2>累加:{{sum}}</h2>
<h2>累加:{{adds}}</h2>
<button @click="sum++">点击</button>
<button @click="adds+='++'">修改信息</button>
</template>
<script>
//需要先引入watch
import {ref,watch} from 'vue'
export default{
setup(){
let sum = ref(0)
let adds=ref('大家好')
//1,第一种写法,监视一个响应式数据
// watch(sum,(newvalue,oldvalue)=>{
// console.log('sum变民',newvalue,oldvalue)
// })
//2,监视所定义的多个响应式数据,直接传一个数组
watch([sum,adds],(newvalue,oldvalue)=>{
console.log(newvalue,oldvalue)
},{immediate:true})
return{
sum,
adds
}
}
}
</script>
其它的监视写法
let Obj=reactive({
name:"张三",
age:18,
other:{
job:"软件开发工程师"
}
})
/*
情况三:监视reactive所定义的一个响应式数据的全部属性
1.注意:此处无法正确的获取oldValue
2.注意:强制开启了深度监视(deep配置无效)
*/
/* watch(Obj,(newValue,oldValue)=>{
console.log('Obj变化了',newValue,oldValue)
},{deep:false}) //此处的deep配置无效 */
//情况四:监视reactive所定义的一个响应式数据中的某个属性,需要是一个函数
/* watch(()=>Obj.name,(newValue,oldValue)=>{
console.log('Obj的name变化了',newValue,oldValue)
}) */
//情况五:监视reactive所定义的一个响应式数据中的某些属性,监视多个,需要一个数组
/* watch([()=>Obj.name,()=>Obj.age],(newValue,oldValue)=>{
console.log('Obj的name或age变化了',newValue,oldValue)
}) */
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('Obj的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
8,watchEffect函数
watchEffect:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect有点像computed:
1,但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
2,而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
<template>
<h1>监视属性的使用</h1>
<h2>累加:{{sum}}</h2>
<h2>累加:{{adds}}</h2>
<button @click="sum++">点击</button>
<button @click="adds+='++'">修改信息</button>
</template>
<script>
//需要先引入watchEffect
import {reactive, ref, watchEffect} from 'vue'
export default{
setup(){
let sum = ref(0)
let adds=ref('大家好')
//可以
watchEffect(()=>{
let val1=sum.value
let val2=adds.value
console.log(val1,val2)
console.log("被调用了")
})
return{
sum,
adds,
Obj
}
}
}
</script>
使用举例
<template>
<div>
<h2>当前求和为{{sum}}</h2>
<h2>总数为{{totoal}}</h2>
<button @click="SumClick">当前求和</button>
<button @click="totalClick">总数</button>
</div>
</template>
<script setup>
import {ref,reactive,watch,watchEffect} from 'vue'
let sum = ref(0);
let totoal = ref(0)
const SumClick=(()=>{
sum.value+=10
})
const totalClick=(()=>{
totoal.value+=10
})
//1,watch监听
watch([sum,totoal],(value)=>{
let [num1,num2]=value//数组解构
console.log(num1,num2)
if(num1>50||num2>50){
console.log("需要做的事情")
}
})
//2,watchEffect监听,想监视那个,就监视那个
watchEffect(()=>{
if(sum.value>=50||totoal.value>=50){
console.log("需要做的事情")
}
})
</script>
<style>
</style>
9,元素中ref的使用
作用:用于注册模板引用。
1,用在就能标签一获取的上dom元素。
2,用在组件标签上获取的是组件实例对象(不常用)
<template>
<div>
<div ref="element"><span>vue</span></div>
<button @click="PointText">测试</button>
</div>
</template>
<script setup>
import {ref} from 'vue'
import childer from './HelloWorld.vue'
let element=ref()
const PointText=(()=>{
//拿到的是一个dom元素
console.log(element.value)
})
</script>
<style>
</style>
10,hooks
1,hooks就是把setup函数中使用的composition API进行封装,和vue2中的mixin很相。
2,可以进行代码复用,让setup中的逻辑更清晰易懂。
具体使用
在src下新建一个hooks文件里面包含一个setup.js文件用于鼠标点击空白处,获取对应的坐标
import { reactive,onMounted,onBeforeUnmount } from "vue"
export default function(){
let point=reactive({
x:0,
y:0
})
//封装一个公用方法
function savePoint(event){
point.x=event.pageX
point.y=event.pageY
console.log(event.pageX,event.pageY)
}
onMounted(()=>{
// window.addEventListener('click',(event)=>{
// point.x=event.pageX
// point.y=event.pageY
// console.log(event.pageX,event.pageY)
// })
window.addEventListener('click',savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
return point
}
在其它页面中的使用
<template>
<h1>hook的使用</h1>
<h2>累加:{{sum}}</h2>
<button @click="sum++">点击</button>
<h2>获取当前的鼠标坐标,x:{{point.x}}坐标点y{{point.y}}</h2>
</template>
<script>
import {ref} from 'vue'
import usePoint from '../hooks/setPoint'
export default{
setup(){
let sum = ref(0)
let point = usePoint()
console.log(point)
return{
sum,
point
}
}
}
</script>
11,路由
一,路由的基础切换
在使用之前先npm i vue-router安装,安装完成之后在项目的src文件下新建router文件,在文件里面创建index.ts文件,ts文件的内容如下
//创建一个 路由并暴露出去
import {createRouter,createWebHashHistory} from 'vue-router'
//引入单个组件
import home from '@/components/home.vue'
import news from '@/components/news.vue'
import about from '@/components/about.vue'
const router=createRouter({
history:createWebHashHistory(),
routes:[
{
path:'/home',
component:home
},
{
path:'/news',
component:news
},
{
path:'/about',
component:about
}
]
})
export default router
在main.ts文件中引入
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//进入路由器
import router from './router'
//引入pinia
import {createPinia} from 'pinia'
//创建pinia
const pinia = createPinia()
//安装pinia
const app =createApp(App)
app.use(pinia)
app.use(ElementPlus)
app.use(router)
app.mount('#app')
在具体的页面中使用(在components下新建三个组件news.home,about)
<template>
<div class="app">
<h2>路由测试</h2>
<!-- 列表区 -->
<div class="navigate">
<RouterLink to="/news">首页</RouterLink>
<RouterLink to="/home">新闻</RouterLink>
<RouterLink to="/about">关于</RouterLink>
</div>
<!-- 展示区-->
<div class="main-content">
<RouterView></RouterView>
</div>
</div>
</template>
<script lang="ts" setup>
import {RouterView,RouterLink} from 'vue-router'
</script>
<style scoped>
.navigate{
float: left;
}
.navigate a{
width: 120px;
height: 40px;
line-height: 40px;
font-size: 16px;
}
</style>
注意点:
1,路由组件通常存放在`pages` 或 `views`文件夹,一般组件通常存放在`components`文件夹
2,通过点击导航,视觉效果上“消失” 了的路由组件,默认是被**卸载**掉的,需要的时候再去**挂载**
二,路由器的工作模式
1. `history`模式
一,优点:`URL`更加美观,不带有`#`,更接近传统的网站`URL`。
二,缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有`404`错误。
const router = createRouter({
history:createWebHistory(), //history模式
})
2,`hash`模式
一,优点:兼容性更好,因为不需要服务器端处理路径。
二,缺点:`URL`带有`#`不太美观,且在`SEO`优化方面相对较差。
const router = createRouter({
history:createWebHashHistory(), //hash模式
})
三,to的两种写法
<!-- 第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link>
<!-- 第二种:to的对象写法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>
四,跳转路由和路由嵌套
<!--简化前:需要写完整的路径(to的字符串写法如果是嵌套的,前面需要加上父级路由) -->
<router-link to="/news/detail">xxxx</router-link>
<!-- 或 -->
<router-link :to="{path:'/news/detail'}">xxxx</router-link>
const router=createRouter({
history:createWebHashHistory(),
routes:[
{
path:'/home',
component:home
},
{
path:'/news',
component:news,
// 子路由嵌套
children:[
{
path:'/message',
component:message
},
{
path:'/comfigs',
component:comfigs
},
]
},
{
path:'/about',
component:about
}
]
})
五,路由传参
1,query传参
<!-- 跳转并携带query参数(to的字符串写法) -->
<router-link to="/news/detail?a=1&b=2&content=欢迎你">
跳转
</router-link>
<!-- 跳转并携带query参数(to的对象写法) -->
<RouterLink
:to="{
//name:'xiang', //用name也可以跳转
path:'/news/detail',
query:{
id:news.id,
title:news.title,
content:news.content
}
}"
>
{{news.title}}
</RouterLink>
2. 接收参数:
import {useRoute} from 'vue-router'
const route = useRoute()
// 打印query参数
console.log(route.query)
2,params参数
<!-- 跳转并携带params参数(to的对象写法) -->
<RouterLink
:to="{
name:'xiang', //用name跳转
params:{
id:news.id,
title:news.title,
content:news.title
}
}"
>
{{news.title}}
</RouterLink>
2. 接收参数:
import {useRoute} from 'vue-router'
const route = useRoute()
// 打印params参数
console.log(route.params)
注意点
1,传递`params`参数时,若使用`to`的对象写法,必须使用`name`配置项,不能用`path`。
六,replace属性
1. 作用:控制路由跳转时操作浏览器历史记录的模式。
2. 浏览器的历史记录有两种写入方式:分别为`push`和`replace`:
- `push`是追加历史记录(默认值)。
- `replace`是替换当前记录。
3. 开启`replace`模式:
<RouterLink replace .......>News</RouterLink>
七,编程式导航
路由组件的两个重要的属性:`$route`和`$router`
具体使用
import {useRoute,useRouter} from 'vue-router'
const route = useRoute()
const router = useRouter()
console.log(route.query)
console.log(route.parmas)
console.log(router.push)
console.log(router.replace)
八,重定向
1. 作用:将特定的路径,重新定向到已有路由。
2. 具体编码:
{
path:'/',
redirect:'/about'
}
九,路由的props配置
让路由组件更方便的收到参数(可以将路由参数作为`props`传给组件)分为三种写法,对象式写法,布尔值写法和函数写法
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail,
// props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
// props:{a:1,b:2,c:3},
// props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件
// props:true
// props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件
props(route){
return route.query
}
}
12,toRef和toRefs
1,toRef创建一个ref对象,其value值指向另一个对象中的某个属性
2,语法const name=toRef(Obj,"name")
3,要将响应式对象中的某个属性单独提供给外部使用
<template>
<h1>toRef的使用</h1>
<h2>姓名:{{name}}</h2>
<h2>年纪:{{age}}</h2>
<h2>年纪:{{money}}</h2>
</template>
<script>
//引入toRef
import {toRef} from 'vue'
export default{
setup(){
let Obj={
name:"张三",
age:18,
job:{
eng:{
money:30
}
}
}
return{
//Obj,
name:toRef(Obj,'name'),
age:toRef(Obj,'age'),
money:toRef(Obj.job.eng,'money')
}
}
}
</script>
4,toRefs与toRef的功能一样,但可以批量创建多个ref对象
<template>
<h1>toRef的使用</h1>
<h2>姓名:{{name}}</h2>
<h2>年纪:{{age}}</h2>
<h2>年纪:{{job.eng.money}}</h2>
</template>
<script>
//引入toRefs
import {toRefs} from 'vue'
export default{
setup(){
let Obj={
name:"张三",
age:18,
job:{
eng:{
money:30
}
}
}
return{
...toRefs(Obj)
}
}
}
</script>
13,其它Composition API
一,shallowReactive与shallowRef
1,shallowReactive:只处理对象最外层的响应式(只处理一层)
2,shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
什么时候使用
什么时候使用?
A:如果有一个对象数据,结构比较深,但变化时只是最外层的属性变化就用 shallowReactive
B:如果有一个对象数据,后续功能不会修改该对象中的属性,而是新的对象来替换就使用shallowRef
shallowReactive的使用
<template>
<h1>shallowReactive的使用</h1>
<h2>姓名:{{name}}</h2>
<h2>年纪:{{age}}</h2>
<h2>年纪:{{job.eng.money}}</h2>
<button @click="age++">修改年纪</button>
</template>
<script>
//引入toRefs
import {toRefs,shallowReactive} from 'vue'
export default{
setup(){
//job里面的数据不会受到影响
let Obj=shallowReactive({
name:"张三",
age:18,
job:{
eng:{
money:30
}
}
})
return{
...toRefs(Obj)
}
}
}
</script>
二,readonly与shallowReadonly
readonly:让一个响应式数据变为直读的(深度)
shallowReadonly:让一个响应式数据变为只读的(只读一层)
应用场景,不希望数据被修改
<template>
<h1>shallowReactive的使用</h1>
<h2>姓名:{{name}}</h2>
<h2>年纪:{{age}}</h2>
<h2>年纪:{{job.eng.money}}</h2>
<button @click="age++">修改年纪</button>
<button @click="job.eng.money++">修改薪水</button>
</template>
<script>
//引入toRefs
import {toRefs,reactive,readonly,shallowReadonly} from 'vue'
export default{
setup(){
let Obj=reactive({
name:"张三",
age:18,
job:{
eng:{
money:30
}
}
})
//禁止响应式的数据进行修改全部
//Obj=readonly(Obj)
//只限制第一层的数据只读
Obj=shallowReadonly(Obj)
return{
...toRefs(Obj)
}
}
}
</script>
三,toRaw与markRaw
toRaw
作用:将一个由reactive生成的响应式对象转为普通对象
使用场景:用于读取响应式对象中的普通对象,这个普通对象的所有操作,不会引起页面更新
<template>
<h1>toRaw的使用</h1>
<h2>姓名:{{name}}</h2>
<h2>年纪:{{age}}</h2>
<h2>年纪:{{job.eng.money}}</h2>
<button @click="showToRaw">输出最原始的Obj</button>
</template>
<script>
//引入toRaw
import {toRefs,reactive,toRaw} from 'vue'
export default{
setup(){
let Obj=reactive({
name:"张三",
age:18,
job:{
eng:{
money:30
}
}
})
function showToRaw(){
let O = toRaw(Obj)
O.age++
console.log(O)//输出的结果为一个普通对象
}
return{
...toRefs(Obj),
showToRaw
}
}
}
</script>
markRaw
作用:标记一个对象,使用其不会成为响应式对象
应用场景:
1,有些值不应该被设置为响应式的,例如复杂的第三方库等
2,当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
<template>
<h1>markRaw的使用</h1>
<h2>姓名:{{name}}</h2>
<h2>年纪:{{age}}</h2>
<h2>年纪:{{job.eng.money}}</h2>
<button @click="showMarkRaw">输出最原始的Obj</button>
</template>
<script>
//引入markRaw
import {toRefs,reactive,markRaw} from 'vue'
export default{
setup(){
let Obj=reactive({
name:"张三",
age:18,
job:{
eng:{
money:30
}
}
})
function showMarkRaw(){
let others={address:"湖北武汉",weight:180}
Obj.others=markRaw(others)
console.log(Obj)
}
return{
...toRefs(Obj),
showMarkRaw,
}
}
}
</script>
四,customRef
作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制。
<template>
<input type="text" v-model="keyWord">
<h3>{{keyWord}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name: 'App',
setup() {
//自定义一个ref——名为:myRef
function myRef(value,delay){
let timer
//需要接受的两个参数track,trigger
return customRef((track,trigger)=>{
return {
get(){
console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`)
track() //通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
return value
},
set(newValue){
console.log(`有人把myRef这个容器中数据改为了:${newValue}`)
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger() //通知Vue去重新解析模板
},delay)
},
}
})
}
// let keyWord = ref('hello') //使用Vue提供的ref
let keyWord = myRef('hello',500) //使用程序员自定义的ref
return {keyWord}
}
}
</script>
五,provide和inject
作用:实现祖与后代组件之间的通信
用法:父组件有一个provide选择来提供数据,后代组件有一个inject选项来开始使用这些数据
//祖祖组件
<template>
<!-- 我是祖祖组件 -->
<div>
<h3>我是App组件</h3>
<h3>{{name}}--{{price}}</h3>
<ChildsVue/>
</div>
</template>
<script>
//引入provide
import {reactive,toRefs,provide} from 'vue'
import ChildsVue from './components/ChildView.vue'
export default {
name: 'App',
components: {
ChildsVue
},
setup(){
//将祖祖组件的内容传递到孙组件
let car = reactive({
name:"三轮车",
price:"3000元"
})
provide('car',car)//给自己的后代元素传递数据
return{
...toRefs(car)
}
}
}
</script>
//父组件
//子组件里面也可以使用inject
<template>
<div class="Childs">
<h1>子组件</h1>
<sonVue/>
</div>
</template>
<script>
import sonVue from './sonView.vue'
export default{
name:"ChildView",
components:{sonVue}
}
</script>
<style>
.Childs{
background-color: gray;
padding: 10px;
}
</style>
//孙子组件
<template>
<div class="son">
<h1>孙组件</h1>
</div>
</template>
<script>
// 在孙子组件里面进行接受
import {inject} from 'vue'
export default{
name:"sonView",
setup(){
let car = inject('car')
console.log(car,'+++++')
}
}
</script>
<style>
.son{
background-color: gold;
padding: 10px;
}
</style>
六,响应式数据的判断
isRef:检查一个值是否为一个ref对象
isReactive:检查一个对象是否由reactive创建的响应式代理
isReadonly:检查一个对象是否由readonly创建的只读对象
isProxy:检查一个对象是否由react或者readonly方法创建的代理对象
<template>
<h3>进行响应式代理的测试</h3>
</template>
<script>
import {ref,reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue'
export default {
name:'App',
setup(){
let car = reactive({name:'三轮车',price:'3000元'})
let sum = ref(0)
let car2 = readonly(car)
console.log(isRef(sum))//true
console.log(isReactive(car))//true
console.log(isReadonly(car2))//true
console.log(isProxy(car))//true
console.log(isProxy(sum))//false
return {...toRefs(car)}
}
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
14,各组件之间的互相通信
1,父子组件之间相互通信
一,父组件向子组件传值利用props
<!--父组件-->
<template>
<div class="father">
<h2>父组件</h2>
<h4>{{cars}}</h4>
<child :cars="cars"/>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import child from './childer.vue'
let cars = ref('火车')
</script>
<style scoped>
.father{
width: 100%;
height: 100px;
background: red;
display: inline-block;
}
</style>
<!--子组件-->
<template>
<div class="childer">
<h2>子组件</h2>
<h4>玩具1:{{tools}}</h4>
<h4>玩具1:{{cars}}</h4>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
let tools =ref("玩具飞机")
//声明并接收父组件传递过来的数据
defineProps(['cars'])
</script>
<style scoped>
.childer{
width: 100%;
height: 100px;
background: blue;
display: inline-block;
}
</style>
一,子组件向父组件传值也是利用props
<!--子组件-->
<template>
<div class="childer">
<h2>子组件</h2>
<h4>玩具1:{{tools}}</h4>
<button @click="sendToy(tools)">子向父传值</button>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
let tools =ref("玩具飞机")
defineProps(['sendToy'])
</script>
<style scoped>
.childer{
width: 100%;
height: 100px;
background: blue;
display: inline-block;
}
</style>
<!--父组件-->
<template>
<div class="father">
<h2>父组件</h2>
<h4>{{cars}}</h4>
<h4>{{tools}}</h4>
<child :sendToy="sendToy"/>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import child from './childer.vue'
let cars = ref('火车')
let tools=ref('')
function sendToy(value:string){
console.log('父',value)
tools.value=value
}
</script>
<style scoped>
.father{
width: 100%;
height: 100px;
background: red;
display: inline-block;
}
</style>
2,自定义事件
和vue2的用法差别不大,需要用到defineEmits
<!--子组件-->
<template>
<div class="childer">
<h2>子组件</h2>
<h4>玩具1:{{tools}}</h4>
<button @click="sendFather()">子向父传值</button>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
let tools =ref("玩具飞机")
//声明事件
const emit= defineEmits(['childClick'])
function sendFather(){
emit('childClick',tools)
}
</script>
<style scoped>
.childer{
width: 100%;
height: 100px;
background: blue;
display: inline-block;
}
</style>
<!--父组件-->
<template>
<div class="father">
<h2>父组件</h2>
<h4>{{cars}}</h4>
<h4>{{ch}}</h4>
<!-- 给子组件childer绑定事件 -->
<child @childClick='testClick'/>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import child from './childer.vue'
let cars = ref('火车')
//存储子组件传递过来的数据
let ch=ref('')
function testClick(num:string){
console.log("asdf",num)
ch.value=num
}
</script>
<style scoped>
.father{
width: 100%;
height: 100px;
background: red;
display: inline-block;
}
</style>
3,mitt
可用于任意组件之间的通信,这里实现两兄弟组件之间的通信,不过在使用之前需要npm i mitt进行安装
<!--兄弟组件1-->
<template>
<div class="childer">
<h2>兄弟组件</h2>
<h4>玩具1:{{tools}}</h4>
<button @click="emitter.emit('send-toy',tools)">向兄弟组件传值</button>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import emitter from '../utils/emitter'
let tools =ref("玩具火箭")
//声明事件
</script>
<style scoped>
.childer{
width: 100%;
height: 100px;
background: blue;
display: inline-block;
}
</style>
<!--兄弟组件2-->
<template>
<div class="childer">
<h2>兄弟组件</h2>
<h4>玩具1:{{tools}}</h4>
<h4>玩具1:{{toys}}</h4>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import emitter from '../utils/emitter'
let tools =ref("玩具飞机")
//声明事件
let toys=ref('')
emitter.on('send-toy',(value:any)=>{
console.log('send-toy')
toys.value=value
})
</script>
<style scoped>
.childer{
width: 100%;
height: 100px;
background: blue;
display: inline-block;
}
</style>
4,pinia
1,在使用pinia之前需要先进行安装npm i pinia(老规矩哈)
2,在main.ts里面进行引入,创建和安装
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
//引入pinia
import {createPinia} from 'pinia'
//创建pinia
const pinia = createPinia()
//安装pinia
const app =createApp(App)
app.use(pinia)
app.mount('#app')
3,在src下新建文件store,并创建对应的文件。比如某个组件叫childer,那么在store中就可以创建一个childer.ts文件
下面的示例是存储和读取数据
在childer.ts文件中存储
import {defineStore} from 'pinia'
export const useChilder=defineStore('childer2',{
//真正存储数据的地方
state(){
return{
sum:6
}
}
})
在childer.vue组件中读取
<template>
<h3>当前求和为:{{result.sum}}</h3>
<select v-model.number="nowValue">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="add">加</button>
<button @click="jian">减</button>
</template>
<script setup lang="ts">
import {ref} from 'vue'
//进行调用pinia中存储的数据
import {useChilder} from '../../src/store/childer2'
//这里是一个方法
const result=useChilder()
console.log(result.sum)
//let sum=ref(1)
let nowValue=ref(1)
const add=(()=>{
//修改单个数据的方法
result.sum+=nowValue.value
})
const jian=(()=>{
result.sum-=nowValue.value
})
</script>
4,修改数据的2种方式
一,批量修改数据
批量修改sum,age,height
import {defineStore} from 'pinia'
export const useChilder=defineStore('childer2',{
//真正存储数据的地方
state(){
return{
sum:6,
age:18,
height:180
}
}
})
组件中的数据
<template>
<h3>当前求和为:{{result.sum}}</h3>
<h3>当前求和为:{{result.sum}}</h3>
<h3>当前求和为:{{result.height}}</h3>
<select v-model.number="nowValue">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="add">加</button>
<button @click="jian">批量修改</button>
</template>
<script setup lang="ts">
import {ref} from 'vue'
//进行调用pinia中存储的数据
import {useChilder} from '../../src/store/childer2'
//这里是一个方法
const result=useChilder()
console.log(result.sum)
let nowValue=ref(1)
const add=(()=>{
//修改单个数据
result.sum+=nowValue.value
})
const jian=(()=>{
//批量修改数据
result.$patch({
sum:456,
age:100,
height:6666
})
})
</script>
<style scoped>
</style>
二,使用action
childer.ts中的写法
import {defineStore} from 'pinia'
export const useChilder=defineStore('childer2',{
//action里面放的是一个一个的方法,用于响应组件的中动作
actions:{
btnclick(value){
console.log("被点击了",value)
if(this.sum<10){
this.sum+=value
}
}
},
//真正存储数据的地方
state(){
return{
sum:6,
age:18,
height:180
}
}
})
在组件中的使用
<template>
<h3>当前求和为:{{result.sum}}</h3>
<h3>当前求和为:{{result.age}}</h3>
<h3>当前求和为:{{result.height}}</h3>
<select v-model.number="nowValue">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="add">加</button>
</template>
<script setup lang="ts">
import {ref} from 'vue'
//进行调用pinia中存储的数据
import {useChilder} from '../../src/store/childer2'
//这里是一个方法
const result=useChilder()
console.log(result.sum)
let nowValue=ref(1)
const add=(()=>{
//第三种修改方式
result.btnclick(nowValue.value)
})
</script>
<style scoped>
</style>
15,Teleport
teleport是一种能够将我们的组件html结构移动到指定位置的技术
例如父组件里面包含子组件,子组件是一个弹窗,需要把弹窗移动给body
//父组件
<template>
<div class="son">
<h1>父组件</h1>
<DialogVue/>
</div>
</template>
<script>
// 父组件里面包括子组件
import DialogVue from './DialogView.vue'
export default{
name:"sonView",
components:{DialogVue},
}
</script>
<style>
.son{
background-color: gold;
padding: 10px;
}
</style>
//子组件,要移动的组件
<template>
<div>
<button @click="isShow=true">弹窗</button>
<!--这里的to可以是html,body或者是某个指定的选择器,如果是选择器前面需要加#号-->
<teleport to="body">
<div v-if="isShow" class="DialogView">
<h1>这是一个对话弹</h1>
<h4>描述</h4>
<button @click="isShow=false">关闭</button>
</div>
</teleport>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name:"DialogView",
setup(){
let isShow = ref(false)
return{
isShow
}
}
}
</script>
<style>
.DialogView{
width:300px;
height: 300px;
background-color: aqua;
}
</style>
12,Susperse
等待异步组件渲染时做一些其它的事情
使用步骤
1,引入异步组件
2,使用Susperse包裹好组件,并配置好default与fallback
<template>
<div class="Childs">
<h1>父组件</h1>
<Suspense>
<template v-slot:default>
<sonVue/>
</template>
<template v-slot:fallback>
<h3>正在加载中.....</h3>
</template>
</Suspense>
</div>
</template>
<script>
//静态引入
//import sonVue from './sonView.vue'
//异步引入或者叫动态引入
import {defineAsyncComponent} from 'vue'
const sonVue = defineAsyncComponent(()=>import('./sonView.vue'))
export default{
name:"ChildView",
components:{sonVue}
}
</script>
<style>
.Childs{
background-color: gray;
padding: 10px;
}
</style>
16,vue3工程创建
一,vue/cli创建
1,使用vue-cli创建,查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
2,安装或升级@vue/cli
npm install -g @veu/cli
3,创建vue create vue_test
4,选择vue3对应的版本
5,项目启动
npm run serve
二,vite创建
1,创建工程
输入命令 npm init vite-app vite_vue3_test
注意:执行以上命令后会创建一个模板,但没有安装依赖npm
2,通过命令进入到vite_vue3_test
3,npm i安装对应的依赖
4,npm run dev启动并运行项目