vue3挑战-简单篇[1](适合入门)
前言
Vue挑战是一个非常适合Vue3入门的项目,里面的题目基本涵盖了Vue3的所有基础用法(特别是Vue3新引入的组合式写法),并且还在持续更新中,关于这个项目的具体介绍和原理,可以看一下这篇文章。并且在看这篇文章之前,你最好自己先去做一遍,这个文章里的写法只是我自己的方式(当然基础用法大家应该都大同小异),今天先分享一下我在简单篇的几道题的思路和写法。
你好,世界(虽然没什么必要)
对于这个挑战,您将需要更改以下代码,以使页面正确显示“Hello World”。
这个没有什么好说的,最最基础的vue用法,与vue2,angular等框架相同,使用双大括号来向模板中填入变量取值。与vue2不同的是,vue3对于响应式状态的声明多了一种组合式写法,可以直接引用API来将一个对象(实际上基本类型也可以,ref
API自行进行了处理)直接声明成响应式对象,只要记得在script
标签中加入一个setup
属性即可:
ref全家桶
在这个挑战中,你将使用
响应式 API: ref
来完成它。 以下是你要实现的内容 👇: 挑战 1: 更新 ref 挑战 2: 检查count
是否为一个 ref 对象 挑战 3: 如果参数是一个 ref,则返回内部值,否则返回参数本身, 确保以下输出为true 挑战 4: 为源响应式对象上的某个property
新创建一个ref
。 然后,ref
可以被传递,它会保持对其源property
的响应式连接。 确保以下输出为true
挑战1 更新ref
主要就是对ref
及其相关API的使用,通过ref
声明了一个响应式对象之后,在模板中取值时其实自动进行了解构,也就是你填入的是{{count}}
,而实际上模板取值取得是count.value
,而在js中进行数据的更新时,则需要取用.value
进行修改:
const count = ref(0)
function update(value) {
count.value = value
}
挑战2 挑战3 判断是否为ref对象
使用isRef
方法可以判断当前内容是否具有响应性:
import { isRef } from "vue"
console.log(
isRef(count) ? 1 : 0
)
function initialCount(value: number | Ref<number>) {
console.log(isRef(value) ? value.value: value)
}
initialCount(initial)
挑战4 从源响应式对象Reactive上创建新的ref对象
使用toRef
方法可以在从源响应式对象上创建一个新的ref
,而且值得注意的是,他们之前的响应性是可以被传递的,也就是说修改原来响应式对象的属性会改变ref
对象的值,修改ref
的值也会改变源响应对象属性的值。
const state = reactive({
foo: 1,
bar: 2
})
const fooRef = toRef(state, 'foo')
// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2
// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3
而如果我们直接使用ref来生成新的响应对象的话,源响应式对象的属性就不会跟着发生变化:
const fooRef = ref(state.foo)
fooRef.value++
console.log(state.foo) // 1
因为传入的state.foo
只是被当做一个数值而已。
ref全家桶问题的全部解法
响应性丟失
在
JavaScript
中,我们经常解构/扩展对象。 在Vue.js
中,我们同样解构/扩展“响应式”对象,但它会失去响应性。 如何保证解构/扩展不丢失响应性 ? 让我们开始吧 !
自从引入组合式 API 的概念以来,一个主要的未解决的问题就是ref
对象和响应式对象到底用哪个,而不选择响应式对象的原因主要是响应式对象存在解构丢失响应性的问题,而这个问题,ref
对象就不存在,因此出现了一个toRefs
的方法,将响应式对象转化为ref
对象以确保解构时响应性不丢失:
可写的计算属性
在这个挑战中,你需要创建一个可写的计算属性 : 确保
plusOne
可以被写入。 最终我们得到的结果应该是plusOne
等于 3 和count
等于 2。
计算属性使用computed
声明的时候,默认传入一个参数是计算属性的getter
,如果要同时设定setter
就需要传入一个对象了,在setter
的逻辑中,记得将被计算的值同步改变,以实现计算属性和原属性的双向变化:
watch全家桶
在这个挑战中,你将使用
响应式 API: watch
来完成它。 以下是你要实现的内容 👇: 挑战 1: Watch 一次 确保副作用函数只执行一次 挑战 2: Watch 对象 确保副作用函数被正确触发 挑战 3: 副作用函数刷新时机 确保正确访问到更新后的eleRef
值
挑战1 Watch一次
实际上,watch
在组件实例卸载后自动停止,一般来说不用手动停止,除非你通过异步回调创建了一个watch
:
<script setup>
import { watchEffect } from 'vue'
// 它会自动停止
watchEffect(() => {})
// ...这个则不会!
setTimeout(() => {
watchEffect(() => {})
}, 100)
</script>
以及当我们想要让watch
生效有限次的时候,都需要手动停止它。当一个watch
创建的时候,它的返回值就是它的停止方法,直接调用这个方法即可停止watch
的监听:
const unwatch = watch(count, () => {
console.log("Only triggered once")
unwatch()
})
挑战2 Watch对象
当watch
方法传入的参数是reactive
响应式对象时,watch
会自动创建一个深层监听,可以监听到响应式对象的每一个属性:
const obj = reactive({ count: 0 })
watch(obj, (newValue, oldValue) => {
// 在嵌套的属性变更时触发
// 注意:`newValue` 此处和 `oldValue` 是相等的
// 因为它们是同一个对象!
})
obj.count++
而如果你传入的是一个ref
对象的话,则不会如此操作了:
const state = ref({
count: 0,
})
watch(state, () => {
// 这段不会被触发,因为没有深层监听
console.log("The state.count updated")
})
state.value.count = 2
这时候有两个选择,一个是使用deep属性强制将其转化为深层监听:
watch(state, () => {
console.log("The state.count updated")
},{deep:true})
或者是将参数替换成一个getter函数(注意不能直接传参数state.count
,那样只会得到一个number
):
watch(() => state.count, () => {
console.log("The state.count updated")
},{deep:true})
两种方式其实更推荐后者,明显前者在属性数量增多的时候会出现一大堆不必要的开销。
挑战3 副作用函数的刷新时机
在watch
的实际使用过程中,被监听的对象往往都是和模板相关的,对象的变化会触发模板的Diff算法来更新视图,这时候有可能就需要在视图更新后在触发副作用函数,方式就是传入一个参数{flush: 'post'}
:
const eleRef = ref()
const age = ref(2)
watch(age, () => {
console.log(eleRef.value)
}, {flush: 'post'})
age.value = 18
watch全家桶问题的全部解法
转载自:https://juejin.cn/post/7147636801916633096