1. 程式人生 > >vue外掛開發流程詳解-從開發到釋出至npm(二)

vue外掛開發流程詳解-從開發到釋出至npm(二)

  前記:上一篇 https://www.cnblogs.com/adouwt/p/9211003.html,(這裡感謝部落格園的網友,給我點贊推薦了) 說到了一個完整的vue外掛開發、釋出的流程,總結下來就講了這麼一個事,如何注入vue, 如果封裝vue外掛,如何測試vue外掛,以及如何釋出vue外掛到npm。但是,這裡開發測試到釋出是分開在兩個專案的,未免有些多餘,今天的筆記講的就是在上一篇的基礎上,重新換了一下專案框架,講開發測試,到打包釋出一個完整的專案,這個專案歡迎大家測試使用,一個基於vue上傳檔案的一個外掛,能夠顯示上傳的速度和進度,如果是圖片的話,也可以顯示上傳預覽,有bug即時反饋哦!

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 官網上看看我們的專案。

8.使用外掛

安裝使用:

npm install vue-ajax-upload --save

測試:

 訪問頁面:

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

  結束語: 這個筆記繼上一篇 vue外掛開發流程詳解-從開發到釋出至npm(一),替換了開發和測試的專案架構。除了在npm 登陸的那快,基本都是可以跳過上一篇,直接看這篇的,這個外掛可以顯示上傳圖片預覽,上傳速度和上傳進度,如果需要講解這部分話,請在下方留言,我會單獨開一篇講解這部分的內容。接下來的任務,還是需要做的vue 外掛按需載入外掛的方法,盡情期待我下一篇的筆記。如有不足,敬請指教,

     如需轉載,請說明轉載出處。