包含內容#NavigationBar
、#TabBar
、#MainContext
;css
爲何#NavigationBar
、#TabBar
分在Layout
中,而不是components
中?vue
代碼上其實是沒有差異的,只是認爲#NavigationBar
、#TabBar
是加載一次的,而非複用,且屬於頁面佈局內容。webpack
Vue實例化的根組件,咱們在這裏進行佈局:web
src/App.vue
文件:json
<template> <div id="app"> <navigation-bar></navigation-bar> <router-view></router-view> <tab-bar></tab-bar> </div> </template> <script> import TabBar from "@/views/layout/TabBar"; import NavigationBar from "@/views/layout/NavigationBar"; export default { name: 'App', components: { 'navigation-bar': NavigationBar, 'tab-bar':TabBar, } } </script> <style> ...//未動原有樣式; </style>
<template />
標識 其內部的HTML爲Vue Template。<template />
內部必有一個且惟一的節點(這裏是div#app
)包裹內容(即便只是一串字符)-->若存在同級節點,則會報錯(這是由於VNode會經過createElement('div')來建立真實節點,只能是單個元素);components
屬性以鍵值對的形式引入組件,模板(HTML)中使用的標籤名爲鍵名(自定義元素VNode),值爲導入的組件模塊;components
定義組件使用的方式,限制了組件應用的範圍。即:若是你在其它文件直接使用<navigation-bar></navigation-bar>
,控制檯會報錯:組件未註冊-->這就是組件的局部註冊。components
註冊一次。import TabBar from "@/views/layout/TabBar";
路徑以@
起,這是由於build/webpack.base.conf.js
中配置的路徑別名'@' === "resolve('src')"
:resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), //能夠追加當前項目下,想快捷訪問的文件目錄 } },
臨時定義的組件文件:segmentfault
src/views/layout/NavigationBar.vue
文件:數組
<template> <header>NavigationBar</header> </template> <style scoped> header{ border-bottom: 4px solid #821910; } </style>
style[scoped]
定義一份樣式,其做用範圍僅限於當前文件(又可稱模塊)模板中的元素。TabBar.vue
中的header
元素就沒有使用到該文件中的對應樣式,這就是局部做用域的樣式。<template/>
中的元素起做用,想改變body
的樣式,很差意思,請全局導入或不使用局部做用域。src/views/layout/TabBar.vue
文件:app
<template> <div> <header>測試是否和NavigationBar同樣的效果</header> <footer>TabBar</footer> </div> </template> <style scoped> footer{ border-bottom: 4px solid #07776e; } </style>
#NavigationBar
中分左右結構,左邊按鈕後退,右邊按鈕更新頁面。ide
更新頁面只是更新數據,而不是整個頁面的刷新,每一個頁面更新數據的接口不一樣,因此,要做爲組件屬性傳入。函數
在src/views/layout/navigationBar.vue
中:
<template> <header class="navigation_bar"> <button @click="goBack" class="navigation_back"> <i class="arrow"></i> <span class="back_tip">關閉</span> </button> <h2 v-if="hasTitle" class="navigation_title">{{title}}</h2> <button class="refresh" @click="onRefresh">刷新</button> </header> </template>
#NavigationBar
的Template部分。@click
是v-on:click
的簡寫,用於綁定點擊事件。v-if
是Vue
中的條件指令,根據返回的布爾值動態添加或移除DOM元素。<script> /** * @title:頭部標題; * @refresh:刷新處理函數; */ export default { props: ['title', 'refresh'], computed: { hasTitle() { return this.title && this.title.trim(); }, }, methods: { goBack() { this.$router.back() }, onRefresh() { this.refresh(); }, }, } </script>
#NavigationBar
的組件配置對象。props
爲父級(調用該組件的組件)傳過來的屬性。
傳值方式<navigation-bar title="我是標題" :refresh="refresh"></navigation-bar>
(須要在src/App.vue
中定義refresh
函數)
title
傳的值爲字符串,不須要:
前綴;:refresh
傳的值爲非字符串(數字、布爾值、函數、數組、對象...),:
爲前綴,值爲Javascript表達式計算結果;this.title
引用props
的值。{{title}}
引用。methods
爲該組件內,元素綁定的事件處理函數。
this.refresh()
引用。@click="onRefresh"
調用,傳入的是函數應用;若傳參,如@click="onRefresh(param)"
調用。computed
自己寫法和函數定義一致,然而,其自己是一個data
(數據源),字段名爲函數名,值爲函數的返回值。
props
一致。區別 | method | computed |
---|---|---|
類型 | 函數 | 數據變量 |
參數 | 能夠帶參 | 不帶參(非函) |
觸發 | 交互時觸發 | 聲明內部的this屬性的值變化時執行 |
這裏樣式請你們隨意設定,我使用的是flexBox佈局。
點擊刷新,我定義了console.log('refresh success')
。
#TabBar
分如下狀況:
每一個視圖中#TabBar
按鈕是不一樣的,因此,按鈕的配置要看成組件屬性傳入(控制變化的量)。
const tabBars = [ { label: '提交', eventType: 'click', disabled: false, callBack(vm) { console.log('單擊,提交'); } }, { label: '取消', eventType: 'dblclick', //該事件在手機模式下沒法響應呢,只能在PC模式下調試 disabled: false, callBack(vm) { console.log('雙擊,取消'); } } ]
src/views/layout/TabBar.vue
的模板:
<template> <footer class="tab-bar" v-if="isRender"> <div class="tab-button" v-for="tab in tabBars" :key="tab.label"> <template> <tab-button :el="$parent" :disabled="tab.disabled" :event="tab.eventType" :callBack="tab.callBack"> <span>{{tab.label}}</span> </tab-button> </template> </div> </footer> </template>
v-for="tab in tabBars"
是Vue
中的循環結構,搭配:key
使用,優化Vue
的渲染機制;
tabBars
進行遍歷,tab
爲數組中的元素。key
值,在更新時,會複用組件,而不是銷燬後,再建立一個新的組件。<tab-button :el="$parent" :disabled="tab.disabled" :event="tab.eventType" :callBack="tab.callBack">
這是是一個新組件的引用。
$parent
是組件實例#TabBar
的父實例(#App
)。src/views/layout/TabBar.vue
組件配置對象:
<script> const tabButton = { render(createElement) { return createElement( 'button', { "class": this.className, on: { [this.event]: this.tabClick, }, }, this.$slots.default, //指代<span>{{tab.label}}</span> ) }, props: ['event', 'callBack', 'disabled','el'], computed: { className() { return this.disabled ? 'tab-label disabled' : 'tab-label'; } }, methods: { tabClick() { if (this.disabled) return; this.callBack && this.callBack(this.el) } } } export default { components: { 'tab-button': tabButton }, props: ['tabBars'], computed: { isRender() { const isRender = _.isArray(this.tabBars) && this.tabBars.length !== 0; return isRender; }, }, } </script>
這裏使用了另外一種方式定義組件tabButton
,其與 單文件組件 的區別僅僅在於使用render
方法定義模板。
優點:定義出來的組件更具備靈活性,在這裏on
屬性能夠動態綁定事件類型。
[this.event]
是做爲參數傳進來的呢!Vue
規定的成員屬性構建,區別只在於Template
的寫做模式。underscore.js
(_.isArray
),須要在build/webpack.base.conf.js
中配置:const webpack = require('webpack'); ... module.exports = { ... module:{ ... }, plugins:[ new webpack.ProvidePlugin({ _: 'underscore', }), ], ...
而後,underscore
在全局可用。
由於這裏的配置對dev
和prod
環境是一致的,因此,直接在build/webpack.base.conf.js
中配置了。
最終,咱們要作一個頂天立地的內滾動結構(使用flexBox佈局便可):
src/App.vue
樣式中:
<style lang="scss" scoped> @import "@/styles/mixins.scss"; #app { @include flex($direction: column); } .main-context { flex-grow: 1; overflow: hidden; overflow-y: auto; } </style>
其中src/styles/mixins.scss
:
@mixin flex($direction:row, $alignItems: stretch, $justifyContent: space-between, $basis: auto,$wrap:nowrap) { display: flex; flex-direction: $direction; align-items: $alignItems; justify-content: $justifyContent; flex-basis: $basis; flex-wrap: $wrap; }
#NavigationBar
、#TabBar
替換原臨時搭建的對應組件,我相信你能處理好,對吧?!#App
小節中,是怎樣註冊局部組件的,若是想要在項目全部模板中能夠直接使用標籤名來應用組件,該怎麼處理呢?#App
小節中,如何定義局部樣式的,若是想讓app.vue
中header的樣式全局可用,該怎麼處理呢?render
函數如何渲染組件模板,使用該方法如何定義組件?slot
用於什麼狀況下呢,有什麼好處?