铺垫

p136 vue3简介

文档

1.性能提升(打包、渲染……)

2.源码升级(使用Proxy代替defineProperty实现响应式、重写虚拟DOM的实现和Tree-Shaking)

3.拥抱ts

4.新的特性

  • tree shaking概念

摇树-实际上就是把不要的代码移除

  • 其他改变

1.新的生命周期钩子

2.data 选项应始终被声明为一个函数

3.移除keyCode支持作为 v-on 的修饰符 (就是没有在这里插入图片描述这种写法了 )

P137 使用vue-cli创建工程

正文

  • vue create xxx创建项目命令行

{}

  • vue3脚手架要求-image-20220126192722678
  • 运行vue3项目,发现调试工具不亮image-20220126192805802

是因为该调试工具是针对vue2

P138 使用vite创建工程

文档

  • 啥是vite

官网介绍:下一代的前端构建工具

  • 构建工具历史

gruntgulpwebpack->vite

  • vite优势

1.开发环境中,无需打包操作,可快速的冷启动。

2.轻量快速的热重载(HMR)。

3.真正的按需编译,不再等待整个应用编译完成。

  • vite快的原因

如图所示,就是webpack是要引起全部文件打包再编译到服务器,而vite是先指定服务器然后动态引入文件

P:这块是随便写的,知识点有缘再看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tzzAfIYP-1652603476757)(Images/image-20220127105608085.png)]

  • 使用方式-命令行
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
  • 目前还没有大规模应用-可能会有坑、所以教程还是用传统的vuecli

P:原来这个vite相当于是一种脚手架

P139 分析工程结构

核心code

1.main.js
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'
//import Vue form 'vue'-控制台报错没有该对象,说明vue3不兼容vue2

//创建App实例对象——app(比vm更“轻”)
const app = createApp(App)
app.mount('#app')//挂载
//createApp(App).mount('#app')-简写形式

//unmount使用
setTimeout(()=>{
    app.unmount("#app")
},1000)

//vue2版本
const vm=new Vue({
    render:h=>h(App)
})
vm.$mount("#app")

2.vue.config.js
module.exports = {
	lintOnSave:false, //关闭语法检查
}

{}

  1. vue3组件无需设置根标
image-20220127114523086

P140 安装开发者工具

文档

  • 商城下载调试工具image-20220127134603568

  • Vue3调试工具是测试版,没有vuex

image-20220127134702675

P141 初始setup

setup是所有Composition API(组合API)“ 表演的舞台 ”

核心code

1.template
<template>
	<h1>一个人的信息</h1>
	<h2>姓名:{{name}}</h2>
    <h2>性别:{{sex}}</h2>
	<button @click="sayHello">说话(Vue3所配置的——sayHello)</button>
	<button @click="sayWelcome">说话(Vue2所配置的——sayWelcome)</button>
	<button @click="test1">测试一下在Vue2的配置中去读取Vue3中的数据、方法</button>
	<button @click="test2">测试一下在Vue3的setup配置中去读取Vue2中的数据、方法</button>
</template>

2.vue2
//vue2的语法还是可以正常使用-以后可能会去除
data() {
	return {
		sex:'男',
	}
},
methods: {
	sayWelcome(){
		alert('欢迎来到尚硅谷学习')
	},
	test1(){
		console.log(this.name) //vue2中能拿到vue3的配置
		console.log(this.sayHello)
	}
},

3.vue3
// import {h} from 'vue'
export default {
    setup(){
	let name = '张三'
	function sayHello(){
		alert(`我叫${name},你好啊!`)
        //注:这里的name不需要想vue2一样加“this”-都在setup函数下
	}
	function test2(){
		console.log(name)
		console.log(sayHello)
		console.log(this.sex) //vue3拿不到vue2的配置
		console.log(this.sayWelcome)
	}
    //return一个对象,上面的模板才能使用!!
	return {
		name,
		sayHello,
		test2,
	}

	//return一个函数(渲染函数)-需要引入h,函数回覆盖return对象
	// return ()=> h('h1','尚硅谷')
}
}

{}

  1. vue2和vue3不用混用

因为vue3不能读到vue2参数,而且两者定义相同参数名会冲突-取vue3

  1. setup通常不能是一个async函数(后期也可以返回一个Promise实例,但需要Suspense异步组件的配合)

因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。

