由于最近作H5项目,须要拍照上传照片的功能。vue
因而就写了一个传统的Vue组件,功能实现都没问题,但是用着用着发现了问题:element-ui
多个项目都有拍照上传功能,可是界面样式都不同,每次还要临时改样式。后端
因此本着偷懒的原则,把这个组件拍照上传到客户端这部分功能和UI给单独分离开出来,单独写成一个插件,即用即插。数组
优势:浏览器
// upload-basic.vue
export default {
name: 'UploadBasic',
methods: {
_changeFile(e) {
const file = e.target.files[0];
const reader = new FileReader();
const self = this
reader.onloadend = function() {
const result = this.result;
const img = new Image();
img.src = result;
img.onload = function() {
self.$el.appendChild(img)
}
}
reader.readAsDataURL(file);
}
}
render() {
return (
<input ref="upBIuput" type="file" accept="image/*" onChange={(e) => {this._changeFile(e)}}/> ) } } 复制代码
上面代码很简单,主要就是在页面上渲染一个input元素,这个input用于文件的上传,在手机上点击后的效果就是唤醒系统的相机和相册(2选1)bash
当你上传图片,或者拍照上传后,就会触发onChange事件,经过e.target.files[0]就能够拿到当前上传的文件(这里指照片)。服务器
接着我使用了一个FileReader去读取文件的内容,将文件格式转换成base64位的形式,而后赋值到img的src属性上,当图片加载完毕后,经过this.$el.appenChild()方法把img这个DOM元素渲染到页面上进行图片的预览。app
这样就实现了拍照上传到浏览器而且预览,而后后续能够经过把file塞到formData对象中上传到后端服务器(这部份内容不提)。框架
由于咱们通常上传都有一个很好看的UI界面对吧,不须要这个丑丑的input,因此咱们只须要加一句样式代码去隐藏这个input。函数
<input ref="upBIuput" style="display:none;" type="file" accept="image/*" onChange={(e) => {this._changeFile(e)}}/>
复制代码
这样按钮就隐藏了,并且不占文档流,可是按钮是存在的。
把按钮隐藏了,咱们怎么触发它的上传事件呢?
很简单,由于咱们使用了一个ref属性,能够去获取到input的DOM对象。咱们只须要模拟按钮点击事件便可:
this.$refs.upBInput.click()
复制代码
咱们把这句话包裹一层函数,须要的地方调用便可。
methods: {
upload() {
this.$refs.upBInput.click()
}
}
复制代码
咱们写好一个漂亮的上传UI界面后,绑定一个click事件为this.upload()就能够触发上传了。
这样效果也达到了,可是仍是没有和UI分离,引入的时候也须要引入这个组件。想达到一开始说的效果怎么办呢?
这就要用到咱们Vue中的插件功能了。
另起一个文件,以index.js命名。
// index.js
import Vue from 'vue'
import uploadComponent from './upload-basic.vue'
// 使用Vue构造器去建立一个上传组件的子类
const uploadBasic = Vue.extend(uploadComponent)
// 建立一个div
const createDiv = () => document.createElement("div")
function useUpload(callback) {
// 建立一个上传组件类的实例化对象,这个uploadVm你能够认为就是上面那个组件
const uploadVm = new uploadBasic()
// 挂载目标
let uploadEl = createDiv()
// 将实例化对象挂载到uploadEl中
uploadVm.$mount(uploadEl)
// 触发上传操做,这个upload()方法就是咱们刚刚封装的
uploadVm.upload()
// 绑定一个finish事件去通知咱们上传完成了
uploadVm.$on('finish', data => {
uploadVm.$nextTick(() => {
// 上传完成后,作销毁工做
uploadVm.$destroy()
uploadEl = null
})
// 经过callback回调函数将咱们须要的内容回调出去,供外部环境使用
if(callback) callback(data)
})
}
// 注册这个插件,挂载到prototype上
export default {
install: function (Vue, options) {
Vue.prototype.$useUpload = function () {
// [].splice.call(类数组对象, 0) 这个方法是将一个类数组对象拷贝一份转换成一个Array
// 经过...解构变成这个函数的参数
useUpload(...([].splice.call(arguments, 0)))
}
}
}
复制代码
如今咱们定义好了一个上传组件的插件,可是咱们组件中并没与去emit一个叫finish的事件,因此咱们须要回到组件中去写这个。
// upload-basic.vue
// 重构一下这个函数
_changeFile(e) {
const file = e.target.files[0];
const reader = new FileReader();
const self = this
reader.onloadend = function() {
// 这个this指代的是reader实例
// result就是一个base64格式的文本
const result = this.result;
// 当reader实例读取文件完毕后,我们把这个result和file都emit出去
self.emit('finish', {
base64: result,
file: file
})
}
reader.readAsDataURL(file);
}
复制代码
如今,咱们的组件upload-basic.vue和插件index.js都写好了,将它们放到一个uploadPlugin文件夹中。
// main.js
import uploadPlugin from './uploadPlugin/index'
Vue.use(uploadPlugin)
复制代码
接着到须要的页面中:
// 业务1.vue
<template>
<div>
<h2 @click="handleClick">点击上传</h2>
<img src="imgUrl" />
</div>
</template>
<script>
export default {
data() {
return {
imgUrl: ''
}
},
methods: {
handleClick() {
this.$useUpload(data => {
this.imgUrl = data.base64;
console.log('文件:', data.file)
})
}
},
}
</script>
复制代码
以上这个例子,当你点击“上传文件”这个h2标签时,就会去调用咱们封装好的上传组件,而后放到img中显示,后续要上传到后端也能够。
这样很是方便的,你就能够在Vue组件中任意去调用这个全局的方法去使用上传功能,经过回调函数中的data对象能够拿到上传文件的file和base64。
5、优势
就如我刚开始说的那样。
6、后话