欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Vue寫(xiě)一個(gè)datepicker的示例

 更新時(shí)間:2018年01月27日 15:16:33   作者:Showonne  
這篇文章主要介紹了使用Vue寫(xiě)一個(gè)datepicker的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

前言

寫(xiě)插件是很有意思,也很鍛煉人,因?yàn)檫@個(gè)過(guò)程中能發(fā)現(xiàn)許多的細(xì)節(jié)問(wèn)題。在前端發(fā)展的過(guò)程中,jQuery無(wú)疑是一個(gè)重要的里程碑,圍繞著這個(gè)優(yōu)秀項(xiàng)目也出現(xiàn)了很多優(yōu)秀的插件可以直接使用,大大節(jié)省了開(kāi)發(fā)者們的時(shí)間。jQuery最重要的作用是跨瀏覽器,而現(xiàn)在瀏覽器市場(chǎng)雖不完美,但已遠(yuǎn)沒(méi)有從前那么慘,數(shù)據(jù)驅(qū)動(dòng)視圖的思想倍受歡迎,大家開(kāi)始使用前端框架取代jQuery,我個(gè)人比較喜歡Vue.js,所以想試著用Vue.js寫(xiě)一個(gè)組件出來(lái)。

為了發(fā)布到npm上,所以給項(xiàng)目地址改名字了,但是內(nèi)部代碼沒(méi)有改,使用方法比之前方便。

GitHub地址: Here

功能&期望

這個(gè)datepicker目前僅實(shí)現(xiàn)了一些常用的功能:

  1. 選擇時(shí)間(這話說(shuō)得有點(diǎn)多余)
  2. 最大/最小時(shí)間限制
  3. 中/英文切換(其實(shí)也就星期和月份需要切換)
  4. 可以以.vue形式使用,也可在瀏覽器環(huán)境中直接使用
  5. 沒(méi)了。。。

目錄結(jié)構(gòu)

萬(wàn)事的第一步依然是創(chuàng)建項(xiàng)目,只是單一組件,結(jié)構(gòu)并不復(fù)雜,Datepicker.vue是最重要的組件文件,dist是webpack的輸出文件夾,index.js是webpack打包的入口文件,最后是webpack的配置文件,用來(lái)對(duì)我們的庫(kù)文件進(jìn)行打包用的。因此項(xiàng)目結(jié)構(gòu)就是這樣:

.
├── Datepicker.vue
├── LICENSE
├── README.md
├── dist
│ └── vue-datepicker.js
├── index.js
├── package.json
└── webpack.config.js

從Datepicker.vue入手

以.vue的方式寫(xiě)Vue組件是一種特殊寫(xiě)法,每個(gè)Vue文件包括template, script, style三部分,template最好不要成為片段實(shí)例,所以最外層先套一層div,當(dāng)做整個(gè)組件的根元素。一個(gè)datepicker一般由兩部分組成,一個(gè)用來(lái)顯示日期的input框,一個(gè)用來(lái)選擇日期的panel,因?yàn)槲野l(fā)現(xiàn)input在移動(dòng)端會(huì)自動(dòng)喚起鍵盤(pán),所以沒(méi)有使用input,直接用了div模擬,通過(guò)點(diǎn)擊事件決定panel的顯隱。value是最終的結(jié)果,需要和父組件通信,所以將value寫(xiě)成了prop,在父組件中使用value.sync="xxx",datepicker的value就和父組件的xxx雙向綁定了。

<template>
 <div class="date-picker">
  <div class="input" v-text="value" @click="panelState = !panelState">
 </div>
 <div class="date-panel" v-show="panelState">
 </div>
</template>

<scrip>
 export default {
  data () {
   return {
    panelState: false //初始值,默認(rèn)panel關(guān)閉
   }
  },
  props: {
   value: String
  }
 }
</script>

渲染日期列表

一個(gè)月最少是28天,如果把周日排在開(kāi)頭,那么最少(1號(hào)恰好是周日)需要4行,但是每個(gè)月天數(shù)30,31居多,而且1號(hào)又不一定是周日,我索性干脆按最多的情況設(shè)計(jì)了,共6行,當(dāng)月日期沒(méi)填滿的地方用上個(gè)月或下個(gè)月的日期補(bǔ)齊,這樣就方便計(jì)算了,而且切換月份時(shí)候panel高度不會(huì)變化。日期列表的數(shù)組需要?jiǎng)討B(tài)計(jì)算,Vue提供了computed這個(gè)屬性,所以直接將日期列表dateList寫(xiě)成計(jì)算屬性。我的方法是將日期列表固定為長(zhǎng)度為42的數(shù)組,然后將本月,上個(gè)月,下個(gè)月的日期依次填充。

