前記:上一篇 https://www.cnblogs.com/adouwt/p/9211003.html, 說到了一個完整的vue插件開發、發布的流程,總結下來就講了這么一個事,如何注入vue, 如果封裝vue插件,如何測試vue插件,以及如何發布vue插件到npm。但是,這里開發測試到發布是分開在兩個項目的,未免有些多余,今天的筆記講的就是在上一篇的基礎上,重新換了一下項目框架,講開發測試,到打包發
布一個完整的項目,這個項目歡迎大家測試使用,一個基於vue上傳文件的一個插件,能夠顯示上傳的速度和進度,如果是圖片的話,也可以顯示上傳預覽,有bug即時反饋哦!
github 地址: https://github.com/adouwt/vue-upload
1.項目架構目錄:
這里采用的是vue-cli 腳手架,版本是2.**,vue-cli 3.0 已經出來有一段了。今天暫不用3.0 的,回頭會詳細說上3.0的打包使用。項目目錄如下:

這個項目結構直接用 vue init webapck vue-upload ,腳手架生的模版,大架構我基本沒動,添加了一點自己的配置文件和新加了自己的文件夾。如下:

2.打包插件源碼的配置文件
var path = require('path') var webpack = require('webpack') module.exports = { entry: './src/plugin/ajax-upload.js', output: { path: path.resolve(__dirname, '../dist'), publicPath: '/dist/', filename: 'vueAjaxUpload.js', library: 'vueAjaxUpload', // library指定的就是你使用require時的模塊名,這里便是require("vueAjaxUpload") libraryTarget: 'umd', //libraryTarget會生成不同umd的代碼,可以只是commonjs標准的,也可以是指amd標准的,也可以只是通過script標簽引入的。 umdNamedDefine: true // 會對 UMD 的構建過程中的 AMD 模塊進行命名。否則就使用匿名的 define。 }, module: { rules: [ { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ], }, { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' }, extensions: ['*', '.js', '.vue', '.json'] }, devServer: { historyApiFallback: true, noInfo: true, overlay: true }, performance: { hints: false }, devtool: '#eval-source-map' } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // http://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }
上面的配置文件也很簡單,主要就是入口文件和輸出文件,上面紅色標記的部分,我們需要經常修改的也是這個兩個參數,下面的打包規則我們可以不用多管。
3.package.json 的執行腳本的修改。

4.開發插件
在plugin下,新建ajax-upload.js ajax-upload.vue。

ajax-upload.js
import upload from './ajax-upload.vue' let ajaxUpload = {} ajaxUpload.install = function (Vue, options) { Vue.prototype.$msg = 'Hello I am test.js' Vue.prototype.$myMethod = function (arr) { if (arr.length < 0) { return false } else { arr = arr.join('連接你我') return arr } } Vue.component(upload.name, upload) } export default ajaxUpload
ajax-upload.vue
<template>
<div class="wt-upload">
<div class="file-area">
<div>
<input type="file" name="file" id="file" class="file" @change="previewImage($event)" multiple/>
<label for="file" class="file-label">選擇文件</label>
</div>
<div v-show="options.imagePreview">
<div class="img-preview" ref="imgPreview">
</div>
</div>
<p class="mt-sm">
<button id="upload" @click="uploadFile(file)" class="upload">上傳</button>
</p>
<div class="progress-area" v-show="options.showProgress ? options.showProgress : false">
<p class="mb-sm">進度顯示:</p>
<div class="progress">
<div class="progress-bar" id="progress" ref="progress">0%</div>
</div>
<div>
<p class="time" ref="time"></p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'hupload',
props: ['options'],
data () {
return {
imgPreview: null,
xhr: null,
loaded: 0,
ot: 0,
total: 0,
oloaded: 0,
file: null
}
},
components: {
},
mounted () {
this.imgPreview = this.$refs.imgPreview
},
created () {
this.xhr = new XMLHttpRequest()
},
methods: {
uploadFile (file) {
if (!file) {
alert('請選擇文件')
return
}
if (this.options.limitSize) {
if (file.files[0].size > (this.options.limitSize) * 1e6) {
alert(`文件大小不得超過${this.options.limitSize}M`)
return
}
} else {
if (file.files[0].size > 10000000) {
alert(`文件大小不得超過10M`)
return
}
}
if (!this.options.fileUploadName) {
alert('請配置與后端約定上傳的key值')
return
}
if (!this.options.url) {
alert('請配置與后端約定的上傳接口地址')
return
}
let formData = new FormData()
formData.append(this.options.fileUploadName, file.files[0])
this.xhr.onload = this.uploadSuccess
this.xhr.upload.onprogress = this.setProgress
this.xhr.onerror = this.uploadFailed
this.xhr.open('post', this.options.url, true)
this.xhr.send(formData)
},
previewImage (event) {
this.file = event.target
this.imgPreview.innerHTML = ''
// 每次重新選擇文件的時候,都會去除上次選擇產生的img標簽
let isImg = (event.target.files[0].type).indexOf('image/') > -1
if (isImg) {
// 如果是圖片 就解析圖片預覽
let img = document.createElement('img')
this.imgPreview.appendChild(img)
let reader = new FileReader()
reader.onload = function (event) {
img.src = event.target.result
img.width = '200'
}
reader.readAsDataURL(event.target.files[0])
} else {
console.log('為文件選擇一個默認的logo')
}
},
setProgress (event) {
let progress = this.$refs.progress
// event.total是需要傳輸的總字節,event.loaded是已經傳輸的字節。如果event.lengthComputable不為真,則event.total等於0
if (event.lengthComputable) {
this.loaded = event.loaded
this.total = event.total
let complete = (event.loaded / event.total * 100).toFixed(1)
progress.innerHTML = Math.round(complete) + '%'
progress.style.width = complete + '%'
}
// let time = document.getElementById('time')
let time = this.$refs.time
let nt = new Date().getTime() // 獲取當前時間
let pertime = (nt - this.ot) / 1000
// 計算出上次調用該方法時到現在的時間差,單位為s
this.ot = new Date().getTime() // 重新賦值時間,用於下次計算
let perload = event.loaded - this.oloaded
// 計算該分段上傳的文件大小,單位b
this.oloaded = event.loaded // 重新賦值已上傳文件大小,用以下次計算
// 上傳速度計算
let speed = perload / pertime // 單位b/s
let bspeed = speed
let units = 'b/s' // 單位名稱
if (speed / 1024 > 1) {
speed = speed / 1024
units = 'k/s'
}
if (speed / 1024 > 1) {
speed = speed / 1024
units = 'M/s'
}
speed = speed.toFixed(1)
// 剩余時間
let resttime = ((event.total - event.loaded) / bspeed).toFixed(1)
resttime = resttime > 0 ? resttime : '0'
time.innerHTML = '傳輸速度:' + speed + units + ',剩余時間:' + resttime + 's'
},
uploadSuccess () {
if (this.xhr.readyState === 4 && this.xhr.status === 200) {
setTimeout(() => {
// 回調給父組件
this.sendMsgToParent('success')
}, 1000)
}
},
uploadFailed (err) {
console.log(err)
this.sendMsgToParent({'error': err})
},
sendMsgToParent (msg) {
this.$emit('receiveUploadMsg', msg)
}
}
}
</script>
<!-- 公共的樣式 -->
<style>
.mb-sm {
margin-bottom: 10px;
}
.mt-sm {
margin-top: 10px;
}
.wt-upload {
text-align: left;
}
.file-area {
width: 80%;
margin: 0 auto;
}
.file-area .file {
display: none;
}
.wt-upload .file-label {
display: block;
width: 100px;
padding: 8px;
background: #39D2B4;
color: #fff;
font-size: 1em;
transition: all .4s;
cursor: pointer;
text-align: center;
}
.wt-upload .file-label:hover {
background: rgb(123, 219, 200);
}
.wt-upload .file-label:focus {
background: rgb(32, 148, 125);
}
.wt-upload .img-preview {
margin-top: 20px;
margin-bottom: 20px;
}
.wt-upload .upload,.wt-upload .abort {
display: inline-block;
width: 100px;
padding: 8px;
background: #39D2B4;
color: #fff;
font-size: 1em;
transition: all .4s;
cursor: pointer;
outline: none;
border: none;
}
.wt-upload .upload:hover {
background: rgb(123, 219, 200);
}
.wt-upload .upload:focus {
background: rgb(32, 148, 125);
}
.wt-upload .progress-area {
padding: 20px;
}
.wt-upload .progress {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
height: 1rem;
overflow: hidden;
font-size: 0.75rem;
background-color: #e9ecef;
border-radius: 0.25rem;
}
.wt-upload .progress-bar {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
color: #fff;
text-align: center;
background-color: #007bff;
transition: width 0.6s ease;
}
.wt-upload .time {
margin-top: 10px;
}
</style>
ajax-upload.js的講解: 引入同級目錄的ajax-upload.vue ,通過給一個自定義的對象添加install 方法,然后通過vue.component 方法注冊到vue。這里也可以給vue,通過原型方法注冊變量和方法。這里我們不需要,我們就只做了一個demo;
ajax-upload.vue的講解:里面具體的js業務邏輯不講解了,就提個 props,和name 的屬性,我們通過這個方法接受到父組件(調用該組件的組件)傳遞過來的參數,然后可以通過我們的定義的方法,如果是必須傳遞的參數,沒有該參數我們就alert 提示;name 屬性就是調用的封裝的標簽。為了減少項目的依賴插件的個數,我們也直接使用css不使用less等。
5.本地測試開發的插件的使用:
在dev文件夾下新建文件 dev-upload.vue

代碼:
<template>
<div>
<h2>開發測試</h2>
<hupload :options=options v-on:receiveUploadMsg="receiveUploadMsg"> </hupload>
</div>
</template>
<script> import Vue from 'vue' import hupload from '../plugin/ajax-upload.js' Vue.use(hupload)
export default {
data () {
return {
options: { 'showProgress': true,
'imagePreview': true,
'url': 'str',
'fileUploadName': 'ajax-upload',
'limitSize': 1 }
}
},
components: {
},
methods: {
receiveUploadMsg (msg) { console.log(msg) }
}
}
</script>
<style>
</style>
這里使用組件就不直接在main.js下引入插件,直接在使用該插件的組件中,引入,然后通過vue.use 的方法使用組件:
import Vue from 'vue' import hupload from '../plugin/ajax-upload.js' Vue.use(hupload)
使用組件,傳遞參數和接受參數
<hupload :options=options v-on:receiveUploadMsg="receiveUploadMsg"> </hupload>
這里組件的通信 父組件 -> 子組件, 子組件 -> 父組件 ,通過props 接受到父組件的參數,通過事件的方式接受子組件傳遞過來的參數。
當然,要看到這個組件的使用是否正確,就要看頁面了,頁面路由 vue-router 如下:

訪問頁面正常顯示,沒有報錯:

6.打包插件,並本地測試插件
上面第5步驟已經展示了插件是可以使用的,接下里就是打包了。
執行:
npm run dist

本地測試,先開頁面路由:

訪問頁面,沒有報錯:

以上看到了插件的引入方式不同。
7.發布插件
測試OK了,接下里就是發布了(假設你這里已經登陸npm了),執行命令:
npm publish
記住,每發布一次,需要修改package.json 的版本號:

不然報錯。
發布完之后,我們就可以轉到我們的npm 官網上看看我們的項目。
https://www.npmjs.com/package/vue-ajax-upload

8.使用插件
安裝使用:
npm install vue-ajax-upload --save
![]()
測試:

訪問頁面:

注意:大大的紅字,如果要在本項目下測試,需要修改package.json 項目名稱,不然報項目名字和包名字是一致的錯,無法安裝的,如下;

我們來一個實際跑一下這個插件:
修改options:

演示視頻:

結束語: 這個筆記繼上一篇 vue插件開發流程詳解-從開發到發布至npm(一),替換了開發和測試的項目架構。除了在npm 登陸的那快,基本都是可以跳過上一篇,直接看這篇的,這個插件可以顯示上傳圖片預覽,上傳速度和上傳進度,如果需要講解這部分話,請在下方留言,我會單獨開一篇講解這部分的內容。接下來的任務,還是要豐富和完善這個插件的具體內容,里面還有一些不足。若您發現不足的地方,敬請指教,
如果覺得好的話,https://github.com/adouwt/vue-upload,以star表示鼓勵,繼續完善這個plugin哇!
下一篇:基於vue-cli 3 的插件開發,將減少很多的配置工作
https://cli.vuejs.org/zh/dev-guide/plugin-dev.html
如需轉載,請說明轉載出處。
