<template>
<h2>name: {{state.name}}</h2>
<h2>age: {{state.age}}</h2>
<h2>wife: {{state.wife}}</h2>
<hr>
<button @click="update">更新</button>
</template>
<script>
/*
reactive:
作用: 定义多个数据的响应式
const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
响应式转换是“深层的”:会影响对象内部所有嵌套的属性
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
*/
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>
核心:
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
问题:
1.对象直接新添加的属性或删除已有属性, 界面不会自动更新
2.直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}
核心:
<script>
// 目标对象
const user = {
name:'小明',
age:20,
wife:{
name:'大明',
age:10
}
};
// 把目标对象变成代理对象
// 参数1 user ---> target目标对象
// 参数2 handler ---> 处理器对象,用来监视数据及数据的操作
/*
核心:
通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等...
通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
*/
const proxyUser = new Proxy(user,{
// 获取(读取)拦截的数据
get(target,prop){
console.log('我是get方法')
return Reflect.get(target,prop);
},
// 设置拦截的数据(修改目标对象的数据) val 当前参数的值
set(target,prop,val){
console.log('我是set方法')
return Reflect.set(target,prop,val)
},
// 删除拦截的数据
deleteProperty(target,prop){
return Reflect.deleteProperty(target,prop)
}
});
// 读取属性值
console.log(proxyUser===user)
// 修改目标对象
proxyUser.name = '张三'
console.log(user)
// 添加属性
proxyUser.sex = '男'
console.log(user)
// 删除目标对象
delete proxyUser.name
</script>
<template>
<div>
<h3>{{n}}</h3>
<h3>{{m}}</h3>
<h3>msg: {{msg}}</h3>
<h3>msg2: {{$attrs.msg2}}</h3>
<slot name="xxx"></slot>
<button @click="update">更新</button>
</div>
</template>
<script lang="ts">
import {ref,defineComponent} from 'vue'
export default defineComponent({
name: 'child',
props: ['msg'],
emits: ['fn'], // 可选的, 声明了更利于程序员阅读, 且可以对分发的事件数据进行校验
data () {
console.log('data', this)
return {
// n: 1
}
},
beforeCreate () {
console.log('beforeCreate', this)
},
methods: {
// update () {
// this.n++
// this.m++
// }
},
// setup (props, context) {
setup (props, {attrs, emit, slots}) {
console.log('setup', this)
console.log(props.msg, attrs.msg2, slots, emit)
const m = ref(2)
const n = ref(3)
function update () {
// console.log('--', this)
// this.n += 2
// this.m += 2
m.value += 2
n.value += 2
// 分发自定义事件
emit('fn', '++')
}
return {
m,
n,
update,
}
},
})
</script>
<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>
import { defineComponent,computed, reactive } from 'vue'
setup(props,context) {
const firstName = reactive({
name: '东方',
age:'不败'
})
// 计算属性
// 第一种方式
// 有getter与setter的计算属性
const fullName2 = computed({
get(){
return firstName.name + '_' +firstName.age
},
set(val:string) {
// console.log(val)
const obj = val.split('_')
firstName.name = obj[0]
firstName.age = obj[1]
// console.log(obj)
}
})
// 第二种方式
// 只有getter的计算属性
// const fullName2 = computed(()=>{
// return '哈哈哈哈哈'
// })
return {
firstName,
fullName2
}
}
import { defineComponent,ref,watch, watchEffect } from 'vue'
export default defineComponent({
setup(props,context) {
const firstName = reactive({
name: '东方',
age:'不败'
})
// 监听 监听数据变化 immediate: 进入页面就执行一次 deep 深度监听
const fullName3 = ref('')
// 第一种方式
// firstName 代表监视的数据
// watch(firstName,({name,age})=>{
// fullName3.value = name + age
// },{immediate:true,deep:true})
// 第二种方式
// 特点:不需要配置immediate,本身默认就会进行监听,(默认执行一次,只要页面一加载就会立即执行)
watchEffect(()=>{
fullName3.value = firstName.name + firstName.age
})
// 第三种 监听多个数据
/*
watch多个数据:
使用数组来指定
如果是ref对象, 直接指定 例如:fullName3
如果是reactive对象中的属性, 必须通过函数来指定 例如:user
*/
watch([() => firstName.name, () => firstName.age, fullName3], (values) => {
console.log('监视多个数据', values)
})
return {
fullName3,
firstName
}
},
})
与 2.x 版本生命周期相对应的组合式 API
我们导入它们并在我们的代码中访问它们:
导入在setup中定义的生命周期函数
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted,onErrorCaptured } from 'vue'
export default {
// setup相当于vue2中的beforeCreate和created,并且在两者之前执行
setup() {
1.挂载前/后
onBeforeMount(() => {
// ...
})
onMounted(() => {
// ...
})
2.更新前/后
onBeforeUpdate(() => {
// ...
})
DOM更新后,不会保证所有的子组件也都一起被重绘
onUpdated(() => {
// ...
})
3.销毁前(卸载组件实例之前,此时实例仍然是完全正常的)
onBeforeUnmount(() => {
// ...
})
销毁后(卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。)
onUnmounted(() => {
// ...
})
当捕获一个来自子孙组件的异常时激活钩子函数
onErrorCaptured(() => {
// ...
})
}
}
<template>
<h2>点击页面获取坐标</h2>
<h2>x:{{x}},y{{y}}</h2>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import hooksComponent from './hook/hooks'
export default defineComponent({
name: 'App',
setup() {
const {x,y} = hooksComponent()
return {
x,
y
}
},
})
</script>
import {ref,onMounted,onBeforeUnmount} from 'vue'
export default function(){
const x = ref(-1)
const y = ref(-1)
// 事件处理函数
const clickHandler = (e: MouseEvent)=>{
console.log(e)
x.value = e.pageX
y.value = e.pageY
}
// 在页面加载完成后
onMounted(()=>{
// 事件监听
window.addEventListener('click',clickHandler)
})
// 在页面销毁前的API
onBeforeUnmount(()=>{
window.removeEventListener('click',clickHandler)
})
return {
x,
y,
clickHandler
}
}
toRefs()将响应式的对象变为普通对象 再解构,在模板中就可以直接使用属性,不用state.name
<template>
<div>
<h2>toRef的使用</h2>
<p> 姓名:{{state.name }} </p>
<p>年龄:{{ state.age }} </p>
<button @click="changeHander">改变内容</button>
</div>
</template>
<script>
import { reactive } from "vue"
export default {
name: "App",
setup() {
let state=reactive({
name:'李四',
age:40
})
function changeHander(){
state.name="李四222"
state.age=80
}
return {state,changeHander}
}
};
</script>
虽然上面可以使用数据的响应式。
但是有时属性值很多,
可能有达到几十个值。
这样在视图上一个个属性点(.) 会很麻烦
<template>
<div>
<h2>toRef的使用</h2>
<p> 姓名:{{name }} </p>
<p>年龄:{{ age }} </p>
<button @click="changeHander">改变内容</button>
</div>
</template>
<script>
import { reactive, toRefs } from "vue"
export default {
name: "App",
setup() {
let state=reactive({
name:'李四',
age:40
})
// toRefs 可以把一个响应式对象转换为普通的对象。
// 该普通对象的每一个值都是ref
// age: ObjectRefImpl {_object: Proxy, _key: "age", __v_isRef: true}
// name: ObjectRefImpl {_object: Proxy, _key: "name", __v_isRef: true}
let state2 = toRefs(state);
console.log( 'state2==>ref',state2 )
function changeHander(){
// 由于变成了ref,所以我们需要使用value
state2.name.value="李四222"
state2.age.value=80
}
// ...state虽然我们通过解构的方式。不要在视图上进行点了。但是数据却没有响应
return {...state2,changeHander}
}
};
</script>
1.第一种方式:
<template>
<div>
<h2>toRef的使用</h2>
<p> 姓名:{{name }} </p>
<p>年龄:{{ age }} </p>
<button @click="changeHander">改变内容</button>
</div>
</template>
<script>
import { reactive, toRefs } from "vue"
export default {
name: "App",
setup() {
let state=reactive({
name:'李四',
age:40
})
// 这里进行了一次解构
let {name,age }=toRefs(state);
function changeHander(){
name.value="李四222"
age.value=80
}
return {name,age,changeHander}
}
};
</script>
2.第二种方式:
<p>姓名:{{name}}</p>
<p>
年龄:{{age}}</p>
setup() {
const data = reactive({ // 建立一个响应式对象
name: "小四",
age: 18,
...
})
}
return {
//必须返回 模板中才能使用
...toRefs(data), //look
...
};
利用ref函数获取组件中的标签元素
<template>
<h2>hhh</h2>
<h2 ref="haha">哈哈哈哈哈哈</h2>
</template>
<script lang="ts">
import { defineComponent,onMounted,ref } from 'vue'
export default defineComponent({
// vue2 获取元素 this.$refs.haha
name:'App',
setup() {
// ref获取元素: 利用ref函数获取组件中的标签元素
/**
* 1.首先我们创建了一个 haha 的监听对象,
* 2.然后将这个监听对象暴露出去,
* 3.从而实现 setup 函数中和 节点haha 的绑定。
*/
// vue3 获取元素
const haha = ref<HTMLElement | null>(null)
onMounted(()=>{
console.log(haha)
})
return {
haha
}
}
})
</script>
注意点: 但由于 setup 函数的执行时间要先于 html 标签的渲染,所以我们不能直接在 setup 函数中初始化 box 标签。可以在 onMounted 中