告別原生,打造自己的topBar

神經叨發表於2019-04-09

前段時間專案忙到一半,產品突然說h5頁面要加個top-bar(之前說好的混合開發呢)。WFT,專案做到了一半,突然要加這個; 看了下設計圖,大概是這樣的

img
經過認真分析,總結出了幾種製作top-bar的方法;

方法一

直接在view-router外新增,在路由處配置是否顯示該top-bar即可 路由配置

{ path: 'xxx', name: 'xxx', component: xxx, meta: { title: 'xxxxx', topBar: false } } // topBar 為false則不顯示
複製程式碼

top-bar元件大致如下

<template>
  <div class="top-bar" v-if="$route.meta.topBar !== false">
    {{$route.meta.title}}
  </div>
</template>
<script>
export default {
  name: 'topBar'
}
</script>
<style lang="sass" scoped>

</style>
複製程式碼

然後在app.vue內引入TopBar元件

<template>
  <div id="app">
    <top-bar></top-bar>
    <router-view class="container"></router-view>
  </div>
</template>

<script>
import TopBar from '@/components/common/TopBar'

export default {
  name: 'App',
  components: {
    TopBar
  }
}
</script>
複製程式碼

優點

  • 直接在vue入口配置,直觀
  • 通過路由傳參控制其顯示隱藏,配置簡單
  • 當前專案程式碼改動小

缺點

  • 位置相對固定,不能放在router-view,會被router-view內的元件覆蓋掉
  • 無法動態修改title內容(原因是meta物件為只讀),若想動態修改title,需脫離vue環境來操縱dom

方法二

全域性註冊top-bar元件,在需要的頁面呼叫; index.js內新增

import TopBar from '@/components/common/TopBar'
Vue.components(TopBar)
複製程式碼

頁面內,將meta 賦值給當前頁面的 page 物件

<top-bar>{{page.title}}</top-bar>
複製程式碼

路由配置不變

優點

  • 在router-view內外皆可使用
  • 可動態修改title值
  • 操作簡單,新手必備

缺點

  • 使用起來比較麻煩,每次都需要手動呼叫
  • 每個都頁面要手動去存一個物件,用來改變title的值,重複程式碼多
  • 當前專案程式碼改動大

方法三

建立一個TopBar的元件構造器,需要extend,在每次路由跳轉的時候,生成一個構造器例項;此方法的難點在於如何在路由跳轉的時候,獲取TopBar元件的例項;有以下注意點

  • 獲取元件例項:這裡我們通過 $mount方法來獲取元件例項;$mount會在vue內呼叫compile方法;編譯後,我們便可拿到$el;從而進行手動掛載;
  • 模板資料:在beforeRouteEnter鉤子函式內獲取
  • 每個page都要執行,因此將此方法放到全域性的mixin內,頁面載入及路由跳轉的時候,自動執行

關於$mount用法,可以參考官網 點選這裡

mixin 如下, topBarMixin.js

import Vue from 'vue'
import '@/style/topBar.scss'

let TopBar = null
let App = null

const getApp = function (vm) {
  App = vm.$el
  return App
}

const clearTopBar = function () {
  if (TopBar) { // 先清掉之前的,防止keep-alive的cache產生多個topbar
    const topBarElement = TopBar.$el
    const parentNode = topBarElement.parentNode
    parentNode.removeChild(topBarElement)
  }
}

const compile = function (meta) {
  const show = (meta.topBar !== false)
  const title = meta.title || ''
  if (!show) {
    return
  }
  clearTopBar() // 先清除之前的TopBar
  const TopBarComponent = Vue.extend({
    data () {
      return {
        show,
        title
      }
    },
    template: `  // 模板
    <div class="xsj-top-bar" v-if="show">
      <div class="xsj-top-bar-content">
        <div class="top-bar-arrow" @click="back">
          <icon name="return" class="return"></icon>
        </div>
        <div class="top-bar-title">{{title}}</div>
      </div>
    </div>`
  })
  TopBar = new TopBarComponent().$mount() // 掛載獲取例項
  if (App.nodeType === 1) {
    App.insertBefore(TopBar.$el, App.firstChild)
  } else {
    throw new Error('無法給非元素節點增加topbar')
  }
}

export default {
  methods: {
    _resetTitle (title = TopBar.title) { // 可手動呼叫修改當前title的內容
      this.$nextTick(() => { // 在頁面模板掛載上去以後,執行
        if (TopBar.show) {
          TopBar.title = title
          document.title = title
        }
      })
    }
  },
  beforeRouteEnter (to, from, next) {
    const meta = to.meta
    next(vm => {
      getApp(vm)
      compile(meta)
    })
  }
}

複製程式碼

index.js 內直接呼叫Vue.mixin(topBarMixin)

優點

  • 通過路由傳參控制其顯示隱藏,配置簡單
  • 當前專案程式碼改動小

缺點

  • TopBar為動態插入,所以會引起頁面重排
  • 對於有keep-alive的頁面,需要清除之前的TopBar,不然每次enter就會增加一個TopBar例項

方法四

方法四和方法三實現邏輯差不多,只不過把元件構造器換為元件例項TopBar.vue;

  • 元件例項 引入元件,$mount掛載獲取例項
  • 資料方面:由於TopBar無法拿到當前route的資訊,所以需要通過props傳入

TopBar.vue如下

<template>
  <div class="top-bar" v-if="bar.topBar !== false">
    {{bar.title}}
  </div>
</template>
<script>
export default {
  name: 'TopBar',
  props: {
    bar: {
      type: Object,
      default () {
        return {}
      }
    }
  }
}
</script>
複製程式碼

mixin 如下, topBarMixin.js檔案

import Vue from 'vue'
import TopBar from '@/components/common/TopBar'  // 引入TopBar元件

let App = null

const getApp = function (vm) {
  App = vm.$el
  return App
}
export default {
  name: 'AddTopBar',
  beforeRouteEnter (to, from, next) {
    next(vm => {
      getApp(vm)
      const tb = new Vue(TopBar).$mount()  // 掛載獲取例項
      if (App.nodeType === 1) {
        clearTopBar()  // 先清掉之前的TopBar,防止keep-alive的cache產生多個topbar
        App.insertBefore(tb.$el, App.firstChild)
        tb._props.bar = to.meta  // 將路由資料傳給top-bar
      } else {
        throw new Error('無法給非元素節點增加topbar')
      }
    })
  }
}
複製程式碼

入口處呼叫 Vue.mixin(topBarMixin) 即可

優點

  • 通過路由傳參控制其顯示隱藏,配置簡單
  • 當前專案程式碼改動小
  • 可手動設定props,動態修改title
  • 元件分離出來,便於維護和擴充套件

缺點

  • TopBar為動態插入,所以會引起頁面重排
  • 對於有keep-alive的頁面,需要清除之前的TopBar,不然每次enter頁面就會增加一個TopBar例項

最後

本文涉及的知識點和細節點還是蠻多的,自己在整理的時候,也認真的消化吸收了一波;有些小知識點,寫起來比較佔篇幅,再者比較時間匆忙,筆者在此就一筆帶過了。

當然,如果大家有更好的解決方法,歡迎留言,一起探討~

相關文章