computed: {
 dateList () {
  //獲取當(dāng)月的天數(shù)
  let currentMonthLength = new Date(this.tmpMonth, this.tmpMonth + 1, 0).getDate()
  //先將當(dāng)月的日期塞入dateList
  let dateList = Array.from({length: currentMonthLength}, (val, index) => {
   return {
    currentMonth: true,
    value: index + 1
   }
  })
  //獲取當(dāng)月1號(hào)的星期是為了確定在1號(hào)前需要插多少天
  let startDay = new Date(this.year, this.tmpMonth, 1).getDay()
  //確認(rèn)上個(gè)月一共多少天
  let previousMongthLength = new Date(this.year, this.tmpMonth, 0).getDate()
 }
 //在1號(hào)前插入上個(gè)月日期
 for(let i = 0, len = startDay; i < len; i++){
  dateList = [{previousMonth: true, value: previousMongthLength - i}].concat(dateList)
 }
 //補(bǔ)全剩余位置
 for(let i = 0, item = 1; i < 42; i++, item++){
  dateList[dateList.length] = {nextMonth: true, value: i}
 }
 return dateList
}

這里用Array.from來(lái)初始化了一個(gè)數(shù)組,傳入一個(gè)Array Like,轉(zhuǎn)化成數(shù)組,在拼接字符串時(shí)候采用了arr[arr.length]和[{}].concat(arr)這種方式,因?yàn)樵贘sTips上學(xué)到這樣做性能更好,文章的最后會(huì)貼出相關(guān)鏈接。
這樣,日期列表就構(gòu)建好了,在template中使用v-for循環(huán)渲染出來(lái)

<ul class="date-list">
 <li v-for="item in dateList"
  v-text="item.value" 
  :class="{preMonth: item.previousMonth, nextMonth: item.nextMonth,
   selected: date === item.value && month === tmpMonth && item.currentMonth, invalid: validateDate(item)}"
  @click="selectDate(item)">
 </li>
</ul>

樣式上就可以自己發(fā)揮了,怎么喜歡怎么寫(xiě)。需要注意的是循環(huán)日期可能會(huì)出現(xiàn)上個(gè)月或這個(gè)月的日期,我通過(guò)previuosMonth,currentMonth和nextMonth分別做了標(biāo)記,對(duì)其他功能提供判斷條件。
年份和月份的列表都是差不多的道理,年份列表的初始值我直接寫(xiě)在了data里,以當(dāng)前年份為第一個(gè),為了和月份保持一致,每次顯示12個(gè),都通過(guò)v-for渲染。

data () {
 return {
  yearList: Array.from({length: 12}, (value, index) => new Date().getFullYear() + index)
 }
}

選擇日期功能

選擇順序是:年 -> 月 -> 日,所以我們可以通過(guò)一個(gè)狀態(tài)變量來(lái)控制panel中顯示的內(nèi)容,綁定適合的函數(shù)切換顯示狀態(tài)。

<div>
 <div class="type-year" v-show="panelType === 'year'">
  <ul class="year-list">
   <li v-for="item in yearList"
    v-text="item"
    :class="{selected: item === tmpYear, invalid: validateYear(item)}" 
    @click="selectYear(item)"
   >
   </li>
  </ul>
 </div>
 <div class="type-month" v-show="panelType === 'month'">
  <ul class="month-list">
   <li v-for="item in monthList"
    v-text="item | month language"
    :class="{selected: $index === tmpMonth && year === tmpYear, invalid: validateMonth($index)}" 
    @click="selectMonth($index)"
   >
   </li>
  </ul>
 </div>
 <div class="type-date" v-show="panelType === 'date'">
  <ul class="date-list">
   <li v-for="item in dateList"
    v-text="item.value" 
    track-by="$index" 
    :class="{preMonth: item.previousMonth, nextMonth: item.nextMonth,
     selected: date === item.value && month === tmpMonth && item.currentMonth, invalid: validateDate(item)}"
    @click="selectDate(item)">
   </li>
  </ul>
 </div>
</div>

選擇日期的方法就不細(xì)說(shuō)了,在selectYear,selectMonth中對(duì)年份,月份變量賦值,再分別將panelType推向下一步就實(shí)現(xiàn)了日期選擇功能。

不過(guò)在未選擇完日期之前,你可能不希望當(dāng)前年月的真實(shí)值發(fā)生變化,所以在這些方法中可先將選擇的值賦給一個(gè)臨時(shí)變量,等到seletDate的時(shí)候再一次性全部賦值。

selectMonth (month) {
 if(this.validateMonth(month)){
  return
 }else{
  //臨時(shí)變量
  this.tmpMonth = month
  //切換panel狀態(tài)
  this.panelType = 'date'
 }
},
selectDate (date) {
 //validate logic above...
 //一次性全部賦值
 this.year = tmpYear
 this.month = tmpMonth
 this.date = date.value
 this.value = `${this.tmpYear}-${('0' + (this.month + 1)).slice(-2)}-${('0' + this.date).slice(-2)}`
 //選擇完日期后,panel自動(dòng)隱藏
 this.panelState = false
}

