Vue11 Vue3完结撒花

shallowRef和shallowReactive

shallowRef

  1. 作用:创建一个响应式数据,但只对顶层属性进行响应式处理

  2. 用法

    vue 复制代码
    let myVar = shallowRef(initialValue)
  3. 特点:只跟踪引用值变化,不关心值内部的属性变化

案例

VUE 复制代码
<template>
	<div class="app">
		<h2>求和为:{{ sum }}</h2>
		<h2>名字为:{{ person.name }}</h2>
		<h2>年龄为:{{ person.age }}</h2>
		<h2>汽车为:{{ car }}</h2>
		<button @click="changeSum">sum+1</button>
		<button @click="changeName">修改名字</button>
		<button @click="changeAge">修改年龄</button>
		<button @click="changePerson">修改整个人</button>
		<span>|||||</span>
		<button @click="changeBrand">修改品牌</button>
		<button @click="changeColor">修改颜色</button>
		<button @click="changeEngine">修改发动机</button>
		<!-- <button @click="changeCar">修改整个车</button> -->
	</div>
</template>

<script setup lang="ts" name="App">
	import { ref,reactive,shallowRef,shallowReactive } from 'vue'

	let sum = shallowRef(0)
	let person = shallowRef({
		name: '小黑',
		age:2
	})
	let car = shallowReactive({
		brand: '特斯拉',
		options: {
			color: '黑色',
			engine:'v8'
		}
	})

	function changeSum (){
		sum.value += 1;
	}
	// 不会改变
	function changeName (){
		person.value.name = '大狗';
	}
	function changeAge (){
		person.value.age = 5;
	}
	//只能改变第一层
	function changePerson (){
		person.value = {name:'乐乐',age:3}
	}
	/* ********** */
	function changeBrand () {
		car.brand = '宝马'
	}
	function changeColor () {
		car.options.color = '红色'
	}
	function changeEngine () {
		car.options.engine = 'v12'
	}
	// function changeCar () {
	// 	Object.assign(car,{})
	// }
</script>

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

总结

通过使用shallowRef()和shallowReactive()来绕开深度响应。浅层式API创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问控制变得更快、可提升性能。

readonly与shallowReadonly

readonly

  1. 作用:用于创建一个对象的深只读副本

  2. 用法

    vue 复制代码
    <template>
    	<div class="app">
    		<h2>当前求和为:{{ sum }}</h2>
    		<h2>当前sum2求和为:{{ sum2 }}</h2>
    		<h2>当前car1为:{{ car }}</h2>
    		<h2>当前car2为:{{ car2 }}</h2>
    		<button @click="changeSum">点我sum+1</button>
    		<button @click="changeSum2">点我sum2+1</button>
    	</div>
    </template>
    
    <script setup lang="ts" name="App">
    	import { ref, readonly, reactive,shallowReadonly } from 'vue';
    	
    	let sum = ref(0)
    	let sum2 = readonly(sum)
    
    
    	function changeSum() {
    		sum.value += 1
    	}
    	function changeSum2() {
    		sum2.value += 1  //sum2不可修改
    	}
    </script>
    
    <style scoped>
    	.app{
    		background-color: #ddd;
    		border-radius: 10px;
    		box-shadow: 0 0 10px;
    		padding: 10px;
    	}
    	button{
    		margin:0 5px;
    	}
    </style>
  3. 特点:

    • 对象的所有嵌套属性变成只读
    • 任何尝试修改这个对象的操作都会被组织(在开发模式下,还会再控制台中发出警告)
  4. 应用场景:

    • 创建不可变的状态快照
    • 保护全局状态或配置不被修改

