本集定位:
聽到計數器這個名字不少人是否是一瞬間沒有什麼印象, 畢竟這個組件用的比較少,就是那種左邊一個'-'右邊一個'+', 控制某些數量的時候纔會用到, 好比我以前作的商城小程序只有'下單'頁面的規格彈出框裏面纔有他的身影, 若是是涉及處處理商品數量很頻繁的業務場景應該會很常見吧, 可是不要看這個組件小, 編寫它的時候坑還很多, 本次咱們就來作一個計數器, 目標就是儘量小, 儘量的省性能.css
先展現一章普通狀態的圖, 讓咱們更直觀的去完成它, 造型比較別緻, 是本套組件的一個特色, 哈哈作的與別人同樣會致使思想的禁錮, 本身寫代碼多嘗試新的東西, 可是工做中必定要中規中矩, 以公司條款爲準則.前端
vue-cc-ui/src/components/InputNumber/index.jsvue
import inputNumber from './main/input-number.vue' inputNumber.install = function(Vue) { Vue.component(inputNumber.name, inputNumber); }; export default inputNumber
vue-cc-ui/src/components/InputNumber/main/input-number.vuegit
<template> <div class="cc-input-number"> // 左側的'➖'符號 <div class="cc-input-number__reduce"> // 本身封裝的icon組件, 出鏡率還挺高😼. <cc-icon name='cc-reduce2'/> </div> // 中間的顯示與輸入部分,讓人又愛又恨的number屬性 // 下面的屬性就能幹掉凡人的上下按鈕 // input::-webkit-outer-spin-button, // input::-webkit-inner-spin-button { // -webkit-appearance: none; // } <input ref="input" type="number" class="cc-input-number__input"> <div class="cc-input-number__add"> <cc-icon name='cc-add2'/> </div> </div> </template>
這裏咱們選擇吧input與button放在一個div裏面, 且同級別這種方式, 與其餘的不太同樣, 由於這樣更直觀, 並且也足夠實現我想要的功能.github
// 減小 <div class="cc-input-number__reduce" @click='reduce'> // 增長 <div class="cc-input-number__add" @click="add"> // 輸入框的監控 <input ref="input" type="number" class="cc-input-number__input" @input="inputChange($event)">
這裏咱們有個問題, 就是本組件採用的是v-model的形式編寫, v-model有一些弊端, 在測試的時候我發現, 好比說用戶爲多個組件綁定了相同的v-model會致使無限渲染的bug, 下面會解讀解決這類bug的相關代碼.web
prpos數據庫
props: { max: { type: Number }, // 數字不傳默認是undefined min: { type: Number }, step: { // 每次計算的單位 type: Number, default: 1 }, value: { // 綁定的數值, 這裏容許兩種type, 爲了方便用戶書寫,具體判斷下面咱們本身寫 type: [String, Number], required: true }, precision: { // 顯示小數點後幾位數 type: Number, validator(value) { if (value < 1 || value === undefined) { return 1; } else { return parseInt(value); } } } },
add 方法的實現小程序
add() { // 極可能用戶就輸入了一個string屬性, // 1: 好比後臺返回的就是字符串; // 2: input框輸入的就是字符串類型; // 3: 用v-model綁定了一樣的值的其餘組件賦予了這個值string類型; let num = Number(this.value) + this.step; // 加上固定的長度 // 這裏咱們抽象出一個專門負責數值的變化的函數 this.emitVal(num); },
reduce 方法的實現app
reduce() { let num = Number(this.value) - this.step; this.emitVal(num); },
監聽input框的輸入事件dom
inputChange(e) { // 這裏就有可能出現string類型的了 this.emitVal(Number(e.target.value)); },
關鍵性的賦值函數
emitVal
emitVal(newVal) { let { max, min } = this; // 不傳參數的時候默認值就是undefined // 對這個值的限制就是, max以內, min以上 if (max !== undefined && newVal > max) newVal = max; if (min !== undefined && newVal < min) newVal = min; // 這裏兼容一下位數控制 let value = Number(newVal).toFixed(this.precision); // 這個oldVal下面會解釋👇 if (value === this.oldVal) return; this.oldVal = ls; // 發出兩個事件, 一個負責改變value, 一個負責返回給用戶 // 畢竟用戶不可能監聽input事件而後再把值附上去, 太麻煩 this.$emit("input", value); this.$emit("change", value); // 這一步很重要 // 下面會詳細說 this.$refs.input.value = value; }
上面遺留的問題,這裏解釋一下.
上面的兩個問題都是涉及到v-model的問題, 下面還有一個同類的問題, 咱們來看看.
對value進行的監控
由於value的變化, 不必定全是 經過+-輸入這三種方式, 還有第三方經過v-model的方式, 還有用戶手動亂填的方式.
watch: { value: { handler() { // 爲了解決, 多組件共同v-model採用的這個方法, 也算是另闢蹊徑了 let { value, time } = this; clearTimeout(time); // 畢竟把它放入宏任務Macrotasks能夠躲過不少無限循環. time = setTimeout(() => { if (value !== undefined) this.emitVal(value); }); }, // 這個是開啓進頁面的瞬間就出發一次的意思, 頗有用, 可是數據稍大會消耗性能, 慎用 // watch還有一個deep屬性, 更是吃性能吃的厲害, 能夠深度監控裏面的數據 immediate: true } }
上面的問題都是基於v-model的, 因此很早就有人剔除雙向綁定的壞處, 封裝越多的組件感受就越明顯.
在計算屬性裏面咱們隊當前值進行了監控, 返回的是置灰的顏色, 這個讓用戶自定的意義不大, 因此直接寫了.
computed: { valueMin() { if (this.value === this.min) return "#bbbbbb"; return ""; }, valueMax() { if (this.value === this.max) return "#bbbbbb"; return ""; } },
dom, 點擊到了最大值的話就會置灰, 咱們上面已經阻止了繼續點擊的渲染
<cc-icon size='25px' name='cc-add2' :color="valueMax" />
作點有意思的事
slot是個自由度很高的標籤
把左右按鈕都包上, 讓用戶能夠本身定義顯示的標籤是什麼樣子的
<div class="cc-input-number__reduce" @click='reduce'> <slot name='left'> <cc-icon size='25px' name='cc-reduce2' :color='valueMin' /> </slot> </div>
vue-cc-ui/src/style/inputNumber.scss
@import './common/var.scss'; @import './common/extend.scss'; @import './common/mixin.scss'; @import './config/index.scss'; @include b(input-number) { // 友好的小手 cursor: pointer; // 有個放大動畫, 看過我文章的同窗都知道, 操做類的組件, 我喜歡有一個懸停放大效果. transition:all .1s; align-items: center; display: inline-flex; background-color: white; &:hover { // 放大被其餘組件擋住就划不來了 z-index: 6; transform: scale(1.2); } // 招牌陰影 @include commonShadow($--color-black); @include e(add) { @include flexCenter(); padding: 4px 6px; } @include e(reduce) { @include flexCenter(); padding: 4px 6px; } @include e(input) { // 去掉輸入框的默認樣式 border: none; outline:none; display: block; text-align: center; width:60px; height: 20px; } }
效果展現
end
總的來講是這些組件中比較簡單的一個了, 有些坑可以讓我更好的學習vue以及前端的思想, 總的來講挺有趣的.
你們繼續一塊兒學習,一塊兒進步, 早日實現自我價值!!
下一集準備聊聊 tab切換組件的相關知識;
github:連接描述
我的博客: 連接描述