P:不要为了使用await,给整个setup()加上async

  1. Composition(组合式) API-是vue3中的很重要的概念

ref函数

P142 处理基本类型-

P143 处理对象类型-

文档

  • 作用:定义一个响应式的数据

  • 基本类型-接收数据

原理和vue2相同,都是用到了Object.defineProperty中的getset

get set藏在原型对象中,并把value拿出-方便使用

image-20220127145936137
  • 对象类型-接收数据
image-20220127153345883

对象地址用的是数据截持,但对于里面的属性用的是更高级的proxy

ref对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。

reactive里面用到了proxy

核心code

<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2> //和vue2相同不需要value-模板已经做了一层处理、用了反而报错
<h3>工作种类:{{job.type}}</h3> //通俗用法
<h3>工作薪水:{{job.salary}}</h3>
<button @click="changeInfo">修改人的信息</button>

import {ref} from 'vue' //引入ref
setup(){
	let name = ref('张三') //使用ref让数据变响应式
	let job = ref({ //ref创建对象类型数据
		type:'前端工程师',
		salary:'30K'
	})
	function changeInfo(){
		 name.value = '李四' //数据修改要加".vlaue"
        console.log(job.value)
		job.value.type = 'UI设计师' //对象类型修改不用套.value
		job.value.salary = '60K'
	}
	return {
		name,
		job,
		changeInfo
	}
}

{}

  1. reflmpl单词解释image-20220127145216023

  2. let name = ref(‘张三’) 是啥

创建的不是简单类型,而是一个refImpl对象-引用实现的实例

image-20220127145529055

简称:引用对象

P144 reactive函数

正文

  • 定义一个对象类型响应式数据基本类型不要用它,要用ref函数)

  • reactive语法:const 代理对象= reactive(源对象)