shallowReadonly

  1. 作用:与readonly类似,但只作用于对象的顶层属性,其他地方仍然可以修改

  2. 用法

    vue 复制代码
    <template>
    	<div class="app">
    		<h2>当前car1为:{{ car }}</h2>
    		<h2>当前car2为:{{ car2 }}</h2>
    		<button @click="changeBrand1">修改car1品牌</button>
    		<button @click="changeColor1">修改car1颜色</button>
    		<button @click="changePrice1">修改car1价格</button>
    		<button @click="changeBrand2">修改car2品牌</button>
    		<button @click="changeColor2">修改car2颜色</button>
    		<button @click="changePrice2">修改car2价格</button>
    	</div>
    </template>
    
    <script setup lang="ts" name="App">
    	import { reactive,shallowReadonly } from 'vue';
    	
    	let car = reactive({
    		brand:'奔驰',
    		options: {
    			color:'黑色',
    			price:100
    		}
    	})
    	let car2 = shallowReadonly(car)
    
    	function changeBrand1() {
    		car.brand = '宝马'
    	}
    	function changeColor1() {
    		car.options.color = '红色'
    	}
    	function changePrice1() {
    		car.options.price += 10
    	}
    	
    	function changeBrand2() {
    		car2.brand = '宝马'
    	}
    	function changeColor2() {
    		car2.options.color = '红色'
    	}
    	function changePrice2() {
    		car2.options.price += 10
    	}
    </script>
    
    <style scoped>
    	.app{
    		background-color: #ddd;
    		border-radius: 10px;
    		box-shadow: 0 0 10px;
    		padding: 10px;
    	}
    	button{
    		margin:0 5px;
    	}
    </style>

toRaw与markrow

toRaw

  1. 作用:用于获取一个响应式对象的原始对象,toRaw返回的对象不再是响应式,不会触发视图更新。

    • 何时使用

      在需要将响应式对象传递给非Vue的库或外部系统时,使用toRaw可以确保他们收到的是普通对象

  2. 用法:使用toRaw之后 年龄不再改变

    vue 复制代码
    <template>
    	<div class="app">
    		<h2>姓名:{{ person.name }}</h2>
    		<h2>年龄:{{ person2.age }}</h2>
    		<button @click="person.age += 1">修改年龄</button>
    	</div>
    </template>
    
    <script setup lang="ts" name="App">
    import { reactive,toRaw } from 'vue';
    
    let person = reactive({
    	name: '小黑',
    	age:2
    })
    let person2 = toRaw(person)
    
    console.log('响应式数据', person)
    console.log('原始数据',person2)
    </script>
    
    <style scoped>
    	.app{
    		background-color: #ddd;
    		border-radius: 10px;
    		box-shadow: 0 0 10px;
    		padding: 10px;
    	}
    	button{
    		margin:0 5px;
    	}
    </style>

markRaw

  1. 作用:标记一个对象,使其永远都不会变成响应式

    • 何时使用

      使用第三方库的时候,防止错误把第三库对象变成响应式对象,就可以使用markRaw

  2. 用法

    vue 复制代码
    <template>
    	<div class="app">
    		<h2>汽车:{{ car2 }}</h2>
    		<button @click="car2.price += 10">修改价格</button>
    	</div>
    </template>
    
    <script setup lang="ts" name="App">
    import { reactive,toRaw,markRaw } from 'vue';
    
    /* markRaw */
    //添加markRaw 28行就失去作用,无法改变成响应式
    let car = markRaw({ brand: '奔驰', price: 100 })
    let car2 = reactive(car)
    
    console.log(car);
    console.log(car2);
    
    </script>
    
    <style scoped>
    	.app{
    		background-color: #ddd;
    		border-radius: 10px;
    		box-shadow: 0 0 10px;
    		padding: 10px;
    	}
    	button{
    		margin:0 5px;
    	}
    </style>

customRef的使用

作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行逻辑控制

实现防抖效果(useSumRef.ts)

vue 复制代码
<template>
	<div class="app">
		<h2>{{ msg }}</h2>
		<input type="text" v-model="msg">
	</div>
</template>

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

	//使用vue提供的默认ref定义响应式数据,数据一变,页面视图也更新
	// let msg = ref('你好')

	//使用vue提供的customRef定义响应式数据
	let initValue = '你好'
	let timer:number
	//track(跟踪)、trigger(触发) 不加上页面效果不会改变
	let msg = customRef((track,trigger) => {
		return {
			//get何时调用?------msg被读取时
			get() { 
				track() //告诉vue数据很重要,需要对msg进行持续关注,一旦msg变化就去更新
				return initValue
			},
			//set何时调用?------msg被修改时
			set(value) {
				clearTimeout(timer)
				timer = setTimeout(() => {
					// console.log('set',value);
					initValue = value
					trigger() //通知vue数据变化了
				}, 1000);
			}
			}
		})
