Note 6
上传文件
FileReader
对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用File
或Blob
对象指定要读取的文件或数据。
FileReader
方法
方法 | 说明 |
---|---|
readAsArrayBuffer() | 开始读取指定的 Blob中的内容,一旦完成,result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象。 |
readAsBinaryString() | 开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。 |
readAsDataURL() | 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL 格式的 Base64 字符串以表示所读取文件的内容。 |
readAsText() | 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。 |
Blob
对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。
Blob.slice()
用于创建一个包含源 Blob的指定字节范围内的数据的新 Blob 对象语法:
var blob = instanceOfBlob.slice([start [, end [, contentType]]]};
参数:
start
可选代表 Blob 里的下标,表示第一个会被会被拷贝进新的Blob
的字节的起始位置。如果你传入的是一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。举例来说,-10 将会是Blob
的倒数第十个字节。它的默认值是 0,如果你传入的 start 的长度大于源Blob
的长度,那么返回的将会是一个长度为 0 并且不包含任何数据的一个Blob
对象。end
可选代表的是Blob
的一个下标,这个下标 -1 的对应的字节将会是被拷贝进新的Blob
的最后一个字节。如果你传入了一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。举例来说, -10 将会是Blob
的倒数第十个字节。它的默认值就是它的原始长度 (size).contentType
可选给新的Blob
赋予一个新的文档类型。这将会把它的 type 属性设为被传入的值。它的默认值是一个空的字符串。 返回值:一个新的Blob
对象,它包含了原始Blob
对象的某一个段的数据。
File
文件(File)接口提供有关文件的信息,并允许网页中的 JavaScript 访问其内容。File 接口没有定义任何方法,但是它从
Blob
接口继承了以下方法:Blob.slice([start[, end[, contentType]]])
返回一个新的
Blob
对象,它包含有源Blob
对象中指定范围内的数据。
- 以
base64
字符串上传(FileReader
获取文件的base64
字符串)
<input
ref="upload"
type="file"
accept="image/*,application/pdf"
@change="uploadFile($event.target.files)"
style="display: none;" />
<!-- 普通点击上传 -->
<div @click="$refs.upload.click()" class="u-upload">选择文件</div>
.u-upload {
line-height: 32px;
width: 100px;
height: 32px;
border-radius: 3px;
color: #333;
background: #DDDDDD;
cursor: pointer;
border: 1px solid #CCCCCC;
&:hover {
opacity: 0.8;
}
}
<!-- 拖动文件上传 -->
<div v-show="!preImg" @click="$refs.upload.click()" ref="dragUpload" class="m-upload">
<img src="@/assets/images/add-upload.svg" class="u-upload" />
<div class="assist"></div>
</div>
.m-upload {
position: relative;
display: inline-block;
width: 98px;
height: 98px;
background: #FFFFFF;
border: 1px dashed #D1D1D1;
text-align: center;
cursor: pointer;
.u-upload { // 垂直居中
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
}
.assist { // 垂直居中辅助元素
height: 100%;
width: 0;
display: inline-block;
vertical-align: middle;
}
}
mounted () {
// 将ref为dragUpload的元素变成drop区域
this.$refs.dragUpload.addEventListener('dragenter', this.dragenter, false)
this.$refs.dragUpload.addEventListener('dragover', this.dragover, false)
this.$refs.dragUpload.addEventListener('drop', this.drop, false)
}
dragenter (e) {
e.stopPropagation()
e.preventDefault()
this.$once('hook:beforeDestroy', function () {
removeEventListener('dragenter', this.dragenter)
})
}
dragover (e) {
e.stopPropagation()
e.preventDefault()
this.$once('hook:beforeDestroy', function () {
removeEventListener('dragover', this.dragover)
})
}
drop (e) { // 拖拽上传方法
e.stopPropagation()
e.preventDefault()
const files = e.dataTransfer.files
this.uploadFile(files)
this.$once('hook:beforeDestroy', function () {
removeEventListener('drop', this.drop)
})
}
uploadFile (files) { // 统一上传文件方法
console.log('开始上传 upload-event files:', files)
if (files.length) {
var reader = new FileReader()
// reader.readAsBinaryString(files[0])
reader.readAsDataURL(files[0]) // 以base64方式读取文件内容
reader.onloadstart = function (e) { // 当读取操作开始时触发
const fileSize = e.total
if (fileSize > 1024 * 500) { // 大于500KB时取消上传
reader.abort()
} else {
console.log('开始读取 onloadstart:', e)
}
}
reader.onabort = function (e) { // 当读取操作被中断时触发
console.log('读取中止 onabort:', e)
}
reader.onerror = function (e) { // 当读取操作发生错误时触发
console.log('读取错误 onerror:', e)
}
reader.onprogress = function (e) { // 在读取Blob时触发,读取上传进度,50ms左右调用一次
console.log('读取中 onprogress:', e)
console.log('已读取:', Math.ceil(e.loaded / e.total * 100) + '%')
}
const that = this
reader.onload = function (e) { // 当读取操作成功完成时调用
console.log('读取成功 onload:', e)
// 该文件的base64数据,前端可直接用来展示图片
// that.preImg = e.target.result
// 若使用<img id="img">标签展示图片,可直接赋值src属性
// document.getElementById('img').src = e.target.result
// 调用上传接口,上传base64格式的文件数据
const fileType = files[0].type.slice(files[0].type.indexOf('/') + 1)
var formData = {
uploadFile: e.target.result,
fileFormat: fileType
}
console.log('提交数据:', formData)
upload(formData).then(res => {
console.log('upload-res:', res)
if (res.message.code === 0) {
that.fileUrl = res.data.url
}
}).finally(() => {
// input的change事件默认保存上一次input的value值,同一value值(根据文件路径判断)在上传时不重新加载
that.$refs.upload.value = ''
})
}
reader.onloadend = function (e) { // 当读取操作结束时触发(要么成功,要么失败)触发
console.log('读取结束 onloadend:', e)
}
}
}
- 以二进制文件流上传(使用FormData(),模拟form表单提交)
FormData
接口提供了一种表示表单数据的键值对 key/value
的构造方式,并且可以轻松的将数据通过 XMLHttpRequest.send()
方法发送出去,本接口和此方法都相当简单直接。如果送出时的编码类型被设为 "multipart/form-data"
,它会使用和表单一样的格式。 实现了 FormData
接口的对象可以直接在 for...of
结构中使用,而不需要调用 entries()
: for (var p of myFormData)
的作用和 for (var p of myFormData.entries())
是相同的。
FormData
方法
方法 | 说明 |
---|---|
FormData.append() | 向 FormData 中添加新的属性值,FormData 对应的属性值存在也不会覆盖原值,而是新增一个值,如果属性不存在则新增一项属性值。 |
FormData.delete() | 从 FormData 对象里面删除一个键值对。 |
FormData.entries() | 返回一个包含所有键值对的 iterator 对象。 |
FormData.get() | 返回在 FormData 对象中与给定键关联的第一个值。 |
FormData.getAll() | 返回一个包含 FormData 对象中与给定键关联的所有值的数组。 |
FormData.has() | 返回一个布尔值表明 FormData 对象是否包含某些键。 |
FormData.keys() | 返回一个包含所有键的 iterator 对象。 |
FormData.set() | 给 FormData 设置属性值,如果 FormData 对应的属性值存在则覆盖原值,否则新增一项属性值。 |
FormData.values() | 返回一个包含所有值的 iterator 对象。 |
uploadFile (files) {
console.log('开始上传 upload-event files:', files)
if (files.length) {
const file = files[0]
if (file.size > 50 * 1024) { // 校验文件大小
this.content = '文件大小不能超过500KB'
this.showTip = true
} else {
const fileType = file.type.slice(file.type.indexOf('/') + 1)
var formData = new FormData()
formData.set('uploadFile', file)
formData.set('fileFormat', fileType)
// 调用接口上传文件
upload(formData).then(res => {
console.log('upload-res:', res)
if (res.message.code === 0) {
this.fileUrl = res.data.url
}
}).finally(() => {
this.$refs.upload.value = ''
})
}
}
}
Image()
对象
Image()
函数将会创建一个新的HTMLImageElement实例。
它的功能等价于:document.createElement('img')
语法:
Image(width, height)
参数:
width
:图片的宽度 (即width
属性)height
:图片的高度 (即height
属性)示例:
jsvar myImage = new Image(100, 200) myImage.src = 'picture.jpg' document.body.appendChild(myImage)
MutationObserver
监听DOM变化
MutationObserver 接口提供了监视对 DOM 树所做更改的能力。它被设计为旧的 Mutation Events 功能的替代品,该功能是 DOM3 Events 规范的一部分
构造函数
-MutationObserver()
:创建并返回一个新的 MutationObserver
它会在指定的 DOM 发生变化时被调用
方法
disconnect()
: 阻止 MutationObserver 实例继续接收的通知,直到再次调用其observe()
方法,该观察者对象包含的回调函数都不会再被调用。observe()
: 配置MutationObserver
在DOM
更改匹配给定选项时,通过其回调函数开始接收通知。语法:
jsconst observer = new MutationObserver(callback);
callback
: 一个回调函数,每当被指定的节点或子树以及配置项有DOM
变动时会被调用。 回调函数拥有两个参数:一个是描述所有被触发改动的MutationRecord
对象数组,另一个是调用该函数的MutationObserver
对象。
参数:
target
:DOM
树中的一个要观察变化的DOM Node
(可能是一个Element
),或者是被观察的子节点树的根节点。options
: 此对象的配置项描述了DOM
的哪些变化应该报告给MutationObserver
的callback
。当调用observe()
时,childList
、attributes
和characterData
中,必须有一个参数为 true。否则会抛出TypeError
异常。options
的属性如下:subtree
可选当为
true
时,将会监听以target
为根节点的整个子树。包括子树中所有节点的属性,而不仅仅是针对target
。默认值为false
。childList
可选当为
true
时,监听target
节点中发生的节点的新增与删除(同时,如果subtree
为true
,会针对整个子树生效)。默认值为false
。attributes
可选当为
true
时观察所有监听的节点属性值的变化。默认值为true
,当声明了attributeFilter
或attributeOldValue
,默认值则为false
。attributeFilter
可选一个用于声明哪些属性名会被监听的数组。如果不声明该属性,所有属性的变化都将触发通知。
attributeOldValue
可选当为
true
时,记录上一次被监听的节点的属性变化;可查阅监听属性值了解关于观察属性变化和属性值记录的详情。默认值为false
。characterData
可选当为
true
时,监听声明的target
节点上所有字符的变化。默认值为true
,如果声明了characterDataOldValue
,默认值则为false
characterDataOldValue
可选当为
true
时,记录前一个被监听的节点中发生的文本变化。默认值为false
返回值
undefined
。
takeRecords()
: 从MutationObserver
的通知队列中删除所有待处理的通知,并将它们返回到MutationRecord
对象的新Array
中。
示例
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
const textarea = ref()
const observer = ref()
onMounted(() => {
// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: false, subtree: false }
// 创建一个观察器实例并传入回调函数
observer.value = new MutationObserver(callback)
// 以上述配置开始观察目标节点
observer.value.observe(textarea.value, config)
})
onUnmounted(() => {
// 之后,可停止观察
observer.value.disconnect()
})
/*
例如:使用 MutationObserver 监听 textarea resize 时的高度属性变化
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
*/
// 当观察到变动时执行的回调函数
const callback = function (mutationsList: any, observer: any) {
console.log('mutation', textarea.value.scrollHeight)
// Use traditional 'for loops' for IE 11
// for(let mutation of mutationsList) {
// if (mutation.type === 'childList') {
// console.log('A child node has been added or removed.')
// }
// if (mutation.type === 'attributes') {
// console.log('The ' + mutation.attributeName + ' attribute was modified.')
// console.log(mutation.target.style.height)
// }
// }
}
</script>
<template>
<textarea ref="textarea" />
</template>
IntersectionObserver
监听元素交叉状态变化
IntersectionObserver
接口(从属于 Intersection Observer
API)提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根(root)。
当一个 IntersectionObserver
对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver
被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。
构造函数
IntersectionObserver()
: 创建一个新的 IntersectionObserver
对象,当其监听到目标元素的可见部分(的比例)超过了一个或多个阈值(threshold
)时,会执行指定的回调函数。 如果指定 rootMargin
则会检查其是否符合语法规定,检查阈值以确保全部在 0.0
到 1.0
之间,并且阈值列表会按升序排列。如果阈值列表为空,则默认为一个 [0.0]
的数组。
语法
jsvar observer = new IntersectionObserver(callback[, options])
参数
callback
: 当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数:entries
: 一个IntersectionObserverEntry
对象的数组,每个被触发的阈值,都或多或少与指定阈值有偏差。observer
: 被调用的IntersectionObserver
实例。
options
可选: 一个可以用来配置observer
实例的对象。如果options
未指定,observer
实例默认使用文档视口作为root
,并且没有margin
,阈值为0%
(意味着即使一像素的改变都会触发回调函数)。你可以指定以下配置:root
: 监听元素的祖先元素Element
对象,其边界盒将被视作视口。目标在根的可见区域的任何不可见部分都会被视为不可见。rootMargin
: 一个在计算交叉值时添加至根的边界盒 (bounding_box) 中的一组偏移量,类型为字符串 (string) ,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和CSS
中的margin
属性等同; 可以参考intersection root
和root margin
来深入了解margin
的工作原理及其语法。默认值是"0px 0px 0px 0px
"。threshold
: 规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组0.0
到1.0
之间的数组。若指定值为0.0
,则意味着监听元素即使与根有1
像素交叉,此元素也会被视为可见。若指定值为1.0
,则意味着整个元素都在可见范围内时才算可见。可以参考阈值来深入了解阈值是如何使用的。阈值的默认值为0.0
。
返回值 一个可以使用规定阈值监听目标元素可见部分与
root
交叉状况的新的IntersectionObserver
实例。调用自身的observe()
方法开始使用规定的阈值监听指定目标。
实例属性
IntersectionObserver.root
只读测试交叉时,用作边界盒的元素或文档。如果构造函数未传入root
或其值为null
,则默认使用顶级文档的视口。IntersectionObserver.rootMargin
只读计算交叉时添加到根边界盒的矩形偏移量,可以有效的缩小或扩大根的判定范围从而满足计算需要。此属性返回的值可能与调用构造函数时指定的值不同,因此可能需要更改该值,以匹配内部要求。所有的偏移量均可用像素(px)或百分比(%)来表达,默认值为“0px 0px 0px 0px
”。IntersectionObserver.thresholds
只读一个包含阈值的列表,按升序排列,列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会生成一个通知(Notification
)。如果构造器未传入值,则默认值为0
。
实例方法
IntersectionObserver.disconnect()
: 使IntersectionObserver
对象停止监听目标。IntersectionObserver.observe()
: 使IntersectionObserver
开始监听一个目标元素。IntersectionObserver.takeRecords()
: 返回所有观察目标的IntersectionObserverEntry
对象数组。IntersectionObserver.unobserve()
: 使IntersectionObserver
停止监听特定目标元素。
示例
const intersectionObserver = new IntersectionObserver((entries) => {
// 如果 intersectionRatio 为 0,则目标在视野外,
// 我们不需要做任何事情。
if (entries[0].intersectionRatio <= 0) return
loadItems(10)
console.log("Loaded new items")
})
// 开始监听
intersectionObserver.observe(document.querySelector(".scrollerFooter"))
if ('IntersectionObserver' in window) {
console.log('window:', window.IntersectionObserver)
const observer = new IntersectionObserver(entries => {
console.log('entries:', entries)
entries.forEach(entry => {
console.log('entryRatio:', entry.intersectionRatio)
if (entry.intersectionRatio > 0) { // 如果元素可见
loadImage()
}
})
}, { threshold: [0, 1] }) // 第二个参数threshold默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数,[0, 1]表示,当目标元素达到0%,100%可见时,触发回调函数
// 监听多个元素
const nodes = document.querySelectorAll('#about') // NodeList数组
console.log('nodes:', nodes)
nodes.forEach(node => {
observer.observe(node)
console.log('node:', node)
})
// 监听单个元素
const map = document.getElementById('#map') // 获取元素,等价于this.$refs.map
observer.observe(map) // 监听map对应的元素
}