【vue系列之三】從一個(gè)vue-pdf-shower,說說vue組件和npm包
前言
從去年年初開始,自己便下決心要寫一個(gè)vue系列的博客,但時(shí)至今日,才寫系列的第三篇博客,想來甚是慚愧。
但是慢歸慢,每一篇都要保證質(zhì)量,以及要寫出自己的心路歷程,防止自己工作中填的坑再讓讀者走一遍。
vue上手相對react來說是比較簡單的,對于vue的基本指令以及語法,應(yīng)該沒有什么能比官網(wǎng)更詳細(xì),更生動(dòng)的了。仔細(xì)想來,vue值得一說的,在項(xiàng)目中會(huì)讓新手感到困惑的,是vue的組件,今天就最近工作中用到的一個(gè)pdf查看組件,和大家聊聊vue的組件。最后會(huì)講如何將自己的代碼封裝成一個(gè)npm包,發(fā)布到npm官網(wǎng)。
去年5月份的,寫了vue系列的第一篇使用vue-cli腳手架工具搭建vue-webpack項(xiàng)目,今天再次使用vue-cli初始化項(xiàng)目時(shí),發(fā)現(xiàn)vue-cli已經(jīng)升級到2.9.2。

多說一句,因?yàn)関ue-cli的命令為vue,所以查看vue-cli的版本時(shí),需要使用vue -V,而且是大寫的V。仔細(xì)看下vue-cli 2.9的官方模板,驚喜的發(fā)現(xiàn)多了一個(gè)pwa模板。
前一陣子,谷歌開發(fā)者大會(huì)在上海舉辦,會(huì)上主推pwa,在這在簡單說下PWA,大神可直接忽略。
簡述PWA
PWA是Progressive Web App的縮寫,字面意思理解為漸進(jìn)增強(qiáng)的網(wǎng)頁應(yīng)用。一個(gè) PWA 應(yīng)用首先是一個(gè)網(wǎng)頁, 可以通過 Web 技術(shù)編寫出一個(gè)網(wǎng)頁應(yīng)用. 隨后添加上 App Manifest 和 Service Worker 來實(shí)現(xiàn) PWA 的安裝和離線等功能。
在一個(gè)正常的HTML中,添加一個(gè)link標(biāo)簽,href為manifest.json,即可將你的網(wǎng)頁應(yīng)用添加到主屏幕。
manifest.json中會(huì)包含你的圖標(biāo)、名稱。背景色等信息。
{ "name": "你的web app名稱", "short_name": "簡稱", "display": "standalone", "start_url": "/", "theme_color": "主體色(#ffffff)", "background_color": "背景色(#333333)", "icons": [ { "src": "icon.png", "sizes": "256x256", "type": "image/png" } ] }
引入manifest.json
<link rel="manifest" href="manifest.json" />
PWA應(yīng)用能實(shí)現(xiàn)離線訪問的核心是Service Worker,Service Worker 在網(wǎng)頁已經(jīng)關(guān)閉的情況下還可以運(yùn)行, 用來實(shí)現(xiàn)頁面的緩存和離線, 后臺(tái)通知等等功能。
為了讓應(yīng)用離線工作,需要注冊一個(gè) service worker,一段允許在后臺(tái)運(yùn)行的腳本,不需要 用戶打開 web 頁面,也不需要其他交互。在應(yīng)用根目錄放置serviceworker.js,然后在瀏覽器注冊。
if('serviceWorker' in navigator) {
navigator.serviceWorker .register('/service-worker.js')
.then(function() { console.log('Service Worker Registered');
}); }
在serviceworker注冊后,瀏覽器首次訪問該應(yīng)用時(shí),會(huì)執(zhí)行install方法,這個(gè)方法的callback中我們能夠緩存所有需要緩存的數(shù)據(jù)。具體過程為:
首先定義需要緩存的文件類型,以及緩存存放路徑;然后在網(wǎng)頁相應(yīng)所有請求之前,會(huì)將請求統(tǒng)一處理,可以控制一部分請求從緩存里拿數(shù)據(jù)。緩存會(huì)通過字符串名稱,動(dòng)態(tài)的更新。篇幅有限,這里大概簡述下。具體可以移步餓了么團(tuán)隊(duì)知乎。
vue組件vue-pdf-shower
下面言歸正傳,說說vue組件。
最近筆者在項(xiàng)目中遇到一個(gè)pdf預(yù)覽的需求,經(jīng)過調(diào)研,最終決定用火狐的pdf.js封裝一個(gè)vue組件。
其實(shí)需求還是比較簡單的,就是后臺(tái)給一個(gè)URL,前端將pdf加載到網(wǎng)頁中即可。chrome和Firefox是自帶pdf查看器的,簡單的做法是使用iframe嵌入,但該方案兼容性太差,而且不受我們控制,所以pass了。
總體思路如下:
1.通過pdf.js提供的api,我們傳入pdf的URL,在callback中會(huì)拿到所需的pdf對象。
2.通過傳入不同的頁碼,可以拿到指定頁面的page對象。
3.通過canvas,將page對象渲染到頁面中。
4.遍歷所有page,循環(huán)生成多個(gè)canvas對象,插入dom。
把思路縷清楚之后,開發(fā)就比較簡單了。
首先,確定dom結(jié)構(gòu)。由于我們的canvas是動(dòng)態(tài)插入dom的,所以只提供一個(gè)wraper即可。
dom結(jié)構(gòu)如下
<template>
<div class="pdf-wraper">
<div id="cvsWraper">
<div class="loading-pdf" v-if="isloading">{{loadingTxt}}</div>
</div>
</div>
</template>
vue-pdf-viewer組件js
該組件需要傳兩個(gè)參數(shù),一個(gè)是URL,一個(gè)是縮放值scale。
vue組件需要顯式說明自身期望傳入哪些屬性,并且可以賦予默認(rèn)值。調(diào)用組件時(shí),傳入不同的屬性,可以實(shí)現(xiàn)父組件向子組件傳值。
props: {
pdfurl: {
default: ''
},
scale: {
default: 1
}
}
子組件向父組件傳值
子組件向父組件通信時(shí),需要使用vue.$emit事件。
$emit事件接受兩個(gè)參數(shù),第一個(gè)為所要拋出的方法名,第二個(gè)為所拋出方法帶的參數(shù)。
在這個(gè)組件中,只暴露出一個(gè)onErr事件,即當(dāng)pdf加載失敗時(shí)的回調(diào)函數(shù)。
PDFJS.getDocument(me.pdfurl).then(function (pafObj) {
me.isloading = true;
me.pdfDoc = pafObj;
let totalNum = me.pdfDoc.numPages;
// 循環(huán)渲染所有canvas
for (let i = 1; i <= totalNum; i++) {
let id = `canvas${i}`;
let cvsNode = document.createElement('canvas');
cvsNode.setAttribute('id', id);
cvsNode.setAttribute('class', 'canvas-item');
cvsWraper.appendChild(cvsNode);
me.renderPage(i);
if (totalNum === i) {
me.isloading = false;
}
}
}).catch(function (err) {
me.loadingTxt = '加載失敗,請稍后重試';
me.$emit('onErr', err);
});
調(diào)用組件
在調(diào)用組件時(shí),需要傳入所需的屬性和方法。
<template>
<div>
<pdfshower
:pdfurl="pdfurls"
:scale="scale"
@onErr="onErr"
></pdfshower>
</div>
</template>
非父子組件通信
兄弟組件通信也是比較常見的,比如說在一個(gè)頁面中,導(dǎo)航是一個(gè)組件,內(nèi)容區(qū)域是一個(gè)組件;當(dāng)導(dǎo)航切換時(shí),需要通知內(nèi)容組件發(fā)生變化,并告訴他導(dǎo)航的id。
處理兄弟組件通信的問題,一般有兩種方式:
1.兄弟組件都引入一個(gè)公共vue組件hub,通過hub拋出事件,和監(jiān)聽事件,以達(dá)到兄弟組件通信。
2.使用vuex。
項(xiàng)目中比較常見的是第一種做法,我做的vue項(xiàng)目中只有一次使用到了vuex;我對vuex的理解是:
vuex類似于一個(gè)全局的存儲(chǔ)空間,你可以把他理解為將需要傳遞的東西綁在了window下,所以在任何地方都可以拿到,并做修改。
在項(xiàng)目中用到的hub.js
/**
* @file 事件總線
* @author yangtianjiao
/
import Vue from 'vue';
export default new Vue({});
假設(shè)是上面說的那種情況,在導(dǎo)航組件切換時(shí),通過hub發(fā)射信息:
hub.$emit('changeTableData', {
dateKey: this.curDateTab
});
內(nèi)容區(qū)域監(jiān)聽hub發(fā)射的方法:
hub.$on('changeTableData', item => {
this.pageNum = 1;
this.total = 0;
this.dataList = [];
this.orderFieldId = 1;
this.orderType = 1;
this.contenctDesc = '';
this.emptyText = '數(shù)據(jù)加載中...';
this.isLoading = false;
});
在內(nèi)容組件銷毀時(shí),取消對hub事件的監(jiān)聽
beforeDestroy() {
hub.$off('changeTableData');
}
兄弟組件通信并不復(fù)雜,但要深刻理解,必須在項(xiàng)目中多運(yùn)用、實(shí)踐。這塊應(yīng)該是vue最難的部分了,這塊掌握了,vue項(xiàng)目做起來就會(huì)得心應(yīng)手。
發(fā)布npm包
大家平時(shí)工作中,最常用的是npm,很多包、類庫都從npm安裝。其實(shí)我們很容易就會(huì)發(fā)布屬于自己的npm包,下面我會(huì)一步步講講如何將上述的vue-pdf-viewer組件發(fā)布到npm官網(wǎng)的。
1.執(zhí)行npm init

執(zhí)行npm init后,根據(jù)命令行提示,依次輸入
包名稱
版本
描述
入口文件
測試腳本
關(guān)鍵詞
作者
版權(quán)信息(協(xié)議)
等等,最后OK,生成一個(gè)package.json文件。
2.確定包的目錄結(jié)構(gòu)

package.json是npm幫我們生成的,根目錄下有入口文件index.js,和readme.md。
index.js中其實(shí)就是一句話,將真正的index.vue暴露出去
index.js
/**
* @file vue-pdf-shower
* @author v_yangtianjiao(v_yangtianjiao@baidu.com)
* @time 18/01/15
*/
module.exports = require('./lib/index.vue');
readme中放有對包的簡述,以及包的基本用法
readme.md
# vue-pdf-shower
## 介紹
> 基于pdf.js的pdf簡易查看組件。
> 該組件加載全部pdf頁面,不提供翻頁查看功能。
## github
[vue-pdf-shower](https://github.com/TJ666/vue-pdf-shower)
## install
```
npm i vue-pdf-shower --save
```
## example
```
<template>
<div>
<pdfshower
:pdfurl="pdfurls"
:scale="scale"
@onErr="onErr"
></pdfshower>
</div>
</template>
<script>
import pdfshower from 'vue-pdf-shower';
export default {
name: 'pdfshower',
components: {
pdfshower
},
data() {
return {
// 所查看的pdf url
pdfurls: '//cdn.mozilla.net/pdfjs/tracemonkey.pdf',
// 縮放 默認(rèn)為1
scale: 1.2
};
},
methods: {
// 加載失敗的callback
onErr(err) {
console.log('pdf加載失敗,請重試');
console.log('錯(cuò)誤信息:', err);
}
}
};
</script>
```
至于為啥有個(gè)lib文件夾,還有目錄結(jié)構(gòu)為啥長這樣?我的回答是:
看了一遍所有的npm包都是這樣,咱就按人家的來吧 - -
好了,咱們的包已經(jīng)準(zhǔn)備就緒了,就差發(fā)布!!!
3.注冊npm賬號&發(fā)包

打開冰箱,將大象放進(jìn)冰箱,關(guān)上冰箱門。
注冊很簡單的,只需要一個(gè)郵箱就行,連網(wǎng)站都打不開的同學(xué)就好好寫寫jquery去吧。
4.npm login

在命令行輸入npm login,
然后依次輸入用戶名和密碼,以及注冊的郵箱。
注意:輸入密碼時(shí),密碼是不會(huì)顯示出來的,不要方!
登錄后只要沒有錯(cuò)誤提示即登錄成功。
5.npm publish發(fā)包
離成功只差一步。
一切準(zhǔn)備妥當(dāng),cd 到我們的vue-pdf-shower目錄,先檢查下npm有沒有重名的包。
可以去npm官網(wǎng)搜索,也可以直接npm install 包名,如果報(bào)錯(cuò),那么恭喜你包名沒有重復(fù)的。
執(zhí)行npm publish

定睛一看,報(bào)了個(gè)錯(cuò)。原來是package.json 的版本號沒有改。將版本號升一個(gè)級,在執(zhí)行publish。

成功!
6.去npm官網(wǎng)檢驗(yàn)發(fā)包情況

發(fā)現(xiàn)已經(jīng)可以搜到,因?yàn)槲沂亲蛱彀l(fā)的包,一天時(shí)間內(nèi)已有116次下載。嗯,還不賴。
最后附上本組件github地址,歡迎大家拍磚。
https://github.com/TJ666/vue-pdf-shower
參考文獻(xiàn)
PWA 入門: 寫個(gè)非常簡單的 PWA 頁面
手把手教你用npm發(fā)布一個(gè)包

浙公網(wǎng)安備 33010602011771號