歷史回顧:javascript
你是如何界定要不要造輪子的?於我而言,將須要多處調用的重複代碼封裝成組件、插件,把繁瑣的操做流程開發成提效工具,後續能簡化工做量,提升工做效率,就值得造輪子。爲業務封組件、封插件,爲提效造工具,作有價值的事,才能實現自我價值。css
咱們先來看看,此次造的什麼輪子,針對咱們請假審批的需求,請假的時候選擇日期選擇器是帶上下午的,這在移動端並很少見,一般是日曆的形式,咱們的產品要求picker滾動器的形式。找了半天都沒找到合適的。html
就是這種日期選擇器👆。喏,就是這樣效果,沒得辦法,只能本身上手造一個。如下就是我造輪子的過程。從先開發帶半天的日期選擇器組件,知足需求使用;業務裏多處使用,封裝成vue插件;封裝完成發現包太大,進而對插件優化;一個精緻的vue插件封裝完成後,編寫插件的使用文檔,方便你們使用;而後上傳到github,配置gh-pages,展示在線demo;最後發佈到npm,一個三方插件就完成了。✌️✌️✌️前端
咱們先來看看封裝好的帶上、下的日期選擇器,怎麼用?👇vue
一個簡單的基於vue的帶上、下午的日期選擇器java
在線地址:https://habc0807.github.io/vue-halfday-datepicker/index.htmlwebpack
npm i vue-halfday-datepicker -D
cnpm i vue-halfday-datepicker -D
yarn add vue-halfday-datepicker -D
複製代碼
在單頁面應用的入口文件裏引入和註冊git
import VueHalfdayDatepicker from 'vue-halfday-datepicker'
Vue.use(VueHalfdayDatepicker)
複製代碼
<template>
<div id="app">
<h2>一個基於vue的日期選擇器Demo</h2>
<div class="pickerSelect" @click="onOpenPicker">{{ pickerValue }}</div>
<vue-halfday-datepicker :show="isShow" @onConfirm="onConfirmHandle" @onCancel="onCancelHandle"/>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
isShow: false,
pickerValue: '測試一下:請選擇'
}
},
methods: {
onOpenPicker(e) {
this.isShow = true
},
onConfirmHandle(v) {
this.value = v
this.pickerValue = v
this.isShow = false
},
onCancelHandle(e) {
this.isShow = false
}
},
}
</script>
<style>
</style>
複製代碼
若是以爲插件還不錯,就給個Star,助力下,你的支持,是我輸出的動力~~~ github: vue-halfday-datepickergithub
最初的時候,我想省事,找個三方直接用,在github上扒拉了半天,又扒了vue的UI組件庫們,看了一圈,發現VUX的datetime
竟然有這個組件。開森,趕忙試試,開始按需引入這個組件,經過VUX官網的手動配置使用,配置真繁瑣,重點配完還不能用。固然我不會輕易放棄的,從頭至尾按照配置再來一遍,依舊很差使。web
沒辦法,我暫時放棄了VUX。另闢捷徑,日期選擇器picker,不也是picker嗎,爲何不基於picker的基礎上作二次封裝呢?
因而我找到了 Mint UI
,使用它的 picker
作二次封裝。不得不認可 Mint UI 的快速上手文檔寫的很贊,配置起來很順手。由於只須要使用一個組件,按需引入須要藉助 babel-plugin-component
,以達到減少項目體積的目的。首先,安裝 npm install babel-plugin-component -D
,而後將 .babelrc
修改成:
{
"presets": [
["es2015", { "modules": false }]
],
"plugins": [["component", [
{
"libraryName": "mint-ui",
"style": true
}
]]]
}
複製代碼
固然最重要的是 picker 的 slots
四列數組的數據處理,以及 picker 改變的數據更新。先來看看 slots
:
slots: [
{
flex: 1,
values: yearsArr,
className: 'slot1',
textAlign: 'center',
defaultIndex: defalutYear,
},
{
flex: 1,
values: monthsArr,
className: 'slot2',
textAlign: 'center',
defaultIndex: defalutMonth
},
{
flex: 1,
values: daysArr,
className: 'slot3',
textAlign: 'center',
defaultIndex: defalutDay
},
{
flex: 1,
values: noonArr,
className: 'slot4',
textAlign: 'center',
defaultIndex: 0,
}
]
複製代碼
咱們須要四列數組,分別是年份數組,當前年份對應的月份數組,當前月份對應的天數數組,以及一個上、下午數組。其中月份數組一般都是 1 ~ 12月,上、下午數組也很簡單,年份數組咱們默認指定先後10年就夠用了,須要特殊計算的是天數數組。其實計算規則大體是是不是2月,如果經過是不是閏年計算出天數,若不是 4, 6, 9, 11月份爲30天,其它月份爲31天。嗯,就醬,來看代碼👇。
/** * 是不是閏年 * @param {*} year */
function isLeapYear (year) {
return year % 100 !== 0 && year % 4 === 0 || year % 400 === 0
}
/** * 計算每一年的每月的天數 * @param {*} year * @param {*} month */
function getMaxDay (year, month) {
year = parseFloat(year)
month = parseFloat(month)
if (month === 2) {
return isLeapYear(year) ? 29 : 28
}
return [4, 6, 9, 11].indexOf(month) >= 0 ? 30 : 31
}
複製代碼
每次picker更新,從新計算數組裏的數據值,及時更新選出的日期。組件就開發完成,裏面其實還有不少細節地方須要注意,好比默認顯示,更新後的顯示等,我只是說了說主流程。
組件開發完成了,我萌生了封插件的想法。首先這個組件須要引用mint-ui
三方,按需引入 picker
,其次,在項目根目錄下添加 .babelrc
文件來配置按需加載。這一些列的操做,若是後續有需求或者其餘小夥伴也要用它的話,就會以爲比較麻煩。咱們這個多頁面應用,就要把上述的流程再操做一遍。還不如封裝一個 vue 插件,用着多方便。
從業務的價值出發,值得造火箭,若是能將以上流程封裝成 vue 插件,後續只要引入使用就好了。配置流程,按需加載什麼的都放到 vue 插件裏,爲業務造輪子,這就是我要作的事情。
查封裝vue插件的資料的時候,看到有人推薦 webpack-simple
,我就試試了試,還蠻好用。經過 vue init webpack-simple vue-插件名
,建立出已 vue-插件名
命名的文件夾,項目的目錄樹以下。
.
├── README.md
├── dist
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── lib
│ │ ├── VueHalfdayDatepicker.vue
│ │ └── index.js
│ ├── main.js
└── webpack.config.js
複製代碼
很簡單,前端的猿猿們一眼就能看的懂吧,其中src爲源碼部分,lib是插件部分。
主要更改了 name、main、author、repository
,具體以下:
{
"name": "vue-halfday-datepicker",
"description": "A date picker",
"main": "dist/vue-halfday-datepicker.js",
"version": "1.0.1",
"author": "habc0807 <gao0807@foxmail.com>",
"license": "MIT",
"private": false,
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
},
"repository": {
"type": "git",
"url": "git+https://github.com/habc0807/vue-halfday-datepicker.git"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-component": "^1.1.1",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.5",
"css-loader": "^0.28.7",
"file-loader": "^1.1.4",
"mint-ui": "^2.2.13",
"vue": "^2.5.11",
"vue-loader": "^13.0.5",
"vue-template-compiler": "^2.4.4",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^3.8.0",
"webpack-bundle-tracker": "^1.0.0-alpha.1",
"webpack-dev-server": "^2.9.1"
},
"bugs": {
"url": "https://github.com/habc0807/vue-halfday-datepicker/issues"
},
"homepage": "https://github.com/habc0807/vvue-halfday-datepicker#readme"
}
複製代碼
這裏咱們預留了頁面的入口,和插件的入口,先以組件的形式使用,沒有問題後,再將組件打包成插件,再在頁面中查看展示效果,或者修復插件中的小bug。
entry: './src/main.js', // 頁面的入口
// entry: './src/lib/index.js', // 插件的入口
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js', // 頁面的生成文件
// filename: 'vue-halfday-datepicker.js', // 插件的生成文件
libraryTarget: 'umd',
umdNamedDefine: true
},
複製代碼
後續在優化插件的過程當中,添加一些其它配置,最終的
webpack.config.js
在插件優化部分中展現。
VueHalfdayDatepicker.vue
插件的具體內容以下,其中須要注意的地方,這個插件的封裝使用了 mint-ui
的 mt-picker
。我主要要作的事情就是 picker 的四列數組 slots
,第一個模塊介紹過,這裏就不贅述了。
<template>
<div v-show="show" class="half-day-date-picker">
<div class="bg"></div>
<div class="select-date-container">
<section class="picker-wrap">
<div class="picker-toolbar">
<div class="btn cancel" @click="onCancel">取消</div>
<div class="btn confirm" @click="onConfirm">肯定</div>
</div>
<mt-picker
:valueKey="''"
:defaultIndex=10
:visibleItemCount=9
:slots="slots"
@change="onValuesChange"
></mt-picker>
</section>
</div>
</div>
</template>
<script>
import { Picker } from 'mint-ui'
import 'mint-ui/lib/style.css'
import {
yearsArr,
monthsArr,
daysArr,
noonArr,
defalutYear,
defalutMonth,
defalutDay
} from '../tools/makeData.js'
export default {
name: 'vue-halfday-datepicker',
props: {
show: {
type: Boolean,
required: true,
default: () => null,
}
},
components: {
[Picker.name]: Picker,
},
data: function() {
return {
pickerValue: '',
slots: [
{
flex: 1,
values: yearsArr,
className: 'slot1',
textAlign: 'center',
defaultIndex: defalutYear,
},
{
flex: 1,
values: monthsArr,
className: 'slot2',
textAlign: 'center',
defaultIndex: defalutMonth
},
{
flex: 1,
values: daysArr,
className: 'slot3',
textAlign: 'center',
defaultIndex: defalutDay
},
{
flex: 1,
values: noonArr,
className: 'slot4',
textAlign: 'center',
defaultIndex: 0,
}
],
}
},
methods: {
onOpenPicker(e) {
this.$emit('onOpenPicker')
},
onConfirm(e) {
this.$emit('onConfirm', this.pickerValue)
},
onCancel(e) {
this.$emit('onCancel')
},
/** picker改變的處理 */
onValuesChange(picker, values) {
// 按照時間組合字符串
const pickerValue = values[0] + '-' + values[1] + '-' + values[2] + ' ' + values[3]
this.pickerValue = pickerValue
this.$emit('onValuesChange', pickerValue)
},
},
}
</script>
<style scoped>
// …
</style>
複製代碼
將此組件封裝成插件,vue插件的格式要經過 install
來掛載組件,使用 Vue.component
來註冊一個插件,在外部 use
一個插件, index.js
的完整內容以下。
import VueHalfdayDatepicker from './VueHalfdayDatepicker.vue'
const datePicker = {
install: function(Vue) {
Vue.component(VueHalfdayDatepicker.name, VueHalfdayDatepicker)
}
}
// 這裏的判斷很重要
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(datePicker)
}
export default datePicker
複製代碼
如何測試一個插件開發是否成功呢,我沒有直接去構建插件,而是以直接引用本地插件的方式,在 main.js
中引入插件
import VueHalfdayDatepicker from '../dist/vue-halfday-datepicker.js'
Vue.use(VueHalfdayDatepicker)
複製代碼
在 App.vue
中使用組件
<template>
<div id="app">
<h2>一個基於vue的日期選擇器Demo</h2>
<div class="pickerSelect" @click="onOpenPicker">{{ pickerValue }}</div>
<vue-halfday-datepicker :show="isShow" @onConfirm="onConfirmHandle" @onCancel="onCancelHandle"/>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
isShow: false,
pickerValue: '測試一下:請選擇'
}
},
methods: {
onOpenPicker(e) {
this.isShow = true
},
onConfirmHandle(v) {
this.value = v
this.pickerValue = v
this.isShow = false
},
onCancelHandle(e) {
this.isShow = false
}
},
}
</script>
<style>
// …
</style>
複製代碼
執行 npm run dev
,緊接着在頁面中查看插件,修復插件bug等,在頁面中查看沒有問題了。接着修改 webpack.config.js
的入口文件,和生成文件名稱。
// entry: './src/main.js', // 頁面的入口
entry: './src/lib/index.js', // 插件的入口
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
// filename: 'build.js', // 頁面的生成文件
filename: 'vue-halfday-datepicker.js', // 插件的生成文件
libraryTarget: 'umd',
umdNamedDefine: true
},
複製代碼
執行 npm run build
,在目錄 dist
文件夾下 構建生成插件 vue-halfday-datepicker.js
。而後再配置回頁面的構建,查看插件效果。
一個vue插件基本上就開發好了,咱們想看看構建生成的插件各個模塊的大小,有哪些可優化的空間,能夠借用 webpack-bundle-analyzer
可視化工具查看。 在項目根目錄下使用命令行 cnpm i webpack-bundle-analyzer
,去 webpack.config.js
中配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins: [
new BundleAnalyzerPlugin()
]
複製代碼
執行 npm run dev
,看到打包後的插件的各個模塊的大小。咱們發現插件把 vue
打包進去了,是由於 mint-ui
引入了 vue
。插件大小,Parsed size: 161.33kb。
咱們經過配置 externals
來防止將某些 import 的包(package)打包到 bundle 中,而是在運行時(runtime)再去從外部獲取這些擴展依賴。經過設置 externals
防止把 vue
打包進去,打包後插件包小了許多,Parsed size: 64.46kb。
externals: {
vue: 'Vue'
},
複製代碼
部署好的DEMO地址,快來看看效果。
猿猿們git操做都很溜,我就不贅述github的基本操做了,當咱們把插件庫推到github以後,默認是推到master分支上,而後新建分支 gh-pages,將master 分支上的內容merge 到gh-pages分支上,並提交到遠程。須要注意的點是 gh-pages 分支是github提供的靜態網站部署器,默認會讀取項目根目錄下的 index.html,個人index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,viewport-fit=cover">
<title>vue-halfday-datepicker</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app"></div>
<script src="./dist/build.js"></script>
</body>
</html>
複製代碼
其中 <script src="./dist/build.js"></script>
裏的 dist/build.js 是頁面的構建文件,注意 .gitignore
中不要配置 dist
文件,否則 builds.js 文件就讀取不到了。部署好的DEMO地址,快來看看效果。
娃哈哈,到最後一步了,咱們來看看怎麼發佈。熟悉該流程的猿猿們,就能夠忽悠該模塊了,若是本文,能幫助到你,能夠爲我助力點贊。【不害臊的要關注,要贊,嘻嘻~】
去npm官網註冊,註冊的時候,要記住本身的用戶名、密碼和郵箱,後面咱們還要用。
回到咱們vue插件的倉庫根目錄下,打開命令行,輸入命令 npm login
,按照提示輸入你的用戶名和密碼,再執行 npm publish
進行發佈。發佈成功,命令裏會顯示 npm的包名和版本號。
發包過程當中若是遇到包同名,或者版本號沒更新的小問題,可自行搜索,很容易解決的。
但願能幫助你們,瞭解編寫插件的過程和思路,你會發如今業務中遨遊,時而作些業務插件更能體現作業務的價值。快去試試吧,你也能夠寫出各式各樣的小插件,歡迎在評論區跟我溝通交流。