接收一个对象(或数组)-源对象,返回一个代理对象(Proxy的实例对象,简称proxy对象

image-20220127161222279
  • reactive定义的响应式数据是“深层次的”。

  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

核心code

<h1>一个人的信息</h1>
<h2>姓名:{{person.name}}</h2>
<h3>工作种类:{{person.job.type}}</h3>
<h3>工作薪水:{{person.job.salary}}</h3>
<h3>爱好:{{person.hobby}}</h3>
<button @click="changeInfo">修改人的信息</button>

import {reactive} from 'vue' //引入reactive
export default {
	name: 'App',
	setup(){
        //let name=ref("张三") //如果是基本类型只能用ref来响应
		let person = reactive({ 
            //可以把基本类型写在一个!对象里-作属性,这样就可以用reactive来响应了
			name:'张三',
			job:{
				type:'前端工程师',
				salary:'30K',
			},
			hobby:['抽烟','喝酒','烫头']
		})
		function changeInfo(){
			person.name = '李四'
			person.job.type = 'UI设计师' 
            //之前用ref()时,修改写法:person.value.job.type,如果是reactive则不用
			person.job.salary = '60K'
			person.hobby[0] = '学习'
		}
		return {
			person, //数据封装到一个对象里,return也能少写点
			changeInfo
		}
	}
}

Vue3响应式原理

P145 回顾Vue2的响应式原理

核心code

<h2>{{person.name}}</h2>
<h2>{{person.age}}</h2>
<button @click="addSex">添加一个sex属性</button>
<button @click="deleteName">删除name属性</button>

return{
    person:{
        name:'张三',
        age:18,
    }
}
import Vue form 'vue'
methods:{
    addSex(){
        //this.person.sex='女'-这种普通方法修改,页面不会更新
        this.$set(this.person,'sex','女')
        Vue.set(this.person,'sex','女')//功能同上-引入Vue
    }
    deleteName(){
        //delete this.person.name-这种普通方法删除,页面不会更新
        this.$delete(this.person,'name','李四')
        Vue.delete(this.person,'name','李四')
    }
}

正文

  • 实现原理:

    对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

    数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

Object.defineProperty(data, 'count', {
    get () {}, 
    set () {}
})
  • 存在问题:

    新增属性、删除属性, 界面不会更新

    直接通过下标修改数组, 界面不会自动更新。

P146 Proxy

code

//传送中的源对象
let person = {
		name:'张三',
		age:18
}
2.Vue2
	let p = {} //传说中的代理对象
	Object.defineProperty(p,'name',{
		configurable:true, //定义了这个配置defineProperty才能删除-return true
		get(){ 
			return person.name
		},
		set(value){ 
			console.log('更新界面!')
			person.name = value
		}
	})
	Object.defineProperty(p,'age',{
		get(){ 
			return person.age
		},
		set(value){ 
			console.log('更新界面!')
			person.age = value
		}
	}) 

3.vue3
	const p = new Proxy(person,{ //p是代理数据,person是源数据
		//读取p的某个属性
		get(target,propName){
            return target[propName]//注:不能.propName因为propName是变量,只能用[]
		},
		//修改p的某个属性、或给p追加某个属性时
		set(target,propName,value){
			console.log(`更新界面!`)//-这里实际要写真正的更新代码
            target[propName]=value
		},
		//删除p的某个属性时
		deleteProperty(target,propName){
			console.log(`更新界面!`)
            return delete target[propName]//delete是带返回值的-是否成功删除
		}
	})
	//#endregion

正文

  • const p = new Proxy(person,{})

上面这段如果第二个参数为空,就只能实现数据代理,并不能实现响应式

image-20220128113139304

还需要在里面写内容实现响应式的修改-get()、set()

other

  • reacitve进步点

可以直接进行对象属性的删除修改,页面也会更新

  • 可通过下标操作数组

P147 Reflect

核心代码

//传送中的源对象
let person = {
		name:'张三',
		age:18
}
1.上述例子使用上述例子使用Reflect代替
	const p = new Proxy(person,{ //p是代理数据,person是源数据
		// 拦截读取属性值
		get(target,propName){
			return Reflect.get(target,propName) 
            //用Reflect代替对象操作更有利于封装-不会报错
		},
		// 拦截设置属性值或添加新属性
		set(target,propName,value){
			console.log(`更新界面!`)//-这里实际要写真正的更新代码
			Reflect.set(target,propName,value)
		},
		// 拦截删除属性
		deleteProperty(target,propName){
			console.log(`更新界面!`)
			return Reflect.deleteProperty(target,propName)
		}
	})
	//#endregion

2.使用Reflect的原因
	let obj = {a:1,b:2}
	//Object.defineProperty-应该是obj.c的全写
	 try { //如果使用defineProperty必须要这样接收报错,不然项目无法启动
		Object.defineProperty(obj,'c',{
			get(){
				return 3
			}
		})
		Object.defineProperty(obj,'c',{ //虽然这样写是错的-但也不好影响项目运行
			get(){
				return 4
			}
		})
	} catch (error) {
		console.log(error)
	} 
	//Reflect.defineProperty
	 const x1 = Reflect.defineProperty(obj,'c',{ //Reflect有布尔返回值而不是报错
		get(){
			return 3
		}
	})
	const x2 = Reflect.defineProperty(obj,'c',{
		get(){
			return 4
		}
	}) 
	if(x2){ //比起上面的try,这样的判断更好做大的框架封装
		console.log('操作成功了!') 
	}else{
		console.log('操作失败了!')
	} 

正文

  • 实现原理

通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。

通过Reflect(反射): 对源对象的属性进行操作

  • Reflect(反射)

这个东西的window的内置对象,es6新语法

简单用法:Reflect.set(obj,'a',666)image-20220128141209590

others

  • 为什么好好的obj.a不用,要用Reflect.set(obj,'a')来拿属性值?

这个因为Reflect有我们看不到的优点,ECMA已经准备把Object的属性移植window的Reflect属性上image-20220128141513964

P148 reactive对比ref

code

//可以在所有数据外面套个data,这样基本数据也可以使用reactive
let data =reactive({ //像vue2一样用data来包装所有数据
    aa:'11'
	person:{
        name:"张三"
    }
}),
funtion change(){
    data.person.name="张三" //使用reactive就不需要用data.value.person.name这种麻烦写法了
    //p:但是这样同时每个数据都要加个前缀-data,也挺麻烦的
}
return {
    data //这里也可以少写数据
}

正文

  • 从定义数据角度对比:
    • ref用来定义:基本类型数据。
    • reactive用来定义:对象(或数组)类型数据。
    • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。
  • 从原理角度对比:
    • ref通过Object.defineProperty()的getset来实现响应式(数据劫持)。
    • reactive通过使用Proxygetset来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:
    • ref定义的数据:操作数据需要**.value**,读取数据时模板中直接读取不需要**.value**。
    • reactive定义的数据:操作数据与读取数据:均不需要**.value**。

P149 setup的两个注意点(参数和时机)

code

1.App.vue-父组件
<Demo @hello="showHelloMsg" msg="你好啊" school="尚硅谷">
	<template v-slot:qwe>  	
	<!-- v-slot:qwe在子组件的content.slots有对应展示-->
		<span>尚硅谷</span>
	</template>
</Demo>
<script>
export default {
	setup(){
		function showHelloMsg(value){
			alert(`你触发了hello事件,${value}!`)
		}
		return {
			showHelloMsg
		}
	}
}
</script>

2.Demo.vue-子组件
<button @click="test">触发Hello事件</button>
<script>
	export default {
		props:['msg','school'],
		emits:['hello'], //可以不加但会警告
		setup(props,context){
			// context.attrs-Vue2中的this.$attrs
			// context.emit-this.$emit
			// context.slots-this.$slots
           console.log(context.slots) 
            //输出结果-Proxy{_:1,_vInternal:1,qwe:f}
			function test(){
				context.emit('hello',666)
			}
			return {
				test
			}
		}
	}
</script>

正文

  • 文档

setup执行的时机

  • beforeCreate之前执行一次,this是undefined

setup的参数!!

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

other

  • vue2中vc. a t t r s ∗ ∗ 和 ∗ ∗ attrs**和** attrsslots

组件实例对象(vc)没有用props接收父组件属性,那么组件的**$attrs也会收到传递属性**,只不过不能进行限制

组件实例对象(vc)会用**$slots接收插槽对象**

image-20220207204111936

计算属性

P150 computed计算属性

code

import {reactive,computed} from 'vue'
setup(){
	let person = reactive({
		firstName:'张',
		lastName:'三'
	})
	person.fullName = computed(()=>{ //简写-属性只读不能修改
         //P:为什么都是reactive对象里的属性了,还不能修改呢??
		return person.firstName + '-' + person.lastName
	}) 
	person.fullName = computed({//完整-属性可读可写
    //person.fullName-体现了reactive可增加响应式属性的好处,如果是之前这样加属性不会相应 
		get(){
			return person.firstName + '-' + person.lastName
		},
		set(value){
			const nameArr = value.split('-') //split-对字符串切片
			person.firstName = nameArr[0]
			person.lastName = nameArr[1]
		}
	})
	return {
		person //一般数据只输出一个
	}
}
computed:{
    fullName(){
        return this.person.firstName+'-'+this.person.lastName 
        //在setup外部需要使用this
    }
}

监听属性

P151 watch监听ref定义的数据

code

1.vue2
watch(){
	sum:(newV,oldV){
        console.log(newV,oldV)
    }
    sum:{
        immediate:true,
        handler(newV,oldV){
            console.log(newV,oldV)
        }
    }
}

2.vue3
import {ref,reactive,watch} from 'vue'
setup(){
	let sum = ref(0)
	let msg = ref('你好啊')
    let person =ref({
        name:'张三'
    })
	//监视一个-ref
	watch(sum,(newValue,oldValue)=>{
		console.log('sum变了',newValue,oldValue) //1,0
	},{immediate:true}) //第三个参数写配置
	//监视多个-ref
	 watch([sum,msg],(newValue,oldValue)=>{
		console.log('sum或msg变了',newValue,oldValue)//[1,'你好啊!'],[0,'你好啊']
	})
   	//监视对象-ref
	 watch(person.value,(newValue,oldValue)=>{ //注:如果ref是对象要加.value
		console.log('person变了',newValue,oldValue)//{name:'张三'},[name,'张三']-oldV监听不到
	}) 
} 

P152 watch监听reactive定义的数据

code

<button @click="sum++">点我+1</button> //简单方法可以直接写在模板里-sum++
2.vue3
import {reactive,watch} from 'vue'
setup(){
  	let person = reactive({
		name:'张三',
        age:18,
		job:{
			j1:{
				salary:20
			}
		}
	})
    
    //对象的全部属性-reactive	
    watch(person,(newValue,oldValue)=>{
		console.log('person变化了',newValue,oldValue)//!!无法正确获取oldValue
	}) //强制开启了深度监视(deep配置无效)
     	 
    //对象的单个属性(监听oldV)-reactive	
    watch(()=>person.name,(newValue,oldValue)=>{//需要写成函数形式
		console.log('person的name变化了',newValue,oldValue)
	}) //能获取到oldV且不用设置deep-因为这里本质监听的是基本数据
    
   	//对象的多个属性(监听oldV)-reactive	
	 watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{//列表函数形式
		console.log('person的name或age变化了',newValue,oldValue)
	})  
    
	//对象的单个d属性(对象、监听oldV)-reactive	
	 watch(()=>person.job,(newValue,oldValue)=>{
		console.log('person的job变化了',newValue,oldValue)
	},{deep:true}) //监视的reactive对象属性值还是对象-deep配置有效 
} 