最大/小時(shí)間限制

最大/小值是需要從父組件傳遞下來(lái)的,因此應(yīng)該使用props,另外,這個(gè)值可以是字符串,也應(yīng)該可以是變量(比如同時(shí)存在兩個(gè)datepicker,第二個(gè)的日期不能比第一個(gè)大這種邏輯),所以應(yīng)該使用Dynamically bind的方式傳值。

<datepicker :value.sync="start"></datepicker>
<!-- 現(xiàn)在min的值會(huì)隨著start的變化而變化 -->
<datepicker :value.sync="end" :min="start" ></datepicker>

增加了限制條件,對(duì)于不合法的日期,其按鈕應(yīng)該變?yōu)橹没覡顟B(tài),我用了比較時(shí)間戳的方式來(lái)判斷日期是否合法,因?yàn)榫退惝?dāng)前panel中的日期是跨年或是跨月的,通過(guò)日期構(gòu)造函數(shù)創(chuàng)建時(shí)都會(huì)幫你轉(zhuǎn)換成對(duì)應(yīng)的合法值,省去很多判斷的麻煩:

new Date(2015, 0, 0).getTime() === new Date(2014, 11, 31).getTime() //true
new Date(2015, 12, 0).getTime() === new Date(2016, 0, 0).getTime() //true

因此驗(yàn)證日期是否合法的函數(shù)是這樣的:

validateDate (date) {
 let mon = this.tmpMonth
 if(date.previousMonth){
  mon -= 1
 }else if(date.nextMonth){
  mon += 1
 }
 if(new Date(this.tmpYear, mon, date.value).getTime() >= new Date(this.minYear, this.minMonth - 1, this.minDate).getTime()
  && new Date(this.tmpYear, mon, date.value).getTime() <= new Date(this.maxYear, this.maxMonth - 1, this.maxDate).getTime()){
  return false
 }
 return true
}

動(dòng)態(tài)計(jì)算位置

當(dāng)頁(yè)面右側(cè)有足夠的空間顯示時(shí),datepicker的panel會(huì)定位為相對(duì)于父元素left: 0的位置,如果沒(méi)有足夠的空間,則應(yīng)該置于right: 0的位置,這一點(diǎn)可以通過(guò)Vue提供的動(dòng)態(tài)樣式和樣式對(duì)象來(lái)實(shí)現(xiàn)(動(dòng)態(tài)class和動(dòng)態(tài)style其實(shí)只是動(dòng)態(tài)props的特例),而計(jì)算位置的時(shí)刻,我放在了組件聲明周期的ready周期中,因?yàn)檫@時(shí)組件已經(jīng)插入到DOM樹(shù)中,可以獲取style進(jìn)行動(dòng)態(tài)計(jì)算:

ready () {
 if(this.$el.parentNode.offsetWidth + this.$el.parentNode.offsetLeft - this.$el.offsetLeft <= 300){
  this.coordinates = {right: '0', top: `${window.getComputedStyle(this.$el.children[0]).offsetHeight + 4}px`}
 }else{
  this.coordinates = {left: '0', top: `${window.getComputedStyle(this.$el.children[0]).offsetHeight + 4}px`}
 }
}
<!-- template中對(duì)應(yīng)的動(dòng)態(tài)style -->
<div :style="coordinates"></div>

為了panel的顯隱可以平滑過(guò)渡,可以使用transition做過(guò)渡動(dòng)畫(huà),這里我簡(jiǎn)單地通過(guò)一個(gè)0.2秒的透明度過(guò)渡讓顯隱更平滑。

<div :style="this.coordinates" v-show="panelState" transition="toggle"></div>

//less syntax
.toggle{
 &-transition{
  transition: all ease .2s;
 }
 &-enter, &-leave{
  opacity: 0;
 }
}

中英文切換

這里其實(shí)也很簡(jiǎn)單,這種多語(yǔ)言切換實(shí)質(zhì)就是一個(gè)key根據(jù)不同的type而輸出不同的value,所以使用filter可以很容易的實(shí)現(xiàn)它!比如渲染星期的列表:

<ul class="weeks">
  <li v-for="item in weekList" v-text="item | week language"></li>
 </ul>
 
