Vue3+Ts 入门
一、组合式API
1、计算属性API
-
单向响应
<template> 姓:<input v-model="username.surname"> <br> 名:<input v-model="username.name"> <br> 单向响应:<input v-model="username.nick"> <br> </template> <script setup lang="ts"> import { computed } from '@vue/reactivity'; import { reactive } from 'vue'; const username = reactive({ surname: "张", name: "三" }) username.nick = computed(()=>{ return username.surname + '*' + username.name }) </script>
-
双向响应
<template> 姓:<input v-model="username.surname"> <br> 名:<input v-model="username.name"> <br> 双向响应:<input v-model="username.nick"> <br> </template> <script setup lang="ts"> import { computed } from '@vue/reactivity'; import { reactive } from 'vue'; const username = reactive({ surname: "张", name: "三" }) username.nick = computed({ get(){ return username.surname + '*' + username.name }, set(value){ const arr = value.split('*') username.surname = arr[0] username.name = arr[1] } }) </script>
2、监听属性API
-
监听整个对象
# 监听整个对象,由于是浅拷贝,他们新旧指向的是同一个对象 <template> <h2>年龄:{{ userInfo.age }}</h2> <button @click="userInfo.age++"> 年龄+1</button> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; const userInfo = reactive({ name: '张三', age: 20 }) watch(userInfo, (newVal, oldVal) => { console.log('新:'); console.log(newVal); console.log('************************'); console.log('旧:'); console.log(oldVal); }) </script>
-
监听对象中单个属性
# 监听对象中单个属性可以检测到新旧值的变化 <template> <h2>年龄:{{ userInfo.age }}</h2> <button @click="userInfo.age++"> 年龄+1</button> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; const userInfo = reactive({ name: '张三', age: 20 }) watch(()=>userInfo.age, (newVal, oldVal) => { console.log('新:'); console.log(newVal); console.log('************************'); console.log('旧:'); console.log(oldVal); }) </script>
-
监听多个对象
# 监听对象中多个属性 <template> <h2>姓名:{{ userInfo.name }}</h2> <button @click="userInfo.name+='是个傻逼'">改名</button> <br> <h2>年龄:{{ userInfo.age }}</h2> <button @click="userInfo.age++"> 年龄+1 </button> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; const userInfo = reactive({ name: '张三', age: 20 }) watch([()=>userInfo.name,()=>userInfo.age], (newVal, oldVal) => { console.log('新:'); console.log(newVal); console.log('************************'); console.log('旧:'); console.log(oldVal); }) </script>
-
监听对象中的对象(深度监听)
<template> <h2>年龄:{{ userInfo.hobby.like }}</h2> <button @click="userInfo.hobby.like += '很快乐'">点我</button> <br> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; const userInfo = reactive({ name: '张三', age: 20, hobby: { like: '打球' } }) watch(() => userInfo.hobby.like, (newVal, oldVal) => { console.log('新:'); console.log(newVal); console.log('************************'); console.log('旧:'); console.log(oldVal); }, { deep: true }) </script>
3、高级监听API (watchEffect)
-
基本使用(默认先执行一次)
# watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调 <template> <h2>姓名:{{ userInfo.name }}</h2> <button @click="userInfo.name += '111'">修改</button> <br> </template> <script setup lang="ts"> import { reactive, watch, watchEffect } from 'vue'; const userInfo = reactive({ name: '张三' }) watchEffect(()=>{ userInfo.name console.log('姓名变了'); }) </script>
-
监听预前处理oninvalidate参数
# oninvalidate 监听值修改前的回调 <template> <h2>姓名:{{ userInfo.name }}</h2> <button @click="userInfo.name += '111'">修改</button> <br> </template> <script setup lang="ts"> import { reactive, watchEffect } from 'vue'; const userInfo = reactive({ name: '张三' }) watchEffect((oninvalidate)=>{ oninvalidate(()=>{ console.log("before"); }) userInfo.name console.log('姓名变了'); }) </script>
-
停止监听
# 停止后不再监听值的变化 <template> <h2>姓名:{{ userInfo.name }}</h2> <button @click="userInfo.name += '111'">修改</button> <button @click="stop">停止监听</button> <br> </template> <script setup lang="ts"> import { reactive, watchEffect } from 'vue'; const userInfo = reactive({ name: '张三' }) const stop = watchEffect((oninvalidate)=>{ oninvalidate(()=>{ console.log("before"); }) userInfo.name console.log('姓名变了'); }) </script>
4、响应式对象解构API
-
toRef()
<template> <h2>姓名:{{ AA }}</h2> <button @click="userInfo.name += '加1'">修改</button> </template> <script setup lang="ts"> import { reactive, toRef } from 'vue'; const userInfo = reactive({ name: '张三' }) const AA = toRef(userInfo,'name') </script>
-
toRefs()
<template> <h2>姓名:{{ name }}</h2> <button @click="name += '加1'">修改姓名</button> <br> <h2>年龄:{{ age }}</h2> <button @click="age++">修改年龄</button> </template> <script setup lang="ts"> import { reactive, toRef, toRefs } from 'vue'; const userInfo = reactive({ name: '张三', age: 20 }) const {name,age} = toRefs(userInfo) </script>
5、生命周期API
<script setup lang="ts">
import { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated } from 'vue';
console.log('setup执行');
onBeforeMount(() => {
console.log('*** 挂载前 ***');
})
onMounted(() => {
console.log('*** 挂载后 ***');
})
onBeforeUpdate(() => {
console.log('*** 更新前 ***');
})
onUpdated(() => {
console.log('*** 更新后 ***');
})
onBeforeUnmount(() => {
console.log('*** 卸载前 ***');
})
onUnmounted(() => {
console.log('*** 卸载后 ***');
})
</script>
6、ref 获取 dom
<template>
<div ref="box">我是box</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
let box = ref(null) // 本质是reactive({value:null})
// dom挂载后的生命周期
onMounted(()=>{
console.log(box.value);
})
// 这里会输出null,setup是最先执行,此时dom还没生成
console.log(box.value);
</script>
7、Hooks
-
官方hooks
-
useAttrs()
// 父组件 <template> <SonVue tit="父组件传值1" title="父组件传值2"/> </template> // 子组价 <script setup lang="ts"> import { useAttrs } from 'vue'; let attr = useAttrs() console.log(attr); </script>
-
-
自定义hooks
-
鼠标跟踪器示例
# convertImg.ts import { ref, onMounted, onUnmounted } from 'vue' // 按照惯例,组合式函数名以“use”开头 export function useMouse() { // 被组合式函数封装和管理的状态 const x = ref(0) const y = ref(0) // 组合式函数可以随时更改其状态。 function update(event) { x.value = event.pageX y.value = event.pageY } // 一个组合式函数也可以挂靠在所属组件的生命周期上 // 来启动和卸载副作用 onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) // 通过返回值暴露所管理的状态 return { x, y } }
# 在组件中的使用 <template> <div>横轴:{{ x }}</div> <div>纵轴:{{ y }}</div> </template> <script setup lang="ts"> import { useMouse } from '../../hooks/convertImg' const { x, y } = useMouse() </script>
-
第三方hooks
-
安装依赖
npm install @vueuse/core
-
简单使用
<template> <div ref="el" :style="style" style="position: fixed;background-color: pink;"> 请拖动我 {{ x }}, {{ y }} </div> </template> <script setup lang="ts"> import { ref } from 'vue'; import { useDraggable } from '@vueuse/core' const el = ref<HTMLElement | null>(null) const { x, y, style } = useDraggable(el, { initialValue: { x: 40, y: 40 } }) </script>
-
-
8、插槽
8.1、匿名插槽
-
A组件
<template> <B> 这是XXX数据 这是YYY数据 </B> </template> <script setup lang="ts"> import B from '@/views/A/B.vue' </script>
-
B组件:
<template> <div> <header> <div>头部</div> <slot></slot> </header> <footer> <div>底部</div> <slot></slot> </footer> </div> </template>
8.2、具名插槽
-
A组件
<template> <B> <!-- 标准写法 --> <template v-slot:xxx>这是xxx数据</template> <template v-slot:yyy>这是yyy数据</template> <!-- 简写 --> <template #xxx>这是yyy数据</template> <template #yyy>这是yyy数据</template> </B> </template> <script setup lang="ts"> import B from '@/views/A/B.vue' </script>
-
B组件
<template> <div> <h1> 子:A组件 </h1> <header> <div>头部</div> <slot name="xxx"></slot> </header> <footer> <div>底部</div> <slot name="yyy"></slot> </footer> </div> </template>
8.3、作用域插槽
-
A组件
<template> <B> <!-- 标准写法 --> <template v-slot="{ data }">{{ data.name }}==>{{ data.age }}</template> <!-- 简写 --> <template #default="{ data }">{{ data.name }}==>{{ data.age }}</template> </B> </template> <script setup lang="ts"> import B from '@/views/A/B.vue' </script>
-
B组件
<template> <div> <section> <div v-for="item in list" :key="item.id"> <slot :data="item"></slot> </div> </section> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; const list = ref([ {id:1,name:'张三',age:15}, {id:2,name:'李四',age:18}, {id:3,name:'王二麻子',age:20}, {id:4,name:'刘五',age:28}, {id:5,name:'小六子',age:22}, ]) </script>
8.4、动态插槽 -【通过数据进行切换】
-
A组件
<template> <B> <template #[yyy]>这是yyy数据</template> </B> </template> <script setup lang="ts"> import B from '@/views/A/B.vue' import { ref } from 'vue'; let yyy = ref('yyy') </script>
-
B组件
<header> <div>头部</div> <slot name="xxx"></slot> <div>底部</div> <slot name="yyy"></slot> </header>
9、Teleport(传送)
<Teleport>
是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
<Teleport to=".main">这是传送1</Teleport>
<Teleport to="#home">这是传送2</Teleport>
<Teleport to="body">这是传送3</Teleport>
注意:必须dom存在才能传送
10、异步组件(提升性能)
Vue 提供了 defineAsyncComponent
方法来实现异步组件
<template>
<A></A>
<B></B>
<div ref="target">
<AsyncComp v-if="targetIsShow"></AsyncComp>
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
import { useIntersectionObserver } from '@vueuse/core'; // npm插件
import A from './A.vue';
import B from './B.vue';
const AsyncComp = defineAsyncComponent(() =>
import('./C.vue')
)
const target = ref<any>(null);
const targetIsShow = ref(false);
useIntersectionObserver(
target,
([{ isIntersecting }]) => {
if (isIntersecting) {
targetIsShow.value = isIntersecting
}
}
)
</script>
defineAsyncComponent 打包时做分包处理,异步组件是一个单独的js文件。
11、Mixin(混入)
含义:分发vue组件中可复用的功能。
注意:vue3中使用Hooks
// mixin.ts
import { ref } from "vue";
export default function() {
let num = ref(1);
let bel = ref(true)
const btn = () => {
num.value += 1
bel.value = false
setTimeout(() => {
bel.value = true
}, 3000);
}
return {
num,
bel,
btn
}
}
A组件使用
<template>
<div style="width:300px;height: 200px;background-color: #f60;">
<h1>A组件</h1>
{{ num }}
<button @click="btn">{{ bel }}</button>
</div>
</template>
<script setup lang="ts">
import mixin from '../mixin/mixin';
let { num, bel, btn } = mixin();
</script>
B组件使用
<template>
<div style="width:300px;height: 600px;background-color: blue;">
B组件
{{ num }}
<button @click="btn">{{ bel }}</button>
</div>
</template>
<script setup lang="ts">
import mixin from '../mixin/mixin';
let { num, bel, btn } = mixin();
</script>
12、Vue3生命周期
在组合API中,我们需要将生命周期钩子导入到项目中,才能使用,这有助于保持项目的轻量性。
import { onMounted } from 'vue'
除了 beforecate 和 created (它们被 setup 方法本身所取代),我们可以在 setup 方法中访问的API生命周期钩子有9个选项
onBeforeMount – 在挂载开始之前被调用:相关的 render 函数首次被调用。
onMounted – 组件挂载时调用
onBeforeUpdate – 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
onUpdated – 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
onBeforeUnmount – 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
onUnmounted – 卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
onActivated – 被 keep-alive 缓存的组件激活时调用。
onDeactivated – 被 keep-alive 缓存的组件停用时调用。
onErrorCaptured – 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
二、组件间通信
1、props父传子
-
父组件
<template> <SonVue msg="父组件的值" /> </template>
-
子组件
<script setup lang="ts"> const props = defineProps( { msg: String } ) console.log(props); </script>
2、emit子传父
-
父组件
<template> <SonVue @getuser="getuser" /> </template> <script setup lang="ts"> const getuser = (val:string) => { console.log(val) } </script>
-
子组件
<template> <button @click="buttonClick">传输</button> </template> <script setup lang="ts"> const emit = defineEmits(['getuser']) const buttonClick = ()=>{ emit('getuser','我是子组件传给父组件的值') } </script>
3、插槽通讯
-
匿名插槽
-
子组件
<template> <slot></slot> </template>
-
父组件
<template> <SonVue> 插槽传递 </SonVue> </template>
-
-
具名插槽
-
父组件
<template> <SonVue> <template #title>这是具名插槽</template> </SonVue> </template>
-
子组件
<template> <slot name="title"></slot> </template>
-
-
作用域插槽
-
含义
# 数据在子组件的自身,但根据数据生成的结构需要父组件决定
-
父组件
<template> <!-- 父组件将信息传递给子组件 --> <SonVue :person="person"> <!-- 子组件接收父组件的插槽中传的值 --> <template #tab="scope"> <tr v-for="(item, index) in scope.person" :key="index"> <th>{{ item.name }}</th> <th>{{ item.age }}</th> <th> <button>编辑</button> </th> </tr> </template> </SonVue> </template> <script setup lang="ts"> const person = [ { name: '小明', age: 20 }, { name: '小红', age: 18 } ] </script>
-
子组件
<template> <table border="1"> <tr> <th>姓名</th> <th>年龄</th> <th>操作</th> </tr> <slot name="tab" :person="props.person"></slot> </table> </template> <script setup lang="ts"> const props = defineProps<{ person: { name: string, age: number } }>() </script>
-
4、Provide / Inject
官网称为依赖注入,Provide 为提供,Inject为注入
-
Provide (提供)
-
父组件(祖先组件)
<template> <h1>我是父组件</h1> <SonVue></SonVue> </template> <script setup lang="ts"> import { provide, ref } from "vue"; let flag = ref<number>(10); provide("flag", flag); </script>
-
-
Inject (注入)
-
子组件(后代组件)
<template> <button>子组件</button> <div> {{ flag }} </div> <button @click="flag++">+1</button> </template> <script setup lang="ts"> import { inject, ref } from 'vue'; const flag = inject('flag',ref(1)) </script>
-
注意:Provide / Inject是具有响应式的,而且是单向数据流。
三、路由(vue插件)
1、安装vue-router
npm install vue-router
2、创建router文件
// src/router/index.js
import { createRouter, createWebHistory } from "vue-router";
const routes = [
{
path: '/',
redirect: '/index'
},
{
path: '/index',
name: 'index',
component: () => import('../views/index/index.vue')
},
{
path: '/account',
name: 'account',
component: () => import('../views/account/index.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
});
export default router
3、全局引入router
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index.js'
createApp(App).use(router).mount('#app')
4、使用
-
app.vue设置路由出口
// App.vue <template> <router-view></router-view> </template>
-
index.vue 跳转到 account.vue
<template> <div> <h1>首页</h1> <!-- 路由跳转方式 1 --> <router-link to="/account">去个人中心</router-link> <!-- 路由跳转方式 2 --> <button @click="gotoAccount">去个人中心</button> </div> </template> <script setup> import { useRoute, useRouter } from 'vue-router'; // useRoute 类似于 vue2 的 this.$route useRoute == this.$route // useRouter 类似于 vue2 的 this.$router useRouter == this.$router const router = useRouter(); const gotoAccount = () => { console.log(router); router.push('/account') } </script>
四、Typescript的支持
1、模板中的TS - 类型推断
<template>
{{ (x as number).toFixed(2) }} // 正确
{{ (<number>x).toFixed(2) }} // 正确
{{ x.toFixed(2) }} // IDE提示:类型“string | number”上不存在属性“toFixed”
</template>
<script setup lang="ts">
let x:string | number = 1
</script>
2、全局接口的抽取
1.src下定义types文件夹命名xx.d.ts
2.建立全局接口global.d.ts
interface globalInterface{
name:string
age:string
}
3.组件中直接使用
<script setup lang="ts">
const props = defineProps<{person:globalInterface[]}>()
</script>
4.如果不是在src下则需要在tsconfig.json中配置全局目录
{
{
...
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], //配置全局目录
"references": [{ "path": "./tsconfig.node.json" }]
}
3、类型增强
1.使用环境:全局定义的数据,函数在vue组件中直接访问报错
2.index.html中定义数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script>
const global=1
</script>
</body>
</html>
3.定义类型增强
// common.d.ts
declare const global:string;
4.组件中直接读取
<script setup lang="ts">
console.log(global)
</script>
4、父传子props通讯 - TS
-
父组件
<template> <SonVue msg="父组件传过来的值"></SonVue> </template>
-
子组件
<template> {{ props.msg }} </template> <script setup lang="ts"> interface msgInterface{ msg:string } // withDefaults 帮助程序为默认值提供类型检查,并确保返回的 props 类型删除了已声明默认值的属性的可选标志。 const props = withDefaults(defineProps<msgInterface>(),{ msg:'dasdas' }) console.log(props.msg); </script>
5、子传父emit通讯 - TS
-
父组件
<template> <SonVue @getuser="getuser"></SonVue> </template> <script setup lang="ts"> const getuser = (a:number)=>{ console.log('我是父组件打印的值'); } </script>
-
子组件
<template> <button @click="buttonClick">传输</button> </template> <script setup lang="ts"> const emit = defineEmits<{(e:'getuser',id:number):void}>() // (e: 事件名, 键名:类型): void const buttonClick = ()=>{ emit('getuser',1) } </script>
6、依赖注入类型推断
-
父组件(祖先组件)
<template> <button>我是父组件</button> <SonVue></SonVue> </template> <script setup lang="ts"> import { provide, ref } from 'vue'; let flag = ref<number>(99) provide('flag',flag) </script>
-
子组件(后代组件)
<template> <h2>我是子组件</h2> <div>{{flag}}</div> <button @click="(flag as number)++">+1</button> </template> <script setup lang="ts"> import { Ref, inject, ref } from 'vue'; const flag:Ref<number | string> = inject('flag',ref(1)) </script>
五、指令的重构
1、v-model指令
-
父组件
<template> {{ num }} <HelloWorld v-model="num" /> </template> <script setup lang="ts"> import { ref } from 'vue'; import HelloWorld from './components/HelloWorld.vue'; const num = ref(1) </script>
-
子组件
<template> <input type="text" v-model="value"> </template> <script lang="ts" setup> import { computed } from "vue"; const props = defineProps<{modelValue:number}>(); const emit = defineEmits<{(e:'update:modelValue',id:number):void}>(); const value = computed({ get(){ return + props.modelValue }, set(value){ emit('update:modelValue',+value) } }) </script>
-
v-model原理
<template> {{ num }} <HelloWorld :modelValue="num" @update:modelValue="num = $event"/> </template> <script setup lang="ts"> import { ref } from 'vue'; import HelloWorld from './components/HelloWorld.vue'; const num = ref(1) </script>
2、自定义指令
2.1、自定义指令简单使用
-
全局自定义指令
// main.ts import { createApp } from 'vue' import './style.css' import App from './App.vue' const app = createApp(App) app.directive('focus',{ mounted(el){ el.focus() } }) app.mount('#app')
-
使用自定义指令
<template> <input type="text" v-model="value" v-focus /> </template> <script lang="ts" setup> import { ref } from 'vue' let value = ref('全局自定义指令') </script>
-
局部自定义指令
<template> <input type="text" v-focus /> </template> <script setup lang="ts"> const vFocus = { mounted: (el: any)=>{ el.focus() } } </script>
2.2、自定义指令详解
-
自定义指令的生命周期
<template> <!-- 自定义指令,参数,修饰符 --> <div v-move:a.x="{ background: 'red' }"></div> </template> <script setup lang="ts"> import type { Directive, DirectiveBinding } from 'vue'; type Dir = { background: string }; const vMove: Directive = { // 元素初始化的时候 created() { }, // 指令绑定到元素后调用 只调用一次 beforeMount() { }, // 元素插入父级dom调用 mounted(el: HTMLElement, dir: DirectiveBinding<Dir>) { console.log(dir.value.background); el.style.background = dir.value.background }, // 元素被更新之前调用 beforeUpdate() { }, // 元素被更新之后调用 改用updated updated() { }, // 在元素被移除前调用 beforeUnmount() { }, // 指令被移除后调用 只调用一次 unmounted() { } } </script>
-
生命周期的简写
<template> <!-- 自定义指令,参数,修饰符 --> <div v-move:a.x="{ background: 'red' }">自定义指令生命周期简写</div> </template> <script setup lang="ts"> import type { Directive, DirectiveBinding } from 'vue'; type Dir = { background: string }; const vMove: Directive = (el: HTMLElement, dir: DirectiveBinding<Dir>)=>{ el.style.background = dir.value.background } </script>
-
自定义拖拽指令
<template> <!-- 自定义指令,参数,修饰符 --> <div v-move style=" background-color: red; width: 200px; height: 200px; position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); "> <div style="background-color: black; width: 200px; color: white"> 自定义指令 </div> </div> </template> <script setup lang="ts"> import type { Directive } from 'vue'; const vMove: Directive = (el: HTMLElement) => { const move = (e: MouseEvent) => { console.log(e); el.style.left = e.clientX + 'px' el.style.top = e.clientY + 'px' } // 鼠标按下 el.addEventListener('mousedown', () => { // 鼠标按下拖拽 document.addEventListener('mousemove', move); // 鼠标松开 document.addEventListener('mouseup', () => { // 清除事件 document.addEventListener('mousemove', move) }); }) } </script>
3、响应式原理
3.1、了解Proxy代理
-
Proxy代理的get方法
<script> let obj = { name: "小明", age: 26, }; let agent = new Proxy(obj, { /* target表示obj这个对象 property表示读取的属性的key */ get(target, property) { console.log("执行了get方法"); return target[property]; }, }); console.log(agent.age); </script>
-
Proxy代理的set方法
let obj = { name: "小明", age: 26, }; let agent = new Proxy(obj, { /* target表示obj这个对象 property表示读取的属性的key newValue表示设置的值 */ set(target, property, newValue) { console.log('执行了set方法'); target[property] = newValue return true }, }); agent.age=30 console.log(agent.age); 注意:定义 Proxy 代理对象的 set 的时候,要返回 return true ,特别是在严格模式下,否则, 会报错 'set' on proxy: trap returned falsish for property 'age'
3.2、了解Object.defineProperty
-
Object.defineProperty(对象.定义属性,用来为一个对象添加新属性)
<script> let person = { name: "小明", sex: '男', }; // 为 person对象 传输了一个新属性 “age”,并且设定它的值为 18 Object.defineProperty(person,'age',{ value : 18 }) console.log(person) </script>
-
Object.defineProperty() 的get()方法
<script> let person = { name: '张三', sex: '男', } function Observer(obj) { const keys = Object.keys(obj) keys.forEach((key) => { Object.defineProperty(this,key,{ get() { return obj[key] } }) }) } const obs = new Observer(person) console.log(obs.sex); </script>
-
Object.defineProperty() 的set()方法
<script> let person = { name: '张三', sex: '男', } function Observer(obj) { const keys = Object.keys(obj) keys.forEach((key) => { Object.defineProperty(this,key,{ set(val) { console.log('set方法调用了') obj[key] = val } }) }) } const obs = new Observer(person) obs.name=15 </script>