V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiangyuecn
V2EX  ›  Vue.js

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

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

    刚学 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   ·   实用小工具   ·   1015 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 20:21 · PVG 04:21 · LAX 12:21 · JFK 15:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.