喊你学习 vue3.0

在这里插入图片描述
2020年9月18日,Vue.js发布3.0版本,代号:One Piece
重要的是它带了什么?四字总结:更快更轻。喔~对了,它还更好的支持TypeScript(TS可能以后也会成为主流了吧)
这次笔记重点是vue3.0的新特性!
中文文档
GitHub文档
学习视频,感谢尚硅谷博主

准备

创建vue3.0项目方式:
一、 vue-cli

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create <project-name>
## 启动
cd <project-name>
npm run serve

二、 vite
官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite
vite官网:https://vitejs.cn

## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev

Composition API(组合API)

setup

作为vue3.0新配置项,本质是一个函数
注意:① 不要和vue2.x配置项混合使用
② vue2配置项(data、methos、computed…)能访问setup里的配置,但setup不能访问到vue2.x配置
③ 如果重名,setup优先
④ setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
⑤ 执行时机:在beforeCreate之前执行一次,this是undefined
⑥ setup的参数:(props,context)

  • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
  • context:上下文对象
    • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
    • slots: 收到的插槽内容, 相当于 this.$slots
    • emit: 分发自定义事件的函数, 相当于 this.$emit
setup(){
			//数据
			
			//方法
			function sayHello(){}
			function test2(){}

			//返回一个对象(常用)
			return {sayHello,test2}

			//返回一个函数(渲染函数)
			// return ()=> h('h1','尚硅谷')
		}

ref函数

作用:定义响应式数据(常用于基本类型数据)
如果定义数据是对象,内部其实还求助了vue3.0的一个新函数——reactive函数

reactive函数

作用:定义一个对象类型的响应式数据
reactive定义的响应式数据是“深层次的”。
基本类型数据不能用reactive定义!

<template>
	<h1>一个人的信息</h1>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
	<h3>工作种类:{{person.job.type}}</h3>
	<h3>工作薪水:{{person.job.salary}}</h3>
	<h3>爱好:{{person.hobby}}</h3>
	<h3>测试的数据c:{{person.job.a.b.c}}</h3>
	<button @click="changeInfo">修改人的信息</button>
</template>

<script>
// 引用对应函数
	import {ref,reactive} from 'vue'
	export default {
		name: 'App',
		setup(){
			//数据
			// ref数据
			let name = ref('张三')
			let age = ref(18)
			// reactive数据
			let person = reactive({
				job:{
					type:'前端工程师',
					salary:'30K',
					a:{
						b:{
							c:666
						}
					}
				},
				hobby:['抽烟','喝酒','烫头']
			})

			//方法
			function changeInfo(){
			    // ref数据操作:xxx.value
				name.value = '李四'
				age.value = 48
				// reactive数据不需要.value
				person.job.type = 'UI设计师'
				person.job.salary = '60K'
				person.job.a.b.c = 999
				person.hobby[0] = '学习'
			}

			//返回一个对象(常用)
			return {
				name,
				age,
				person,
				changeInfo
			}
		}
	}
</script>


响应式原理对比

vue2.0

问题:

  • 新增属性、删除属性, 界面不会更新。
  • 直接通过下标修改数组, 界面不会自动更新。
// 数据劫持
  Object.defineProperty(data, 'count', {
        get () {}, 
        set () {}
    })

vue3.0 —— proxy(代理)
完美解决vue2的问题

new Proxy(data, {
      	// 拦截读取属性值
          get (target, prop) {
          	return Reflect.get(target, prop)
          },
          // 拦截设置属性值或添加新属性
          set (target, prop, value) {
          	return Reflect.set(target, prop, value)
          },
          // 拦截删除属性
          deleteProperty (target, prop) {
          	return Reflect.deleteProperty(target, prop)
          }
      })

computed计算属性