P153 watch时value的问题

code

import {ref,watch} from 'vue'
let sum = ref(0)
let person =ref({
    name:'张三',
    age:18,
	job:{
		j1:{
			salary:20
		}
	}
})
watch(sum,(……)=>{
	console.log('监听sum第一层属性') 
})
watch(person.value,(……)=>{ 
	console.log('监听person.value(Proxy)属性') 
})
watch(person,(……)=>{ 
	console.log('深度监听person所有属性') 
},{deep:true}) 

正文

  • watch ref 基本类型不加value

watch(sum,(……)=>{}-可监听到sum属性更改,但watch(sum.value,(……)=>{}报错,因为.value会变成监听数值

image-20220308210100208 image-20220308212531084 image-20220308210526223
  • watch ref 对象注意点

由于监听ref只是监听ref对象第一层属性,所以只能监听value值变化(整个Proxy),但是像value属性里面二级属性(value.name:张三)就监听不到

image-20220308212145094

所以需要使用person.value监听value(Proxy)

也可以使用深度监视ref-watch(person,(……)=>{},{deep:true}) 来实现对ref所有属性的监视

P154 watchEffect函数

code

import { ref, reactive, watchEffect } from "vue";
let sum = ref(0)
let person = reactive({
	name:'张三',
	age:18,
	job:{
		j1:{
			salary:20
		}
	}
})
watchEffect(()=>{  
//watchEffect回调中用到的数据发生变化,就重新执行回调;且默认为立即监听
	const x1 = sum.value
	const x2 = person.job.j1.salary
    console.log('watchEffect配置的回调执行了')
})

文档

  • 文档

watch的套路是:既要指明监视的属性,也要指明监视的回调

watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

watchEffect有点像computed

  • computed 注重的计算出来的(回调函数的返回值),所以必须要写返回值
  • watchEffect注重的是过程(回调函数的函数体),所以不用返回值

生命周期

P155 Vue3生命周期

code

import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
setup(){
	console.log('---setup---') //对应create相关配置项-且比beforeCreate等优先级高
	//通过组合式API的形式去使用生命周期钩子-按顺序展示
	onBeforeMount(()=>{
		console.log('---onBeforeMount---') //所有的onxx都比配置项xx优先级高
	})
	onMounted(()=>{
		console.log('---onMounted---')
	})
	onBeforeUpdate(()=>{
		console.log('---onBeforeUpdate---')
	})
	onUpdated(()=>{
		console.log('---onUpdated---')
	})
	onBeforeUnmount(()=>{
		console.log('---onBeforeUnmount---')
	})
	onUnmounted(()=>{
		console.log('---onUnmounted---')
	})
},
//通过配置项的形式使用生命周期钩子-按顺序展示
beforeCreate() {
	console.log('---beforeCreate---')
},
created() {
	console.log('---created---')
},
beforeMount() {
	console.log('---beforeMount---')
},
mounted() {
	console.log('---mounted---')
},
beforeUpdate(){
	console.log('---beforeUpdate---')
},
updated() {
	console.log('---updated---')
},
beforeUnmount() {
	console.log('---beforeUnmount---')
},
unmounted() {
	console.log('---unmounted---')
},

正文

  • 生命周期
image-20220210154954343
  • 文档
  1. Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名

beforeDestroy改名为 beforeUnmount

destroyed改名为 unmounted

  1. Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:

beforeCreate===>setup() P:setup本身**就是create,**所以这里就没有对应钩子了

created=======>setup()

beforeMount ===>onBeforeMount

mounted=======>onMounted

beforeUpdate===>onBeforeUpdate

updated =======>onUpdated

beforeUnmount ==>onBeforeUnmount

unmounted =====>onUnmounted

  • 生命周期两种方式
  1. 配置项-mounted() {}
  2. 组合API-onMounted(() => {})

other

  • 生命周期可以按之前的写法,不一定要写在setup

P156 自定义hook

code

1.hooks-usePoint.js //像这样把组合api抽离成一个方法-便于复用
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function (){ //默认暴露简写-单个内容、不用写方法名
	let point = reactive({
		x:0,
	})
	function savePoint(event){
		point.x = event.pageX
	}
	onMounted(()=>{
		window.addEventListener('click',savePoint) //window绑定事件-全屏点击都有效
	})
	onBeforeUnmount(()=>{ //如果绑定事件不需要了记得卸载-毕竟window是全局的
		window.removeEventListener('click',savePoint)
	})
	return point
}

2.test.vue-components
//组件引用hooks
<h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
import usePoint from '../hooks/usePoint'
export default {
	name:'Test',
	setup(){
		const point = usePoint() 
		return {point}
	}
}

正文

  • hoos文件创建例子-和components同级

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c0B3E6Y4-1652603476759)(Images/image-20220216104549472.png)]一般会加个use前缀

  • 文档

