zoukankan      html  css  js  c++  java
  • 【vue系列之三】从一个vue-pdf-shower,说说vue组件和npm包

    前言

    从去年年初开始,自己便下决心要写一个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中会包含你的图标、名称。背景色等信息。

    {
      "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应用能实现离线访问的核心是Service Worker,Service Worker 在网页已经关闭的情况下还可以运行, 用来实现页面的缓存和离线, 后台通知等等功能。

    为了让应用离线工作,需要注册一个 service worker,一段允许在后台运行的脚本,不需要 用户打开 web 页面,也不需要其他交互。在应用根目录放置serviceworker.js,然后在浏览器注册。

    if('serviceWorker' in navigator) { 
      navigator.serviceWorker .register('/service-worker.js')
      .then(function() { console.log('Service Worker Registered');
    }); }

      在serviceworker注册后,浏览器首次访问该应用时,会执行install方法,这个方法的callback中我们能够缓存所有需要缓存的数据。具体过程为:
    首先定义需要缓存的文件类型,以及缓存存放路径;然后在网页相应所有请求之前,会将请求统一处理,可以控制一部分请求从缓存里拿数据。缓存会通过字符串名称,动态的更新。篇幅有限,这里大概简述下。具体可以移步饿了么团队知乎

    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结构如下

    <template>
        <div class="pdf-wraper">
            <div id="cvsWraper">
                <div class="loading-pdf" v-if="isloading">{{loadingTxt}}</div>
            </div>
        </div>
    </template>
    

     

    vue-pdf-viewer组件js

    该组件需要传两个参数,一个是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

    /**
     * @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');
    

      

    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',
                // 缩放 默认为1
                scale: 1.2
            };
        },
        methods: {
            // 加载失败的callback
            onErr(err) {
                console.log('pdf加载失败,请重试');
                console.log('错误信息:', err);
            }
        }
    };
    </script>
    ```
    

      

    至于为啥有个lib文件夹,还有目录结构为啥长这样?我的回答是:

    看了一遍所有的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发布一个包

  • 相关阅读:
    docker
    手动处理datanode磁盘间使用不均的问题
    Hadoop op 1)
    Python class and function json
    scala Basic 第三课
    spark streaming kafka example
    hadoop io PART1
    elasticsearch 集群搭建
    Scala编程第二课
    scala 第一课
  • 原文地址:https://www.cnblogs.com/tjyoung/p/8289141.html
Copyright © 2011-2022 走看看