setup(){
			//数据
			let person = reactive({
				firstName:'张',
				lastName:'三'
			})
			//计算属性——简写(没有考虑计算属性被修改的情况)
			/* person.fullName = computed(()=>{
				return person.firstName + '-' + person.lastName
			}) */

			//计算属性——完整写法(考虑读和写)
			person.fullName = computed({
				get(){
					return person.firstName + '-' + person.lastName
				},
				set(value){
					const nameArr = value.split('-')
					person.firstName = nameArr[0]
					person.lastName = nameArr[1]
				}
			})

			//返回一个对象(常用)
			return {
				person
			}
		}

watch属性

格式:watch(xxx数据,(newvalue,oldvalue)=>{},{immediate:true/false})

	//情况一:监视ref所定义的一个响应式数据
			watch(sum,(newValue,oldValue)=>{
				console.log('sum变了',newValue,oldValue)
			},{immediate:true}) 

			//情况二:监视ref所定义的多个响应式数据,数组形式返回
			watch([sum,msg],(newValue,oldValue)=>{
				console.log('sum或msg变了',newValue,oldValue)
			},{immediate:true}) 
			
			/* 
				情况三:监视reactive所定义的一个响应式数据的全部属性
						1.注意:此处无法正确的获取oldValue
						2.注意:强制开启了深度监视(deep配置无效)
			*/
			watch(person,(newValue,oldValue)=>{
				console.log('person变化了',newValue,oldValue)
			},{deep:false}) //此处的deep配置无效

			//情况四:监视reactive所定义的一个响应式数据中的某个属性
			watch(()=>person.name,(newValue,oldValue)=>{
				console.log('person的name变化了',newValue,oldValue)
			})

			//情况五:监视reactive所定义的一个响应式数据中的某些属性
			watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
				console.log('person的name或age变化了',newValue,oldValue)
			})

			//特殊情况
			watch(()=>person.job,(newValue,oldValue)=>{
				console.log('person的job变化了',newValue,oldValue)
			},{deep:true}) //此处由于监视的是reactive定义的对象中的某个属性,所以deep配置有效

watchEffect函数

智能版的watch(谁用就监视谁,不必写明白我在监视谁)

setup(){
			//数据
			let sum = ref(0)
			let msg = ref('你好啊')
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})
			watchEffect(()=>{
				const x1 = sum.value //我用了sum的数据,那么我是在监视sum
				const x2 = person.job.j1.salary  //同理
				console.log('watchEffect所指定的回调执行了')
			})

			//返回一个对象(常用)
			return {
				sum,
				msg,
				person
			}
		}

生命周期

在setup外配置:

  • beforeDestroy改名为 beforeUnmount
  • destroyed改名为 unmounted

在setup内配置:

  • beforeCreate===>setup()
  • created=======>setup()
  • beforeMount ===>onBeforeMount
  • mounted=======>onMounted
  • beforeUpdate===>onBeforeUpdate
  • updated =======>onUpdated
  • beforeUnmount ==>onBeforeUnmount
  • unmounted =====>onUnmounted

hook

本质是一个函数,把同功能所用的的composition api封装起来
类似于mixin
优势:复用代码,逻辑清晰易管理

toRef和toRefs

作用:要将响应式对象中的某个属性单独提供给外部使用时。

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

			const name1 = person.name  //此时的name1 只是普通的数据,没有响应式了
			console.log('%%%',name1)

			const name2 = toRef(person,'name')  //name2 依然是响应式数据
			console.log('####',name2)
// toRefs ——批量创建多个 ref 对象
const p = {...toRefs(person)}

shallowReactive和shallowRef

三个字:浅响应

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
  • 什么时候使用?
  • 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
  • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。

readonly和shallowReadonly

两个字:只读

  • readonly: 让一个响应式数据变为只读的(深只读)。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)。
  • 应用场景: 不希望数据被修改时。

toRaw和markRaw

作用:把reactive定义的响应式对象变成普通对象

