// setup 里面
// 不好的写法
return (
)
3. 功能的功能尽量封装成 hooks, 比如实现 v-model
## vue3 新特性的实际使用
### setup 参数
setup 函数接受两个参数,一个是 props, 一个是 context
#### props 参数
不能用 es6 解构,解构出来的数据会失去响应式,一般会使用 toRef 或者 toRefs 去处理
#### context 参数
该参数包含 attrs、slot、emit,如果里面需要使用到某个 emit,则要在 emits 配置中声明
import { definedComponent } from 'vue';
export default definedComponent({
emits: ['update:currentPage'],
setup(props, { emit, slot, attrs }) {
emit('update:currentPage');
...
}
})
### v-model 使用
pageSize 和 currentPage 两个属性都需要实现 v-model。
#### vue2 实现双向绑定的方式
[vue 2 实现自定义组件一到多个v-model双向数据绑定的方法](https://blog.csdn.net/Dobility/article/details/110147985)
#### vue3 实现双向绑定的方式
##### 实现单个数据的绑定
假如只需要实现 currentPage 双向绑定, vue3 默认绑定的是 modelValue 这个属性
// 父组件使用时候
// 相当于
{currentPage = val}" />
// Pagination 组件
import { defineComponent } from vue;
export default defineComponent({
props: {
currentPage: {
type: Number,
default: 1
}
}
emits: ['update:currentPage'],
setup(props, { emit }) {
当 Pagaintion 组件改变的时候,去触发 update:currentPage 就行
emit('update:currentPage', newCurrentPage)
}
})
##### 实现多个数据的绑定
pageSize 和 currentPage 两个属性都需要实现 v-model
// 父组件使用时候
// Pagination 组件
import { defineComponent } from vue;
export default defineComponent({
pageSize: {
type: Number,
default: 10,
},
currentPage: {
type: Number,
default: 1,
},
emits: ['update:currentPage', 'update:pageSize'],
setup(props, { emit }) {
当 Pagaintion 组件改变的时候,去触发相应的事件就行
emit('update:currentPage', newCurrentPage)
emit('update:pageSize', newPageSize)
}
})
#### Vue3 和 Vue2 v-model 比较
对于绑定单个属性来说感觉方便程度区别不大,而绑定多个值可以明显感觉 Vue3 的好处,不需要使用 sync 加 computed 里面去触发这种怪异的写法,直接用 v-model 统一,更加简便和易于维护。
### reactive ref toRef toRefs 的实际使用
#### 用 reactive 为对象添加响应式
实际组件中未使用,这里举例说明
import { reactive } from 'vue';
// setup 中
const data = reactive({
count: 0
})
console.log(data.count); // 0
#### 用 ref 给值类型数据添加响应式
jumper 文件中用来给用户输入的数据添加响应式
import {
defineComponent, ref,
} from 'vue';
export default defineComponent({
setup(props) {
const current = ref('');
return () => (
);
},
});
当然,其实用 ref 给对象添加响应式也是可以的,但是每次使用的时候都需要带上一个value, 比如,变成 data.value.count 这样使用, 可以 ref 返回的数据是一个带有 value 属性的对象,本质是数据的拷贝,已经和原始数据没有关系,改变 ref 返回的值,也不再影响原始数据
import { ref } from 'vue';
// setup 中
const data = ref({
count: 0
})
console.log(data.value.count); // 0
### toRef
1. toRef 用于为源响应式对象上的属性新建一个 **ref**,从而保持对其源对象属性的响应式连接。接收两个参数:源响应式对象和属性名,返回一个 ref 数据,本质上是值的引用,改变了原始值也会改变
2. 实际组件并未使用,下面是举例说明
import { toRef } from 'vue';
export default {
props: {
totalCount: {
type: number,
default: 0
}
},
setup(props) {
const myTotalCount = toRef(props, totalCount);
console.log(myTotalCount.value);
}
}
### toRefs
toRefs 用于将响应式对象转换为结果对象,其中结果对象的每个属性都是指向原始对象相应属性的 ref。常用于es6的解构赋值操作,因为在对一个响应式对象直接解构时解构后的数据将不再有响应式,而使用 toRefs 可以方便解决这一问题。本质上是值的引用,改变了原始值也会改变
// setup 中
const {
small, pageSizeOption, totalCount, simple, showSizeChanger, showQuickJumper, showTotal,
} = toRefs(props);
// 这样就可以把里面所有的 props '解构'出来
console.log(small.value)
由于 props 是不能用 es6 解构的,所以必须用 toRefs 处理
#### 四种给数据添加响应式的区别
##### 是否是原始值的引用
reactive、toRef、toRefs 处理返回的对象是原始对象的引用,响应式对象改变,原始对象也会改变,ref 则是原始对象的拷贝,和原始对象已经没有关系。
##### 如何取值
ref、toRef、toRefs 返回的响应式对象都需要加上 value, 而 reactive 是不需要的
##### 作用在什么数据上
reactive 为普通对象;ref 值类型数据;toRef 响应式对象,目的为取出某个属性; toRefs 解构响应式对象;
### 用 vue3 hooks 代替 mixins
如果想要复用是一个功能,vue2 可能会采用 mixins 的方法,mixins 有一个坏处,来源混乱,就是有多个 mixin 的时候,使用时不知道方法来源于哪一个 mixins。而 Vue3 hooks 可以很好解决这一个问题。我们把 v-model 写成一个 hook
const useModel = (
props,
emit,
cOnfig= {
prop: 'modelValue',
isEqual: false,
},
) => {
const usingProp = config?.prop ?? 'modelValue';
const currentValue = ref(props[usingProp]);
const updateCurrentValue = (value) => {
if (
value === currentValue.value
|| (config.isEqual && isEqual(value, currentValue.value))
) { return; }
currentValue.value = value;
};
watch(currentValue, () => {
emit(update:${usingProp}
, currentValue.value);
});
watch(
() => props[usingProp],
(val) => {
updateCurrentValue(val);
},
);
return [currentValue, updateCurrentValue];
};
// 使用的时候
import useModel from '.../xxx'
// setup 中
const [currentPage, updateCurrentPage] = useModel(props, emit, {
prop: 'currentPage',
});
可以看到,我们可以清晰的看到 currentPage, updateCurrentPage 的来源。复用起来很简单快捷,pager、simpler 等页面的 v-model 都可以用上 这个 hook
### computed、watch
感觉和 Vue2 中用法类似,不同点在于 Vue3 中使用的时候需要引入。
举例 watch 用法由于 currentPage 改变时候需要触发 change 事件,所以需要使用到 watch 功能
import { watch } from 'vue';
// setup 中
const [currentPage, updateCurrentPage] = useModel(props, emit, {
prop: 'currentPage',
});
watch(currentPage, () => {
emit('change', currentPage.value);
})
# 参考文档
1. [看懂「测试覆盖率报告」](https://github.com/JChehe/blog/issues/49)