Vue组件封装npm发布及应用

本文参考:
https://www.cnblogs.com/max-tlp/p/9338855.html
https://www.cnblogs.com/caideyipi/p/7705052.html
本文源码下载地址
https://github.com/moyemoji/moyeSwitchButton

前言

如果想要高效地复用先前编写的Vue组件,简单的Ctrl C+V肯定不是明智做法,将这些组件进行封装并发布到npm是可选的一种方案。本文以一个开关按钮的开发过程为例,详细说明组件开发、封装及发布的全过程。

目录

  • 前言
  • 1. Vue组件开发
    • 1.1 组件编写
      • 1.1.1 项目初始化
      • 1.1.2 .vue编写
    • 1.2 组件接口
  • 2 发布Vue组件
    • 2.1 编辑index.js
    • 2.2 修改配置项
      • 2.2.1 修改`webpack.config.js`
      • 2.2.2 修改`package.json` 文件
      • 2.2.3 修改`.gitignore`
    • 2.3 提交发布
  • 3 应用组件

1. Vue组件开发

1.1 组件编写

1.1.1 项目初始化

一个Vue项目一般采用webpack 的完整配置模板,但是因为我们的目标是实现一个简单Vue组件,因此可以采用简单版本的webpack配置模板。

vue init webpack-simple my-component

初始化后的项目结构如下所示:

my-component/
├── index.html
├── package.json
├── README.md
├── .babelrc
├── .editorconfig
├── .gitignore
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   └── main.js
└── webpack.config.js

我们在src文件夹下新建switchButton作为组件源文件存放处,同时在该文件夹下新建switch-button.vue文件以及index.js文件。switch-button.vue文件中为组件源码,index.js文件实现组件注册,使得依赖本组件的工程能使用组件。修改后的项目目录长这样:

my-component/
├── index.html
├── package.json
├── README.md
├── .babelrc
├── .editorconfig
├── .gitignore
├── src
│   ├── App.vue
│   ├── switchButton
│   │   └── switch-button.vue
│   │   └── index.js
│   ├── assets
│   │   └── logo.png
│   └── main.js
└── webpack.config.js

1.1.2 .vue编写

首先实现静态的开关按钮,开始编辑switchButton目录下的switch-button.vue文件:

<template>
    <div class='sb_wrapper'>
    	<span class='sb_button'></span>
    </div>
</template>


<script>
export default({
    name:'switch-button', // 这个name和import没关系
    data(){
        return {}
    },
    mounted(){},
    methods:{}
})
</script>


<style>
.sb_wrapper{
    display: inline-block;
    position: relative;
    width: 200px;
    height: 100px;
    border-radius: 50px;
    border: 2px solid #ddd;
    box-sizing: border-box;
    background: #32CD32;
}

.sb_button{
    display: inline-block;
    position: absolute;
    width: 92px;
    height: 92px;
    border-radius: 46px;
    top: 2px;
    right: 2px;
    background: #fff;
    border: 2px solid #ddd;
    box-sizing: border-box;
}
</style>

那么编写出来的开关按钮怎么查看呢?我们都知道,src目录下的App.vue文件是Vue项目的默认主页,因此你可以修改项目App.vue文件,引用刚编写的开关按钮组件并显示到页面上去。 修改后的App.vue长成这样:

<template>
  <div id="app">
    <switch-button></switch-button>
  </div>
</template>


<script>
import switchButton from '../src/switchButton/switch-button.vue'; // 引入组件,App的template中switch-button标签名字是这里产生的,但在script中变量采用驼峰法而非短线符号

export default {
  name: 'app',
  data () {
    return {}
  },
  components:{ //注册插件
    switchButton
  },
  methods:{}
}
</script>


<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

当然,如果想要顺利看到页面内容,必须要安装相应依赖库并启动项目:

npm install
npm run dev

不出意外的话,你将得到下图一样的开关按钮:
Vue组件封装npm发布及应用_第1张图片
但是静态按钮肯定是不能满足功能需求的,我们要实现一个动态的开关按钮也就是实现点击按钮以后途中小圆圈能够左右滑动效果。可以借助Vue的transiton来实现这个滑动效果,具体参见相关文档,本文直接贴出代码了:

<template>
    <div class='sb_wrapper' v-bind:class='{ sb_wrapper_off: button_off }' @click='toggle()'>
        <transition name="fade-on">
            <span v-if='button_on' class='sb_button sb_button_on'}"></span>
        </transition>
        <transition name="fade-off">
                <span v-if='button_off' class='sb_button sb_button_off'></span>
        </transition>
    </div>
</template>


<script>
export default({
    name:'switch-button',
    data(){
        return {}
    },
    mounted(){},
    methods:{
        toggle(){
            this.button_on = !this.button_on;
            this.button_off = !this.button_off;
        }
    }
})
</script>


