请问: v-once 到底是个啥玩意?😂

2019-12-03 02:27:45 +08:00
 xiangyuecn

刚学 vue,感觉离入门到放弃不远了😂😂😂 太难了,看好几遍 cn.vuejs.org 中的那坨 guide,还是没明白用 Vue 一把梭到底能解决什么 G 点。。。

回到 v-once 这个指令,我就想在页面里面输出一行行带当前时间的 log,不知道是不是缺陷还是我写的有毛病:

//循环的 obj 里面没有额外存时间,也拒绝存时间( idx 是倒序索引,妥协加上的)
<div v-for="obj in logs" :key="obj.idx">
    <template v-once>[{{ getTime() }}]</template> //无效,每个周期都会进行一次计算导致节点发生变更,
                                                  //导致所有已显示的日志都会被重新计算成当前时间
    <span v-once>[{{ getTime() }}]</span> //有效,但需要多输出一个标签(细思极恐)

我们会发现 template + v-once 并不会起反应,v-once 的文档里也并没有写和 template 结合有什么效果。


但是,没有对比就没有伤害,看 v-if 文档:

> 如果元素是 <template> ,将提出它的内容作为条件块。

# 在 <template> 元素上使用 v-if 条件渲染分组

因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。

这算不算在赤裸裸的歧视 v-if 是一个另类😂 细思这也许是个什么缺陷,或者这就是别人写的代码该有的样子。


那么问题来了,v-once 这个目测也算是非常重要的指令,为什么就不能学学 v-if 特殊对待一下;或者说类似我这种类似静态方法的调用有别的手段? 静态方法,嗯,还是无参的静态方法(例子里面不要指望我会去额外存储一份时间,其他类似的结果会变化的方法调用估计也会产生此类问题)😂

太难学了,不容易入门,再不学好就要被扫地出门了😂

6558 次点击
所在节点    Vue.js
28 条回复
rabbbit
2019-12-03 03:05:28 +08:00
xiangyuecn
2019-12-03 04:42:20 +08:00
@rabbbit 原来是要这样写才没问题😂😂,不过嵌入到 v-for 里面立马暴露出问题,测试代码: http://jsrun.pro/qcWKp/edit?v=2


运行效果蔚为壮观,看样子不是我的写法有问题就是 vue 有 bug😂

xiangyuecn
2019-12-03 04:57:36 +08:00
测试发现,外面再完全单独的套一层标签就能生效
<span><template v-once>[{{ getTime() }}]</template></span>

还不如回到原点
<span v-once>[{{ getTime() }}]</span>


还是需要多输出一个标签(细思极恐),难道不是 bug,是 feature ???😂
TangMonk
2019-12-03 04:57:42 +08:00
@xiangyuecn 刚起床?
xiangyuecn
2019-12-03 04:59:55 +08:00
@TangMonk 还没下班😊
Terry05
2019-12-03 09:48:05 +08:00
vuejs 的 guide 已经写得这么好了,get 不到点的话放弃吧
dk7952638
2019-12-03 10:04:50 +08:00
感觉 vue 的 guide 都看不懂,确实不适合 code 了
guolaopi
2019-12-03 10:09:56 +08:00
f12 看了下,
<div>
<template>{{msg}}</template>
</div>

被直接渲染成了
<div>
{{msg}}
</div>

所以我觉得 v-once 应该写在 div 上。
<div v-once>
<span v-once>{{ getTime() }}</span> {{ obj.msg }}
<template >{{ getTime() }}</template> {{ obj.msg }}
</div>
这样就实现了你想要的效果。

我也是正在学 vue,关于 template 不是很懂,我理解他并不是一个标签,而是把一些内容包括起来,然后直接渲染。

好像 w3c 有 template 相关标准啥的,LZ 可以深入了解一下
xiangyuecn
2019-12-03 11:03:10 +08:00
@Terry05 @dk7952638 哈哈,发现两个老实人,你们说的也都是事实。

不过,两位大佬能不能看一下 我写的这个这个问题的复现代码,帮我解解惑,为啥会产生这种现象,code url: http://jsrun.pro/qcWKp/edit?v=2
Curtion
2019-12-03 11:36:04 +08:00
是这个问题吗? https://github.com/vuejs/vue/issues/8021,看来这个问题要从 VNode 中找答案,可惜我的水平不够
angel001ma
2019-12-03 11:53:11 +08:00
是有什么问题,我看代码和效果是一致的,v-once 是只渲染一次,之后绑定值变动不会重新渲染

