# composition(组合式api)

## 1.为什么使用composition

vue3里面不需要Mixins了?因为有compoition api 能讲逻辑进行抽离和复用

大型组件中,其中**逻辑关注点**按颜色进行分组。

这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。

如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的

![重构后的文件夹组件](https://user-images.githubusercontent.com/499550/62783026-810e6180-ba89-11e9-8774-e7771c8095d6.png)

## 2.setup函数

执行顺序在beforeCreate,created之前,不能在此操作data

### props

使用 `setup` 函数时,它将接收两个参数:

1. `props`
2. `context` ### Props `setup` 函数中的第一个参数是 `props`。正如在一个标准组件中所期望的那样,`setup` 函数中的 `props` 是响应式的,当传入新的 prop 时,它将被更新。 ```js
// MyBook.vue export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
``` > WARNING 但是,因为 `props` 是响应式的,你**不能使用 ES6 解构**,它会消除 prop 的响应性。 如果需要解构 prop,可以在 `setup` 函数中使用 [`toRefs`](https://v3.cn.vuejs.org/guide/reactivity-fundamentals.html#响应式状态解构) 函数来完成此操作: ```js
// MyBook.vue import { toRefs } from 'vue' setup(props) {
const { title } = toRefs(props) console.log(title.value)
}
``` 如果 `title` 是可选的 prop,则传入的 `props` 中可能没有 `title` 。在这种情况下,`toRefs` 将不会为 `title` 创建一个 ref 。你需要使用 `toRef` 替代它: ```js
// MyBook.vue
import { toRef } from 'vue'
setup(props) {
const title = toRef(props, 'title')
console.log(title.value)
}
``` ### Context 传递给 `setup` 函数的第二个参数是 `context`。`context` 是一个普通 JavaScript 对象,暴露了其它可能在 `setup` 中有用的值: ```js
// MyBook.vue export default {
setup(props, context) {
// Attribute (非响应式对象,等同于 $attrs)
console.log(context.attrs) // 插槽 (非响应式对象,等同于 $slots)
console.log(context.slots) // 触发事件 (方法,等同于 $emit)
console.log(context.emit) // 暴露公共 property (函数)
console.log(context.expose)
}
}
``` `context` 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 `context` 使用 ES6 解构。 ```js
// MyBook.vue
export default {
setup(props, { attrs, slots, emit, expose }) {
...
}
}
``` ### setup中的this 在 `setup()` 内部,`this` 不是该活跃实例的引用**,因为 `setup()` 是在解析其它组件选项之前被调用的,所以 `setup()` 内部的 `this` 的行为与其它选项中的 `this` 完全不同。这使得 `setup()` 在和其它选项式 API 一起使用时可能会导致混淆。 beforeCreate setup(){} ### setup中获取data 通过getCurrentInstance获取的是当前组件的实例,但是我们无法去获取具体的data属性,因为setup的执行顺序在created之前 我们的编程思路不应该考虑在setup中如何获取data,我们正确的做法是使用composition给我们提供的响应式api ```js
import {getCurrentInstance} from "vue"
export default {
setup(props,context){
console.log(props)
console.log(context)
console.log(getCurrentInstance())
}
}
``` ## 3.响应式语法 ### ref ref可以对数据进行响应,也可以对复杂数据类型进行响应,也可以用于对模板的引用 在 Vue 3.0 中,我们可以通过一个新的 `ref` 函数使任何响应式变量在任何地方起作用,如下所示: ```js
import { ref } from 'vue' const counter = ref(0)
``` `ref` 接收参数并将其包裹在一个带有 `value` property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值: ```js
import { ref } from 'vue' const counter = ref(0) console.log(counter) // { value: 0 }
console.log(counter.value) // 0 counter.value++
console.log(counter.value) // 1
``` 将值封装在一个对象中,看似没有必要,但为了保持 JavaScript 中不同数据类型的行为统一,这是必须的。这是因为在 JavaScript 中,`Number` 或 `String` 等基本类型是通过值而非引用传递的: ![按引用传递与按值传递](https://blog.penjee.com/wp-content/uploads/2015/02/pass-by-reference-vs-pass-by-value-animation.gif) ### 模板引用 在使用组合式 API 时,响应式引用和模板引用的概念是统一的。为了获得对模板内元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回: - 作用在dom元素返回dom
- 作用在组件返回组件实例 ```jsx <template>
<div ref="root">This is a root element</div>
</template> <script>
import { ref, onMounted } from 'vue' export default {
setup() {
const root = ref(null) onMounted(() => {
// DOM 元素将在初始渲染后分配给 ref
console.log(root.value) // <div>This is a root element</div>
}) return {
root
}
}
}
</script>
``` ### ref在v-for中的使用 组合式 API 模板引用在 `v-for` 内部使用时没有特殊处理。相反,请使用函数引用执行自定义处理: ```html
<template>
<div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
{{ item }}
</div>
</template> <script>
import { ref, reactive, onBeforeUpdate } from 'vue' export default {
setup() {
const list = reactive([1, 2, 3])
const divs = ref([]) // 确保在每次更新之前重置ref
onBeforeUpdate(() => {
divs.value = []
}) return {
list,
divs
}
}
}
</script>
``` ### 模板改变后引用 侦听模板引用的变更可以替代前面例子中演示使用的生命周期钩子。 但与生命周期钩子的一个关键区别是,`watch()` 和 `watchEffect()` 在 DOM 挂载或更新*之前*运行副作用,所以当侦听器运行时,模板引用还未被更新。 ```vue
<template>
<div ref="root">This is a root element</div>
</template> <script>
import { ref, watchEffect } from 'vue' export default {
setup() {
const root = ref(null) watchEffect(() => {
// 这个副作用在 DOM 更新之前运行,因此,模板引用还没有持有对元素的引用。
console.log(root.value) // => null
}) return {
root
}
}
}
</script>
``` 因此,使用模板引用的侦听器应该用 `flush: 'post'` 选项来定义,这将在 DOM 更新*后*运行副作用,确保模板引用与 DOM 保持同步,并引用正确的元素。 ```vue
<template>
<div ref="root">This is a root element</div>
</template> <script>
import { ref, watchEffect } from 'vue' export default {
setup() {
const root = ref(null) watchEffect(() => {
console.log(root.value) // => <div>This is a root element</div>
},
{
flush: 'post'
}) return {
root
}
}
}
</script>
``` ### reactive 使用reactive可以直接将对象变成响应式,甚至可以往对象中继续添加computed这样的属性,比如 ```js
setup() {
let rea = reactive({
name: "李雷",
count: 1,
jisuan: computed(() => rea.count + 99)
}) function increase() {
rea.count++
}
}
``` 这样写的好处就是,可以不需要每次在处理这个数据的使用想ref那样去.value了,但是这样写的时候也需要注意,返回数据的时候如果操作不当,那么就会丢失对数据的响应,比如下面这样 ```js
return { ...rea, increase } //这样写能够展示数据,但是不能对数据保持响应
``` 那这个时候我们就需要使用到下面个方法了,toRefs 一份完整的代码 ```jsx
<template id="test">
<fieldset>
<legend>测试组件</legend>
<button @click="increaseCapacity()">++</button>
<h1>{{capacity}}</h1>
<h2>{{spacesLeft}}</h2>
</fieldset>
</template>
<scirpt>
const { createApp,reactive, computed, toRefs } = Vue;
let app = createApp({})
app.component('test-com', {
template: "#test",
setup() {
let event = reactive({
capacity: 4,
attending: ["Tim","Bob","Joe"],
spaceLeft: computed(() => event.capacity-event.attending.length + 99)
}) function increaseCapacity() {
event.capacity++
} return { ...event, increaseCapacity } //这样写能够展示数据,但是不能对数据保持响应
}
})
app.mount('#app')
</script>
``` ### ref和reactive的区别 1. reactive在使用的时候,会自动解包不需要像ref那样去.value
2. reactive不能对简单数据类型进行响应,需要在reactive中传入引用数据类型 ### toRefs 如果直接返回reactive对应的变量,那么会失去响应,正确的写法是将reactive的属性放到toRefs方法中,保持其响应式 <img src="img/image-20220728155544943.png" alt="image-20220728155544943" style="zoom:45%;" /> 可以这样写 ```js
return { ...toRefs(event), increaseCapacity }
``` 如果不需要返回方法,只需要event那么也可以这样写 ```js
return toRefs(event)
``` ## 4.methods 如果我们想要让这个值增加,那么在composition的代码中应该如何编写?传统的方式我们可以使用mehods来定义方法,但是现在我们只需要写成传统函数就可以了 - 传统写法 <img src="img/image-20220728154943165.png" alt="image-20220728154943165" style="zoom:50%;" /> - composition
- 使用ref响应属性的时候,不然要忘记了.value属性 <img src="img/image-20220728153215064.png" alt="image-20220728153215064" style="zoom:20%;" /> ## 5.computed属性 接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 [ref](https://v3.cn.vuejs.org/api/refs-api.html#ref) 对象。 ```js
const count = ref(1)
const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // 错误
``` 或者,接受一个具有 `get` 和 `set` 函数的对象,用来创建可写的 ref 对象。 ```js
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
}) plusOne.value = 1
console.log(count.value) // 0
``` ## 6.生命周期 在composition中使用"on"来访问组件的钩子 created,beforeCreated在组合式api这种模式下,是没有的 ### 新增钩子 - errorCaptured 在捕获一个来自后代组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 `false` 以阻止该错误继续向上传播。 一般情况下可以用抽象Error组件,然后专门用于处理错误 单独创建一个叫做Error的组件 ```vue
<template>
<slot></slot>
</template>
<script setup>
import { onErrorCaptured } from 'vue'; onErrorCaptured((err, instance, info) => {
console.log(err)
console.log(instance)
console.log(info)
// 可以在这通过条件判断来决定是否要阻止错误的传播 //return true 继续传播 //return false 阻止错误的传播 return false
})
</script>
``` 后续使用,将这个Error组件作为其它组件的父组件使用 ```vue
<template>
<fieldset>
<legend>app</legend>
<div id="app">
<Error>
<lifeCircle></lifeCircle>
<reactiveCom></reactiveCom>
<counter></counter>
<computedCom></computedCom>
</Error>
</div>
</fieldset>
</template>
<script setup>
import reactiveCom from "./components/01.Reactive.vue"
import counter from "./components/02.计数器/counter.vue"
import computedCom from "./components/03.计算属性computed.vue"
import lifeCircle from "./components/03.生命周期.vue"
import Error from "./components/Error.vue"
</script>
``` - renderTracked 跟踪虚拟 DOM 重新渲染时调用。钩子接收 `debugger event` 作为参数。此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。 ```js
当组件第一次渲染时,这将被记录下来
``` - renderTriggered 当虚拟 DOM 重新渲染被触发时调用。和 [`renderTracked`](https://v3.cn.vuejs.org/api/options-lifecycle-hooks.html#rendertracked) 类似,接收 `debugger event` 作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。 ```js
import {onMounted} from "vue" export default {
setup(){
onMounted(()=>{
console.log('onMounted')
})
}
}
``` | 选项式 API | Hook inside `setup` |
| ------------------------------------------- | ------------------- |
| `beforeCreate` | Not needed* |
| `created` | Not needed* |
| `beforeMount` | `onBeforeMount` |
| `mounted` | `onMounted` |
| `beforeUpdate` | `onBeforeUpdate` |
| `updated` | `onUpdated` |
| `beforeUnmount` | `onBeforeUnmount` |
| `unmounted` | `onUnmounted` |
| `errorCaptured` | `onErrorCaptured` |
| `renderTracked`跟踪虚拟dom更新的时候,会调用 | `onRenderTracked` |
| `renderTriggered` | `onRenderTriggered` |
| `activated` | `onActivated` |
| `deactivated` | `onDeactivated` | ![组件生命周期图示](https://cn.vuejs.org/assets/lifecycle.16e4c08e.png) ## 7.watch ### `watch` 响应式更改 就像我们在组件中使用 `watch` 选项并在 `user` property 上设置侦听器一样,我们也可以使用从 Vue 导入的 `watch` 函数执行相同的操作。它接受 3 个参数: - 一个想要侦听的**响应式引用**或 getter 函数
- 一个回调
- 可选的配置选项 **下面让我们快速了解一下它是如何工作的** ```js
import { ref, watch } from 'vue' const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
``` 每当 `counter` 被修改时,例如 `counter.value=5`,侦听将触发并执行回调 (第二个参数),在本例中,它将把 `'The new counter value is:5'` 记录到控制台中。 **以下是等效的选项式 API:** ```js
export default {
data() {
return {
counter: 0
}
},
watch: {
counter(newValue, oldValue) {
console.log('The new counter value is: ' + this.counter)
}
}
}
``` 有关 `watch` 的详细信息,请参阅我们的[深入指南](https://v3.cn.vuejs.org/guide/reactivity-computed-watchers.html#watch)。 ### 在setup语法糖获取props 明确引入`defineProps`来获取props 如果是定义触发的事件名可以使用`defineEmits`来进行声明 ```vue
<script setup>
const props = defineProps({
foo: String
}) const emit = defineEmits(['change', 'delete'])
// setup code
</script>
``` **现在我们将其应用到我们的示例中:** ```js
<script setup>
import { watch, ref, defineProps, toRefs } from "vue"
//监听props的变化 let props = defineProps({
msg: String,
age: String
}) // console.log(props)
//1.监听写法,第一个参数传入一个函数,返回一个需要监听的字段
watch(() => props.msg, (newVal, oldVal) => {
console.log('newVal', newVal)
console.log('oldVal', oldVal)
}) //2.第二种监听props变化的写法
//toRefs可以保持解构之后的响应性
let { age } = toRefs(props) watch(age, (newVal, oldVal) => {
console.log('newVal', newVal)
console.log('oldVal', oldVal)
})
</sript>
``` 你可能已经注意到在我们的 `setup` 的顶部使用了 `toRefs`。这是为了确保我们的侦听器能够根据 `user` prop 的变化做出反应。 ### watch可以一次侦听多个 侦听器还可以使用数组同时侦听多个源: ```js
const firstName = ref('')
const lastName = ref('') watch([firstName, lastName], (newValues, prevValues) => {
console.log(newValues, prevValues)
}) firstName.value = 'John' // logs: ["John", ""] ["", ""]
lastName.value = 'Smith' // logs: ["John", "Smith"] ["John", ""]
``` 尽管如此,如果你在同一个函数里同时改变这些被侦听的来源,侦听器仍只会执行一次: ```js
setup() {
const firstName = ref('')
const lastName = ref('') watch([firstName, lastName], (newValues, prevValues) => {
console.log(newValues, prevValues)
}) const changeValues = () => {
firstName.value = 'John'
lastName.value = 'Smith'
// 打印 ["John", "Smith"] ["", ""]
} return { changeValues }
}
``` ### 强制触发侦听器 > 多个同步更改只会触发一次侦听器。我们也办法强制触发 通过更改设置 `flush: 'sync'`,我们可以为每个更改都强制触发侦听器,尽管这通常是不推荐的。或者,可以用 [nextTick](https://v3.cn.vuejs.org/api/global-api.html#nexttick) 等待侦听器在下一步改变之前运行。例如: ```js
import {next}
const changeValues = async () => {
firstName.value = 'John' // 打印 ["John", ""] ["", ""]
await nextTick()
lastName.value = 'Smith' // 打印 ["John", "Smith"] ["John", ""]
}
``` ### 监听对象中属性变化 ```vue
<template>
<div>
<div>{{obj.name}}</div>
<div>{{obj.age}}</div>
<button @click="changeName">改变值</button>
</div>
</template> <script>
import { reactive, watch } from 'vue';
export default {
setup(){
const obj = reactive({
name:'zs',
age:14
});
const changeName = () => {
obj.name = 'ls';
};
watch(() => obj.name,() => {
console.log('监听的obj.name改变了')
})
return {
obj,
changeName,
}
}
}
</script>
``` ### 深度监听 (deep)、默认执行(immediate) ```javascript
<template>
<div>
<div>{{obj.brand.name}}</div>
<button @click="changeBrandName">改变值</button>
</div>
</template> <script>
import { reactive, ref, watch } from 'vue';
export default {
setup(){
const obj = reactive({
name:'zs',
age:14,
brand:{
id:1,
name:'宝马'
}
});
const changeBrandName = () => {
obj.brand.name = '奔驰';
};
watch(() => obj.brand,() => {
console.log('监听的obj.brand.name改变了')
},{
deep:true,
immediate:true,
})
return {
obj,
changeBrandName,
}
}
}
</script>
``` ## 8.watchEffect高级侦听器(变化) ### watchEffect 的使用 watchEffect 也是一个帧听器,是一个副作用函数。 它会监听引用数据类型的所有属性,不需要具体到某个属性,一旦运行就会立即监听,组件卸载的时候会停止监听。 ```javascript
<template>
<div>
<input type="text" v-model="obj.name">
</div>
</template> <script>
import { reactive, watchEffect } from 'vue';
export default {
setup(){
let obj = reactive({
name:'zs'
});
watchEffect(() => {
console.log('name:',obj.name)
}) return {
obj
}
}
}
</script>
``` <img src="img/4.png" alt="在这里插入图片描述" style="zoom: 67%;" /> ### 停止侦听 当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。 在一些情况下,也可以显式调用返回值以停止侦听: ```javascript
<template>
<div>
<input type="text" v-model="obj.name">
<button @click="stopWatchEffect">停止监听</button>
</div>
</template> <script>
import { reactive, watchEffect } from 'vue';
export default {
setup(){
let obj = reactive({
name:'zs'
});
const stop = watchEffect(() => {
console.log('name:',obj.name)
})
const stopWatchEffect = () => {
console.log('停止监听')
stop();
} return {
obj,
stopWatchEffect,
}
}
}
</script>
``` ![在这里插入图片描述](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1375eaaec5cb41139d14c47277f44612~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp) ### onInvalidate 当执行副作用函数时,它势必会对系统带来一些影响,如在副作用函数里执行了一个定时器`setInterval`,因此我们必须处理副作用。`Vue3`的`watchEffect`侦听副作用传入的函数可以接收一个 `onInvalidate` 函数作为入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发: - 副作用即将重新执行时(即依赖的值改变)
- 侦听器被停止 (通过显示调用返回值停止侦听,或组件被卸载时隐式调用了停止侦听) ```js
import { watchEffect, ref } from 'vue' const count = ref(0)
watchEffect((onInvalidate) => {
console.log(count.value)
onInvalidate(() => {
console.log('执行了onInvalidate')
})
}) setTimeout(()=> {
count.value++
}, 1000)
``` 上述代码打印的顺序为: `0` -> `执行了onInvalidate,最后执行` -> `1` ### 案例: 有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (场景:有一个页码组件里面有5个页码,点击就会异步请求数据。于是做一个监听,监听当前页码,只要有变化就请求一次。问题:如果点击的比较快,从1到5全点了一遍,那么会有5个请求,最终页面会显示第几页的内容?第5页?那是假定请求第5页的ajax响应的最晚,事实呢?并不一定。于是这就会导致错乱。还有一个问题,连续快速点5次页码,等于我并不想看前4页的内容,那么是不是前4次的请求都属于带宽浪费?这也不好。 于是官方就给出了一种解决办法: 侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调。 当以下情况发生时,这个失效回调会被触发: - 副作用即将重新执行时;
- 侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时) ```javascript
watchEffect(onInvalidate => {
const token = performAsyncOperation(id.value)
onInvalidate(() => {
// id has changed or watcher is stopped.
// invalidate previously pending async operation
token.cancel()
})
}) ``` 构建一个结构 ```javascript import axios from 'axios';
import { ref, watchEffect } from 'vue';
export default {
setup() {
let pageNumber = ref(1);
let content = ref(''); const changePageNumber = () => {
pageNumber.value++;
} watchEffect((onInvalidate) => {
// const CancelToken = axios.CancelToken;
// const source = CancelToken.source();
// onInvalidate(() => {
// source.cancel();
// });
axios.get(`http://chst.vip:1234/api/getmoneyctrl?pageid=${pageNumber.value}`, {
// cancelToken: source.token,
}).then((response) => {
content.value = response.data.result[0].productName;
}).catch(function (err) {
if (axios.isCancel(err)) {
console.log('Request canceled', err.message);
}
});
});
return {
pageNumber,
content,
changePageNumber,
};
},
};
</script> ``` 上面注释掉的代码**先保持注释**,然后经过多次疯狂点击之后,得到这个结果,显然,内容错乱了: ![在这里插入图片描述](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/52c46d680203449eb17b159efb96dee9~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp) 现在**取消注释**,重新多次疯狂点击,得到的结果就正确了: ![在这里插入图片描述](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/61472e6fffd543aea6d247b5bfc42cf2~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp) 除了最后一个请求,上面那些请求有2种结局: - 一种是响应的太快,来不及取消的请求,这种请求会返回200,不过既然它响应太快,没有任何一次后续 ajax 能够来得及取消它,说明任何一次后续请求开始之前,它就已经结束了,那么它一定会被后续某些请求所覆盖,所以这类请求的 content 会显示一瞬间,然后被后续的请求覆盖,绝对不会比后面的请求还晚。
- 另一种就是红色的那些被取消的请求,因为响应的慢,所以被取消掉了。 所以最终结果一定是正确的,而且节省了很多带宽,也节省了系统开销。 ### 面试题 当一个用户快速的点击分页的时候,导致页面数据加载混乱怎么处理? 答:我用的是vue3,会在watchEffect的onInvalidate中调用axios.cancel这个方法来取消上一个axios请求就可以了 如果你在vue2中会怎么做? 如果是这样的话,可以使用防抖的思路,用户一直点击的时候,总是取消上一个axios请求,停止点击之后会发送最后一个请求 ### watchEffect的配置项 watchEffect(()=>{},{flush:"post"}),第二个参数是个配置项,有个属性叫flush,有几个值 - pre dom加载之前运行
- post dom加载之后运行watchEffect的回调
- sync 如果在相同的函数中修改多个数据,而这些数据被watchEffect侦听了,只会触发一次回调,如果希望每次修改都触发,那么可以加上flush:'sync' ### 总结 **watch 特点** watch 监听函数可以添加配置项,也可以配置为空,配置项为空的情况下,watch的特点为: - 有惰性:运行的时候,不会立即执行;
- 更加具体:需要添加监听的属性;
- 可访问属性之前的值:回调函数内会返回最新值和修改之前的值;
- 可配置:配置项可补充 watch 特点上的不足: immediate:配置 watch 属性是否立即执行,值为 true 时,一旦运行就会立即执行,值为 false 时,保持惰性。 deep:配置 watch 是否深度监听,值为 true 时,可以监听对象所有属性,值为 false 时保持更加具体特性,必须指定到具体的属性上。 **watchEffect 特点** - 非惰性:一旦运行就会立即执行;
- 更加抽象:使用时不需要具体指定监听的谁,回调函数内直接使用就可以;
- 不可访问之前的值:只能访问当前最新的值,访问不到修改之前的值; ## 9.自定义指令的使用 ## vite代理配置 ```js
// vite.config.js import { defineConfig } from "vite"; export default defineConfig({
server: {
proxy: {
"/api": {
target: "http://localhost:3001",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
});
``` ## 响应式原理 vue3的响应式主要使用了es6的reflect和proxy ![image-20220728152059790](img/vue3-2.png)

  

最新文章

  1. 一个简单的ASP.NET MVC异常处理模块
  2. c#使用多线程的几种方式示例详解
  3. 移动web中一些问题处理与事件说明
  4. python logging模块
  5. django rest framework 的url标签的问题
  6. oracle断电重启之ORA-00600[4194]
  7. JavaBean 动作元素事例
  8. HTML 5 Canvas
  9. MSSQL 2005数据库与SP4补丁安装
  10. Web Navigation
  11. Android Activity四种加载方式
  12. iOS页面间传值的六种方式
  13. Java 期末考试
  14. javascript + sql编写SQL客户端工具tabris
  15. Java实现简单工厂模式
  16. python 中的 args,*args,**kwargs的区别
  17. bzoj 2510 弱题 矩阵乘
  18. jdk源码阅读笔记-LinkedHashMap
  19. 洛谷P1041 传染病控制
  20. Confluence 6 配置验证码(Captcha)来防止垃圾

热门文章

  1. tfidf与bm25
  2. ComPiler200004:Library-Oriented Programming
  3. python学习●错误点●expected an indented block
  4. 浅谈前端自动化构建(Grunt、gulp。webpack)
  5. 循环2-if与case语法
  6. Python_DL_July_深度学习_10_深度学习与各种迁移学习应用
  7. vagrant用密码连接ssh
  8. 【Java】List
  9. nacos之服务注册、发现及维持心跳
  10. canvas 模糊