この記事では主に Vue を使用した datepicker の作成例を紹介しますので、参考にしてください。
プラグインの作成は、その過程で多くの詳細を発見できるため、非常に興味深く、非常にトレーニングになります。フロントエンド開発のプロセスにおいて、jQuery は間違いなく重要なマイルストーンであり、この優れたプロジェクトを中心に、直接使用できる多くの優れたプラグインが登場し、開発者の時間を大幅に節約します。 jQuery の最も重要な役割はクロスブラウザーです。現在、ブラウザー市場は完全ではありませんが、データ駆動型ビューの考え方は非常に普及し始めています。個人的には Vue.js の方が好きなので、Vue.js を使ってコンポーネントを書いてみたいと思います。
npmに公開するためにプロジェクトアドレスの名前を変更しましたが、内部コードは変更されておらず、以前よりも使い勝手が良くなりました。
GitHubアドレス: こちら
この日付ピッカーは現在、いくつかの一般的な機能のみを実装しています:
時間を選択する(これは少し冗長です)
最大/最小時間制限
Chinese/英語の切り替え(実際には週と月だけを切り替える必要があります)
は、.vueフォームまたはブラウザ環境で直接使用できます
これ以上はありません。 。 。
すべての最初のステップは、やはりプロジェクトを作成することです。これは単なる単一のコンポーネントであり、構造は複雑ではありません。Datepicker.vue は最も重要なコンポーネント ファイルであり、dist は webpack の出力フォルダーです。 、index.js は webpack です。パッケージ化エントリ ファイル、そして最後に webpack 構成ファイルは、ライブラリ ファイルをパッケージ化するために使用されます。プロジェクトの構造は次のようになります:
. ├── Datepicker.vue ├── LICENSE ├── README.md ├── dist │ └── vue-datepicker.js ├── index.js ├── package.json └── webpack.config.js
.vue で Vue コンポーネントを記述するのは特殊な方法です。各 Vue ファイルには、テンプレート、スクリプト、スタイルの 3 つの部分が含まれています。は template のフラグメント インスタンスになるため、最初に最も外側の層が p の層で覆われ、これがコンポーネント全体のルート要素として使用されます。日付ピッカーは通常、日付を表示するための入力ボックスと日付を選択するためのパネルの 2 つの部分で構成されます。入力するとモバイル端末上のキーボードが自動的に呼び出されることがわかったので、input を使用せずに直接 p を使用しました。イベントによってパネルの表示/非表示が決まります。値は最終結果であり、親コンポーネントと通信する必要があるため、値は prop として書き込まれ、value.sync="xxx" が親コンポーネントで使用されます。日付ピッカーの値は、親コンポーネントの xxx に双方向にバインドされます。親コンポーネント。
<template> <p class="date-picker"> <p class="input" v-text="value" @click="panelState = !panelState"> </p> <p class="date-panel" v-show="panelState"> </p> </template> <scrip> export default { data () { return { panelState: false //初始值,默认panel关闭 } }, props: { value: String } } </script>
月は少なくとも 28 日あり、日曜日が先頭に配置されている場合は、少なくとも 4 行が必要になります (1 日はたまたま日曜日です)。ただし、各月の日数は次のとおりです。は 30、31 が最も多く、1 日は日曜日である必要はないので、最も一般的な状況に合わせて単純に設計し、合計 6 行で今月の日付の未記入の部分を で埋めます。前月または翌月の日付を表示すると、計算が容易になり、この間はパネルの高さが変わりません。日付リストの配列は動的に計算される必要があるため、Vue は計算属性を提供するため、日付リスト dateList は計算属性として直接書き込まれます。私の方法は、日付リストを長さ42の配列に固定し、今月、先月、来月の日付を順番に入力することです。
computed: { dateList () { //获取当月的天数 let currentMonthLength = new Date(this.tmpMonth, this.tmpMonth + 1, 0).getDate() //先将当月的日期塞入dateList let dateList = Array.from({length: currentMonthLength}, (val, index) => { return { currentMonth: true, value: index + 1 } }) //获取当月1号的星期是为了确定在1号前需要插多少天 let startDay = new Date(this.year, this.tmpMonth, 1).getDay() //确认上个月一共多少天 let previousMongthLength = new Date(this.year, this.tmpMonth, 0).getDate() } //在1号前插入上个月日期 for(let i = 0, len = startDay; i < len; i++){ dateList = [{previousMonth: true, value: previousMongthLength - i}].concat(dateList) } //补全剩余位置 for(let i = 0, item = 1; i < 42; i++, item++){ dateList[dateList.length] = {nextMonth: true, value: i} } return dateList }
ここでは、Array.from を使用して配列を初期化し、Array Like を渡し、文字列を結合するときに、arr[arr.length] と [{}].concat(arr) を使用します。 , JsTips からこの方法の方がパフォーマンスが良いことを学んだので、関連リンクを記事の最後に掲載します。
このようにして、日付リストが構築され、テンプレート内の v-for ループを使用してレンダリングされます
<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>
スタイルを自分で使用して、好きなように書くことができます。なお、周期の日付は先月、今月、それぞれpreviuosMonth、currentMonth、nextMonthで表示される場合があるので、他の機能の判断条件を提供します。
年と月のリストは似ていますが、月との一貫性を保つために、年のリストの初期値を最初の値として、v-for を通じて毎回 12 が表示されます。与える。
data () { return { yearList: Array.from({length: 12}, (value, index) => new Date().getFullYear() + index) } }
選択順序は: 年 -> 月 -> 日であるため、状態変数を通じてパネルに表示されるコンテンツを制御し、表示状態を切り替える適切な関数をバインドできます。
<p> <p 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> </p> <p 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> </p> <p 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> </p> </p>
日付の選択方法については詳しく説明しません。select Year と selectMonth の年と月の変数に値を代入し、panelType をそれぞれ次のステップにプッシュして日付選択機能を実現します。
ただし、日付を選択する前に現在の年と月の実際の値を変更したくない場合があるため、これらのメソッドでは、最初に選択した値を一時変数に割り当ててから、すべての値を一度に割り当てることができますseletDate が呼び出されたとき。
selectMonth (month) { if(this.validateMonth(month)){ return }else{ //临时变量 this.tmpMonth = month //切换panel状态 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自动隐藏 this.panelState = false }
最大/最小値は親コンポーネントから渡す必要があるため、props を使用する必要があります。さらに、この値は文字列または変数にすることができます (たとえば、2 つの日付ピッカーがあります)。同時に、2 番目の日付は最初の日付より大きくすることはできないため (このロジック)、値を渡すには動的バインドを使用する必要があります。
<datepicker :value.sync="start"></datepicker> <!-- 现在min的值会随着start的变化而变化 --> <datepicker :value.sync="end" :min="start" ></datepicker>
增加了限制条件,对于不合法的日期,其按钮应该变为置灰状态,我用了比较时间戳的方式来判断日期是否合法,因为就算当前panel中的日期是跨年或是跨月的,通过日期构造函数创建时都会帮你转换成对应的合法值,省去很多判断的麻烦:
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
因此验证日期是否合法的函数是这样的:
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 }
当页面右侧有足够的空间显示时,datepicker的panel会定位为相对于父元素left: 0的位置,如果没有足够的空间,则应该置于right: 0的位置,这一点可以通过Vue提供的动态样式和样式对象来实现(动态class和动态style其实只是动态props的特例),而计算位置的时刻,我放在了组件声明周期的ready周期中,因为这时组件已经插入到DOM树中,可以获取style进行动态计算:
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中对应的动态style --> <p :style="coordinates"></p>
为了panel的显隐可以平滑过渡,可以使用transition做过渡动画,这里我简单地通过一个0.2秒的透明度过渡让显隐更平滑。
<p :style="this.coordinates" v-show="panelState" transition="toggle"></p> //less syntax .toggle{ &-transition{ transition: all ease .2s; } &-enter, &-leave{ opacity: 0; } }
这里其实也很简单,这种多语言切换实质就是一个key根据不同的type而输出不同的value,所以使用filter可以很容易的实现它!比如渲染星期的列表:
<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 } } }
对于一个Vue组件,如果是使用webpack + vue-loader的.vue单文件写法,我希望这样使用:
//App.vue <script> import datepicker from 'path/to/datepicker.vue' export default { components: { datepicker} } </script>
如果是直接在浏览器中使用,那么我希望datepicker这个组件是暴露在全局下的,可以这么使用:
//index.html <html> <script src="path/to/vue.js"></script> <script src="path/to/datepicker.js"></script> <body> <p id="app"></p> <script> new Vue({ el: '#app', components: { datepicker } }) </script> </body> </html>
这里我选择了webpack作为打包工具,使用webpack的output.library和output.linraryTarget这两个属性就可以把你的bundle文件作为库文件打包。library定义了库的名字,libraryTarget定义了你想要打包的格式,具体可以看文档。我希望自己的库可以通过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']} ] } }
打包完成的模块就是一个umd格式的模块啦,可以在浏览器中直接使用,也可以配合require.js等模块加载器使用!
Vue 2.0已经发布有段时间了,现在把之前的组件适配到Vue 2.0。迁移过程还是很顺利的,核心API改动不大,可以借助vue-migration-helper来找出废弃的API再逐步修改。这里只列举一些我需要修改的API。
2.0中的filter只能在mustache绑定中使用,如果想在指令式绑定中绑定过滤后的值,可以选择计算属性。我在月份和星期的显示中使用到了过滤器来过滤语言类型,但我之前是在指令式绑定中使用的filter,所以需要如下修改,:
//修改前 <p class="month-box" @click="chType('month')" v-text="tmpMonth + 1 | month language"></p> //修改后,filter传参的方式也变了,变成了函数调用的风格 <p class="month-box" @click="chType('month')">{{tmpMonth + 1 | month(language)}}</p>
这两个属性不会在v-for中被自动创建了,如需使用,要在v-for中自行声明:
<li v-for="item in monthList" @click="selectMonth($index)"></li> // <li v-for="(item, index) in monthList" @click="selectMonth(index)"></li>
ready从生命周期钩子中移除了,迁移方法很简单,使用mounted和this.$nextTick来替换。
prop的sync弃用了,迁移方案是使用自定义事件,而且Datepicker这种input类型组件,可以使用表单输入组件的自定义事件作为替换方案。自定义组件也可以使用v-model指令了,但是必须满足两个条件:
接收一个value的prop
值发生变化时,触发一个input事件,传入新值。
所以Datepicker的使用方式也不是
//1.x版本,设置了value的值会同步到父级 this.value = `${this.tmpYear}-${('0' + (this.month + 1)).slice(-2)}-${('0' + this.date).slice(-2)}` //2.x版本,需要自己触发input事件,将新值作为参数传递回去 let value = `${this.tmpYear}-${('0' + (this.month + 1)).slice(-2)}-${('0' + this.date).slice(-2)}` this.$emit('input', value)
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上がVue の日付ピッカーのサンプル コードの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。