vue3-Composition-API实操
- 没有
this
了,我要怎么获取组件实例? - 没有
this
了,怎么派发自定义事件? - 我该如何在
reactive
和ref
之间做选择? - setup函数太长了怎么办?
- 我的属性怎么就不响应了
-
watchEffect
和watch
有啥不同? - 生命周期钩子能不能写多个?顺序是怎样的?
我要怎么获取组件实例?
我们都知道composition api是可以和options api一起使用、友好相处的,比如下面的示例:
const { createApp } = Vue
createApp({
data() {
return {
foo: 'foo'
}
},
setup() {
// 没有this,我该如何获取data中的foo和methods中的bar哪?
return { }
},
methods: {
bar() {
console.log('我是bar方法');
}
},
}).mount('#app')
但是setup里面this指向window,composition的文档中也没有提到怎么获取组件实例呀,这着实难住了不少小伙伴,方法自然是有的:咱们可以通过getCurrentInstance()这个接口获取组件实例:
setup() {
// getCurrentInstance()可以获取组件实例
const instance = getCurrentInstance()
console.log(instance);
onMounted(()=>{
// 组件实例的上下文才是我们熟悉的this
console.log(instance.ctx.foo); // 'foo'
console.log(instance.ctx.bar()); // '我是bar方法'
})
return {}
},
但是很快我们又蒙圈了,这个组件实例和我们以前熟悉的this
不一样,直接访问this.foo
还是找不到数据。
vue3中组件实例结构如下,各个选项中的this
实际上是ctx
或proxy
当然坑还是有的,你仔细观察这个ctx,发现它不是一个Proxy对象,也就是这位兄台只有值却没有响应性,所以如果要利用响应特性,还得用proxy这个属性返回上下文对象,如果只是想要数据,上图中不是有个data也是Proxy类型的嘛。
setup() {
const { proxy, data } = getCurrentInstance()
// 想要利用响应能力,就要使用proxy这个上下文
watchEffect(() => {
console.log(proxy.foo) // foo变化会有输出
console.log(data.foo) // foo变化会有输出
})
},
最后大家还要注意,setup()执行的时间点是很早的,甚至早于created,因此foo和bar的访问如果没有特意放到onMounted里面还真没有。
setup() {
const instance = getCurrentInstance()
console.log(instance.ctx.foo); // undefined
console.log(instance.ctx.bar()); // undefined
},
怎么派发自定义事件? 突然之间没有this了之后,好像突然生活不能自理了,再也不能用this.$emit()派发事件了。
其实通过组件实例是可以派发事件的,比如:
setup() {
getCurrentInstance().emit('ooxx')
}
但是这样比较麻烦,所以我们要用到setup函数的第二个参数:
setup(props, ctx) {
ctx.emit('ooxx')
}
当然,还能把emit直接结构出来使用更方便:
setup(props, { emit }) {
emit('ooxx')
}
我该如何在reactive和ref之间做选择?
composition-api引入了独立的数据响应式方法reactive,它可以将传入的对象做响应式处理:
const state = reactive({
foo: 'foo',
})
watchEffect(() => {
console.log(state.foo)
})
state.foo = 'foooooo' // 输出 'foooooo'
这个方式类似于我们设置的data选项,能够解决我们大部分需求。但是也有以下问题:
- 当我们直接导出这个state时,我们在模板中就要带上state这个前缀
setup() {
const state = reactive({})
return { state }
}
<div>{{ state.foo }}</div>
为了解决这个问题又要引入toRefs
setup() {
const state = reactive({})
return { ...toRefs(state) }
}
<div>{{ foo }}</div>
小伙伴们又懵了,toRefs是个啥?为啥不直接展开?
- 单个值时用reactive()显得比较多余
于是就有了Ref
的概念,通过包装单值为Ref
对象,这样就可以对其做响应式代理
setup() {
const foo = ref('foo')
return { foo }
}
模板中使用还可以省掉前缀,toRefs就是利用这一点将reactive()返回代理对象的每个key对应的值都转换为Ref
<div>{{ foo }}</div>
但是Ref对象也有副作用:
- 在JS中修改这个值要额外加上value
setup() {
const foo = ref('foo')
setTimeout(() => {
// 额外的value让人恼火
foo.value++
}, 1000)
return { foo }
}
- 额外增加的心智负担:一个值到底是不是Ref,我要不要加.value?
setup(props) {
const foo = props.foo
// foo是Proxy还是Ref?
// 编写`watch`方法的时候写法完全不同
// Ref可以被直接watch
watch(foo, () => {})
// Proxy则需要写成函数形式
watch(() => foo.bar, () => {})
}
对比之后发现都有一些问题,我们来讨论一下两者选择问题:
- 如果是单值,建议ref,哪怕是个单值的对象也可以
const counterRef = ref(1)
const usersRef = ref(['tom', 'jerry'])
一个业务关注点有多个值,建议reactive
const mouse = reactive({
x: 0,
y: 0
})
- 降低Ref心智负担的方法:利用unref、isRef、isProxy等工具方法,利用一些命名约定。
setup(props) {
const foo = unref(props.foo) // foo是我们要的值
// 等效于
const foo = isRef(props.foo) ? props.foo.value : props.foo
}
setup函数太长了怎么办?
虽然很好的将关注点集中起来,就像下面这样:
但是难免还是太长了(怎么太长还成了困扰?),此时就可以开始函数拆分
setup(){
let { val, todos, addTodo } = useTodo()
let {count,double,add} = useCounter()
let {x, double:doubleX} = useMouse()
return {
val, todos, addTodo,
count,double,add,
x,doubleX
}
}
return的上下文太长了,我们可以使用vue3的setup script功能,把setup这个配置也优化掉,一个功能export一次
<script setup>
import useCounter from './counter'
import useMouse from './mouse'
import useTodo from './todos'
let { val, todos, addTodo } = useTodo()
export {val, todos, addTodo}
let {count,double,add} = useCounter()
export {count,double,add}
let {x, double:doubleX} = useMouse()
export {x,doubleX}
</script>
我的属性怎么就不响应了 下面的代码是小伙伴们可能会写出来的:
setup({ foo, bar }) {
watchEffect(() => {
console.log(foo, bar) // foo,bar发生变化,也不会有输出
})
}
props是一个Proxy对象,直接结构就失去了响应能力,所以对待props要温柔,不能动不动就劈开了
setup(props) {
watchEffect(() => {
console.log(props.foo, props.bar) // foo,bar发生变化,会有输出
})
}
真想劈开也行,看你喜欢什么姿势了
setup(props) {
const { foo, bar } = toRefs(props)
watchEffect(() => {
console.log(foo, bar) // foo,bar发生变化,会有输出
})
}
watchEffect和watch有啥不同?
这俩方法很相似,都是观察响应式数据,变化执行副作用函数,但有如下不同:
watch需要明确指定监视目标,
watch(() => state.counter, (val, prevVal) => {})
watchEffect不需要
watchEffect(() => {
console.log(state.counter)
})
- watch可以获取变化前后的值
- watch是懒执行的,它等效于vue2中的this.$watch(),watchEffect为了收集依赖,要立即执行一次
现在知道怎么选择它们了吧?
生命周期钩子能不能写多个? 当然可以写多个,最后它们会按注册顺序依次执行:
setup() {
onMounted(() => {})
onMounted(() => {})
onMounted(() => {})
}
甚至还能拆分出多个相同生命周期钩子到独立函数中呢,相当帅气
function fun1() {
// 这里可以用onMounted执行代码
onMounted(() => {})
}
function fun2() {
// 这里还可以用onMounted执行代码
onMounted(() => {})
}
setup() {
fun1()
fun2()
onMounted(() => {})
}
抄袭自掘金羊村杨老师
相关文章
- vue3+ts 使用高德地图JSAPI 加载API并初始化地图
- api接口对接_接口API
- Vue3开发实战02-vue项目添加状态管理Vuex,路由router,以及http请求a
- 微前端qiankun接入Vite子应用含Vue3和React18
- 应该从vue2 升级到vue3吗,到底该怎么升级呢?
- 个人号微信api接口开发_API接口授权系统php源码
- vue3+vite+axios中proxy跨域配置
- Vue3 响应式 API 之 ref 和 reactive
- (10月最新) 前端图形学实战: 从零开发几何画板(vue3 + vite版)
- 前端图形学实战: 从零实现编辑器的图层管理面板和实时缩略图(vue3 + vite版)
- 软件测试|测试平台vue3 模版语法
- Vue3学习笔记 Composition API和script setup 新搭档的好处是?
- 【Vue3】利用vite创建vue3项目
- 开心档之Vue3
- vue3报错 Vue received a Component which was made a reactive object.
- 异步API的精彩之处:Redis实现快速响应(redis异步api)
- API控制MySQL数据库快速便捷(api控制mysql)
- API技术连接MySQL数据库的最佳方法(api如何连接mysql)
- MySQL的API关闭全面调控(api关闭mysql)
- API实现Mysql数据写入的完美解决方案(api 写入mysql)
- API精准查询Oracle,轻松解决问题(Api查询oracle)
- Vue3连接Redis挑战迎刃而解(vue3访问redis)
- 如何使用MySQL下载API数据(mysql下载api数据)
- 研究Oracle API接口,助推业务发展(oracle api接口)