setup(){
let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			function showRawPerson(){
				const p = toRaw(person) //此时的p只是一个普通对象
				p.age++           
				console.log(p)   //更新了age也不会显示在页面上
			}

			function addCar(){
				let car = {name:'奔驰',price:40}  
				person.car = markRaw(car)  // 新添加的属性不会有响应式效果
			}
			return {
				person,  //新添加的属性必须把整个person返回才能读取到
				...toRefs(person)
			}
}

customRef

作用:自定义ref
例如:有一个需求,输入框的内容我隔1秒再显示出来

<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  //防抖
				return customRef((track,trigger)=>{
					return {
						get(){
							console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`)
							track() //通知Vue追踪value的变化(页面再显示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

简单来说:祖孙组件传值

// 祖组件
setup(){
			let car = reactive({name:'奔驰',price:'40W'})
			provide('car',car) //给自己的后代组件传递数据
			return {...toRefs(car)}
		}

// 孙组件
setup(){
			let car = inject('car')
			return {car}
		}

响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
import {ref, reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue'
	export default {
		name:'App',
		setup(){
			let car = reactive({name:'奔驰',price:'40W'})
			let sum = ref(0)
			let car2 = readonly(car)

			console.log(isRef(sum))
			console.log(isReactive(car))
			console.log(isReadonly(car2))
			console.log(isProxy(car))
			console.log(isProxy(sum))

			
			return {...toRefs(car)}
		}
	}

新的内置组件

Fragment组件

vue2.0:必须有一个根标签
vue3.0:可以多标签,可以没有根标签

Teleport组件

可以把组件某部分内容移动到别的指定位置
例如:在某个页面的弹窗移到body页

<template>
	<div>
		<button @click="isShow = true">点我弹个窗</button>
		<teleport to="body">
			<div v-if="isShow" class="mask">
				<div class="dialog">
					<h3>我是一个弹窗</h3>
					<h4>一些内容</h4>
					<button @click="isShow = false">关闭弹窗</button>
				</div>
			</div>
		</teleport>
	</div>
</template>

<script>
	import {ref} from 'vue'
	export default {
		name:'Dialog',
		setup(){
			let isShow = ref(false)
			return {isShow}
		}
	}
</script>

<style>
// 此时的定位是相对于body
	.mask{
		position: absolute;
		top: 0;bottom: 0;left: 0;right: 0;
		background-color: rgba(0, 0, 0, 0.5);
	}
	.dialog{
		position: absolute;
		top: 50%;
		left: 50%;
		transform: translate(-50%,-50%);
		text-align: center;
		width: 300px;
		height: 300px;
		background-color: green;
	}
</style>

Suspense组件

异步引入组件时,提高用户体验

// 模板
<template>
    	<div class="app">
    		<h3>我是App组件</h3>
    		<Suspense>
    			<template v-slot:default>
    				//我们需要的组件
    				<Child/>
    			</template>
    			<template v-slot:fallback>
    				// 等待时候显示内容
    				<h3>加载中.....</h3>
    			</template>
    		</Suspense>
    	</div>
    </template>
    
// 引入组件
	import {defineAsyncComponent} from 'vue'
    const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
    

其他改变

  • 将全局的API,即:Vue.xxx调整到应用实例(app)上

    2.x 全局 API(Vue3.x 实例 API (app)
    Vue.config.xxxxapp.config.xxxx
    Vue.config.productionTip移除
    Vue.componentapp.component
    Vue.directiveapp.directive
    Vue.mixinapp.mixin
    Vue.useapp.use
    Vue.prototypeapp.config.globalProperties
    filter移除
    v-on.native 修饰符移除
  • 过度类名的更改:

    // Vue2.x写法
    .v-enter,
    .v-leave-to {
      opacity: 0;
    }
    .v-leave,
    .v-enter-to {
      opacity: 1;
    }
    
    // Vue3.x写法
    .v-enter-from,
    .v-leave-to {
      opacity: 0;
    }
    
    .v-leave-from,
    .v-enter-to {
      opacity: 1;
    }
    

版权声明:本文为weixin_46736220原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。