前端vue ts
TypeScript
安装ts
第一个ts代码
vscode自动编译
类型注解
接口interface
类class
基本数据类型
数组 元组
enum 枚举
any
void
函数
vue3 快速上手
Composition API
练习
cloudflare crud
vue-router
Axios
project
Vue3+Element-Plus 实现点击左侧菜单时显示不同内容组件展示在Main区域功能
h5 css
文档声明和字符编码
div和span
列表
图片标签
表格
表单标签
本文档使用 MrDoc 发布
-
+
首页
Composition API
# Composition API(常用部分) 文档: * https://composition-api.vuejs.org/zh/api.html ## 1) setup * 新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次 * 函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用 ## 2)ref * 作用: 定义一个数据的响应式 * 语法: const xxx = ref(initValue): * 创建一个包含响应式数据的引用(reference)对象 * js中操作数据: xxx.value * 模板中操作数据: 不需要.value * 一般用来定义一个基本类型的响应式数据 ``` <template> <h2>{{count}}</h2> <hr> <button @click="update">更新</button> </template> <script> import { ref } from 'vue' export default { /* 在Vue3中依然可以使用data和methods配置, 但建议使用其新语法实现 */ // data () { // return { // count: 0 // } // }, // methods: { // update () { // this.count++ // } // } /* 使用vue3的composition API */ setup () { // 定义响应式数据 ref对象 const count = ref(1) console.log(count) // 更新响应式数据的函数 function update () { // alert('update') count.value = count.value + 1 } return { count, update } } } </script> ``` ## 3)reactive * 作用: 定义多个数据的响应式 * const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象 * 响应式转换是“深层的”:会影响对象内部所有嵌套的属性 * 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的 --- 箭头函数 箭头函数是 ES6 引入的一种新的函数语法,它可以更简洁地定义一个函数。箭头函数不需要使用 function 关键字来声明函数,而是使用 => 来表示函数的定义。例如: ``` function square(x) { return x * x } const square = x => x * x ``` 箭头函数有以下特点: * 可以省略函数体中的大括号:当函数体只有一条语句时,可以省略大括号和 return 关键字。例如:x => x * x * 可以省略参数括号:当函数只有一个参数时,可以省略参数列表的圆括号。例如:x => x * x * 没有自己的执行上下文:箭头函数没有自己的 this、arguments、super 或 new.target,它们继承自外层作用域。 * 在实际应用中,箭头函数常用于简化代码或传递回调函数。例如,可以将箭头函数作为参数传递给数组的 map 方法,对数组中的每个元素进行处理:[1, 2, 3].map(x => x * 2)。 --- ``` <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> ``` ## 4) 比较Vue2与Vue3的响应式(重要) ### vue2的响应式 * 核心: * 对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截) * 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持 ``` Object.defineProperty(data, 'count', { get () {}, set () {} }) ``` * 问题 * 对象直接新添加的属性或删除已有属性, 界面不会自动更新 * 直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {} ### Vue3的响应式 * 核心: * 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等... * 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作 * 文档: * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect ``` new Proxy(data, { // 拦截读取属性值 get (target, prop) { return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 拦截删除属性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) } }) proxy.name = 'tom' ``` ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Proxy 与 Reflect</title> </head> <body> <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) }, deleteProperty (target, prop) { console.log('劫持delete属性', prop) return Reflect.deleteProperty(target, prop) } }); // 读取属性值 console.log(proxyUser===user) console.log(proxyUser.name, proxyUser.age) // 设置属性值 proxyUser.name = 'bob' proxyUser.age = 13 console.log(user) // 添加属性 proxyUser.sex = '男' console.log(user) // 删除属性 delete proxyUser.sex console.log(user) </script> </body> </html> ``` ## 5) setup细节 * setup执行的时机 * 在beforeCreate之前执行(一次), 此时组件对象还没有创建 * this是undefined, 不能通过this来访问data/computed/methods / props * 其实所有的composition API相关回调函数中也都不可以 * setup的返回值 * 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法 * 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性 * 返回对象中的方法会与methods中的方法合并成功组件对象的方法 * 如果有重名, setup优先 * 注意: * 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods * setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据 * setup的参数 * setup(props, context) / setup(props, {attrs, slots, emit}) * props: 包含props配置声明且传入了的所有属性的对象 * attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs * slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots * emit: 用来分发自定义事件的函数, 相当于 this.$emit ``` <template> <h2>App</h2> <p>msg: {{msg}}</p> <button @click="fn('--')">更新</button> <child :msg="msg" msg2="cba" @fn="fn"/> </template> <script lang="ts"> import { reactive, ref, } from 'vue' import child from './child.vue' export default { components: { child }, setup () { const msg = ref('abc') function fn (content: string) { msg.value += content } return { msg, fn } } } </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> ``` ## reactive与ref-细节 * 是Vue3的 composition API中2个最重要的响应式API * ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式) * 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象 * ref内部: 通过给value属性添加getter/setter来实现对数据的劫持 * reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据 * ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value) ``` <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> ``` ## 7) 计算属性与监视 * computed函数: * 与computed配置功能一致 * 只有getter * 有getter和setter * watch函数 * 与watch配置功能一致 * 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调 * 默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次 * 通过配置deep为true, 来指定深度监视 * watchEffect函数 * 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据 * 默认初始时就会执行第一次, 从而可以收集需要监视的数据 * 监视数据发生变化时回调 ``` <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"> /* 计算属性与监视 1. computed函数: 与computed配置功能一致 只有getter 有getter和setter 2. watch函数 与watch配置功能一致 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调 默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次 通过配置deep为true, 来指定深度监视 3. watchEffect函数 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据 默认初始时就会执行第一次, 从而可以收集需要监视的数据 监视数据发生变化时回调 */ 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('') /* watchEffect: 监视所有回调中使用的数据 */ /* watchEffect(() => { console.log('watchEffect') fullName3.value = user.firstName + '-' + user.lastName }) */ /* 使用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] }) /* watch多个数据: 使用数组来指定 如果是ref对象, 直接指定 如果是reactive对象中的属性, 必须通过函数来指定 */ watch([() => user.firstName, () => user.lastName, fullName3], (values) => { console.log('监视多个数据', values) }) return { user, fullName1, fullName2, fullName3 } } } </script> ```
admin
2023年3月19日 00:47
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码