• Vue 普通对象数据更新与 file 对象数据更新


    最近在做一个多图片上传的组件,需求是做到多文件依次上传,并显示上传进度条。

    逻辑部分实现了以后,在更新进度条视图的时候出现一点问题:动态计算生产的进度 progress 属性不会自动更新。

    原来的代码是这样写的:

    复制代码
    let files = this.filePicker.files;
    if(!files.length) {
        return;
    }
    
    let arr = [];
    for(let i = 0, len = files.length; i < len; i++) {
        let item = files[i];
        // 每个文件初始进度为0
        item.progress = '0';
    
        arr.push(obj);
    }
    
    this.fileArr = arr;
    复制代码

    这里直接将 file 对象添加一个 progress 属性记录上传进度,并初始化为0,然后上传时候实时计算更新 progress。但奇怪的是这个 progress 在视图里并不会自动更新,岿然不动,一直都是0。还了N中办法,百思不得其解。

    后来一怒之下做了一个小 demo,看看问题到底出现在哪里,把不想关的代码都剔除,只保留核心代码,并用最简单的数据来模拟一下。代码如下:

    复制代码
    // 用数组模拟 files, 用对象模拟 file 对象
    let files = [];
    for(let i = 0, len = 5; i < len; i++) {
        let obj = {name: 'name_' + 1};
    
        files.push(obj);
    }
    
    let arr = [];
    for(let i = 0, len = files.length; i < len; i++) {
        files[i].progress = '0';
        arr.push(files[i]);
    }
    复制代码

    这里仅仅是把 files 对象换成了数组来模拟,把 file 对象换成了普通对象模拟。

    神奇的是,这样居然就自动更新了。

    由于文件 file 后来都保存在数组里,这说明唯一的区别就在 file 对象上面!于是打算用普通对象保存 file 对象的属性再试试。

    复制代码
    let files = this.filePicker.files;
    if(!files.length) {
        return;
    }
    
    
    let arr = [];
    for(let i = 0, len = files.length; i < len; i++) {
        let item = files[i];
        let obj = {};
    
        obj.name = item.name;
        obj.size = item.size;
    
        obj.progress = '0';
    
        arr.push(obj);
    }
    复制代码

    这样视图也是可以自动更新的,果然是 file 对象和普通对象的区别。

    它们究竟是什么区别呢?看一下他们的类型先。

    复制代码
    console.log('files type', Object.prototype.toString.call(files));
    // files type [object FileList]
    console.log('arr   type', Object.prototype.toString.call(arr));
    // arr   type [object Array]
    
    console.log('item type', Object.prototype.toString.call (files[0]));
    // item type [object File]
    console.log('obj  type', Object.prototype.toString.call (obj));
    // obj  type [object Object]
    复制代码

    原来 files 是 FileList 类型,file 是 File 类型。而普通的 obj 是 Object 类型。

    Vue 的数据更新利用的是 Object.defineProperty 的 getter setter 函数来实现的,而 Vue 默认没有对 File 对象设置 getter setter, 因此用 File 对象不会自动更新。

    解决办法,就是用普通对象保存 file 对象里需要的信息,然后用来构造视图数据。或者自己手动设置 File 对象的 setter,也可以自动更新。代码如下:

    复制代码
    <div id="app">
        <input type="text" id='a'>
        <span id='b'></span>
    
        <input type="file" id='file'>
        <button type="button" id='button'>点击更改file属性</button>
    </div>
    
    <script>
        // 普通对象设置 setter
        var obj = {};
        Object.defineProperty(obj, 'hello', {
            set: function(newVal) {
                document.getElementById('a').value = newVal;
                document.getElementById('b').innerHTML = newVal;
            }
        });
    
        document.addEventListener('keyup', function(e){
            obj.hello = e.target.value;
        });
    
        // File 对象设置 setter
        var fileInput = document.getElementById('file');
        var file;
        fileInput.addEventListener('change', function(e){
            file = fileInput.files[0];
    
            Object.defineProperty(file, 'progress', {
                set: function(newVal) {
                    // document.getElementById('a').value = newVal;
                    document.getElementById('b').innerHTML = newVal;
                }
            });
        });
    
        document.getElementById('button').addEventListener('click', function(){
            file.progress = 'hello file';
        });
    
    </script>
  • 相关阅读:
    mysql原生语句基础知识
    利用layui前端框架实现对不同文件夹的多文件上传
    简述layui前端ui框架的使用
    利用bootstrap-select.min.js实现bootstrap下拉列表的单选和多选
    使用pycharm进行远程开发部署调试设置 与 远程部署调试是否必须使用远程主机的解释器?
    博客园积分规则
    mysql 数据库的备份与恢复
    flask 利用flask_wtf扩展 创建web表单
    jquery ajax几种书写方式的总结
    LightSpeed 的Left Join Bug解决方案
  • 原文地址:https://www.cnblogs.com/libin-1/p/6768062.html
Copyright © 2020-2023  润新知