项目使用了vue分片上传的插件vue-simple-upload(github地址https://github.com/simple-uploader/vue-uploader)。但是在此基础上,需要将样式、交互和功能与element-ui官方的el-upload组件靠拢,所以直接clone一份代码,准备直接修改(缝合)后build成单个js,直接作为内部库应用。通过观察require该js的结果{vue-uploader:{} }后, 我使用引用方式: Vue.use(require(“@/assets/js/vue-uploader.js”)[“vue-uploader”])。
问题描述
在修改了组件引用方式之后,在上传结束后出现了问题:<br /> <br /> 网上的标准答案是Object.assign()或者Object.keys()的参数传成null或者undefined时出现该问题,在本次代码里是在 delete this.data 处报错(this就是undefined),总结一下规律,猜想 delete是Object方法的变种,但是调用时参数不是支持的类型所以抛出该错误。<br /> 以上只是问题本身(what),接下来是why。为什么只是修改了vue和css部分,编译后会报错?<br /><br />其中1处send是准备发出请求的地方,下一步应该是用webAPIFileRead,根据分块的简单数据读取出要上传的chunk(包含文件流),然后再send然后test忽略,最后调用4处的_chunkEvent,其中根据status判断分块是否成功(?),决定是否上传。如果成功,下一步是调用this._updateUploadedChunks()开始上传并决定是否分片。<br />
问题出现的平台版本及自己尝试过哪些方法
排查到问题的契机就是我为了排除问题,删除了一下checkChunkUploadedByResponse(用于检查是否需要上传下一片),发现报错无了。也就是checkChunkUploaded为true时执行的独特内容是问题源,直接问题找到!
_updateUploadedChunks: function (message, chunk) {//判断配置是否有变量checkChunkUploadedByResponse存在var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponseif (checkChunkUploaded) {var xhr = chunk.xhrutils.each(this.chunks, function (_chunk) {if (!_chunk.tested) {var uploaded = checkChunkUploaded.call(this, _chunk, message)if (_chunk === chunk && !uploaded) {// fix the first chunk xhr status// treated as success but checkChunkUploaded is false// so the current chunk should be uploaded again_chunk.xhr = null}if (uploaded) {// first success and other chunks are uploaded// then set xhr, so the uploaded chunks// will be treated as success too_chunk.xhr = xhr}_chunk.tested = true}}, this)if (!this._firstResponse) {this._firstResponse = truethis.uploader.upload(true)} else {this.uploader.uploadNextChunk()}} else {this.uploader.uploadNextChunk()}},
其中,又有 `var uploaded = checkChunkUploaded.call(this, _chunk, message)`的`checkChunkUploaded`部分和压缩后浏览器定位到的代码很相似。<br /> 上面胡说乱说了一堆,我总结以下两点..<br /> 1, 根据uploader文件option内是否有checkChunkUploadedByResponse,决定了执行以下哪个方法: <br />this.uploader.upload(true) 会执行doneHandler,内含delete this.data。而 this.uploader.uploadNextChunk()不会。<br /> 2, 因为`this``.xhr.addEventListener(``'load'``, doneHandler, ``false``)`调用方式,决定了非严格模式下doneHandler内的this指向windows;严格模式下this是undefined,于是抛出这个错误。
相关代码
粘贴代码文本(请勿用截图)
//实例的sendsend: function () {var preprocess = this.uploader.opts.preprocessvar read = this.uploader.opts.readFileFnif (utils.isFunction(preprocess)) {switch (this.preprocessState) {case 0:this.preprocessState = 1preprocess(this)returncase 1:return}}//初始实例readState为0,如果加工后为2,说明成功可以跳过本次switchconsole.warn("readstate",this.readState);switch (this.readState) {case 0:this.readState = 1//获取分片的文件,结束后再次调用sendread(this.file, this.file.fileType, this.startByte, this.endByte, this)returncase 1:return}//是否开启了testChunks用来验证服务端实现快传或者秒传,如果开启了强制测试一次if (this.uploader.opts.testChunks && !this.tested) {this.test()return}this.loaded = 0this.total = 0this.pendingRetry = false// Set up request and listen for eventthis.xhr = new XMLHttpRequest()this.xhr.upload.addEventListener('progress', progressHandler, false)this.xhr.addEventListener('load', doneHandler, false)this.xhr.addEventListener('error', doneHandler, false)var uploadMethod = utils.evalOpts(this.uploader.opts.uploadMethod, this.file, this)var data = this.prepareXhrRequest(uploadMethod, false, this.uploader.opts.method, this.bytes)//原生的xhr发起请求,此前已经注册了监听this.xhr.send(data)var $ = thisfunction progressHandler (event) {if (event.lengthComputable) {$.loaded = event.loaded$.total = event.total}$._event(STATUS.PROGRESS, event)}function doneHandler (event) {console.warn("this.xhr",this, $);var msg = $.message()$.processingResponse = true// console.warn("this invoke",$.xhr);// processResponse: function (response, cb) {// cb(null, response) //这里导致内部this指向window// };$.uploader.opts.processResponse(msg, function (err, res) {$.processingResponse = falseif (!$.xhr) {return}$.processedState = {err: err,res: res}var status = $.status()if (status === STATUS.SUCCESS || status === STATUS.ERROR) {delete this.data //=============================>问题就在这$._event(status, res)status === STATUS.ERROR && $.uploader.uploadNextChunk()} else {$._event(STATUS.RETRY, res)$.pendingRetry = true$.abort()$.retries++var retryInterval = $.uploader.opts.chunkRetryIntervalif (retryInterval !== null) {setTimeout(function () {$.send()}, retryInterval)} else {$.send()}}}, $.file, $)}},
总结
多看代码,保持敏锐,就算不能提高思考能力,也能及时定位问题。
