前段時間專案忙到一半,產品突然說h5頁面要加個top-bar(之前說好的混合開發呢)。WFT,專案做到了一半,突然要加這個; 看了下設計圖,大概是這樣的
經過認真分析,總結出了幾種製作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例項
最後
本文涉及的知識點和細節點還是蠻多的,自己在整理的時候,也認真的消化吸收了一波;有些小知識點,寫起來比較佔篇幅,再者比較時間匆忙,筆者在此就一筆帶過了。
當然,如果大家有更好的解決方法,歡迎留言,一起探討~