3

前言

多个平台都用到的一些公共的功能,又不想用git的子模块,就有了开发成npm插件的想法

放个效果图
clipboard.png

开始动手

因为需求很明确,就是做一个公共的回顶部的插件,所以就使用了简洁版的webpack配置创建工程

vue init webpack-simple x-backtotop
为什么用简洁版,以及简洁版和完整版有什么区别,可以去这篇文章看看
  1. 运行脚本后会让配置一些基础信息,直接确认就行
    clipboard.png
  2. 看看初始化后的目录结构

    clipboard.png

  3. 新建一个lib文件夹,存放我们的插件

    clipboard.png

  4. index.js

    import toTop from './x-backToTop.vue'
    
    const comment = {
      install: function (Vue) {
        Vue.component(toTop.name, toTop)
      }
    }
    // global 情况下 自动安装
    if (typeof window !== 'undefined' && window.Vue) {
      window.Vue.use(comment)
    }
    
    export default comment

    Tips:
    此处需要注意的是 install。 Vue的插件必须提供一个公开方法 install,该方法会在你使用该插件,也就是 Vue.use(yourPlugin)时被调用。这样也就给 Vue全局注入了你的所有的组件。

  5. x-backtotop.vue

    <template>
      <div class="backToTop" :style="'z-index: ' + zIndex" @click="backToTop" v-show="bVisible">
        <div class="icon"></div>
      </div>
    </template>
    
    <script>
      export default {
        name: 'back-to-top',
        props: {
          zIndex: {
            type: Number,
            default: 9999
          },
          triggerHeight: {
            type: Number
          },
          smooth: {
            type: Boolean,
            default: true
          },
          scrollInterval: {
            type: Number,
            default: 10
          },
          scrollHeight: {
            type: Number,
            default: 100
          }
        },
        data () {
          return {
            interval: null, // 计时器
            bVisible: false // 按钮显示状态
          }
        },
        methods: {
          resetToTop () {
            window.pageYOffset = 0;
            document.documentElement.scrollTop = 0;
            document.body.scrollTop = 0;
          },
          buttonStatus () {
            var currentHeight = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
            let browserHeight = this.triggerHeight || (window.outerHeight / 4);
            this.bVisible = currentHeight > browserHeight;
          },
          backToTop () {
            if (this.smooth) {
              var that = this,
                _interval = this.scrollInterval,
                _height = this.scrollHeight;
              // 间隔{_interval}移动{_height}
              this.interval = setInterval(function () {
                that.smoothScroll(_height)
              }, _interval)
            } else {
              this.resetToTop();
            }
          },
          smoothScroll (y) {
            if (window.pageYOffset > 0) {
              window.pageYOffset = window.pageYOffset - y;
            }
            if (document.documentElement.scrollTop > 0) {
              document.documentElement.scrollTop = document.documentElement.scrollTop - y;
            }
            if (document.body.scrollTop > 0) {
              document.body.scrollTop = document.body.scrollTop - y;
            }
            var positionNow = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
            if (positionNow <= 0) {
              clearInterval(this.interval);
              // 清除计时器后,全部重置为零,预防回滚为负数时,不再显示的bug
              this.resetToTop();
            }
          }
        },
        created () {
          window.addEventListener('scroll', this.buttonStatus)
        },
        destroyed () {
          window.removeEventListener('scroll', this.buttonStatus);
        }
      }
    </script>
    
    <style lang="scss" scoped>
      .backToTop {
        position: fixed;
        right: 100px;
        bottom: 150px;
        width: 40px;
        height: 40px;
        size: 40px;
        border-radius: 20px;
        cursor: pointer;
        transition: .3s;
        box-shadow: 0 0 6px rgba(0,0,0,.12);
        background-color: #FFF;
        overflow: hidden;
        .icon{
          position: absolute;
          margin: auto;
          left: 0;
          top: -8px;
          bottom: 0;
          right: 0;
          width: 0;
          height: 0;
          border-width: 8px;
          border-style: solid;
          border-color: transparent #0099CC transparent transparent;
          transform: rotate(90deg); /*顺时针旋转90°*/
        }
      }
      .backToTop:hover{
        box-shadow: 0 0 20px #000;
      }
    </style>
    

    这里需要注意的有几个点:
    1.这里使用的是addEventListener而不是window.onscroll方法,因为写成window.onscroll在离开当前界面的时候,还会触发一次这个监听,虽然没有影响,但是因为不清楚原因,所以使用addEventListener方式
    2.使用平滑的移动到顶端,由于我的写法是每隔一段时间减少定高,所以会出现负数的情况,这个时候再怎么滚动,都不会再变更这个值,所以在回到顶部后,将数值重置为0
    3.这里使用的是v-show而不是v-if,具体区别可以查看下官方文档,简单来说就是经常需要变更的建议用v-show

  6. 插件自测试
    我这里是直接在App.vue中引用,效果图如下

    clipboard.png

  7. 修改webpack.config.js,注意的就几个字段

    clipboard.png

  8. 修改package.json
    clipboard.png
  9. 发布到npm

    这里就不过多介绍怎么创建npm账号了,在项目根路径下运行

    npm login

    填写用户名密码

    whoami

    确认是否登陆成功,账号是否正确

    npm publish

    最后进行发布就行

发布中可能遇到的问题

  1. no_perms Private mode enable, only admin can publish this module
    原因:因为镜像设置成淘宝镜像了,设置回来即可
    方案:npm config set registry http://registry.npmjs.org
  2. npm publish failed put 500 unexpected status code 401
    原因:一般是没有登录
    方案:重新登陆一次
  3. npm ERR! you do not have permission to publish “your module name”. Are you logged in as the correct user?
    原因:包名被占用
    方案:修改包名即可

发布后的一些修改

发布完成后,想要自己npm run dev,发现报错
clipboard.png
原因是因为index.html中没有修改资源路径
修改前

<script src="/dist/build.js"></script>

修改后

<script src="/dist/backToTop.js" type="text/javascript"></script>

参考文章

个人疑问

如果想将别人已经发布在npm上的插件,封装到自己插件内部使用,除了手动实现该功能,有别的什么更好的方案吗,希望大佬解惑

写在最后

目前该插件已经发布到npm上了,欢迎大家使用

以上就是我将vue插件封装并发布到npm的总结,如有什么疑问欢迎评论留言。

Sumon
45 声望1 粉丝