Vue是一套用于构建用户界面的渐进式框架。
Vue3也支持Vue2的大多数特性
更好的支持了TypeScript
性能得到了更大的提升:使用proxy代替了defineProperty实现数据响应式
增加了很多的新特性:Composition API(组合式API),新组件(Fragment/Teleport/Suspense)等等.
项目创建方式:
1、vue-cli创建
2、vite创建
新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次(beforeCreate之前执行)
函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
注意:
this是undefined, 不能通过this来访问data/computed/methods / props
methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods\
● setup的参数
○ setup(props, context) / setup(props, {attrs, slots, emit})
○ props: 包含props配置声明且传入了的所有属性的对象
context:
○ attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
○ slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
○ emit: 用来分发自定义事件的函数, 相当于 this.$emit
export default {
setup () {
const msg = ref('abc')
function fn (content: string) {
msg.value += content
}
return {
msg,
fn
}
}
}
1、 作用::定义一个数据的响应式
2、 语法:const xxx = ref(initValue)
○ 创建一个包含响应式数据的引用(reference)对象
○ js中操作数据: xxx.value
○ 模板中操作数据: 不需要.value
3、 一般用来定义一个基本类型的响应式数据
<template>
<h2>{{count}}</h2>
<hr>
<button @click="update">更新</button>
</template>
<script>
import {
ref
} from 'vue'
export default {
setup () {
// 定义响应式数据 ref对象
const count = ref(1)
console.log(count)
// 更新响应式数据的函数
function update () {
// alert('update')
count.value = count.value + 1
}
return {
count,
update
}
}
}
</script>
1、作用: 定义多个数据的响应式
2、语法:const proxy = reactive(obj):
接收一个普通对象然后返回该普通对象的响应式代理器对象
响应式转换是“深层的”:会影响对象内部所有嵌套的属性
基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
<template>
<h2>name: {{state.name}}</h2>
<h2>age: {{state.age}}</h2>
<h2>wife: {{state.wife}}</h2>
<hr>
<button @click="update">更新</button>
</template>
<script>
import {
reactive,
} from 'vue'
export default {
setup () {
const state = reactive({
name: 'tom',
age: 25,
wife: {
name: 'marry',
age: 22
},
})
console.log(state, state.wife)
const update = () => {
state.name += '--'
state.age += 1
state.wife.name += '++'
state.wife.age += 2
}
return {
state,
update,
}
}
}
</script>
同:
● 是Vue3的 composition API中2个最重要的响应式API
异:
● ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
● 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
● ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
● reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
<template>
<h2>App</h2>
<p>m1: {{m1}}</p>
<p>m2: {{m2}}</p>
<p>m3: {{m3}}</p>
<button @click="update">更新</button>
</template>
<script lang="ts">
import {
reactive,
ref
} from 'vue'
export default {
setup () {
const m1 = ref('abc')
const m2 = reactive({x: 1, y: {z: 'abc'}})
// 使用ref处理对象 ==> 对象会被自动reactive为proxy对象
const m3 = ref({a1: 2, a2: {a3: 'abc'}})
console.log(m1, m2, m3)
console.log(m3.value.a2) // 也是一个proxy对象
function update() {
m1.value += '--'
m2.x += 1
m2.y.z += '++'
m3.value = {a1: 3, a2: {a3: 'abc---'}}
m3.value.a2.a3 += '==' // reactive对对象进行了深度数据劫持
console.log(m3.value.a2)
}
return {
m1,
m2,
m3,
update
}
}
}
</script>
vue2的响应式
1、核心:
○ 对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
○ 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
2、问题
○ 对象直接新添加的属性或删除已有属性, 界面不会自动更新
○ 直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}
Vue3的响应式
● 核心:
○ 通过Proxy(代理): 拦截对data任意属性的任意操作,
○ 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
<script>
const user = {
name: "John",
age: 12
};
/*
proxyUser是代理对象, user是被代理对象
后面所有的操作都是通过代理对象来操作被代理对象内部属性
*/
const proxyUser = new Proxy(user, {
get(target, prop) {
console.log('劫持get()', prop)
return Reflect.get(target, prop)
},
set(target, prop, val) {
console.log('劫持set()', prop, val)
return Reflect.set(target, prop, val); // (2)
},
});
// 读取属性值
console.log(proxyUser===user)
console.log(proxyUser.name, proxyUser.age)
// 设置属性值
proxyUser.name = 'bob'
proxyUser.age = 13
console.log(user)
</script>
1、computed函数:
可以只有getter
也可以有getter和setter
2、watch函数
1)监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
2)默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
3)通过配置deep为true, 来指定深度监视
3、watchEffect函数
1)不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
2)默认初始时就会执行第一次, 从而可以收集需要监视的数据
3)监视数据发生变化时回调
姓名案例:
<template>
<h2>App</h2>
fistName: <input v-model="user.firstName"/><br>
lastName: <input v-model="user.lastName"/><br>
fullName1: <input v-model="fullName1"/><br>
fullName2: <input v-model="fullName2"><br>
fullName3: <input v-model="fullName3"><br>
</template>
<script lang="ts">
import {
reactive,
ref,
computed,
watch,
watchEffect
} from 'vue'
export default {
setup () {
const user = reactive({
firstName: 'A',
lastName: 'B'
})
// 只有getter的计算属性
const fullName1 = computed(() => {
console.log('fullName1')
return user.firstName + '-' + user.lastName
})
// 有getter与setter的计算属性
const fullName2 = computed({
get () {
console.log('fullName2 get')
return user.firstName + '-' + user.lastName
},
set (value: string) {
console.log('fullName2 set')
const names = value.split('-')
user.firstName = names[0]
user.lastName = names[1]
}
})
const fullName3 = ref('')
/*
使用watch的2个特性:深度监视 初始化立即执行
*/
watch(user, () => {
fullName3.value = user.firstName + '-' + user.lastName
}, {
immediate: true, // 是否初始化立即执行一次, 默认是false
deep: true, // 是否是深度监视, 默认是false
})
/*
watch默认在数据发生改变时执行回调
*/
watch(fullName3, (value) => {
console.log('watch')
const names = value.split('-')
user.firstName = names[0]
user.lastName = names[1]
})
return {
user,
fullName1,
fullName2,
fullName3
}
}
}
</script>
1、 beforeCreate -> 使用 setup()
2、created -> 使用 setup()
3、 beforeMount -> onBeforeMount
4、mounted -> onMounted
5、beforeUpdate -> onBeforeUpdate
6、updated -> onUpdated
7、beforeDestroy -> onBeforeUnmount
8、destroyed -> onUnmounted
9、errorCaptured -> onErrorCaptured
● 使用Vue3的组合API封装的可复用的功能函数
● 自定义hook的作用类似于vue2中的mixin技术
● 自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂
鼠标点击位置坐标案例:
//hook.js
import { ref, onMounted, onUnmounted } from 'vue'
//收集用户鼠标点击的页面坐标
//自定义hook函数
export default function useMousePosition () {
// 初始化坐标数据
const x = ref(-1)
const y = ref(-1)
// 用于收集点击事件坐标的函数
const updatePosition = (e: MouseEvent) => {
x.value = e.pageX
y.value = e.pageY
}
// 挂载后绑定点击监听
onMounted(() => {
document.addEventListener('click', updatePosition)
})
// 卸载前解绑点击监听
onUnmounted(() => {
document.removeEventListener('click', updatePosition)
})
return {x, y}
}
<template>
<div>
<h2>x: {{x}}, y: {{y}}</h2>
</div>
</template>
<script>
import {
ref
} from "vue"
//在组件中引入并使用自定义hook
import useMousePosition from './hooks/useMousePosition'
export default {
setup() {
const {x, y} = useMousePosition()
return {
x,
y,
}
}
}
</script>
1、作用:把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref
2、应用: 当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用
问题: reactive 对象取出的所有属性值都是非响应式的
解决: 利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
<template>
<h2>App</h2>
<h3>foo: {{foo}}</h3>
<h3>bar: {{bar}}</h3>
<h3>foo2: {{foo2}}</h3>
<h3>bar2: {{bar2}}</h3>
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue'
export default {
setup () {
const state = reactive({
foo: 'a',
bar: 'b',
})
const stateAsRefs = toRefs(state)
setTimeout(() => {
state.foo += '++'
state.bar += '++'
}, 2000);
const {foo2, bar2} = useReatureX()
return {
// ...state,
...stateAsRefs,
foo2,
bar2
}
},
}
function useReatureX() {
const state = reactive({
foo2: 'a',
bar2: 'b',
})
setTimeout(() => {
state.foo2 += '++'
state.bar2 += '++'
}, 2000);
return toRefs(state)
}
</script>
利用ref获取组件中的标签元素
让输入框自动获取焦点案例:
<template>
<h2>App</h2>
<input type="text">---
<input type="text" ref="inputRef">
</template>
<script lang="ts">
import { onMounted, ref } from 'vue'
export default {
setup() {
//声明一个 ref 来存放该元素的引用
//必须和模板里的 ref 同名
const inputRef = ref<HTMLElement|null>(null)
onMounted(() => {
inputRef.value && inputRef.value.focus()
})
return {
inputRef
}
},
}
</script>