vue移動助手實踐(一)——基於vue的換膚功能

最近在作的一個幾月vue的移動端小demo,其中有一塊是實現各個頁面的統一換膚功能的。想着寫一篇文章,來寫一寫實現過程當中遇到的一些問題。javascript

項目在線demo

項目在線演示demo(切換到移動端調試模式哦)css

項目github地址

項目github地址vue

一 先看一下實現效果吧

設置主題顏色
講道理這麼一個功能,我以爲這麼幾點能夠說下,分步實現:
1. 色值的選取
2. scss 的一些小衆用法(多變量CSS值的批量設置)
3. 全局事件巴士的應用java

1 色值的選取和原則

推薦你們看下螞蟻金服的設計指引,裏面對常見的交互和界面設計有一套不錯的指引和建議,喜歡看書的也能夠看看《寫給你們看的設計書》。
對於界面中的色彩元素,咱們通常要保持視覺的連續性,即同一套色彩,儘可能採起同一個色環上的色值ios

同一個圓環上的色值做爲一套顏色會顯得更協調

因此這裏採起ant design 的建議,取某一列色值做爲咱們的系列主題顏色(具體色值參照它的官網吧~)git

而在某些特殊場合,須要表現出顏色的差別,如拋硬幣頁面的兩個顏色,
程序員

2 將格式色值轉換成十六進制顏色值

這裏咱們經過設置主題顏色的透明度來實現區分不一樣顏色, 而後咱們是經過存儲一個諸如#123456的16進制顏色全局變量做爲咱們主題,這裏就須要咱們把這樣一個格式的色值轉化成 rgba 表示的顏色值啦,代碼以下,備用github

