Vue3_compositionAPI
Composition API
1. setup
1.1 setup函数有两个参数:props,context
- props:其实就是父组件传递过来的属性,但是在setup外面依然需要用props接收
- context:里面包含三个属性:
- attrs:父组件传递过来的没有被prop接收的属性
- slots:父组件传递过来的插槽
- emit:当组件内部需要发出事件的时候用(vue2里面是this.$emit,但是vue3中,setup不能访问this)
1.2 setup 函数的返回值
setup() { |
setup的返回值可以在模板 template 中使用,也就是可以代替data
但是直接这么返回变量,是没有响应式的
1.3 setup 不可以使用 this
- this 并没有指向当前组件实例
- 在 setup 被调用之前,data、computed、等都没有被解析
- 所以无法在 setup 中获取this
2. reactive API
为setup中定义的数据提供响应式的特性
reactive API 传入的类型必须是对象或者数组
const state = reactive({ |
3. ref API
3.1 ref API 基本使用
可以传入基本数据类型,在开发中推荐使用 ref,便于代码的抽离,当然如果属性关系很紧密的时候,我们也可以用 reactive
ref 会返回一个可变的响应式对象
const message = ref("hello world") |
- 在 template 中引用 ref 的值时,Vue会自动帮我们进行解包,就是说我们不需要在模板中 xxx.value 来使用
- 但是在 setup 内部,它依然是一个 ref 的引用,所以要使用 ref.value
3.2 ref API 的补充
- toRefs 和 toRef
let info = reactive({ |
- shallowRef:创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的
- triggerRef:手动触发和 shallowRef 相关联的副作用
let info = shallowRef({ |
- customRef 自定义 ref
4. readonly
当我们希望我们给其他组件传递数据时,希望其他组件只是使用我们的内容,但是不允许它们修改的时候,可以用readonly
实际上,readonly 会返回原生对象的只读代理,也就是它依然是一个 Proxy,但是set方法被劫持
开发中常见的readonly方法会传入三个类型的值
- 普通对象
- reactive 返回的对象
- ref 的对象
readonly 使用时,readonly 返回的对象不允许修改,但是经过 readonly 处理的原来的对象是可以修改的
const info = {
name: "why"
}
const state = readonly(info)
state.name = "aaa" // 不可以
info.name = "aaa" // 可以
5. computed
用法一:传入一个getter函数,computed的返回值是一个只读的ref对象(不能修改)
const fullName = computed(() => firstName.value + "-" +lastName.value)
const changeData = () => {
firstName.value = "james" // 可修改
fullName.value = "yuzi bing" // 不可修改
}用法二:传入一个对象,对象包含 getter/setter,返回一个可读写的ref 对象
const fullName = computed({ |
6. watchEffect
6.1 watchEffect 基本使用
- watchEffect 传入的函数会被立即执行一次, 并在执行的过程中自动收集依赖(相当于你在这个函数使用了什么变量,它会自动收集到)
- 只有收集的依赖发生变化时,watchEffect 传入的函数才会再次执行
下面案例中,name 的改变会被侦听到,而 age 不会被侦听
// watchEffect: 自动收集响应式的依赖 |
6.2 watchEffect 停止侦听
如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可
const age = ref(18) |
6.3 watchEffect 清除副作用
清除副作用?
比如我们需要在侦听器中执行网络请求,但是在网络请求还没完成之前,我们停止了侦听器或者修改了数据让侦听器侦听函数再次执行了,这时候我们应该清除上一次的副作用(数据改变了要重新发送请求或者说不需要发了)
const stop = watchEffect((onInvalidate) => { |
6.4 watchEffect 执行时机
首先补充一下:在 setup 中如何属于 ref 或者元素或者组件?
定义一个 ref 对象,绑定到元素或组件的ref属性上
<h2 ref="title">hello</h2> // 绑定到元素的ref属性 |
watchEffect 执行时机
如果我们希望在副作用函数中获取元素,我们会发现打印结果有两个
watchEffect(() => {
console.log(title.value);
})- 这是因为 setup 函数在执行时就会立即执行传入的副作用函数,这个时候 DOM 并没有挂载,所以打印为 null
- 当 DOM 挂载时,会给 title 的 ref 对象赋新的值,副作用函数会再次执行
调整 watchEffect 的执行时机
watchEffect(() => {
console.log(title.value);
}, {
// flush: "pre" // 在元素挂载或更新之前执行
flush: "post" // 元素挂载更新之后执行, 这时候只打印一次<h2></h2>
})
7. watch
7.1 侦听单个数据源
- 侦听一个 getter 函数
const info = reactive({ |
- 直接侦听一个可响应式的对象,reactive 或 ref (ref更常用)
// 传入一个可响应式对象: reactive对象/ref对象 |
7.2 侦听多个数据源
注:如果我们希望侦听一个数组或者对象,那么可以使用一个getter函数,并且对可响应对象进行解构
(不解构也行,不解构n,o就是一个Proxy对象)
const info = reactive({ |
7.3 watch 的选项
const info = reactive({ |
8. 生命周期钩子
// 在挂载开始之前被调用:相关的 render 函数首次被调用。 |
9. Provide 和 Inject
父组件通过 provide 来提供数据(必须要在父组件中使用过子组件,建立联系才能提供数据)
provide(属性名,属性值)
const name = ref("jenny") |
后代组件可以通过 Inject 来注入需要的属性和对应值
inject(要注入的属性名, 默认值) ,默认值就是如果父组件没有提供改数据的话就使用默认值
const name = inject("name") |
10. composition API 练习
自定义 hooks
1. useTitle
改变页面标题
import { ref, watch } from 'vue' |
2. useScrollPosition
监听页面滚动位置
import { ref } from 'vue' |
3. useMousePosition
监听鼠标位置
import { ref } from 'vue' |
4. useLocalStorage
使用 localStorage 存储和获取数据
export default function(key, value) { |
使用:
// 在浏览器存取值 |
5. useCounter
import { ref, computed } from 'vue'; |
11. 认识自定义指令
11.1 简单使用
除了 v-for, v-show,等指令,Vue也允许我们自定义指令
自定义指令分为两种
- 自定义局部指令:组件中通过 directives 选项,只能在当前组件中使用
- 自定义全局指令:app的 directive 方法,可以在任意组件中被使用
简单的案例:当某个元素挂载完成后可以自定获取焦点
默认方式的实现
<template>
<div>
<input type="text" ref="input">
</div>
</template>
<script>
import {ref, onMounted} from 'vue'
export default {
setup() {
const input = ref(null)
onMounted(() => {
input.value.focus()
})
return {
input
}
}
}自定义局部指令 v-focus
<input type="text" ref="input" v-focus>
export default {
directives: {
// 自定义属性的名称(这里不需要写 v-)
focus: {
mounted(el) {
el.focus()
}
}
},
}自定义全局指令 v-focus (main.js中)
app.directive("focus", {
mounted(el) {
el.focus()
}
})
11.2 指令的生命周期
- 一个指令定义的对象,Vue提供了如下的几个钩子函数:
- created:在绑定元素的 attribute 或事件监听器被应用之前调用;
- beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用
- mounted:在绑定元素的父组件被挂载后调用
- beforeUpdate:在更新包含组件的 VNode 之前调用
- updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用
- beforeUnmount:在卸载绑定元素的父组件之前调用
- unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次
指令的生命周期可以拿到几个参数
app.directive("focus", { |
11.3 指令的参数和修饰符
指令接受参数或者修饰符
v-指令名:参数名.修饰符="具体值"
<button v-why:info.aaa.bbb="{title: 'hello', name: 'me'}"></button>
11.4 自定义指令练习
自定义时间格式化的指令 v-format-time
import dayjs from "dayjs"; |
12. nextTick
将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它
案例:当message更新时,拿到它的高度
如果不放到 nextTick执行,拿到的就是未更新之前的数据,比如第一次点添加内容,输出0
<template> |
nextTick的回调函数会被加入到微任务队列之后,在微任务队列中,DOM更新完之后才轮到它执行
其他补充
1. render函数
1.1 认识 h 函数
绝大多数情况下,我们的HTML都是用模板
<template>
创建的,如果在一些特殊的场景,真的需要JavaScript的完全编程能力,这个时候可以使用 渲染函数,它比模板更接近编译器Vue在生成真实的 DOM 之前,会将我们的节点转换成 VNode(虚拟节点),而VNode组合在一起形成一棵树结构,就是虚拟DOM(VDOM)
你想充分的利用JavaScript的编程能力,我们可以自己来编写 createVNode 函数,生成对应的 VNode
h()函数是一个用于创建 VNode 的函数
1.2 h 函数基本使用
- h() 函数接收三个参数,(标签名,组件名…)(属性)(子节点,内容)
- h 函数可以在两个地方使用,render 函数选项中或者 setup 函数选项中
// render 函数选项中 |
setup() { |
1.3 h 函数实现计数器案例
import { h } from 'vue'; |
2. jsx
在项目中使用 jsx 需要添加对 jsx 的支持
安装Babel支持Vue的jsx插件
npm install @vue/babel-plugin-jsx -D
在 babel.config.js 配置文件中配置插件
module.exports = {
plugins: [
"@vue/babel-plugin-jsx"
]
}
基本使用:计数器案例
export default {
data() {
return {
counter: 0
}
},
render() {
const increment = () => this.counter++;
const decrement = () => this.counter--;
return (
<div>
<h2>当前计数: {this.counter}</h2>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
</div>
)
}
}
3. 认识 Teleport
(先了解一下)
在组件化开发中,我们封装一个组件A,在另外一个组件B中使用,p 那么组件A中template的元素,会被挂载到组件B中template的某个位置,最终形成一棵 DOM 树结构
但是某些情况下,我们希望组件不是挂载在这个组件树上的,可能是移动到Vue app之外的其他位置,这个时候就可以通过teleport完成
4. 认识 Vue 插件
通常我们向Vue全局添加一些功能时,会采用插件的模式,它有两种编写方式
对象类型:一个对象,但是必须包含一个 install 的函数,该函数会在安装插件时执行
export default {
install(app) { //
app.config.globalProperties.$name = "hillyee"
}
}函数类型:一个function,这个函数会在安装插件时自动执行
// plugins_function.js
export default function(app) {
console.log(app);
}main.js
app.use(pluginsFunction)
app.use(pluginsObject)