</script>

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

采用hooks封装

定义hooks------useMsgRef.ts

ts 复制代码
import { customRef } from 'vue';

export default function (initValue: string, delay: number) {
    //使用vue提供的customRef定义响应式数据
    // let initValue = '你好'
    let timer: number
    //track(跟踪)、trigger(触发) 不加上页面效果不会改变
    let msg = customRef((track, trigger) => {
        return {
            //get何时调用?------msg被读取时
            get() {
                track() //告诉vue数据很重要,需要对msg进行持续关注,一旦msg变化就去更新
                return initValue
            },
            //set何时调用?------msg被修改时
            set(value) {
                clearTimeout(timer)
                timer = setTimeout(() => {
                    // console.log('set',value);
                    initValue = value
                    trigger() //通知vue数据变化了
                }, delay); //延迟时间不要写死
            }
        }
    })
    //hooks必须要有返回值
    return { msg }
}

App.vue

VUE 复制代码
<template>
	<div class="app">
		<h2>{{ msg }}</h2>
		<input type="text" v-model="msg">
	</div>
</template>

<script setup lang="ts" name="App">
	import useMsgRef from './useMsgRef';

	//使用useMsgRef来定义一个响应式数据且有延迟效果
	let { msg } = useMsgRef('小黑', 2000)
	
</script>

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

Teleport

定义:Teleport是一种能够将我们的组件html结构移动到指定位置的技术

App.vue

vue 复制代码
<template>
    <div class="outer">
        <h2>我是App组件</h2>
        <img src="https://img.zcool.cn/community/014b275d0a1c95a801213ec257bb95.jpg@2o.jpg" alt="">
        <br>
        <Modal/>
    </div>
</template>

<script setup lang="ts" name="App">
    import Modal from './Modal.vue';
</script>

<style  scoped>
.outer{
    background-color: #ddd;
    border-radius: 10px;
    padding:5px;
    box-shadow: 0 0 10px;
    width: 500px;
    height: 500px;
    /* 添加滤镜后,模块定位不再参考视口,参考的是父容器了 */
    filter:saturate(150%);
}
img{
    height: 180px;
    object-fit: cover;
}
</style>

modal.vue

vue 复制代码
<template>
    <button @click="isshow = true">展示弹窗</button>
        <!-- teleport解决添加滤镜后fix定位问题 -->
    <teleport to='body'>
        <div class="modal" v-show="isshow">
            <h2>我是弹窗标题</h2>
            <p>我是弹窗内容</p>
            <button @click="isshow = false">关闭弹窗</button>
        </div>
    </teleport>
</template>

<script setup lang="ts" name="Modal">
import { ref } from 'vue'
let isshow = ref(false)
</script>

<style  scoped>
.modal{
    width: 200px;
    height: 150px;
    background-color: skyblue;
    border-radius: 10px;
    padding: 5px;
    box-shadow: 0 0 5px;
    text-align: center;
    position:fixed;
    left:50%;
    top: 20px;
    margin-left: -100px;
}
</style>

Suspense

  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
  • 使用步骤:
    • 异步引入组件
    • 使用Suspense包裹组件,并配置好default和fallback

App.vue

VUE 复制代码
<template>
    <div class="app">
        <h2>我是App组件</h2>
        <!-- 解决异步事件,如果不加下面,那么Child组件不显示 -->
        <Suspense>
            <template v-slot:default>
                <Child/>
            </template>
        </Suspense>
    </div>
</template>

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

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

Child.vue

vue 复制代码
<template>
    <div class="child">
        <h2>我是Child组件</h2>
        <h3>当前sum求和为:{{ sum }}</h3>
    </div>
</template>

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

let sum = ref(0)
let xxx = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
console.log(xxx);
//如果要看到数据,可以解构 xxx 替换成 {data{content}} log直接打印content即可

