组合式API
setup()
11
响应式API
- ref
ref
用于创建响应式数据(通常用来定义 基本类型数据)
在JavaScript代码中,需要使用 .value
来操作数据
let count = ref(1)console.log(count.value) // 1count.value++console.log(count.value) // 2let count = ref(1) console.log(count.value) // 1 count.value++ console.log(count.value) // 2let count = ref(1) console.log(count.value) // 1 count.value++ console.log(count.value) // 2
在Template模板中不需要
<script setup>import { ref } from 'vue'const count = ref(1)</script><template><button>{{ count }}</button></template><script setup> import { ref } from 'vue' const count = ref(1) </script> <template> <button>{{ count }}</button> </template><script setup> import { ref } from 'vue' const count = ref(1) </script> <template> <button>{{ count }}</button> </template>
- reactive
reactive
用于创建一个响应式对象 (通常用来定义 引用类型数据)
let obj = reactive({ count: 1 })obj.count++ // 2let obj = reactive({ count: 1 }) obj.count++ // 2let obj = reactive({ count: 1 }) obj.count++ // 2
- computed
使用 computed
创建一个计算属性,通过 .valule
暴露返回值
- 创建一个只读的计算属性
默认情况下,创建的计算属性是只读的,如果修改会报错
let count = ref(1)let getCount = computed(() => count.value)console.log(getCount.value) // 1getCount.value++ //error:Write operation failed: computed value is readonlylet count = ref(1) let getCount = computed(() => count.value) console.log(getCount.value) // 1 getCount.value++ //error:Write operation failed: computed value is readonlylet count = ref(1) let getCount = computed(() => count.value) console.log(getCount.value) // 1 getCount.value++ //error:Write operation failed: computed value is readonly
- 创建一个可写的计算属性
computed
可以接收一个带有 get
和 set
函数的对象来创建一个 可写 的计算属性
let count = ref(1)let getCount = computed({get: () => count.value,set: (val) => {count.value = val + 1}})console.log(getCount.value) // 1getCount.value = 1console.log(getCount.value) // 3let count = ref(1) let getCount = computed({ get: () => count.value, set: (val) => { count.value = val + 1 } }) console.log(getCount.value) // 1 getCount.value = 1 console.log(getCount.value) // 3let count = ref(1) let getCount = computed({ get: () => count.value, set: (val) => { count.value = val + 1 } }) console.log(getCount.value) // 1 getCount.value = 1 console.log(getCount.value) // 3
- watch
作用:用于监听响应式数据的变化,初始化的时候不执行
- 监听 ref
let count = ref(1)watch(count,(newVal, oldVal) => {console.log(newVal, oldVal) // 2, 1})count.value++let count = ref(1) watch( count, (newVal, oldVal) => { console.log(newVal, oldVal) // 2, 1 } ) count.value++let count = ref(1) watch( count, (newVal, oldVal) => { console.log(newVal, oldVal) // 2, 1 } ) count.value++
- 监听 reactive
当监听一个响应式对象时,会自动启用深度监听模式
let data = reactive({ count: 1 })watch(data,(newValue, oldValue) => {console.log(newValue, oldValue) // 2 1})data.count++let data = reactive({ count: 1 }) watch( data, (newValue, oldValue) => { console.log(newValue, oldValue) // 2 1 } ) data.count++let data = reactive({ count: 1 }) watch( data, (newValue, oldValue) => { console.log(newValue, oldValue) // 2 1 } ) data.count++
- 监听 reactive 中的某个属性
let data = reactive({ count: 1 })watch(() => data.count,(newVal, oldVal) => {console.log(newVal, oldVal) // 2 1})data.count++let data = reactive({ count: 1 }) watch( () => data.count, (newVal, oldVal) => { console.log(newVal, oldVal) // 2 1 } ) data.count++let data = reactive({ count: 1 }) watch( () => data.count, (newVal, oldVal) => { console.log(newVal, oldVal) // 2 1 } ) data.count++
- 停用监听器
let stop1 = watch(source, callback)// 停用stop1()let stop1 = watch(source, callback) // 停用 stop1()let stop1 = watch(source, callback) // 停用 stop1()
- watchEffect
watchEffect
不需要指定监听的数据源
它是自动跟踪函数内部使用的所有响应式数据源,当任何一个数据发生变化时,都会重新执行这个函数
let count = ref(1)watchEffect(() => console.log(count.value))// 输出:2count.value++let count = ref(1) watchEffect(() => console.log(count.value)) // 输出:2 count.value++let count = ref(1) watchEffect(() => console.log(count.value)) // 输出:2 count.value++
- 属性设置
flush: 'post'
默认情况下,监听器是在组件渲染之前执行
设置flush: 'post'
将会使监听器延迟到组件渲染之后再执行
- 属性设置
flush: 'sync'
在某些情况下,可能有必要在响应式依赖发生改变时立即触发监听器。可以通过设置 flush: 'sync'
来实现
需要注意:当如果有多个属性同时更新,可能会导致性能和数据一致性的问题
- watchPostEffect()
相当于 watchEffect
使用 flush: 'post'
的别名
- watchSyncEffect()
相当于 watchEffect
使用 flush: 'sync'
的别名
工具函数
- toRef
在获取一个响应式对象的子属性的时候,会丢失其响应式,如下面:
let form = reactive({ count: 1 })let count = form.countcount++console.log(form.count, count) // 1, 2let form = reactive({ count: 1 }) let count = form.count count++ console.log(form.count, count) // 1, 2let form = reactive({ count: 1 }) let count = form.count count++ console.log(form.count, count) // 1, 2
为了对响应式对象解构的时候,延续数据的响应式,需要使用 toRef
:
let form = reactive({ count: 1 })let count = toRef(form, 'count')count.value++console.log(form.count) // 2form.count++console.log(count.value) // 3let form = reactive({ count: 1 }) let count = toRef(form, 'count') count.value++ console.log(form.count) // 2 form.count++ console.log(count.value) // 3let form = reactive({ count: 1 }) let count = toRef(form, 'count') count.value++ console.log(form.count) // 2 form.count++ console.log(count.value) // 3
使用 toRef
基于响应式对象创建的属性,与其源属性保持同步。改变源属性的值,也会更新这个新创建的属性
- toRefs
toRefs
和 toRef
的区别就是多了个 s
,一个是复数一个是单数的区别。
使用 toRefs
可以将整个响应式对象都解构出来,不再是针对其中单个属性,如下:
let form = reactive({ count: 1 })let formRefs = toRefs(form)form.count++console.log(formRefs.count.value) // 2formRefs.count.value++console.log(form.count) // 3let form = reactive({ count: 1 }) let formRefs = toRefs(form) form.count++ console.log(formRefs.count.value) // 2 formRefs.count.value++ console.log(form.count) // 3let form = reactive({ count: 1 }) let formRefs = toRefs(form) form.count++ console.log(formRefs.count.value) // 2 formRefs.count.value++ console.log(form.count) // 3
移除的API
Vue.set
、Vue.delete
、 Vue.observable
、Vue.filter
、Vue.mixin
组件
新增组件
- Fragment
在 Vue2 中只能有一个根节点,Vue3 中可以支持多个根节点
其实在 Vue3 中每个组件还是一个根节点,只不过是使用 Fragment
组件将多根节点组件包裹起来了, fragment
和 keep-alive
一样,是一个抽象组件,不会被渲染出来。
<template><div>测试1</div><div>测试2</div></template><template> <div>测试1</div> <div>测试2</div> </template><template> <div>测试1</div> <div>测试2</div> </template>
- Teleport
teleport
组件允许将插槽内容渲染到任意位置,没有父子组件的限制
<Teleport to="#test"><h1>测试内容</h1></Teleport><Teleport to="#test"> <h1>测试内容</h1> </Teleport><Teleport to="#test"> <h1>测试内容</h1> </Teleport>
Props
interface TeleportProps {/*** 必填项。指定目标容器。* 可以是选择器或实际元素。*/to: string | HTMLElement/*** 当值为 `true` 时,内容将保留在其原始位置* 而不是移动到目标容器中。* 可以动态更改。*/disabled?: boolean}interface TeleportProps { /** * 必填项。指定目标容器。 * 可以是选择器或实际元素。 */ to: string | HTMLElement /** * 当值为 `true` 时,内容将保留在其原始位置 * 而不是移动到目标容器中。 * 可以动态更改。 */ disabled?: boolean }interface TeleportProps { /** * 必填项。指定目标容器。 * 可以是选择器或实际元素。 */ to: string | HTMLElement /** * 当值为 `true` 时,内容将保留在其原始位置 * 而不是移动到目标容器中。 * 可以动态更改。 */ disabled?: boolean }
父子组件通信
- 父传子
在 vue2 中我们使用 v-bind + props
的方式向子组件传值
在 vue3 中是使用 v-model + defineProps
的方式向子组件传值。并且支持写多个 v-model
<!--父组件--><script setup>import Child from './Child.vue'import { ref } from 'vue'const name = ref('时光凉忆')const age = ref(26)</script><template><Child v-model:name="name" v-model:age="age"/></template><!--子组件--><script setup>const props = defineProps({name: String,age: Number})</script><template><p>名字:{{name}}</p><p>年龄:{{age}}</p></template><!--父组件--> <script setup> import Child from './Child.vue' import { ref } from 'vue' const name = ref('时光凉忆') const age = ref(26) </script> <template> <Child v-model:name="name" v-model:age="age"/> </template> <!--子组件--> <script setup> const props = defineProps({ name: String, age: Number }) </script> <template> <p>名字:{{name}}</p> <p>年龄:{{age}}</p> </template><!--父组件--> <script setup> import Child from './Child.vue' import { ref } from 'vue' const name = ref('时光凉忆') const age = ref(26) </script> <template> <Child v-model:name="name" v-model:age="age"/> </template> <!--子组件--> <script setup> const props = defineProps({ name: String, age: Number }) </script> <template> <p>名字:{{name}}</p> <p>年龄:{{age}}</p> </template>
- 子传父
在 vue2 中使用 $emit
向父组件传值
在 vue3 中使用 defineEmits
向父组件传值
<!--父组件--><script setup>import Child from './Child.vue'import { ref } from 'vue'const count = ref(1)function addCount(e) {count.value += e}function resetCount() {count.value = 1}</script><template><Child @addCount="addCount" @resetCount="resetCount"/></template><!--子组件--><script setup>const emit = defineEmits(['addCount', 'resetCount'])function addCount() {emit('addCount', 1)}function resetCount() {emit('resetCount')}</script><template><button @click="addCount">点击累加</button><button @click="resetCount">重置</button></template><!--父组件--> <script setup> import Child from './Child.vue' import { ref } from 'vue' const count = ref(1) function addCount(e) { count.value += e } function resetCount() { count.value = 1 } </script> <template> <Child @addCount="addCount" @resetCount="resetCount"/> </template> <!--子组件--> <script setup> const emit = defineEmits(['addCount', 'resetCount']) function addCount() { emit('addCount', 1) } function resetCount() { emit('resetCount') } </script> <template> <button @click="addCount">点击累加</button> <button @click="resetCount">重置</button> </template><!--父组件--> <script setup> import Child from './Child.vue' import { ref } from 'vue' const count = ref(1) function addCount(e) { count.value += e } function resetCount() { count.value = 1 } </script> <template> <Child @addCount="addCount" @resetCount="resetCount"/> </template> <!--子组件--> <script setup> const emit = defineEmits(['addCount', 'resetCount']) function addCount() { emit('addCount', 1) } function resetCount() { emit('resetCount') } </script> <template> <button @click="addCount">点击累加</button> <button @click="resetCount">重置</button> </template>
- 子组件直接修改父组件传过来的数据
在 vue2 中:子组件如果想直接修改父组件传过来数据,需要指定 v-model.sync
在 vue3 中,.sync
被移除了。通过定义一个 emit
事件,指定 update:count
来进行修改数据
<!--父组件--><script setup>import Child from './Child.vue'import { ref } from 'vue'const count = ref(1)</script><template><Child v-model:count="count"/></template><!--子组件--><script setup>const props = defineProps({count: Number})const emit = defineEmits(['update:count'])function updateCount() {emit('update:count', 100)}</script><template><button @click="updateCount">更新父组件数据</button></template><!--父组件--> <script setup> import Child from './Child.vue' import { ref } from 'vue' const count = ref(1) </script> <template> <Child v-model:count="count"/> </template> <!--子组件--> <script setup> const props = defineProps({ count: Number }) const emit = defineEmits(['update:count']) function updateCount() { emit('update:count', 100) } </script> <template> <button @click="updateCount">更新父组件数据</button> </template><!--父组件--> <script setup> import Child from './Child.vue' import { ref } from 'vue' const count = ref(1) </script> <template> <Child v-model:count="count"/> </template> <!--子组件--> <script setup> const props = defineProps({ count: Number }) const emit = defineEmits(['update:count']) function updateCount() { emit('update:count', 100) } </script> <template> <button @click="updateCount">更新父组件数据</button> </template>
- 父组件获取子组件的属性或调用子组件的方法
默认情况下,父组件是不可以获取到子组件的属性。如果想这么做的话,我们可以在子组件使用 defineExpose
指定暴露出去的属性
defineExpose
可以指定那些属性、方法被其父组件访问和使用
<!--子组件--><script setup>import { ref } from 'vue'const content = ref('无形装逼,最为致命')const testFunc = () => {console.log(content.value)}defineExpose({content,testFunc})</script><!--父组件--><script setup>import Child from './Child.vue'// 这里声明的变量应该和子组件标签定义的ref保持一致const child = ref()console.log(child.value.content) //无形装逼,最为致命child.value.testFunc() // 这里会执行子组件的testFunc函数</script><template><Child ref="child"/></template><!--子组件--> <script setup> import { ref } from 'vue' const content = ref('无形装逼,最为致命') const testFunc = () => { console.log(content.value) } defineExpose({ content, testFunc }) </script> <!--父组件--> <script setup> import Child from './Child.vue' // 这里声明的变量应该和子组件标签定义的ref保持一致 const child = ref() console.log(child.value.content) //无形装逼,最为致命 child.value.testFunc() // 这里会执行子组件的testFunc函数 </script> <template> <Child ref="child"/> </template><!--子组件--> <script setup> import { ref } from 'vue' const content = ref('无形装逼,最为致命') const testFunc = () => { console.log(content.value) } defineExpose({ content, testFunc }) </script> <!--父组件--> <script setup> import Child from './Child.vue' // 这里声明的变量应该和子组件标签定义的ref保持一致 const child = ref() console.log(child.value.content) //无形装逼,最为致命 child.value.testFunc() // 这里会执行子组件的testFunc函数 </script> <template> <Child ref="child"/> </template>
异步加载组件
- vue2
const MyComponent = () => import('./MyComponent.vue')const MyComponent = () => import('./MyComponent.vue')const MyComponent = () => import('./MyComponent.vue')
- vue3
import { defineAsyncComponent } from 'vue'// 会为MyComponent.vue 及其依赖创建一个单独的一个快// 它只会按需加载 (及改异步组件在页面中被渲染时)const MyComponent = defineAsyncComponent(() => import('./MyComponent.vue'))import { defineAsyncComponent } from 'vue' // 会为MyComponent.vue 及其依赖创建一个单独的一个快 // 它只会按需加载 (及改异步组件在页面中被渲染时) const MyComponent = defineAsyncComponent(() => import('./MyComponent.vue'))import { defineAsyncComponent } from 'vue' // 会为MyComponent.vue 及其依赖创建一个单独的一个快 // 它只会按需加载 (及改异步组件在页面中被渲染时) const MyComponent = defineAsyncComponent(() => import('./MyComponent.vue'))
生命周期
-
vue3 中移除了 beforeCreate 和 created,添加了 setup 函数
-
所有钩子函数添加一个前缀
on
-
引用方式,因为 vue3 都是模块化,如
import {onMounted} from 'vue'
vue2 | vue3 |
---|---|
beforeCreate created |
setup() |
beforeMount Mounted |
onBeforeMount onMounted |
beforeUpdate updated |
onBeforeUpdate onUpdated |
beforeDestroy destroyed |
obBeforeUnmount onUnmounted |
CSS
css 样式穿透
在 Vue2 中修改子组件或者组件库的样式,都会使用样式穿透 /deep/ .class{}
在 Vue3 中样式穿透的语法变成了 :deep(.class)
<style scoped>:deep(.el-form) {.el-input {width: 200px;}}</style><style scoped> :deep(.el-form) { .el-input { width: 200px; } } </style><style scoped> :deep(.el-form) { .el-input { width: 200px; } } </style>
css 的v-bind
在css中使用js的变量来动态写样式
<script setup>import { ref } from 'vue'const color = ref('red')</script><style>.text {color: v-bind('color');}</style><script setup> import { ref } from 'vue' const color = ref('red') </script> <style> .text { color: v-bind('color'); } </style><script setup> import { ref } from 'vue' const color = ref('red') </script> <style> .text { color: v-bind('color'); } </style>
注意: 在 <script setup>
中使用 v-bind
,里面的变量需要引号包裹起来,特别注意。在 setup()
中不需要
高级函数
逻辑复用 hooks
在 vue2 中我们想要实现逻辑复用一般用 mixins
,但是有一些弊端
在 vue3 中保留了 mixins
, 但更推荐使用Hooks模式。通过自定义组合式函数,这些函数包含状态、生命周期函数等,可以在多个组件中实现复用。
下面以官方鼠标跟踪器示例举例进行讲解,封装函数:
// mouse.jsimport { ref, onMounted, onUnmounted } from 'vue'// 按照惯例,组合式函数名以“use”开头export function useMouse() {// 被组合式函数封装和管理的状态const x = ref(0)const y = ref(0)// 组合式函数可以随时更改其状态。function update(event) {x.value = event.pageXy.value = event.pageY}// 一个组合式函数也可以挂靠在所属组件的生命周期上// 来启动和卸载副作用onMounted(() => window.addEventListener('mousemove', update))onUnmounted(() => window.removeEventListener('mousemove', update))// 通过返回值暴露所管理的状态return { x, y }}// mouse.js 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 } }// mouse.js 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 } }
下面是在组件中使用的方式:
<script setup>import { useMouse } from './mouse.js'const { x, y } = useMouse()</script><template>Mouse position is at: {{ x }}, {{ y }}</template><!--Mouse position is at: 0, 0--><script setup> import { useMouse } from './mouse.js' const { x, y } = useMouse() </script> <template>Mouse position is at: {{ x }}, {{ y }}</template> <!--Mouse position is at: 0, 0--><script setup> import { useMouse } from './mouse.js' const { x, y } = useMouse() </script> <template>Mouse position is at: {{ x }}, {{ y }}</template> <!--Mouse position is at: 0, 0-->
我们通过上面代码发现,在自定义hooks函数中,我们依然可以使用响应式API、生命周期钩子等,可以实现 mixins
想要的效果
全局API转移
在 vue2 全局API 都是挂载在 Vue.prototype
上
在 vue3 改成了 app.config.globalProperties
const app = createApp(App)app.config.globalProperties.$myGlobalMethod = function () {// ...}const app = createApp(App) app.config.globalProperties.$myGlobalMethod = function () { // ... }const app = createApp(App) app.config.globalProperties.$myGlobalMethod = function () { // ... }
调用的话:
this.$myGlobalMethod()this.$myGlobalMethod()this.$myGlobalMethod()
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
暂无评论内容