hexToRgba (hex, opacity = 0.3) {
    let color = []
    let rgb = []
    hex = hex.replace(/#/, '')
    for (let i = 0; i < 3; i++) {
      color[i] = '0x' + hex.substr(i * 2, 2)
      rgb.push(parseInt(Number(color[i])))
    }
    return `rgba(${rgb.join(',')},${opacity})`
  }複製代碼

##3 scss 的一些小衆用法
咱們最終拿到這麼一串咱們想要的主題顏色vuex

$colors: #f04134, #00a854, #108ee9, #f5317f, #f56a00, #7265e6, #ffbf00, #00a2ae, #2e3238;複製代碼

一個很直接的思路,咱們須要在各個view頁面裏面,去定義咱們須要設置主題的元素的顏色,好比文字和icon的color, 以及頭部的background 等。 因而咱們在app 裏面定義一個color變量,派發到各個view組件裏面去,經過這個全局的變量來控制全部路由頁面的顏色,以實現不一樣的主題效果。
派發的實如今下一個部分說,這裏咱們先來完成咱們的第一步,咱們能夠容易提取出咱們的需求:
4 設置並保存一個全局顏色
界面的小事:
我在首頁直接實現這個功能,項目中我引入了mint-ui 框架(餓了麼團隊的移動端框架,稍微遺憾使用感受沒有element.ui 的舒服), 設置的交互就用彈層 mt-popup 的形式好了,而後直接點擊色塊便設置對應顏色值數據庫

<!-- 設置顏色 -->
    <mt-popup v-model="changColor" position="bottom" class="color-panel">
      <div class="color-items">
          <span class="color-item" v-for="(item, $index) in colors"  :key="$index" @click="chooseColor(item)">
            <span class="color-cycle" :class="'bg-color' + ($index + 1)"></span>
            </span>
      </div>
    </mt-popup>複製代碼

接着就是色塊div的呈現,從上面代碼發現,我會很容易出現相似這樣的css樣式表

.bg-color1 {background: #f04134}
.bg-color2 {background: #f04134}
.bg-color3 {background: #f04134}
.bg-color4 {background: #f04134}
···複製代碼

寫代碼時候若是咱們通常發現,一件相似的東西重複出現了,就總隱隱以爲能夠開始表演了,而後可預見的是,這樣的狀況意味着在項目增加後,還可能出現許多單一設置字體顏色或border顏色的樣式表,諸如color1, borderColor1···,這樣每種形式的表現咱們都須要根據咱們主題顏色的數組去逐條書寫,修改爲本也會變高 。因而個人書寫風格是這樣的,

// mixin.scss:
$colors: #f04134, #00a854, #108ee9, #f5317f, #f56a00, #7265e6, #ffbf00, #00a2ae, #2e3238;

// setColor.vue:
@import '~@/assets/mixin.scss';
···
@for $i from 1 to 10 {
        .bg-color#{$i} {
          background-color: nth($colors, $i)
        }
      }複製代碼

scss 除了經常使用的類名嵌套書寫外,還有許多···低調奢華的語法, 對於這類須要重複書寫的樣式類型,個人約定是添加一個scss變量在mixin 文件中, 在須要書寫重複循環樣式時候做爲變量引入,並在書寫樣式時候,利用sass的循環,引用其中對應的值,這樣不管設置顏色的樣式怎麼拓展和變化,變成顏色背景邊框都好,我都只須要維護一份mixin的的文件裏的色值就好了, 一樣的實踐也能夠應用於項目裏面字體大小和間距值的統一之類,總之咱們多嘗試體驗下吧

5 邏輯的小事

這個項目裏面localstorage 基本被當成數據庫使用了,因此點擊色塊設置主題時候,咱們僞裝發出請求,在localstorage存儲咱們改變的顏色就行了( ./static/api.json 是一個返回helloword 的json, 爲了寫實在這裏這麼用,$bus 事件巴士下面說, 做用就是設置全局的主題顏色變量,localStorage 模擬咱們把設置存儲到後臺,每次從新打開頁面就去獲取這些設置值), 目前爲止,咱們的設置頁面就大體完成了

// 僞裝調用接口設置顏色
    chooseColor (color) {
      this.$axios.get('./static/api.json')
        .then((data) => {
          this.$bus.$emit('set-theme', color)
          this.changColor = false
          localStorage.setItem('themeColor', color)
        })
        .catch((data) => {
          console.log(data)
        })
    }複製代碼

6 事件巴士的運用

在上一步最後咱們有個關鍵的東西沒完成, this.$bus.$emit('set-theme', color),將選取的顏色設置到全局,個人代碼結構是這樣的

子組件
子組件

<setColor>home 頁面的一個子組件,而在一開始咱們已經說了,咱們想在咱們在app.vue (home.vue和其餘view的父組件) 裏面定義一個color變量,派發到各個view組件裏面去。 因而這其實就是個,從setColor 觸發app.vue的設置顏色事件, 子組件向父組件通訊的問題。

咱們能夠很直接地用綁定事件配合emit()的作法,在app.vue 定義一個setglobalColor方法, 並綁定到router-view(包含了home.vue),接着在home組件繼續定義一個setglobalColor方法, 實現的功能就是 emit('setglobalColor') 去觸發app.vue的方法, 並把home.vue的這個setglobalColor繼續綁定到組件, 組件裏面點選顏色時候,直接emit這個方法就好了。
爲何我想用事件巴士 .vue 的事件巴士和 vuex, 在一些有追求的程序員手裏老是當心翼翼的,我也同樣,由於做爲涉及全局的東西,通常以爲能不用就不用,代碼能精簡就精簡,咱們常常用一個詞,不提倡。
但是有朝一日我常常在想,代碼的可讀性可維護性,和性能以及「風險」相對比,到底哪一個更重要。對於事件巴士和vuex 這類全局性質的方案的主要擔心大部分在於, 他們是全局的,可能由於一個事件名變量名一致就形成衝突,在小型項目還會形成冗餘和額外開銷。 但事實上,事件和變量的命名咱們均可以經過約定去規範,而在表現上,使用了事件巴士和vuex的項目,在性能上和直接props傳遞數據,emit 回調事件的項目相比,其實並無太大區別,反而是無止境的props 和 emit,給人一種麻煩難以維護的感受。 像上述的setglobalColor, 僅僅是跨越了兩層組件, 過程就顯得繁瑣了。因此我建議在出現兩級以上組件層次,數據流稍微多的項目中均可以這麼去作,定義一個全局的事件巴士

export default (Vue) => {
  let eventHub = new Vue()
  Vue.prototype.$bus = {
    $on (...arg) {
      eventHub.$on(...arg)
    },
    $off (...arg) {
      eventHub.$off(...arg)
    },
    $emit (...arg) {
      eventHub.$emit(...arg)
    }
  }
}複製代碼

將事件巴士綁定到當前vue對象,使用時候只須要:

this.$bus.$on('set-theme', (color) => {··· })
this.$bus.$emit('set-theme', '#000000')複製代碼

在這個demo中,我在app.vue 綁定了

this.$bus.$on('set-theme', (color) => {
      this.loadingColor = color
      this.userinfo.color = color
    })複製代碼

而在setColor.vue則在點擊顏色塊時候觸發 this.$bus.$emit('set-theme', color), 則能實現咱們設置全局顏色的效果。這樣的好處在於,對於跨了多個層次,或者兄弟組件的通訊,咱們再也不須要太繁瑣的props,好比我在header.vue 也綁定了this.$bus.$on('set-theme', (color) => { }),在 this.$bus.$emit發生時候,header 的背景顏色就能直接改變,而不須要等待app.vue 將 全局的color值props傳遞到header.vue裏面(僅作示例,這裏header.vue只是app.vue的下一層級,經過props數據流會更清晰)
而對於其餘路由頁面組件,和app.vue 都是直接上下級關係,咱們依然採用props保持一個清晰的數據流向下傳遞,demo裏我是將color存在userinfo(之後還有其餘數據), userinfo傳到每一個子路由, 最後,每一個頁面在建立時候,經過拿到這個全局的顏色,再用dom去更改對應的樣式就好啦,例如

mounted () {
    this.$nextTick(() => {
      // 綁定設置主題的事件,一旦觸發修改主題,則將當前字體顏色改成對應顏色
      this.$el.querySelector('.myTitle').style.color = this.userinfo.color
      this.$el.querySelector('.weui-btn_primary').style.backgroundColor = this.userinfo.color
      this.$el.querySelector('.add_icon').style.color = this.userinfo.color
    })
  }複製代碼

詳細的實現請參照項目代碼,這裏我只挑一些比較清奇的點出來討論,項目和代碼的一些規範和習慣仍是挺重要的,但願有好的實踐能互相借鑑進步~

我是KimyAndKath ,但願好東西一塊兒分享。

項目在線demo

項目在線演示demo(切換到移動端調試模式哦)

項目github地址

項目github地址

相關文章
相關標籤/搜索