</script>

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

全局API转移到应用对象

  • app.component

App.vue

VUE 复制代码
<template>
    <div class="app">
        <h2>我是App组件</h2>
        <Hello/>
        <Child/>
    </div>
</template>

<script setup lang="ts" name="App">
    import Child from './Child.vue'
</script>

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

Child.vue

VUE 复制代码
<template>
    <div class="child">
        <h2>我是Child组件</h2>
        <h3>当前sum求和为:{{ sum }}</h3>
        <Hello/>
    </div>
</template>

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

let sum = ref(0)

</script>

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

Hello.vue

VUE 复制代码
<template>
    <h2 style="color:red">你好</h2>
</template>

<script setup lang="ts" >

</script>

<style scoped>

</style>

main.ts

ts 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import Hello from './Hello.vue'

// 创建应用
const app = createApp(App)

app.component('Hello', Hello)

// 挂载应用
app.mount('#app')
  • app.config

main.ts

app.config.globalProperties 添加 那么全局都可以使用

ts 复制代码
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

//这样下面x就不会爆红
declare module 'vue' {
    interface ComponentCustomProperties {
        // $http: typeof axios
        // $translate: (key: string) => string
        x: number
    }
}

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

比如:Child.vue 页面就会呈现 我是Child组件99 这里x会爆红 在main.ts添加定义部分

vue 复制代码
<template>
    <div class="child">
        <h3>当前sum求和为:{{ sum }}</h3>
        <Hello/>
    </div>
</template>

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

let sum = ref(0)

</script>

<style scoped>
.child{
    background-color: skyblue;
    border-radius:10px;
    padding:10px;
    box-shadow: 0 0 10px;
}
</style>
  • app.directive

main.ts

ts 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import Hello from './Hello.vue'

// 创建应用
const app = createApp(App)

app.directive('beauty', (element, { value }) => {
    element.innerText += value
    element.style.color = 'green'
    element.style.backgroundColor = 'yellow'
})

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

比如:Child.vue 页面就会呈现 不高兴0------黄色背景,绿色文字

vue 复制代码
<template>
    <div class="child">
        <h2>我是Child组件{{ x }}</h2>
        <h3>当前sum求和为:{{ sum }}</h3>
        <h4 v-beauty="sum">不高兴</h4>
        <Hello/>
    </div>
</template>

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

let sum = ref(0)

</script>

<style scoped>
.child{
    background-color: skyblue;
    border-radius:10px;
    padding:10px;
    box-shadow: 0 0 10px;
}
</style>
  • app.mount

main.ts

ts 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import Hello from './Hello.vue'
import type axios from 'axios'

// 创建应用
const app = createApp(App)

// 挂载应用
app.mount('#app')
  • app.unmount

main.ts 2秒钟之后 app消失在页面上

ts 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import Hello from './Hello.vue'
import type axios from 'axios'

// 创建应用
const app = createApp(App)

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

setTimeout(() => {
    app.unmount()
}, 2000);
  • app.use

见router那一节 安装插件

ts 复制代码
//使用路由器
app.use(router)

Vue3的非兼容性改变

Vue2和Vue3区别

看vue官网:Breaking Changes | Vue 3 Migration Guide (vuejs.org)

  • 过渡类名v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from 。
  • keyCode 作为 v-on 修饰符的支持。
  • v-model 指令在组件上的使用已经被重新设计,替换掉了v-bind.sync。
  • v-if 和 v-for 在同一个元素身上使用时的优先级发生了变化。
  • 移除了 Son、 Soff 和 Sonce 实例方法。
  • 移除了过滤器 filter。
  • 移除了 $children 实例 propert。
相关推荐
乱码三千10 分钟前
密码管理工具站正式上线啦
前端·html·github
IMPYLH15 分钟前
HTML 的 <rt> 元素
前端·html
单线程_0120 分钟前
从案例分析 Vue3 Tokenizer+Parser 源码五
前端·javascript·vue.js
mldong8 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排8 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60619 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角10 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!10 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰10 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi12 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js