什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装

类似于vue2.x中的mixin

自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。

P157 toRef与toRefs

code

1.Demo.vue
<h4>{{person}}</h4>
<h2>姓名:{{name}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="job.j1.salary++">涨薪</button>
import {ref,reactive,toRef,toRefs} from 'vue'
setup(){
	let person = reactive({
		name:'张三',
		job:{
			j1:{
				salary:20
			}
		}
	})
	//const name = toRefs(person,'name')-也可以这样用、一般是直接return写好
    //const x = toRefs(person)
	return {
		person,
        name:toRef(person,'name'),//单个属性输出、赋值的是ref对象有用
		salary:toRef(person.job.j1,'salary'),
		...toRefs(person)//多个属性输出-只输出对象子层属性
        //<错误写法
        // name:person.name-无效、因为这只是赋值了一个简单类型又不是ref对象
        // name:ref(person.name)-无效、相当于new了个ref对象、和reactvie里的没有关系
        //<
	}
}

P:个人感觉toref需要配合reactive使用,毕竟ref不用再toref了,然后toref主要的作用是简写 多层的reactive对象,貌似 别的用处

正文

  • toRef实例结果-实际上是RefImpl响应式对象
image-20220216114449954
  • toRef实例结果image-20220216115043274
  • 文档

作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性

语法:const name = toRef(person,'name')

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

扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

other

  • toRef这种作用禹神形容成桥接

P158 shallow(浅)Reactive与shallowRef

code

let person = shallowReactive({ //只考虑第一层数据的响应式
	name:'张三',
	age:18,
	job:{
		j1:{
			salary:20
		}
	}
})
let x = shallowRef(0)//这样写没有意义,shallowRef针对的是ref一个对象形式
let x = shallowRef({ //修改x.y不响应,修改整个响应
	y:0
})

正文

  • 文档

shallowReactive:只处理对象 最外层属性的响应式(浅响应式)。

shallowRef:只处理基本数据类型的响应式, 不进行 对象的响应式处理。

什么时候使用?

  • 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
  • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象替换 ===> shallowRef。

P159 readonly与shallowReadonly

-code 5:50控制台警告 readonly的意义和const普通区别 使用场景

code

let person = reactive({
	name:'张三',
	age:18,
	job:{
		j1:{
			salary:20
		}
	}
})
person = readonly(person) //数据只读不能修改
person = shallowReadonly(person)//数据的第一层只读不能修改

正文

  • 修改readonly定义数据控制台输出
image-20220329204522544
  • 为什么不用普通const代替readonly

使用const 声明一个普通对象的效果也是可读修改属性模板不响应,但是这个是治标不治本,因为本质上const声明的对象还是改变了,只是模板不响应罢了,但是readonly是让对象都不能修改,修改会警告。

  • readonly的使用场景-当他人组件 返回数据 只读不想让你修改时

P160 toRaw与markRaw

code

<button @click="addCar">给人添加一台车</button>
<button @click="person.car.name+='!'">换车名</button> //后加属性也是响应式的
let sum = ref(0)
let person = reactive({
	name:'张三',
	age:18,
	job:{
		j1:{
			salary:20
		}
	}
})

function showRawPerson(){
	//const s = toRaw(sum)-无效,toRaw只对reactive对象使用
    const p = toRaw(person)
	//p.age++ -不生效,因为p toRaw后只是一个普通对象
	console.log(p)
}

function addCar(){ //添加一个属性且该属性为响应式的-因为Proxy牛逼
	let car = {name:'奔驰',price:40}
	person.car = markRaw(car) //让数据变为不可响应式-markRaw
    //markRaw的使用场景-见正文文档
}

return {
	...toRefs(person), //toRefs不作用于后面添加的属性-car
    person,//模板用到后加属性(car),还需return person
}

正文

  • 文档

toRaw:

  • 作用:将一个由reactive生成的响应式对象转为普通对象
  • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。

markRaw:

  • 作用:标记一个对象,使其永远不会成为响应式对象。
  • 应用场景:
    1. 有些值不应被设置为响应式的,例如复杂的第三方类库等。
    2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

P161 customRef

code

<template>
	<input type="text" v-model="keyword">
	<h3>{{keyword}}</h3>
</template>

<script>
	import {ref,customRef} from 'vue'
	export default {
		name:'Demo',
		setup(){
			// let keyword = ref('hello') //使用Vue准备好的内置ref
			//自定义一个myRef
			function myRef(value,delay){
				let timer
				//通过customRef去实现自定义
				return customRef((track,trigger)=>{ 
					return{
						get(){ //写法和计算属性很像
							track() //告诉Vue这个value值是需要被“追踪”的
							return value
						},
						set(newValue){
							clearTimeout(timer) //需要关闭定时器-不然输入队列内容太多会乱
							timer = setTimeout(()=>{
								value = newValue 
                                //value是传参,为什么能再传给get()??可能和回调有关
								trigger() //告诉Vue去更新界面
							},delay) //!实现了节流
						}
					}
				})
			}
			let keyword = myRef('hello',500) //使用程序员自定义的ref
			return {
				keyword
			}
		}
	}
</script>

正文

  • 文档

创建一个自定义ref,并对其依赖项跟踪更新触发进行显式控制

p:就是让ref使用更自由,可以写自定义逻辑,如节流

P162 provide(提供)与inject(注入)

code

1.grandf.vue
<h3>{{car.name}}--{{car.price}}</h3>
import { toRefs,provide } from 'vue'
export default {
	setup(){
		let car = reactive({name:'奔驰',price:'40W'})
        provide('car',car) //给自己的后代组件传递数据
		return {...toRefs(car)} //toRefs貌似是把reactive“拆分”
	}
}

2.son.vue
<h3>{{car.name}}--{{car.price}}</h3>//没用torefs的话只能正常拿
import {inject} from 'vue'
export default {
	setup(){
		let car = inject('car')
		return {car} 
	}
}

other

  • toRefs作用-把reactive数据 “拆分”
<h3>{{name}}--{{price}}</h3> //toRefs拆分后模板可以直接使用对象属性
import { toRefs } from 'vue'
export default {
	setup(){
		let car = reactive({name:'奔驰',price:'40W'})
		return {...toRefs(car)} 
	}
}

P163 响应式数据的判断

code

let car = reactive({name:'奔驰',price:'40W'})
let sum = ref(0)
let car2 = readonly(car) //创建一个只读的reactive

console.log(isRef(sum)) //以下都为true
console.log(isReactive(car)) 
console.log(isReadonly(car2))
console.log(isProxy(car))
console.log(isProxy(sum)) //false

正文

  • 文档

isRef: 检查一个值是否为一个 ref 对象

isReactive: 检查一个对象是否是由 reactive 创建的响应式代理

isReadonly: 检查一个对象是否是由 readonly 创建的只读代理

isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

P164 CompositionAPI的优势

code

1.usePoint.js //组合api
import {reactive,onMounted,onBeforeUnmount} from 'vue'
//实现窗口window点击打印定位功能
export default function (){ 
	let point = reactive({
		x:0,
	})
	function savePoint(event){
		point.x = event.pageX
        console.log(event.pageX)
	}
	onMounted(()=>{
		window.addEventListener('click',savePoint)
	})
	onBeforeUnmount(()=>{
		window.removeEventListener('click',savePoint)
	})
	return point //必须return出去
}

2.Demo.vue //某个子组件
import usePoint from '../hooks/usePoint'
export default {
	setup(){
		let point = usePoint()
		return {point}
	}
}

正文

  • 文档

1.Options API 存在的问题

使用传统OptionsAPI(配置项)中,新增或者修改一个需求,需要分别在datamethodscomputed里修改

2.Composition API 的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序(集中)的组织在一起

P165 Fragment组件(片段)

正文

  • 文档

Vue2中: 组件必须有一个根标签

Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个**Fragment ** 虚拟元素中

好处: 减少标签层级, 减小内存占用

P166 Teleport组件(传送)

code

<button>点我弹个窗</button>
<teleport to="body"> <!-- 传送到body标签下-这样定位就有个稳定基线了 -->
<!-- <teleport to="body">-也可以指定id跳转、但是cli貌似一般只有一个id(app) -->
	<div>……我是一个弹窗……</div>
</teleport>

正文

  • 文档

什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。

other

  • 绝对定位居中-transform技巧
.dialog{
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%,-50%);/* 不清楚需不需要高宽确定 */
	width: 300px;
	height: 300px;
}