请按问题描述,实际效果,预期效果分点写出来
xiangyuecn
2019-12-03 12:08:58 +08:00
@angel001ma 你用的啥浏览器运行的是一致的?

不在 v-for 里面的 <template v-once>{{ getTime() }}</template> 是不会随着周期发生任何变化
但 v-for 里面的 <template v-once>{{ getTime() }}</template> 会变化,不符合 v-once 语义

通过使用标签 或 额外套一层标签 可修复;但是要在代码里额外去多写一个标签,比如多套一层 span,问题就在这里

marcong95
2019-12-03 12:10:24 +08:00
v-once 这个东西,好像还真没怎么用过的样子。API 里面对 v-once 的描述如下:

Render the element and component once only.

你在<template>里面用了一个 v-once,但是<template>这个东西并不是一个确定的元素或者组件,可能没有一个 VNode 与之对应,v-once 作为一个 directive 也挂不上去,所以无法实现 render only once 这个功能。但是当你在外面套一个元素之后,就可以用外面这个元素的 VNode 对应当作上文的“the element and component”了。

审查元素可以看到你写的

<template v-once>[{{ getTime() }}]</template> {{ obj.msg }}

template 里面渲染的内容跟后面的 obj.msg 构成了同一个 TextNode,所以这个 TextNode 的由于有 obj.msg 的部分,所以其实不应该只渲染一次。所以 v-once 可能就被忽略了。这个可能需要研究一下<template>的行为了,但是 vue 的文档似乎没说。而且也不知道应不应该用 W3C 的东西套上去。

-----------------------------------------------

我当初学 vue 其实感受跟你的 append 其实很一致,guide 啃完一遍了,想从头开始写个 demo 看看,连 JS 的入口都不知道怎么写,找了各种脚手架看到自动生成的代码,才发现了有

new Vue({ el: '#app', render: h => h(App) })

这种写法才豁然开朗。Official Guide 对 Vue 本身的描写是足够了,但是对如何构成一个完整的应用还是有一定的欠缺,可能这不是 Vue.js Official Guide 应该描写的部分,但是适当引导一下可能会比较好。
nxy006
2019-12-03 12:14:55 +08:00
@xiangyuecn
v-once 的语义是,创建成功后不会被重新渲染。
V-for 外面你创建了一个元素,且被初始值渲染。
在 V-for 里,你在不断创建新的内容,新内容里的值取自创建时候的值。

你可能混淆了 “创建” 和 “创建后重新渲染” 的意思。
nxy006
2019-12-03 12:19:09 +08:00
@xiangyuecn 因此,v-once 的表达是正常的,不管在 v-for 里面还是外面,一旦创建,它的值都不会再发生改变。至于你在创建时具有不同的值,与 v-once 无关。
angel001ma
2019-12-03 12:28:01 +08:00
@xiangyuecn 明白你的意思了,确实 vue 文档没对 template 加 v-once 出现的效果做解释,原因可能是#13,具体要看 template v-once 会怎么渲染
x66
2019-12-03 12:52:20 +08:00
赞同,看完 guide 不知所措,然后还是靠重新去看视频,看 vue-cli 来实战,官方文档对初学者太不友好了。
Sendya
2019-12-03 13:03:20 +08:00
后端猿看了两小时就开始写业务了,好像没什么问题。不过独立学习 webpack 时间比学 vue 时间更长 ( ̄へ ̄)
azh7138m
2019-12-03 13:16:25 +08:00
@nxy006
如果不是 bug,那为啥
<span v-once>[{{ getTime() }}]</span> {{ obj.msg }}
<template v-once>[{{ getTime() }}]</template> {{ obj.msg }}
这两种写法会有差异?
xiangyuecn
2019-12-03 13:55:48 +08:00
@marcong95 #13 @guolaopi #8 还以为你们可以看到 vue 生成的虚拟节点结构,研究了半天 vue-devtools,原来并看不到,只能靠 dom 结构来猜测了,哈哈😁


#8 v-once 不能直接写到上级点上,按需使用,哪里需要哪里写,不然子节点全部失去了绑定

#13 虽然“Render the element and component once only.” ,但没有用 v-for 包裹的有效,放到 v-for 里面就无效了。看下图的红色圈起来的部分,和上面绿色圈的部分,代码性质是一样的,但表现不一样

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/625317

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX