前言
从去年年初开始,自己便下决心要写一个 vue 系列的博客,但时至今日,才写系列的第三篇博客,想来甚是惭愧.
但是慢归慢,每一篇都要保证质量,以及要写出自己的心路历程,防止自己工作中填的坑再让读者走一遍.
vue 上手相对 react 来说是比较简单的,对于 vue 的基本指令以及语法,应该没有什么能比官网更详细,更生动的了.仔细想来,vue 值得一说的,在项目中会让新手感到困惑的,是 vue 的组件,今天就最近工作中用到的一个 pdf 查看组件,和大家聊聊 vue 的组件.最后会讲如何将自己的代码封装成一个 npm 包,发布到 npm 官网.
去年 5 月份的,写了 vue 系列的第一篇 使用 vue-cli 脚手架工具搭建 vue-webpack 项目 ,今天再次使用 vue-cli 初始化项目时,发现 vue-cli 已经升级到 2.9.2.
多说一句,因为 vue-cli 的命令为 vue,所以查看 vue-cli 的版本时,需要使用 vue -V,而且是大写的 V.仔细看下 vue-cli 2.9 的官方模板,惊喜的发现多了一个 pwa 模板.
前一阵子,谷歌开发者大会在上海举办,会上主推 pwa,在这在简单说下 PWA,大神可直接忽略.
简述 PWA
PWA 是 Progressive Web App 的缩写,字面意思理解为渐进增强的网页应用.一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用. 随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能.
在一个正常的 html 中,添加一个 link 标签,href 为 manifest.json,即可将你的网页应用添加到主屏幕.
manifest.json 中会包含你的图标,名称.背景色等信息.
引入manifest.json
{
"name": "你的web app名称",
"short_name": "简称",
"display": "standalone",
"start_url": "/",
"theme_color": "主体色(#ffffff)",
"background_color": "背景色(#333333)",
"icons": [
{
"src": "icon.png",
"sizes": "256x256",
"type": "image/png"
}
]
}
<link rel="manifest" href="manifest.json" />
PWA 应用能实现离线访问的核心是 Service Worker,Service Worker 在网页已经关闭的情况下还可以运行, 用来实现页面的缓存和离线, 后台通知等等功能.
为了让应用离线工作,需要注册一个 service worker,一段允许在后台运行的脚本,不需要 用户打开 web 页面,也不需要其他交互.在应用根目录放置 serviceworker.js,然后在浏览器注册.
if ('serviceWorker' in navigator) {navigator.serviceWorker.register('/service-worker.js').then(function() {
在 serviceworker 注册后,浏览器首次访问该应用时,会执行 install 方法,这个方法的 callback 中我们能够缓存所有需要缓存的数据.具体过程为:
console.log('Service Worker Registered');
});
}
首先定义需要缓存的文件类型,以及缓存存放路径;然后在网页相应所有请求之前,会将请求统一处理,可以控制一部分请求从缓存里拿数据.缓存会通过字符串名称,动态的更新.篇幅有限,这里大概简述下.具体可以移步 饿了么团队知乎 .
vue 组件 vue-pdf-shower
下面言归正传,说说 vue 组件.
最近笔者在项目中遇到一个 pdf 预览的需求,经过调研,最终决定用火狐的 pdf.js 封装一个 vue 组件.
其实需求还是比较简单的,就是后台给一个 URL,前端将 pdf 加载到网页中即可.chrome 和 Firefox 是自带 pdf 查看器的,简单的做法是使用 iframe 嵌入,但该方案兼容性太差,而且不受我们控制,所以 pass 了.
总体思路如下:
1. 通过 pdf.js 提供的 api,我们传入 pdf 的 URL,在 callback 中会拿到所需的 pdf 对象.
2. 通过传入不同的页码,可以拿到指定页面的 page 对象.
3. 通过 canvas,将 page 对象渲染到页面中.
4. 遍历所有 page,循环生成多个 canvas 对象,插入 dom.
把思路缕清楚之后,开发就比较简单了.
首先,确定 dom 结构.由于我们的 canvas 是动态插入 dom 的,所以只提供一个 wraper 即可.
dom 结构如下
vue-pdf-viewer 组件 js
<template>
<div class="pdf-wraper">
<div id="cvsWraper">
<div class="loading-pdf" v-if="isloading">{{loadingTxt}}</div>
</div>
</div>
</template>
该组件需要传两个参数,一个是 URL,一个是缩放值 scale.
vue 组件需要显式说明自身期望传入哪些属性,并且可以赋予默认值.调用组件时,传入不同的属性,可以实现父组件向子组件传值.
子组件向父组件传值
props: {
pdfurl: {
default: ''
},
scale: {
default: 1
}
}
子组件向父组件通信时,需要使用 vue.$emit 事件.
$emit 事件接受两个参数,第一个为所要抛出的方法名,第二个为所抛出方法带的参数.
在这个组件中,只暴露出一个 onErr 事件,即当 pdf 加载失败时的回调函数.
调用组件
PDFJS.getDocument(me.pdfurl).then(function (pafObj) {
me.isloading = true;
me.pdfDoc = pafObj;
let totalNum = me.pdfDoc.numPages;
// 循环渲染所有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);
});
在调用组件时,需要传入所需的属性和方法.
非父子组件通信
<template>
<div>
<pdfshower
:pdfurl="pdfurls"
:scale="scale"
@onErr="onErr"
></pdfshower>
</div>
</template>
兄弟组件通信也是比较常见的,比如说在一个页面中,导航是一个组件,内容区域是一个组件;当导航切换时,需要通知内容组件发生变化,并告诉他导航的 id.
处理兄弟组件通信的问题,一般有两种方式:
1. 兄弟组件都引入一个公共 vue 组件 hub,通过 hub 抛出事件,和监听事件,以达到兄弟组件通信.
2. 使用 vuex.
项目中比较常见的是第一种做法,我做的 vue 项目中只有一次使用到了 vuex;我对 vuex 的理解是:
vuex 类似于一个全局的存储空间,你可以把他理解为将需要传递的东西绑在了 window 下,所以在任何地方都可以拿到,并做修改.
在项目中用到的 hub.js
readme 中放有对包的简述,以及包的基本用法
/**
* @file 事件总线
* @author yangtianjiao
/
import Vue from 'vue';
export default new Vue({});
假设是上面说的那种情况,在导航组件切换时,通过 hub 发射信息:
hub.$emit('changeTableData', {
dateKey: this.curDateTab
});
内容区域监听 hub 发射的方法:
hub.$on('changeTableData', item = >{
this.pageNum = 1;
this.total = 0;
this.dataList = [];
this.orderFieldId = 1;
this.orderType = 1;
this.contenctDesc = '';
this.emptyText = '数据加载中...';
this.isLoading = false;
});
在内容组件销毁时,取消对 hub 事件的监听
beforeDestroy() {
hub.$off('changeTableData');
}
兄弟组件通信并不复杂,但要深刻理解,必须在项目中多运用,实践.这块应该是 vue 最难的部分了,这块掌握了,vue 项目做起来就会得心应手.
发布 npm 包
大家平时工作中,最常用的是 npm,很多包,类库都从 npm 安装.其实我们很容易就会发布属于自己的 npm 包,下面我会一步步讲讲如何将上述的 vue-pdf-viewer 组件发布到 npm 官网的.
1. 执行 npm init
执行 npm init 后,根据命令行提示,依次输入
包名称
版本
描述
入口文件
测试脚本
关键词
作者
版权信息(协议)
等等,最后 OK,生成一个 package.json 文件.
2. 确定包的目录结构
package.json 是 npm 帮我们生成的,根目录下有入口文件 index.js,和 readme.md.
index.js 中其实就是一句话,将真正的 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');
> 基于pdf.js的pdf简易查看组件.
readme.md
# vue-pdf-shower
## 介绍
> 该组件加载全部pdf页面,不提供翻页查看功能.
至于为啥有个 lib 文件夹,还有目录结构为啥长这样?我的回答是:
## 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',
// 缩放 默认为1
scale: 1.2
};
},
methods: {
// 加载失败的callback
onErr(err) {
console.log('pdf加载失败,请重试');
console.log('错误信息:', err);
}
}
};
</script>
```
看了一遍所有的 npm 包都是这样,咱就按人家的来吧 - -
好了,咱们的包已经准备就绪了,就差发布!!!
3. 注册 npm 账号 & 发包
打开冰箱,将大象放进冰箱,关上冰箱门.
注册很简单的,只需要一个邮箱就行,连网站都打不开的同学就好好写写 jquery 去吧.
4.npm login
在命令行输入 npm login,
然后依次输入用户名和密码,以及注册的邮箱.
注意:输入密码时,密码是不会显示出来的,不要方!
登录后只要没有错误提示即登录成功.
5.npm publish 发包
离成功只差一步.
一切准备妥当,cd 到我们的 vue-pdf-shower 目录,先检查下 npm 有没有重名的包.
可以去 npm 官网搜索,也可以直接 npm install 包名,如果报错,那么恭喜你包名没有重复的.
执行 npm publish
定睛一看,报了个错.原来是 package.json 的版本号没有改.将版本号升一个级,在执行 publish.
成功!
6. 去 npm 官网检验发包情况
发现已经可以搜到,因为我是昨天发的包,一天时间内已有 116 次下载.嗯,还不赖.
最后附上本组件 github 地址,欢迎大家拍砖.
https://github.com/TJ666/vue-pdf-shower
参考文献
PWA 入门: 写个非常简单的 PWA 页面
手把手教你用 npm 发布一个包
来源: https://www.cnblogs.com/tjyoung/p/8289141.html