P167 Suspense组件(悬念)

code

1.App.vue
<Suspense> <!-- 使用Suspense实现loading加载 -->
	<template v-slot:default>
		<Child/>
	</template>
	<template v-slot:fallback>
		<h3>稍等,加载中...</h3>
	</template>
</Suspense>
// import Child from './components/Child'//静态引入
import {defineAsyncComponent} from 'vue' 
const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入
//异步虽然组件能分开加载了,但往往还不过-需要再加一个loading组件
export default {
	name:'App',
	components:{Child},
}

2.Child.vue
async setup(){ //实际上setup是可以加async-前提是在Suspense里,不然不显示
	let sum = ref(0)
	let p = new Promise((resolve,reject)=>{ //简单Promise写法-还是要自己有缘实践下
		setTimeout(()=>{
			resolve({sum})
		},3000)
	})
	return await p
}

正文

  • 异步组件作用

多层嵌套组件要等最慢的组件加载完才出现,如果组件加载时间要很长的话,白屏会给用户不好的体验

如果时异步组件的话,就展示加载好的组件,对于没有加载好的组件还可以用**“加载中组件”提示**,大大提高用户的体验和耐心

image-20220216152553767
  • defineAsyncComponent(异步引入组件)和Suspense(悬疑)配合使用才有意义