<style>
.sb_wrapper{
    display: inline-block;
    position: relative;
    width: 200px;
    height: 100px;
    border-radius: 50px;
    border: 2px solid #ddd;
    box-sizing: border-box;
    background: #32CD32;
}

.sb_wrapper_off{
    background: #fff;
}

.sb_button{
    display: inline-block;
    position: absolute;
    width: 92px;
    height: 92px;
    border-radius: 46px;
    top: 2px;
    background: #fff;
    border: 2px solid #ddd;
    box-sizing: border-box;
}

.sb_button_on{
    right: 2px;
}

.sb_button_off{
    left: 2px;
}


/* 开始过渡阶段,动画出去阶段 */
.fade-on-enter-active{
    transition: all 0.3s ease-out;
}
/* 进入开始 */
.fade-on-enter{
    transform: translateX(-100%);
}

/* 开始过渡阶段,动画出去阶段 */
.fade-off-enter-active{
    transition: all 0.3s ease-out;
}
/* 进入开始 */
.fade-off-enter{
    transform: translateX(100%);
}
</style>

改造以后就能有下面的效果啦:
Vue组件封装npm发布及应用_第2张图片

1.2 组件接口

组件封装最重要的部分,即接口部分,也就是这个组件与父组件之间的交互,包括了父组件向该封装组件的数据流入与组件向父组件输出数据。以这个开关按钮为例,我们希望在实际引用的时候,能够从父组件传入诸如按钮尺寸初始状态等信息来定制开关按钮,也希望点击按钮时,按钮的开关状态能够向父组件反馈。

不想卖关子,简单来说,父组件向子组件传递数据可以通过组件的props属性实现,子组件向父组件传递数据(事件触发)可以通过$emit来实现。

组件定制具体实现原理:

  1. 父组件向子组件传入数据
  2. 子组件props接收父组件数据
  3. 通过数据绑定来更改template中元素style

子组件事件触发从而向父组件传递数据具体实现原理:

  1. 子组件的点击事件
  2. 子组件的点击事件触发this.$emit,同时附带需要传输的数据,可以是多个
  3. 父组件v-on:事件名,接收数据,解析

子组件代码:

<template>
    <div class='sb_wrapper' v-bind:class='{ sb_wrapper_off: button_off }' :style="{'width': sb_wrapper_width + 'px', 'height': sb_wrapper_height + 'px', 'borderRadius': sb_wrapper_border_radius + 'px'}" @click='toggle()'>
        <transition name="fade-on">
            <span v-if='button_on' class='sb_button sb_button_on' :style="{'width': sb_button_size + 'px', 'height': sb_button_size + 'px', 'borderRadius': sb_button_border_radius + 'px'}"></span>
        </transition>
        <transition name="fade-off">
                <span v-if='button_off' class='sb_button sb_button_off' :style="{'width': sb_button_size + 'px', 'height': sb_button_size + 'px', 'borderRadius': sb_button_border_radius + 'px'}"></span>
        </transition>
    </div>
</template>


<script>
export default({
    name:'switch-button',
    // 父元素传入的数据
    props: {
        wrapper_width:{
            type:Number,  // 类型
            default:200, // 默认值
        },
        initial_state:{
            type: Boolean,
            default: false
        }
    },
    data(){
        return {
        	// 根据传入数据,数据绑定更改样式
            sb_wrapper_width: this.wrapper_width,
            sb_wrapper_height: this.wrapper_width / 2,
            sb_wrapper_border_radius: this.wrapper_width / 4,
            sb_button_size: this.wrapper_width / 2 - 8,
            sb_button_border_radius: (this.wrapper_width / 2 - 8) / 2,
            button_on: this.initial_state,
            button_off: !this.initial_state
        }
    },
    mounted(){},
    methods:{
        toggle(){
            this.button_on = !this.button_on;
            this.button_off = !this.button_off;
            // 触发toggleBtn事件
            this.$emit("toggleBtn", this.button_on);
        }
    }
})
</script>


<style>
/* 
    sb_button_wrapper长宽比为9:5【变量】
    sb_button_wrapper的border-radius为宽的一半【变量】
    sb_button_wrapper的boder宽度为2px
 */
.sb_wrapper{
    display: inline-block;
    position: relative;
    border: 2px solid #ddd;
    box-sizing: border-box;
    background: #32CD32;
}
.sb_wrapper_off{
    background: #fff;
}
/* 
    sb_button长宽为sb_button_wrapper宽度减去8px【变量】
    sb_button的border-radius为长宽一半【变量】
    sb_button的boder宽度为2px
    sb_button的left或者right为2px【变量】
 */
.sb_button{
    display: inline-block;
    position: absolute;
    top: 2px;
    background: #fff;
    border: 2px solid #ddd;
    box-sizing: border-box;
}

.sb_button_on{
    right: 2px;
}

.sb_button_off{
    left: 2px;
}


