请问: v-once 到底是个啥玩意? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiangyuecn
V2EX    Vue.js

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

  •  
  •   xiangyuecn
    xiangyuecn 2019-12-03 02:27:45 +08:00 7014 次点击
    这是一个创建于 2206 天前的主题,其中的信息可能已经有所发展或是发生改变。

    刚学 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 特殊对待一下;或者说类似我这种类似静态方法的调用有别的手段? 静态方法,嗯,还是无参的静态方法(例子里面不要指望我会去额外存储一份时间,其他类似的结果会变化的方法调用估计也会产生此类问题)

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

    第 1 条附言    2019-12-03 10:58:04 +08:00

    写了一段demo,稳定复现这个问题: http://jsrun.pro/qcWKp/edit?v=2 点开运行

    反复修改不在v-for里面的 < template v-once> 和在v-for 里面的 < template v-once> 表现完全不一致。感觉应该可以实锤不是bug就是恶意的feature


    另外附:完全新手入门vue,遇到困难总是会有的,不过先入门还是要的,遇到麻烦直接就放弃就不太好了。不过,仅阅读https://cn.vuejs.org/v2/guide/ (阅读的纯文本,不含外链 和 video),完全搭建不出能运行的实用环境(不是学习测试,是实战,要写实际逻辑代码的),这个是事实,里面起步 明确写到 请注意我们不推荐新手直接使用 vue-cli , 我新学也不爱去用生成工具,手写配置对学习更有益。然后你会发现整篇guide 就没有写 .vue 文件该要怎么个规范去写,需要什么样的环境才能编译成js在浏览器运行。

    你以为有了vue-loader就够了,实际上还要vue-template-compiler,文档有没有告诉你?没有。仅有css-loader还不够,你还要style-loader,编译es6 额外要dependencie babel,有这些才能搭建起基本的编译环境。我还是通过问别人 和 另外运行vue-cli得到有效的配置分析后 才搞定环境配置,新手手写也许是哪里路子不对。

    这只是一个我目前学习中遇到的最大麻烦,其他小问题通篇找不到答案就更多了,实际遇到的时候才会发现并不是特别友好。

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


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

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

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


    还是需要多输出一个标签(细思极恐),难道不是 bug,是 feature ???
    TangMonk
        4
    TangMonk  
       2019-12-03 04:57:42 +08:00 via iPhone
    @xiangyuecn 刚起床?
    xiangyuecn
        5
    xiangyuecn  
    OP
       2019-12-03 04:59:55 +08:00   1
    @TangMonk 还没下班
    Terry05
        6
    Terry05  
       2019-12-03 09:48:05 +08:00
    vuejs 的 guide 已经写得这么好了,get 不到点的话放弃吧
    dk7952638
        7
    dk7952638  
       2019-12-03 10:04:50 +08:00
    感觉 vue 的 guide 都看不懂,确实不适合 code 了
    guolaopi
        8
    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
        9
    xiangyuecn  
    OP
       2019-12-03 11:03:10 +08:00
    @Terry05 @dk7952638 哈哈,发现两个老实人,你们说的也都是事实。

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

    请按问题描述,实际效果,预期效果分点写出来
    xiangyuecn
        12
    xiangyuecn  
    OP
       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
        13
    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
        14
    nxy006  
       2019-12-03 12:14:55 +08:00
    @xiangyuecn
    v-once 的语义是,创建成功后不会被重新渲染。
    V-for 外面你创建了一个元素,且被初始值渲染。
    在 V-for 里,你在不断创建新的内容,新内容里的值取自创建时候的值。

    你可能混淆了 “创建” 和 “创建后重新渲染” 的意思。
    nxy006
        15
    nxy006  
       2019-12-03 12:19:09 +08:00
    @xiangyuecn 因此,v-once 的表达是正常的,不管在 v-for 里面还是外面,一旦创建,它的值都不会再发生改变。至于你在创建时具有不同的值,与 v-once 无关。
    angel001ma
        16
    angel001ma  
       2019-12-03 12:28:01 +08:00
    @xiangyuecn 明白你的意思了,确实 vue 文档没对 template 加 v-once 出现的效果做解释,原因可能是#13,具体要看 template v-once 会怎么渲染
    x66
        17
    x66  
       2019-12-03 12:52:20 +08:00
    赞同,看完 guide 不知所措,然后还是靠重新去看视频,看 vue-cli 来实战,官方文档对初学者太不友好了。
    Sendya
        18
    Sendya  
       2019-12-03 13:03:20 +08:00 via Android
    后端猿看了两小时就开始写业务了,好像没什么问题。不过独立学习 webpack 时间比学 vue 时间更长 ( ̄へ ̄)
    azh7138m
        19
    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
        20
    xiangyuecn  
    OP
       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 里面就无效了。看下图的红色圈起来的部分,和上面绿色圈的部分,代码性质是一样的,但表现不一样

    xiangyuecn
        21
    xiangyuecn  
    OP
       2019-12-03 13:59:54 +08:00
    @nxy006 #14 #15 嗯嗯,感谢你的解答。但目测你应该没有理解我的问题,请看一下#20 楼这幅图,你就明白了,一个不在 v-for 里,一个在 v-for 里,代码基本相同,但表现不一样。
    nxy006
        22
    nxy006  
       2019-12-03 14:13:19 +08:00
    @azh7138m 确实,试了一下里面放变量也有一样的效果,是有点不对劲。从没写过 v-for 里套 template v-once 的代码,等晚上有时间了再查一下。=_=

    一般不会写 <template v-once> 这种结构,如果需要应该写成 <template><div v-once> 结构。虽然没研究过这种规范具体是为什么,结果来看还是有道理的。
    santom
        23
    santom  
       2019-12-03 14:38:05 +08:00
    <div><div><span>[6.738]</span> 日志
    [58.447] 日志

    |||| <span>[6.738]</span> 日志
    </div></div>


    这是渲染之后的实际 dom
    temeplate 标签作为无意义标签,我记得之前好像是为了布局优化去除很多无意义包裹层标签(其他层面不太了解),才允许在 temeplate 标签里可以不放置顶级标签的 。
    在此之前 loader 解析时会直接抛错。

    所以现在渲染结构中“[]”里面想渲染一次,此处添加的 v-once 应该是需要作用到实质标签上。
    具体 v-if 为何效果不同,不太了解了 可能和楼上说的 VNode 及渲染解析有关系吧
    rabbbit
        24
    rabbbit  
       2019-12-03 16:44:06 +08:00
    这应该是个 bug,不过还需要研究研究
    xiangyuecn
        25
    xiangyuecn  
    OP
       2019-12-05 10:15:40 +08:00
    @Curtion #10 不是#8021 这个问题,它是 component 组件中的 template 属性,我的是 template 标签,完全不一样。

    不过有个 vuejs 的 Contribution 同样拿这个问题来怼我,欢迎来观战,看我怎么用中式英语来怼死他,https://github.com/vuejs/vue/issues/10892
    crs0910
        26
    crs0910  
       2019-12-05 11:00:52 +08:00
    @xiangyuecn #25 那个人没有怼你啦。你也不要因此影响自己心情,感觉已经和他对立起来了。
    建议弄个最小的 demo 再提一次 issue 就好了,你那个 demo 搞的有点复杂了。
    xiangyuecn
        27
    xiangyuecn  
    OP
       2019-12-05 11:30:12 +08:00
    @crs0910 哈哈,谢谢你啦。可能是我在 #25 用“怼”这个词不当,我并没有针对那个协作者,只是觉得他们处理 issue 的有欠妥当。那个 demo 我是把发现的问题和没有问题的对比代码都写进去了,也没多少,应该不好精简了。
    gz911122
        28
    gz911122  
       2019-12-05 11:45:45 +08:00
    另外附:完全新手入门 vue,遇到困难总是会有的,不过先入门还是要的,遇到麻烦直接就放弃就不太好了。不过,仅阅读 https://cn.vuejs.org/v2/guide/ (阅读的纯文本,不含外链 和 video ),完全搭建不出能运行的实用环境(不是学习测试,是实战,要写实际逻辑代码的),这个是事实,里面起步 明确写到 请注意我们不推荐新手直接使用 vue-cli , 我新学也不爱去用生成工具,手写配置对学习更有益。然后你会发现整篇 guide 就没有写 .vue 文件该要怎么个规范去写,需要什么样的环境才能编译成 js 在浏览器运行。

    你以为有了 vue-loader 就够了,实际上还要 vue-template-compiler,文档有没有告诉你?没有。仅有 css-loader 还不够,你还要 style-loader,编译 es6 额外要 dependencie babel,有这些才能搭建起基本的编译环境。我还是通过问别人 和 另外运行 vue-cli 得到有效的配置分析后 才搞定环境配置,新手手写也许是哪里路子不对。


    这点深有同感
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1174 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 23:44 PVG 07:44 LAX 15:44 JFK 18:44
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86