P168 Vue3中其他的改变

-弹幕 script setup $

正文

  • 文档

data选项应始终被声明为一个函数

过度类名的更改:Vue2.md-P91

  • 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;
    }
    

移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodesp:@keyup.13

移除v-on.native修饰符 p:native在vue2中作用是让@click成为组件本身事件,而不是自定义事件

  • 父组件中绑定事件

    <my-component
      v-on:close="handleComponentEvent"
      v-on:click="handleNativeClickEvent"
    />
    
  • 子组件中声明自定义事件

    <script>
      export default {
        emits: ['close']
      }
    </script>
    

移除过滤器(filter)

过滤器虽然这看起来方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性替换过滤器。

})
return await p

}


`正文`

- 异步组件作用

多层嵌套组件要等**最慢的组件**的**加载完**才出现,如果组件加载时间要很长的话,**白屏**会给用户**不好**的体验

如果时异步组件的话,就**先**展示**加载好**的组件,对于**没有加载**好的组件还可以用**“加载中组件”提示**,大大**提高**用户的体验和耐心

<img src="Images/image-20220216152553767.png" alt="image-20220216152553767" align='left' style="zoom:50%;" />

- **defineAsyncComponent**(异步引入**组件**)和**Suspense**(悬疑)**配合使用**才有意义

### P168 Vue3中其他的改变

-弹幕 script setup $

`正文`

- 文档

**data**选项应**始终**被声明为一个**函数**。

过度类名的更改:[Vue2.md-P91](..\..\..\学习\Vue\VueJs-B站(张天禹)\P40-P95(Vue2).md)

- **Vue2**.x写法

  ```css
  .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;
    }
    

移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodesp:@keyup.13

移除v-on.native修饰符 p:native在vue2中作用是让@click成为组件本身事件,而不是自定义事件

  • 父组件中绑定事件

    <my-component
      v-on:close="handleComponentEvent"
      v-on:click="handleNativeClickEvent"
    />
    
  • 子组件中声明自定义事件

    <script>
      export default {
        emits: ['close']
      }
    </script>
    

移除过滤器(filter)

过滤器虽然这看起来方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性替换过滤器。

Logo

前往低代码交流专区

更多推荐