他api
前面我们已经学习了 vue3 的一些基础知识,本篇将继续讲解一些常用的其他api
,以及较完整的分析vue2 和 vue3 的改变。
浅层响应式数据
shallowref
shallow 中文:“浅层的”
shallowref:浅的 ref()。
先用 ref 写个例子:
# 组件a
a: {{ a }}
o: {{ o }}
这4个按钮都会触发页面数据的变化。
现在将 ref 改成 shallowref
,其他都不变。你会发现只有 change1 和 change4 能触发页面数据的变化:
// 不变
这是因为 change1 中的 a.value
是浅层,而 change2 中的 o.value.name
是深层。
对于大型数据结构,如果只关心整体是否被替换,就可以使用 shallowref,避免使用 ref 将大型数据结构所有层级都转成响应式,这对底层是很大的开销。
shallowreactive
知晓了 shallowref,shallowreactive也类似。
shallowreactive:浅的 reactive()。
请看示例:
现在3个按钮都能修改页面数据:
# 组件a
o: {{ o }}
将 reactive 改为 shallowreactive:
import {shallowreactive} from 'vue'
let o = shallowreactive({
name: 'p',
options: {
age: 18,
}
})
现在只有 change2 和 change4 能修改页面数据,因为 change3 是多层的,所以失效。
只读数据
readonly
readonly : takes an object (reactive or plain) or a ref and returns a readonly proxy to the original.
readonly 能传入响应式数据,并返回一个只读代理
请看示例:
# 组件a
name: {{ name }}
copyname: {{ copyname }}
浏览器呈现:
# 组件a
name: p2
// 按钮1
change name
copyname: p2
// 按钮2
change copyname
点击第一个按钮,发现 copyname 的值也跟着变化了(说明不是一锤子买卖),但是点击第二个按钮,页面数据不会变化。浏览器控制台也会警告:
[vue warn] set operation on key "value" failed: target is readonly. refimpl {__v_isshallow: false, dep: map(1), __v_isref: true, _rawvalue: 'p2', _value: 'p2'}
readonly 只读代理是深的:任何嵌套的属性访问也将是只读的。对比 shallowreadonly 就知道了。
tip:使用场景,比如同事a定义了一个很重要的数据,同事b需要读取该数据,但又担心误操作修改了该数据,就可以通过 readonly 包含数据。
shallowreadonly
readonly 只读代理是深层的,而 shallowreadonly 是浅层的。也就是深层的 shallowreadonly 数据不是只读的。
请看示例:
# 组件a
obj: {{ obj }}
通过 shallowreadonly 创建一个备份数据,点击第一个按钮没反应,点击第二个按钮,页面变成:
# 组件a
obj: { "name": "p", "options": { "age": 19 } }
shallowreadonly 只处理浅层次的只读。深层次的不管,也就是可以修改。
疑惑
:笔者的开发者工具中, copyobj -> options 中的 age 属性没有表示能修改的铅笔图标。应该要有,这样就能保持和代码一致
原始数据
toraw
toraw() can return the original object from proxies created by reactive(), readonly(), shallowreactive() or shallowreadonly().
用于获取一个响应式对象的原始对象。修改原始对象,不会在触发视图。
const foo = {}
const reactivefoo = reactive(foo)
console.log(toraw(reactivefoo) === foo) // true
比如这个使用场景:
# 组件a
obj: {{ obj }}
markraw
marks an object so that it will never be converted to a proxy. returns the object itself.
标记一个对象
,使其永远不会被转换为proxy。返回对象本身。
- 有些值不应该是响应式的,例如一个复杂的第三方类实例,或者一个vue组件对象。
import {reactive} from 'vue'
let o = {
getage() {
console.log(18)
}
}
// proxy(object) {getage: ƒ}
let o2 = reactive(o)
- 当使用不可变数据源呈现大型列表时,跳过代理转换可以提高性能。
请问输出什么:
import {reactive} from 'vue'
let o = {
name: 'p',
age: 18,
}
let o2 = reactive(o)
console.log(o);
console.log(o2);
答案是:
{name: 'p', age: 18}
proxy(object) {name: 'p', age: 18}
通过 reactive 会将数据转为响应式。
请看 markraw 示例:
import {reactive, markraw} from 'vue'
// 标记 o 不能被转成响应式
let o = markraw({
getage() {
console.log(18)
}
})
let o2 = reactive(o)
// {__v_skip: true, getage: ƒ}
console.log(o2);
比如中国的城市,数据是固定不变的,我不做成响应式的,别人也不许做成响应式的。我可以这么写:
// 中国就这些地方,不会变。我自己不做成响应式的,别人也不许做成响应式的
let citys = markrow([
{name: '北京'},
{name: '上海'},
{name: '深圳'},
...
])
customref
自定义 ref 可用于解决内置 ref 不能解决的问题。
ref 用于创建响应式数据,数据一变,视图也会立刻更新。比如要1秒后更新视图,这个 ref 办不到。
先用ref写个例子:input 输入字符,msg 立刻更新:
# 组件a
msg: {{ msg }}
现在要求:input输入字符后,等待1秒msg才更新。
我们可以用 customref
解决这个问题。
实现如下:
# 组件a
msg: {{ msg }}
customref() 接收一个工厂函数作为参数,这个工厂函数接受 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set 方法的对象。
track()
和 trigger()
缺一不可,需配合使用:
- 缺少 track,即使通知vue 更新了数据,但不会更新视图
- 缺少 trigger,track 则一直在等着数据变,快变,我要更新视图。但最终没人通知它数据变了
实际工作会将上述功能封装成一个 hooks
。使用起来非常方便。就像这样:
// hooks/usemsg.ts
import { customref, } from 'vue'
export function usemsg(value: string, delay = 1000) {
// customref 传入函数,里面又两个参数
let msg = customref((track, trigger) => {
// 防抖
let timeout: number
return {
get() {
// 告诉 vue 这个数据很重要,要持续关注,数据一旦变化,更新视图
track()
return value
},
set(newvalue) {
cleartimeout(timeout)
timeout = settimeout(() => {
value = newvalue
// 告诉vue我更新数据了,你更新视图去吧
trigger()
}, delay)
}
}
})
return msg
}
使用起来和 ref 一样方便。就像这样:
# 组件a
msg: {{ msg }}
teleport
teleport 中文“传送”
teleport 将其插槽内容渲染到 dom 中的另一个位置。
比如 box 内的内容现在在 box 元素中:
# 组件a
我是组件a内的弹框
我可以利用 teleport 新增组件将其移到body下面。
# 组件a
{{ msg }}
现在这段ui内容就移到了 body 下,并且数据链还是之前的,也就是 msg 仍受 button 控制。
tip:to 必填,语法是选择器或实际元素
suspense
d88尊龙官网手机app官网说是一个实验性功能。用来在组件树中协调对异步依赖的处理。
我们首先在子组件中异步请求,请看示例:
# 父亲
# 组件a
tip:我们现在用了 setup 语法糖,没有机会写 async,之所以能这么写,是因为底层帮我们做了。
浏览器查看,发现子组件没有渲染出来。控制台输出:
// main.ts:14 [vue 警告]: 组件 : setup 函数返回了一个 promise,但在父组件树中未找到 边界。带有异步 setup() 的组件必须嵌套在 中才能被渲染。
main.ts:14 [vue warn]: component : setup function returned a promise, but no boundary was found in the parent component tree. a component with async setup() must be nested in a in order to be rendered.
data: {code: 1, data: {…}}
vue 告诉我们需要使用 suspense。
假如我们将 await 用 async 方法包裹,子组件能正常显示。
# 组件a
data: {{ data }}
继续讨论异步的 setup()
的kb88凯时官网登录的解决方案。在父组件中使用 suspense 组件即可。请看代码:
# 父亲
// 组件有两个插槽:#default 和 #fallback。两个插槽都只允许一个直接子节点。
loading...
子组件也稍微调整下:
# 组件a
data: {{ data }}
利用开发者工具将网速跳到 3g,再次刷新页面,发现先显示loading...
,然后在显示
# 组件a
data: { "code": 1, "data": { "name": "阿普的思念", "url": "http://music.163.com/song/media/outer/url?id=2096764279", "picurl": "http://p1.music.126.net/js1io7cwfee6g6ynpyv5fq==/109951169021986117.jpg", "artistsname": "诺米么lodmemo" } }
注
:数据是一次性出来的,不是先展示 {}
在展示 {...}
。所以我们再看d88尊龙官网手机app官网,就能理解下面这段内容:
└─
├─
│ └─ (组件有异步的 setup())
└─
├─ (异步组件)
└─ (异步组件)
在这个组件树中有多个嵌套组件,要渲染出它们,首先得解析一些异步资源。如果没有
,则它们每个都需要处理自己的加载、报错和完成状态。在最坏的情况下,我们可能会在页面上看到三个旋转的加载态,在不同的时间显示出内容。
有了
组件后,我们就可以在等待整个多层级组件树中的各个异步依赖获取结果时,在顶层展示出加载中或加载失败的状态。
tip: 在 react 中可以使用 suspense 组件和 react.lazy() 函数来实现组件的延迟加载。就像这样:
import react, {suspense} from 'react'
// 有当 othercomponent 被渲染时,才会动态加载 ‘./math’ 组件
const othercomponent = react.lazy(() => import('./math'))
function testcompoment(){
return
loading }>