在 Vue2 里如何实现“一边计算,一边更新页面元素”这样的功能 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
abcbuzhiming
V2EX    Javascript

在 Vue2 里如何实现“一边计算,一边更新页面元素”这样的功能

  •  
  •   abcbuzhiming 2017-04-07 10:38:56 +08:00 7704 次点击
    这是一个创建于 3188 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我估计应该是我想的方向不太对,放狗了好久也没找到答案。实际效果并不复杂,页面上有一个按钮和一个简单的数字 v-text ,点击按钮,按钮绑定的方法会把一个变量累加 n 次,要求每累加一次,页面就更新一次并显示出来,然而实际操作发现不行,按钮绑定的 method 会把 n 一次累加完,然后页面才会刷新显示出来,因此我看不到累加过程,只能看到最终结果,最开始发现 Vue2 有一个$forceUpdate()方法能强制刷新,但是实际使用毫无效果
    35 条回复    2017-04-07 21:11:45 +08:00
    loy6491
        1
    loy6491  
       2017-04-07 10:46:20 +08:00
    既然想看到过程,就加上 setTimeout 啊
    445141126
        2
    445141126  
       2017-04-07 10:54:32 +08:00
    abcbuzhiming
        3
    abcbuzhiming  
    OP
       2017-04-07 11:22:30 +08:00
    @loy6491 你是对的,也就是说如果我想看到过程,就必须设置成异步任务,让 Vue 的处理 method 函数结束才行
    abcbuzhiming
        4
    abcbuzhiming  
    OP
       2017-04-07 11:23:09 +08:00
    @445141126 谢谢,但是这个不是我要的,我并不是需要一个过渡效果,而是需要计算的过程
    wly19960911
        5
    wly19960911  
       2017-04-07 11:29:25 +08:00
    那为什么要交给 VUE 去做呢, vue 的计算属性是类似于表达式公式的,用自己的方法异步完成不好么,构造自动机自动执行输出结果如何?

    我认为这种东西不要依赖框架。
    ferrum
        6
    ferrum  
       2017-04-07 11:33:29 +08:00
    这不就是双向数据绑定吗,为什么直接更改会无效?楼主可以把示例发出来看看。
    coolzjy
        7
    coolzjy  
       2017-04-07 11:39:17 +08:00 via iPhone
    Batch Update 机制,可通过异步操作解决
    fszaer
        8
    fszaer  
       2017-04-07 11:44:49 +08:00
    @ferrum
    大概是这个意思吧
    如果在方法中
    const setNumber=()=>{
    data.s=1;
    data.s=2;
    data.s=3;
    }
    方法执行后绑定的 s 属性显示的是 3 ,而 po 希望 显示出 1,2,3 这样?
    abcbuzhiming
        9
    abcbuzhiming  
    OP
       2017-04-07 11:47:55 +08:00
    @wly19960911 我估计可能是需要异步,我以前以为可以在 Vue 的 method 定义的函数中同步实现。
    abcbuzhiming
        10
    abcbuzhiming  
    OP
       2017-04-07 11:50:35 +08:00
    @ferrum 代码如下,我估计我个人还是没理解 Vue 的思路造成的,或者 Vue 并不允许在定义的函数结束前进行 re-render

    <div id="group-send-sms">

    <span v-text="bianliang"></span>
    <input v-on:click="test" type="button" value="测试" />



    </div>

    <script type="text/Javascript">
    var vm = new Vue({
    el: "#group-send-sms",
    data: {

    bianliang:0,
    },
    computed: {

    },
    methods: {
    add:function(){
    this.bianliang += 1;
    },
    //点击按钮,循环 100 次,每次都渲染到页面上去
    test: function () {
    for(var i=0;i<100;i++){
    this.bianliang += 1;
    this.$forceUpdate(); //这个强制渲染是无效的,并不能让循环中 bianliang 的改变立即出现在页面上
    }

    },
    },

    });

    </script>
    abcbuzhiming
        11
    abcbuzhiming  
    OP
       2017-04-07 11:51:25 +08:00
    @coolzjy 其实我现在的问题是,为啥这个地方就必须是异步的呢?
    abcbuzhiming
        12
    abcbuzhiming  
    OP
       2017-04-07 11:53:10 +08:00
    @fszaer 你说的对,这就是我要的效果,实际的需求比这复杂的多,我只是抽象出了最简单的模型,总之就是,每次运算后立即刷新结果到页面上去,这个过程是链式过程,计算->刷新->计算->刷新->计算->刷新->......同步阻塞
    wly19960911
        13
    wly19960911  
       2017-04-07 12:07:43 +08:00
    你这个代码,没有时间怎么看得出来,这种一瞬间计算的东西就算是浏览器处理也是一瞬间的事情,你需要用动画的方式去处理,用 generator 或者 setTimeout (我感觉 setTimeout 太麻烦了)

    <div id="test"></div>
    <script>
    function test() {
    var node = document.getElementById('test');
    for(let a = 0 ; a < 100 ; a ++) {
    node.innerHTML = a;
    }
    }
    </script>

    你试试这段代码就知道,这种计算就是瞬间完成的。所以你需要使用异步去展示这段效果。
    ferrum
        14
    ferrum  
       2017-04-07 12:13:46 +08:00
    @abcbuzhiming 这个变量命名满分……

    我写了个 jsfiddle ,实际上$forceUpdate 是有效的,只是变量一下子从 0 到 100 ,中间没有间隔,看不出来变化而已。

    具体你可以看看[jsfiddle]( https://jsfiddle.net/6yj33wns/)
    maplerecall
        15
    maplerecall  
       2017-04-07 12:20:19 +08:00 via Android
    这难道不该用$nextTick 么, lz 的目的就是计算一次显示一次吧, async 写法把这个加入循环就好了
    coolzjy
        16
    coolzjy  
       2017-04-07 12:38:11 +08:00 via iPhone
    @abcbuzhiming 在极短时间内多次修改一个变量大多数时候是「与界面无关」的,如果这时候频繁操作 DOM 则会降低渲染效率。
    另一方面即使没有 Batch Update 机制,你这样在一个事件循环没多次操作 DOM 的做法也会被浏览器优化为一次渲染,而不会全部把过程显示出来。
    maplerecall
        17
    maplerecall  
       2017-04-07 12:41:17 +08:00 via Android
    @abcbuzhiming 你这个想法大体是没有问题的,但有两点,第一点非异步代码在执行完之前是不会刷新 dom 的,第二点电脑计算太快了,大多数显示器的刷新频率只有 60fps ,也就是每帧 16 毫秒, 16 毫秒内所做的任何改变你都是看不到的,会在下一帧才输出,可以去了解一下 requestAnimationFrame
    reus
        18
    reus  
       2017-04-07 12:52:25 +08:00
    不用 setTimeout 还想用什么……

    // 禁止点击按钮的代码放这里
    let update = (n) => {
    if (n == 0) {
    // 允许点击按钮的代码放这里
    return;
    }
    // 更新视图的代码放这里
    setTimeout(() => update(n - 1), 200);
    }

    根本就没 vue 什么事,它只负责显示。要什么效果,是你自己实现。不要以为用了框架,就什么都要框架帮你做。
    abcbuzhiming
        19
    abcbuzhiming  
    OP
       2017-04-07 13:09:47 +08:00
    @ferrum 变量名是我随便弄上去的,本身就是测试用的不要在意,其次谢谢你的范例, setTimeout 用的比我好。话说作为一个后端要理解 js 的箭头函数感觉好难啊。最后就是,其实你这个范例证明了我的判断,你可以把$forceUpdate 去掉,你就会发现,仍然会更新,因此不是$forceUpdate 本身在起效,而是异步过程在起效。这也证明了我的想法, Vue 似乎没办法在一次函数调用中 forceUpdate ,必须离开这个函数调用范围
    Alexisused
        20
    Alexisused  
       2017-04-07 13:11:15 +08:00 via Android
    计算一次 $nextTick 一次试试呢
    abcbuzhiming
        21
    abcbuzhiming  
    OP
       2017-04-07 13:32:02 +08:00
    @maplerecall 就从文档上来说,没看出 nextTick 这个方法要怎么用在我这种场景
    abcbuzhiming
        22
    abcbuzhiming  
    OP
       2017-04-07 13:33:16 +08:00
    @coolzjy 难道就没有一种机制强制浏览器刷新 dom 吗
    wly19960911
        23
    wly19960911  
       2017-04-07 13:39:29 +08:00 via Android
    @abcbuzhiming
    不要用后台那套来看前台,就算可以,那么意味着这套操作执行很慢,然后如果各相同种操作呢,这只是更新页面而已,还不够复杂,工作量再大一点用户直接崩溃了。

    实际上是你需要让动画堵塞-执行-堵塞,看看 async 和 generator 应该能解决。
    abcbuzhiming
        24
    abcbuzhiming  
    OP
       2017-04-07 13:42:58 +08:00
    @maplerecall 其实我奇怪的是这一点, 60 帧那个问题很好理解,我有做过游戏开发,但是“非异步代码执行完前不能刷新 Dom ”这点,是为什么呢,是浏览器机制限制? Javascript 语言机制限制?因为我以前开发游戏的时候,某些引擎是可以在同步代码里强制刷新当前帧的
    finian
        25
    finian  
       2017-04-07 14:20:29 +08:00
    @abcbuzhiming #24 Vue.js 在一次 tick 中操作一次 dom ( diff, update ...),框架就是这么设计的。如果你非要即时刷新 dom ,你就直接操作 dom ( e.g. `node.innerHTML = xxx`),但是上面的同学都提到了,这个计算很快,你是看不到变化过程的。如果你是按照 Vue.js 的套路来玩的话,要做的事情就是让这个变量以一定间隔变化(不能太快),反映到视图上就是一个变化的“动画”过程。
    finian
        26
    finian  
       2017-04-07 14:42:09 +08:00
    yihouzenmeban
        27
    yihouzenmeban  
       2017-04-07 14:50:11 +08:00
    @ferrum #14 为什么要用 $forceUpdate 呀。。本身就是双向绑定的。。改变 data 就直接改变 view 层了诶。。(实测不用 $forceUpdate 也可以的
    xmflswood
        28
    xmflswood  
       2017-04-07 14:53:02 +08:00
    xmflswood
        29
    xmflswood  
       2017-04-07 14:53:36 +08:00
    看看异步更新队列那一节
    maplerecall
        30
    maplerecall  
       2017-04-07 14:54:22 +08:00
    @abcbuzhiming 非异步代码执行完前不能刷新 dom 这点是现在浏览器共同的做法, dom 重绘的开销非常大,所以在一段非异步代码内所有的 dom 操作只有在这段代码结束后才会渲染,目前并没有浏览器提供在当前强制重绘的接口,因为没有意义, js 是单线程的,你想把每次修改都反应出来又不想阻塞主进程,那就只能把所有操作都写成异步的,但即使异步中每次操作都修改了 dom 浏览器也不会马上刷新,也会等到下一个 frame 才重绘,如果你只是不想写 setTimeout 或者回调嵌套的话就用 async 直接把异步按照同步的方法来写。
    Sapp
        31
    Sapp  
       2017-04-07 16:58:22 +08:00
    实际上你修改了 他就会刷新啊,只要你加上间隔,让人眼可见不就可以了吗?这个和 vue 也没啥关系。
    kaneg
        32
    kaneg  
       2017-04-07 18:55:20 +08:00 via iPhone
    Javascript 是单线程,计算方法没有退出之前 UI 是没机会更新的
    445141126
        33
    445141126  
       2017-04-07 20:24:52 +08:00
    @kaneg js 里面还是可以强制同步重新渲染的 https://gist.github.com/paulirish/5d52fb081b3570c81e3a
    hst001
        34
    hst001  
       2017-04-07 20:30:43 +08:00
    现在好多前端框架主要就是解决这个问题来提供渲染性能的,你是逆行,可行的方法每加 1 就要手动更新一次 dom
    xieranmaya
        35
    xieranmaya  
       2017-04-07 21:11:45 +08:00
    大哥,你对 n 的操作是同步的吧??
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3285 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 11:15 PVG 19:15 LAX 03:15 JFK 06:15
    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