六大亮点:
源码的升级:
使用proxy代替defineProperty实现响应式
重写虚拟DOM的实现和Tree-shaking……
vue create hello-vue3
//创建工程
npm init vite-app <project-name>
//进入工程目录
cd <project-name>
//安装依赖
npm install
//运行
npm run dev
<template>
<div>
<h1>姓名:{{name}}h1>
<h2>年龄:{{age}}h2>
<button @click="sayHello">说话button>
div>
template>
<script>
export default {
name: 'App',
setup() {
let name = 'slineee'
let age =18
function sayhello() {
console.log('hello');
}
return {
name,
age,
sayhello
}
//返回渲染函数:
//return () => h('h1','slineee')
}
}
script>
import { ref } from 'vue'
//创建一个包含响应式数据的引用对象(ref对象)
const counter = ref(0)
JS中操作数据:xxx.value
模板中读取数据:不需要.value,直接{{xxx}}
Object.defineProperty()
的get和set完成的<template>
<div>
<h2>姓名:{{user}}h2>
<h2>年龄:{{age}}h2>
<h3>工作:{{job.type}}h3>
<h3>薪水:{{job.salary}}h3>
<button @click="changeInfo">修改信息button>
div>
template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup() {
// user,age经过ref变成了RefImpl实例对象
// 其中有很多属性,最重要的为value,还有 get和set
let user = ref('slineee');
let age = ref(18);
// job经过ref变成了RefImpl实例对象
//value的值是一个对象,有type和salary
// ref处理基本类型数据用的是ref
// ref处理对象类型数据用的是proxy
let job = ref({
type: '前端开发人员',
salary: '36k'
})
function changeInfo() {
user.value = '睡睡';
age.value = 19;
job.value.type = '端茶倒水';
job.value.salary = '34k'
}
return {
user,
age,
job,
changeInfo
}
}
}
script>
const 代理对象 = reactive(源对象)
接受一个对象(或数组),返回一个代理对象(proxy对象)Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)Object.defineProperty(data,'count',{
get(){}
set(){}
})
存在问题:新增属性、删除属性,界面不会更新
直接通过下标修改数组,界面不会自动更新
2. 补充知识点:如何在vue2中添加属性,且为响应式:this.$set(对象,'添加的属性',value)
也可以引入import Vue from 'vue'
后:Vue.set(对象,'添加的属性',value)
3. 删除属性:this.$delete(对象,'需要移除的属性')
或者Vue.delete(对象,'需要移除的属性')
4.
delect 属性
等等实现vue2不能实现的响应式原理Object.defineProperty()
的get和set实现响应式.value
,读取数据时模板中直接读取不需要.value
.value
this.$attars
this.$slots
this.$emit
<template>
<div>
姓:<input type="text" v-model="person.firstName">
名:<input type="text" v-model="person.lastName">
<h2>姓名:{{person.fullName}}h2>
全名:<input type="text" v-model="person.fullName">
div>
template>
<script>
import { reactive, computed } from 'vue'
export default {
name: 'Demo',
setup() {
let person = reactive({
firstName: '林',
lastName: 'slineee'
})
// // 计算属性简写(没有考虑计算属性被修改的情况)
// 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
}
}
}
script>
watch: {
sum(newValue,oldValue) {
console.log('sum的值变化了',newValue,oldValue);
}
// // 完整写法:
// sum(newValue,oldValue) {
// // 立即监听,一上来没有变化也监听
// immediate: true;
// deep: true;
// handler(newValue,oldValue) {
// console.log('sum的值变化了',newValue,oldValue);
// }
// }
},
<template>
<div>
<h2>当前求和为:{{sum}}h2>
<button @click="sum++">加一button>
<h2>{{msg}}h2>
<button @click="msg += '!'">加感叹号button>
<h2>姓名:{{person.name}}h2>
<h2>年龄:{{person.age}}h2>
<h2>工资:{{person.job.job1.salary}}kh2>
<button @click="person.name += '~'">修改姓名button>
<button @click="person.age++">修改年龄button>
<button @click="person.job.job1.salary++">修改工资button>
div>
template>
<script>
import { reactive, ref,watch } from 'vue'
export default {
name: 'Demo',
setup() {
let sum = ref(0);
let msg = ref('你好啊');
let person = reactive({
name: 'slineee',
age: 18,
job: {
job1: {
salary: 20
}
}
})
// 情况一:监视ref所定义的一个响应式数据
watch(sum,(newValue,oldValue) => {
console.log('sum的值变化了',newValue,oldValue);
},{immediate:true})
// 情况二:监视ref所定义的多个响应式数据
// // 1.最直接的写法:
// watch(sum,(newValue,oldValue) => {
// console.log('sum的值变化了',newValue,oldValue);
// })
// watch(msg,(newValue,oldValue) => {
// console.log('msg的值变化了',newValue,oldValue);
// })
// 简便写法:
watch([sum,msg],(newValue,oldValue) => {
console.log('sum或msg变化了',newValue,oldValue);
},{immediate:true})
// 情况三:监视reactive所定义的一个响应式数据
// 注意,此处无法正确的获得oldValue,无法解决
// 注意:强制开启了深度监视(deep配置无效)
watch(person,(newValue,oldValue) => {
console.log('person变化了',newValue);
},{deep: false})//此处的deep配置无效
// 情况四:监视reactive所定义的一个响应式数据中的某个属性
// 用回调函数返回值
watch(() => person.age,(newValue,oldValue) => {
console.log('person的age变化了',newValue,oldValue);
})
// 情况五:监视reactive所定义的一个响应式数据中的某些属性
watch([() => person.age,() => person.name],(newValue,oldValue) => {
console.log('person的age或name变化了',newValue,oldValue);
})
// 特殊情况:监视reactive所定义的对象中的某个属性
// 所以deep配置有效
watch([() => person.job],(newValue,oldValue) => {
console.log('person的job变化了',newValue,oldValue);
},{deep:true})
return {
sum,
msg,
person
}
}
}
script>
<script>
import { reactive, ref,watch } from 'vue'
export default {
name: 'Demo',
setup() {
let sum = ref(0);
let msg = ref('你好啊');
let person = ref({
name: 'slineee',
age: 18,
job: {
job1: {
salary: 20
}
}
})
// 不能是sum.value,否则直接是0
// sum监听的是RefImpl实例对象,对象里的任一属性修改都能监听
watch(sum,(newValue,oldValue) => {
console.log('sum的值发生了变化',newValue,oldValue);
})
// person是ref定义的对象,直接写person监听不到
// 第一种解决方法:加上.value,此时坚挺的是RefImpl里的value属性
// value是借助了reactive函数生成的,person.value意味着监听的是reactive所定义的数据
// reactive定义数据的特点:深层次而且自动开启了深度监测
watch(person.value,(newValue,oldValue) => {
console.log('person的值发生了变化',newValue,oldValue);
})
// 第二种解决方法:开启深度检测
watch(person,(newValue,oldValue) => {
console.log('person的值发生了变化',newValue,oldValue);
},{deep:true})
return {
sum,
msg,
person
}
}
}
script>
import { watchEffect } from 'vue'
watchEffect(() => {
const x1 = sum.value;
const x2 = person.job.job1.salary;
// 则可以监听sum和salary
console.log('执行了回调');
})
//scr/hooks/usePoint.js
import { reactive,onMounted,onBeforeUnmount } from 'vue'
export default function () {
let position = reactive({
x:0,
y:0
})
function savePoint(event) {
position.x = event.pageX;
position.y = event.pageY;
}
onMounted(() => {
window.addEventListener('click',savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click',savePoint)
})
return position
}
const name = toRef(person,'name')
toRefs(person)
<template>
<div>
<h2>姓名:{{name}}h2>
<h2>年龄:{{age}}h2>
<h2>薪水:{{salary}}kh2>
<button @click="name+='~'">修改姓名button>
<button @click="age++">修改年龄button>
<button @click="salary++">修改薪水button>
div>
template>
<script>
import { reactive,toRef } from 'vue'
export default {
name: 'Demo',
setup() {
let person = reactive({
name: 'slineee',
age: 18,
job:{
job1:{
salary: 37
}
}
})
// 如果直接在这里输出person.name,则是一个死的字符串
// 这里的name1时RefImpl对象,里面有个属性是value
// person对象里的name属性,第一个参数传对象就行
// const name1 = toRef(person,'name')
return {
// 这里如果写name:person.name
// 则交出去的是字符串'slineee'
// 不会发生响应式变化
// 如果这里写name:ref(person.name)
// 则setup里的person里的属性的值不会发生变化
// 因为是直接把ref(person.name)里的改变了
name:toRef(person,'name'),
age:toRef(person,'age'),
salary:toRef(person.job.job1,'salary')
}
}
}
script>
toRaw(只能用于响应式对象):将一个由reactive生成的响应式对象转为普通对象,用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
markRaw:标记一个对象,使其永远不会再成为响应式对象,应用于:有些值不应被设置为响应式的,例如复杂的第三方库类;或当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
markRaw比toRaw应用场景多
//实现:输入框输入后 h3标题里隔一秒才显示
<template>
<div id="app">
<input type="text" v-model="keyWord">
<h2>{{keyWord}}h2>
div>
template>
<script>
import { ref,customRef } from 'vue'
export default {
name: 'App',
setup() {
function myRef(value,delay) {
let timer;
// 第一个return是为了把函数返回值交出去
return customRef((track,trigger) => {
return {
get() {
// 通知vue追踪数据的改变,返回哪个数据追踪哪个数据
track()
return value
},
set(newValue) {
console.log('有人把myRef中的数据修改了');
// value = newValue
// //通知vue重新解析模板
// trigger()
// 如何等一秒再更新:
// 防止输入过快,不断触发定时器
clearTimeout(timer);
timer = setTimeout(() => {
value = newValue
trigger()
},delay)
}
}
})
}
let keyWord = myRef('hello',500);
return {
keyWord
}
}
}
script>
//祖组件:
<template>
<div id="app">
<h3>我是App祖组件,{{name}},{{age}}h3>
<child>child>
div>
template>
<script>
import { reactive, toRefs, provide } from 'vue'
import Child from '../src/components/Child.vue'
export default {
name: 'App',
components: {
Child
},
setup() {
let person = reactive({
name: 'slineee',
age: 18
});
// 第一个参数为给后代组件传递的数据起个名字,第二个为传递过去的数据
provide('person',person)
return {
...toRefs(person)
}
}
}
script>
//后代组件(只要是后代均可这么用)
<template>
<div class="son">
<h3>我是son孙组件,{{person.name}},{{person.age}}h3>
div>
template>
<script>
import { inject } from 'vue'
export default {
name: 'Son',
setup() {
let person = inject('person');
return {
person
}
}
}
script>
<template>
<div>
<button @click="isShow=true">点我弹窗button>
<teleport to="body">
<div class="dialog" v-if="isShow">
<h3>我是一个弹窗h3>
<h4>一些内容h4>
<button @click="isShow=false">关闭弹窗button>
div>
teleport>
div>
template>
import { defineAsyncComponent } from 'vue'
const Child = defineAsyncComponent(()=> import('./components/Child.vue'))
使用suspense包裹组件,并配置好default和fallback
<template>
<div id="App">
<h3>我是App组件h3>
<suspense>
<template v-slot:defalut>
<child>child>
template>
<template v-slot:fallback>
<h3>加载中,请稍等h3>
template>
suspense>
div>
template>