源码分析
xhr相关知识点
Ajax要点分析
拖拽事件以及粘贴事件
具体实现
总结
xhr相关知识点
XMLHttpRequest.upload 属性返回一个 XMLHttpRequestUpload对象,用来表示上传的进度。
通过onprogress属性进行监听,是在 XMLHttpRequest 完成之前周期性调用的函数。
xhr.upload.onprogress = function progress(e) {
event.loaded 已传输的数据量
event.total 总共的数据量
if (e.total > 0) {
e.percent = e.loaded / e.total * 100;
}
};
复制代码
XMLHttpRequestEventTarget.onload 是 XMLHttpRequest 请求成功完成时调用的函数。使用该属性使得判断更加简单
使用onreadystatechange属性
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE) {//当前状态码为'DONE'(4),表示下载操作已完成
console.log(xhr.responseText)
}
}
使用onload属性
xhr.onload = function onload() {
...
};
复制代码
HTTP 响应状态代码指示特定 HTTP 请求是否已成功完成。
信息响应(100-199)
成功响应(200-299)
重定向(300-399)
客户端错误(400-499)
服务器错误(500-599)
Ajax的封装
源码链接 github.com/ElemeFE/ele…
该源码不考虑IE低版本的情况,即ActiveXObject不在考虑范围。
创建一个FormData对象,将相关的数据进行添加,包括文件
const formData = new FormData();
if (option.data) {
Object.keys(option.data).map(key => {
formData.append(key, option.data[key]); //通过遍历添加新的属性值
});
}
formData.append(option.filename, option.file); //添加文件
复制代码
状态码status小于200或大于等于300的状态进行错误提示
xhr.onload = function onload() {
if (xhr.status < 200 || xhr.status >= 300) {
return option.onError(getError(action, option, xhr), getBody(xhr));
}
};
复制代码
设置请求头信息,忽略headers的继承属性同时值不能为null;这里进行headers.hasOwnProperty(item)的判断,可以避免这种情况:请求 中的headers继承共用headers中的请求头信息。
const headers = option.headers || {};
for (let item in headers) {
if (headers.hasOwnProperty(item) && headers[item] !== null) {
xhr.setRequestHeader(item, headers[item]);
}
}
复制代码
拖拽事件以及粘贴事件
拖拽事件中,对需要拖动的元素赋予draggable属性;在释放位置所在的元素上使用drop事件,同时在拖动的元素上dragover阻止默认行为以启用drop
document.addEventListener("dragover", function( event ) {
// 阻止默认动作以启用drop
event.preventDefault();
}, false);
复制代码
利用DataTransfer这个对象可以在拖拽过程中传递数据;涉及Upload组件的属性: e.DataTransfer.files在拖动操作中表示文件列表,是一个类数组
dragevent.dataTransfer.setData("text", xxx); //拖动时设置数据
dropevent.dataTransfer.getData("text"); //释放时获取数据
复制代码
粘贴事件paste: 事件处理程序可以通过调用事件的 clipboardData 属性上的 getData()访问剪贴板内容;涉及Upload组件的属性: e.clipboardData.files获取到粘贴的文件列表,同样也是一个类数组
event.clipboardData.getData()
复制代码
具体实现
Upload组件提供了3种选择文件的方式: 点击,拖拽和粘贴;点击的方式通过调用原生的input点击事件,监听input上的change事件获取到文件列表;拖拽和粘贴的方式则是通过事件drop,paste直接获取到文件列表;由于在点击方式中使用的是点击input实现的,所以在单选时弹出文件选择框时已经限制只能选中一个,但是拖拽和粘贴在单选时仍然可以选择多个文件,所以需要进行过滤,保证单选模式下只能存在一个文件。校验文件格式及大小,符合条件就开始发起请求,为每个文件添加属性(uid,percentage,status...),最后,通过回调函数获取上传进度,成功请求,失败请求相关参数。
事件的处理,通过动态触发input元素的click事件,值得注意的是选择完成后,需要手动设置value值为空,保证下次选择同一文件还会触发input的change事件
//点击方式
handleClick () {
...
this.$refs.input.click();
},
handleChange (e) {
const files = e.target.files;
...
this.$refs.input.value = null;
},
//拖拽方式
onDrop (e) {
this.uploadFiles(e.dataTransfer.files);
},
//粘贴方式
handlePaste (e) {
this.uploadFiles(e.clipboardData.files);
}
复制代码
对文件列表初始化
uploadFiles (files) {
let postFiles = Array.prototype.slice.call(files); // 类数组转数组
if (!this.multiple) postFiles = postFiles.slice(0, 1); //单选下保证所有的文件列表只有一条数据
if (postFiles.length === 0) return;
postFiles.forEach(file => {
this.upload(file);
});
},
复制代码
创建每个文件对应的数据集合,并且发起请求,通过值传递为每个数据集合添加uid
post (file) {
this.handleStart(file);
ajax({
...,
onProgress: e => {
this.handleProgress(e, file);
},
onSuccess: res => {
this.handleSuccess(res, file);
},
onError: (err, response) => {
this.handleError(err, response, file);
}
});
}
复制代码
请求回调函数,为对应的处理函数返回值
handleProgress (e, file) {
const _file = this.getFile(file); //获取到文件的详细信息
this.onProgress(e, _file, this.fileList); //事件,当前文件,文件列表
_file.percentage = e.percent || 0; //进度
}
handleSuccess (res, file) {
const _file = this.getFile(file);
if (_file) {
_file.status = 'finished'; //更新状态
_file.response = res;
this.onSuccess(res, _file, this.fileList);
}
},
handleError (err, response, file) {
const _file = this.getFile(file);
const fileList = this.fileList;
_file.status = 'fail'; //更新状态
fileList.splice(fileList.indexOf(_file), 1);
this.onError(err, response, file);
},
复制代码
总结
Upload组件涉及的API较广,需要了解;还有一些概念,比如代码的值传递,基于引用类型的复制,灵活地为每个文件集合添加uid属性,同时又能快捷方便删除项。