filters : {
 week (item, lang){
  switch (lang) {
   case 'en':
    return {0: 'Su', 1: 'Mo', 2: 'Tu', 3: 'We', 4: 'Th', 5: 'Fr', 6: 'Sa'}[item]
   case 'ch':
    return {0: '日', 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六'}[item]
   default:
    return item
  }
 }
}

多種使用方式

對(duì)于一個(gè)Vue組件,如果是使用webpack + vue-loader的.vue單文件寫(xiě)法,我希望這樣使用:

//App.vue
<script>
 import datepicker from 'path/to/datepicker.vue'
 export default {
  components: { datepicker}
 }
</script>

如果是直接在瀏覽器中使用,那么我希望datepicker這個(gè)組件是暴露在全局下的,可以這么使用:

//index.html
<html>
 <script src="path/to/vue.js"></script>
 <script src="path/to/datepicker.js"></script>
 <body>
  <div id="app"></div>
  <script>
   new Vue({
    el: '#app',
    components: { datepicker }
   })
  </script>
 </body>
</html>

這里我選擇了webpack作為打包工具,使用webpack的output.library和output.linraryTarget這兩個(gè)屬性就可以把你的bundle文件作為庫(kù)文件打包。library定義了庫(kù)的名字,libraryTarget定義了你想要打包的格式,具體可以看文檔。我希望自己的庫(kù)可以通過(guò)datepicker加載到,并且打包成umd格式,因此我的webpack.config.js是這樣的:

module.exports = {
 entry: './index.js',
 output: {
  path: './dist',
  library: 'datepicker',
  filename: 'vue-datepicker.js',
  libraryTarget: 'umd'
 },
 module: {
  loaders: [
   {test: /\.vue$/, loaders: ['vue']},
   {test: /\.js$/, exclude: /node_modules/, loaders: ['babel']}
  ]
 }
}

打包完成的模塊就是一個(gè)umd格式的模塊啦,可以在瀏覽器中直接使用,也可以配合require.js等模塊加載器使用!

適配 Vue 2.x

Vue 2.0已經(jīng)發(fā)布有段時(shí)間了,現(xiàn)在把之前的組件適配到Vue 2.0。遷移過(guò)程還是很順利的,核心API改動(dòng)不大,可以借助vue-migration-helper來(lái)找出廢棄的API再逐步修改。這里只列舉一些我需要修改的API。

filter

2.0中的filter只能在mustache綁定中使用,如果想在指令式綁定中綁定過(guò)濾后的值,可以選擇計(jì)算屬性。我在月份和星期的顯示中使用到了過(guò)濾器來(lái)過(guò)濾語(yǔ)言類(lèi)型,但我之前是在指令式綁定中使用的filter,所以需要如下修改,:

//修改前
<div class="month-box" @click="chType('month')" v-text="tmpMonth + 1 | month language"></div>
//修改后,filter傳參的方式也變了,變成了函數(shù)調(diào)用的風(fēng)格
<div class="month-box" @click="chType('month')">{{tmpMonth + 1 | month(language)}}</div>

移除$index和$key

這兩個(gè)屬性不會(huì)在v-for中被自動(dòng)創(chuàng)建了,如需使用,要在v-for中自行聲明:

<li v-for="item in monthList" @click="selectMonth($index)"></li>
//
<li v-for="(item, index) in monthList" @click="selectMonth(index)"></li>

ready 生命周期移除

ready從生命周期鉤子中移除了,遷移方法很簡(jiǎn)單,使用mounted和this.$nextTick來(lái)替換。

prop.sync棄用

prop的sync棄用了,遷移方案是使用自定義事件,而且Datepicker這種input類(lèi)型組件,可以使用表單輸入組件的自定義事件作為替換方案。自定義組件也可以使用v-model指令了,但是必須滿足兩個(gè)條件:

  1. 接收一個(gè)value的prop
  2. 值發(fā)生變化時(shí),觸發(fā)一個(gè)input事件,傳入新值。

所以Datepicker的使用方式也不是<datepicker value.sync="now"></datepicker>了,而是<datepicker v-model="now"></datepicker>。組件自身向父級(jí)傳值的方式也不一樣了:

//1.x版本,設(shè)置了value的值會(huì)同步到父級(jí)
this.value = `${this.tmpYear}-${('0' + (this.month + 1)).slice(-2)}-${('0' + this.date).slice(-2)}`

//2.x版本,需要自己觸發(fā)input事件,將新值作為參數(shù)傳遞回去
let value = `${this.tmpYear}-${('0' + (this.month + 1)).slice(-2)}-${('0' + this.date).slice(-2)}`
this.$emit('input', value)

總結(jié)

以上就是我在寫(xiě)這個(gè)datepicker時(shí)的大致思路,本身也是很簡(jiǎn)單的事情,沒(méi)有處處展開(kāi)來(lái)說(shuō),寫(xiě)在這里作為自己的一個(gè)總結(jié),如果有剛開(kāi)始使用Vue的同學(xué)也希望這篇文章可以在思路上幫助到你們:P,對(duì)于各位老鳥(niǎo)如果有什么指點(diǎn)的地方我也很感謝:D,那么差不多就這樣。也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論