/* 开始过渡阶段,动画出去阶段 */
.fade-on-enter-active{
    transition: all 0.3s ease-out;
}
/* 进入开始 */
.fade-on-enter{
    transform: translateX(-100%);
}

/* 开始过渡阶段,动画出去阶段 */
.fade-off-enter-active{
    transition: all 0.3s ease-out;
}
/* 进入开始 */
.fade-off-enter{
    transform: translateX(100%);
}
</style>

父组件代码:

<template>
  <div id="app">
  	<!--父组件传入几个初始状态信息,接收事件-->
    <switch-button :wrapper_width="wrapper_width" :initial_state="initial_state" v-on:toggleBtn="showState(arguments)"></switch-button>
    <div>
      <!-- <span>当前开关状态为:{{btnState}}</span> -->
    </div>
  </div>
</template>


<script>
import switchButton from '../src/switchButton/switch-button'; // 引入

/*
  父组件——>switch-button:
  wrapper_width:按钮的宽度
  initial_state:按钮初始开启状态

  switch-button——>父组件
  toggleBtn:切换按钮时传回按钮状态,true表示开启,false表示关闭
*/

export default {
  name: 'app',
  data () {
    return {
      wrapper_width: 500,
      initial_state: true,
      btnState: ''
    }
  },
  components:{ //注册插件
    switchButton
  },
  methods:{
    showState(val){
      console.log(val);
      this.btnState = val[0];
    }
  }
}
</script>


<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

通过以上步骤,基本完成了组件开发了。

2 发布Vue组件

2.1 编辑index.js

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

// switchButton 插件对应组件的名字
import switchButton from './switch-button';

// Vue.js 的插件应当有一个公开方法 install 。第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象
// 参考:https://cn.vuejs.org/v2/guide/plugins.html#%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6
// 此处注意,组件需要添加name属性,代表注册的组件名称,也可以修改成其他的

switchButton.install = Vue => Vue.component(switchButton.name, switchButton); //注册组件
// 标签的方式引入,留到后面再另开新篇讨论
//const install = function(Vue, opts = {}) {
//  Vue.component(switchButton.name, switchButton);
//}
/* 支持使用标签的方式引入 Vue是全局变量时,自动install */
//if (typeof window !== 'undefined' && window.Vue) {
//  install(window.Vue);
//}

export default switchButton;

2.2 修改配置项

2.2.1 修改webpack.config.js

修改webpack.config.js,修改完成后执行npm run build看下是否生效

// 执行环境
const NODE_ENV = process.env.NODE_ENV;
console.log("-----NODE_ENV===",NODE_ENV);

module.exports = {
  // 根据不同的执行环境配置不同的入口
  entry: NODE_ENV == 'development' ? './src/main.js' : './src/switchButton/index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'switchButton.js',
    library: 'switchButton', // 指定的就是你使用require时的模块名
    libraryTarget: 'umd', // 指定输出格式
    umdNamedDefine: true // 会对 UMD 的构建过程中的 AMD 模块进行命名。否则就使用匿名的 define
  },

library:指定的就是你使用require时的模块名

libraryTarget:为了支持多种使用场景,我们需要选择合适的打包格式。常见的打包格式有 CMD、AMD、UMD,CMD只能在 Node 环境执行,AMD 只能在浏览器端执行,UMD 同时支持两种执行环境。显而易见,我们应该选择 UMD 格式。

有时我们想开发一个库,如lodashunderscore这些工具库,这些库既可以用commonjsamd方式使用也可以用script方式引入。

这时候我们需要借助librarylibraryTarget,我们只需要用ES6来编写代码,编译成通用的UMD就交给webpack

umdNamedDefine:会对 UMD 的构建过程中的 AMD 模块进行命名。否则就使用匿名的define

2.2.2 修改package.json 文件

// 开源因此需要将这个字段改为 false
"private": false,

// 这个指 import tlp_plugin_sum 的时候它会去检索的路径
"main": "dist/switchButton.js",

2.2.3 修改.gitignore

修改git上传代码,可以将.gitignore 去掉忽略dist, 把打包的文件也提交上去;

2.3 提交发布

  1. 在npm官网注册账号,地址:https://www.npmjs.com/,注册好之后,
    注意邮箱要验证,会发送验证链接到你的注册邮箱,没有验证的话是不能发布代码的
    看一下package.json 中 author 尽量与 npm 账户一致
  2. 切换到需要发包的项目根目录下,登录npm账号,输入用户名、密码、邮箱
npm login
  1. npm publish 执行发布
npm publish

3 应用组件

在你需要应用该组件的项目中,运行以下命令:

npm install my-component --save-dev

此外,需要修改main.js

import switchButton from 'my-component'
Vue.use(switchButton );

此时,在需要调用组件的页面中即可通过标签名调用啦!

你可能感兴趣的:(Node)