赞助
# 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)

  

posted on 2022-11-08 15:15  Tsunami黄嵩粟  阅读(73)  评论(0)    收藏  举报