生活少不了趣味,趣味活在人間。每一天都是快樂的,happy every day.
各位掘友,又是新的一週,大家週末過得很happy吧!週末,小編利用閒暇的時間,開源一個趣味生活小型vue項目。整個小項目作下來以後還真收穫很多呢。javascript
你將學到:css
項目預期效果,小編帶你們瞅瞅!順便,隨手點贊支持下做者💓
在階段一中,主要難點設計是:在於tabBar和headerBar部分,由於你要考慮怎麼讓兩個導航欄同時在一個頁面出現吶並且還不會影響icon的顏色問題。html
因此,此時你就須要想到使用 組件化 來解決 tabBar和headerBar共處問題。過程以下:vue
首先,將tabBar單獨拿出來作成一個組件,若是你以爲tabBar須要更靈活些,你能夠在父組件中動態綁定數據傳值處理。具體tab.vue組件設計以下:java
<template> <div class="tab"> <router-link to="/recommend" tag="div" class="tab-item"> <div class="icon"></div> <div class="tab-link">推薦</div> </router-link> <router-link to="/img" tag="div" class="tab-item"> <div class="icon"></div> <div class="tab-link">趣圖</div> </router-link> <router-link to="/mine" tag="div" class="tab-item"> <div class="icon"></div> <div class="tab-link">個人</div> </router-link> </div></template><script>export default {}</script><style lang="stylus" scoped>@import '../assets/css/function.styl'.tab display flex position absolute z-index 150 bottom 0 left 0 width 100% background-color #fff border-1px(#F5F5F5) font-size 12px height px2rem(140px) &-item flex 1 text-align center .icon margin-top px2rem(15px) .tab-link padding-bottom 5px color #000 &.router-link-active .tab-link color #1E90FF .icon color #1E90FF</style>複製代碼
在父組件中使用以下:ios
<template> <div id="app"> <!-- tabBar --> <v-tab></v-tab> <router-view/> </div></template><script>import tab from '@/components/tab'export default { name: 'App', components: { 'v-tab': tab }}</script><style></style>複製代碼
by the way,爲啥tab.vue放在App.vue中呢?這裏固然是爲了讓每一個頁面均可以經過點擊相應的tabBar進行跳轉啦。css3
個人部分git
各位,這裏須要注意啦!,在mine.vue中也有一個headerBar,該欄是是什麼用途吶?以下圖:github
在【個人】-的headerBar其做用就是爲了,擴展在【個人】頁面中的子頁面跳轉,好比【設置】、【消息】等功能模塊。headerTab設計具體以下:vue-router
<template> <div class="mHeader"> <div class="mHeader-icon" @click="leftEvent"> <slot name="left-icon"></slot> </div> <div class="mHeader-cont"> <slot name="content"></slot> </div> <div class="mHeader-icon"> <slot name="right-icon"></slot> </div> </div></template><script>export default { name: 'hd', data () { return { } }, methods: { leftEvent () { this.$store.dispatch('setShowSidebar', true) } }}</script><style lang="stylus">@import "../assets/css/function" .mHeader height px2rem(88px) line-height px2rem(88px) display flex align-items center justify-content space-between color #746ca8 font-size px2rem(30px) &-icon flex 0 0 px2rem(88px) margin-top px2rem(6px) cursor pointer .icon font-size px2rem(48px) .left margin-left px2rem(20px)</style>複製代碼
此外,須要再提到的一點就是:<slot name="left-icon"></slot>的具名使用,和不具名使用,當你組件中只有一塊內容須要父組件來填充時,你就是用不具名slot,當組件中須要填充多塊內容時,你就使用具名slot。案例以下:
父組件具名使用slot:
<!-- 頭部 --> <v-header> <i class="icon" slot="left-icon"></i> <span slot="content">個人音樂</span> <router-link to="/user" slot="right-icon"> <i class="icon"></i> </router-link> </v-header>複製代碼
封裝的header組件的slot:
<template> <div class="header"> <div class="header-icon" @click="leftEvent"> <slot name="left-icon"></slot> </div> <div class="header-cont"> <slot name="content"></slot> </div> <div class="header-icon"> <slot name="right-icon"></slot> </div> </div></template>複製代碼
在階段二中主要有兩個重點一個技巧內容,其一就是: axios封裝;其二就是:跨域數據請求;其三就是:藉助better-scroll下拉頁面加載新數據實現的技巧
axios封裝
爲何作axios封裝技術封裝,不用問,問就是爲了開發方便,以及統一化管理數據請求接口,並且方便後期維護項目。
那麼如何來封裝axios吶?很簡單的!
第一步,在項目的src下新建一個api文件夾,而且新建一個index.js文件;第二步,導入Vue和axios;第三步,new 一個 Vue實例;第四步配置axios具體配置以下:
// axios 配置axios.defaults.timeout = 10000// 請求不能超時10S// axios.defaults.baseURL = '/api'axios.defaults.headers.post['Content-Type'] = 'application/json';// 判斷返回狀態, 響應攔截axios.interceptors.response.use((res) => { // 請求不成功時 if (res.status !== 200) { alert('網絡異常') // Promise 有兩個參數 reject, resolve return Promise.reject(res) } return res}, (error) => { alert('服務器開小差了') return Promise.reject(error)})// 請求攔截 - 暫時不寫// ...複製代碼
第五步,導出你須要請求的數據api方法,具體邏輯以下:
export function fetchGet (url, param) { return new Promise((resolve, reject) => { axios.get(url, { params: param }) .then(response => { resolve(response.data) }, err => { reject(err) }) .catch((error) => { reject(error) }) })}export default { // 按更新時間查詢笑話 GetJokeByTime (params) { return fetchGet ('/api/Joke/QueryJokeByTime', params) }, // 最新笑話 NewJoke (params) { return fetchGet ('/api/Joke/NewstJoke', params) }, // 按更新時間查詢趣圖 GetImgByTime (params) { return fetchGet ('/api/Joke/QueryImgByTime', params) }, // 最新趣圖 NewImg (params) { return fetchGet ('/api/Joke/NewstImg', params) }, HeadLine (params) { return fetchGet ('/api//TouTiao/Query', params) },}複製代碼
以上便完成了axios封裝,下面說下在組件中封裝後的使用
在你須要使用axios封裝的api的組件中,導入,並直接經過api.某方法直接調用,具體以下:
<script>import api from '@/api...此處省略一萬行 methods: { _getNewJoke () { const params = { key: '991792145f62460bac35b1c92ee50cdb', page: this.page, rows: 20 } api.NewJoke(params) .then((res) => { console.log(res) if (res.error_code === 0) { this.result = res } }) } }...此處省略一萬行 </script> 複製代碼
跨域數據請求
隨着先後端分離技術的愈來愈盛行,跨域問題也逐漸凸顯了出來。跨域問題的根本緣由:由於瀏覽器收到同源策略的限制,當前域名的js只能讀取同域下的窗口屬性。什麼叫作同源策略?就是不一樣的域名, 不一樣端口, 不一樣的協議不容許共享資源的,保障瀏覽器安全。同源策略是針對瀏覽器設置的門檻。若是繞過瀏覽就能實現跨域,因此說早期的跨域都是打着安全路數的擦邊球,均可以認爲是 hack 處理
網上有不少跨域請求得文章,小編就很少介紹,這次只介紹Vue的域名代理來解決跨域問題。想更深刻學習跨域知識,小編推薦幾款平臺:掘金、思否、簡書、CSDN等。
廢話很少說,來看怎麼經過vue域名代理來解決跨域問題吧。首先找到你的vue項目的config文件夾,在找到該文件夾下的index.js文件,使用Ctrl+F快速搜索proxyTable,在該項進行域名代理配置具體配置以下:
proxyTable: { '/api': { target: 'http://api.avatardata.cn', changeOrigin: true, pathRewrite: { '^/api': '' } } // 第二個域名代理 // '/top': { // target: 'https://www.toutiao.com/', // changeOrigin: true, // pathRewrite: { // '^/top': '' // } // } },複製代碼
其中,'/api'①和'/top'②表示你在數據請求的前綴名,target表示你須要跨域的目標地址;changeOrigin表示是否進行跨域;
①:fetchGet ('/api/Joke/QueryJokeByTime', params)
②:fetchGet ('/top/News', params)
下拉加載數據
在實現拉下加載數據以前,你須要瞭解:better-scroll;那麼你會用到哪些方法和事件吶?你會使用到的方法:refresh(),scrollTo(),scrollToElement();你會使用到的事件:beforeScroll,scrol,scrollToEnd;至於其餘方法和事件請見better-scroll官網;
那麼下面咱們開始來實現下拉加載數據吧
首先,二話不說就來一個組件封裝掉你要使用的better-scroll,以此更方便你使用better-scroll;你可能很疑問,明明人家官方作得會很好封裝了直接調過來用不就完了嗎?你說的也沒錯,可是你仔細考慮一下,你須要更改某些效果或者作更多的邏輯判斷,你把一個頁面寫了幾千行代碼,後期維護,誰願意來看這段代碼,若是你作更進一步的封裝是否是節省了整個頁面的代碼量,維護起來也方便。better-scroll的封裝以下:
<template> <div ref="wrapper"> <slot></slot> </div></template><script>import BScroll from 'better-scroll'import { debounce } from '@/common/utils'const DIRECTION_H = 'horizontal'const DIRECTION_V = 'vertical'export default { name: 'scroll', props: { /** * 1 滾動的時候會派發scroll事件,會節流。 * 2 滾動的時候實時派發scroll事件,不會節流。 * 3 除了實時派發scroll事件,在swipe的狀況下仍然能實時派發scroll事件 */ probeType: { type: Number, default: 1 }, /** * 點擊列表是否派發click事件 */ click: { type: Boolean, default: true }, /** * 是否開啓橫向滾動 */ scrollX: { type: Boolean, default: false }, /** * 是否派發滾動事件 */ listenScroll: { type: Boolean, default: false }, /** * 列表的數據 */ data: { type: Array, default: null }, pullup: { type: Boolean, default: false }, pulldown: { type: Boolean, default: false }, beforeScroll: { type: Boolean, default: false }, /** * 當數據更新後,刷新scroll的延時。 */ refreshDelay: { type: Number, default: 10 }, direction: { type: String, default: DIRECTION_V } }, mounted () { setTimeout(() => { this._initScroll() }, 20) }, methods:{ _initScroll () { if (!this.$refs.wrapper){ return } this.scroll = new BScroll(this.$refs.wrapper, { click: this.click, probeType: this.probeType, eventPassthrough: this.direction === DIRECTION_V ? DIRECTION_H : DIRECTION_V }) // 監聽 滑動, 並拋出其滾動距離 if (this.listenScroll) { debounce(this.scroll.on('scroll', (pos) => { // this.$emit('scrol', pos) // console.log(pos.y) if (pos.y > 100) { this.$emit('scrol') } }), 300) } // 派發 上拉 加載更多 if (this.pullup) { this.scroll.on('scrollEnd', ()=>{ if (this.scroll.y <= this.scroll.maxScrollY + 150) { this.$emit('scrollToEnd') } }) } // 派發下拉 刷新 if (this.pulldown) { this.scroll.on('touchend', (pos) => { if (pos.y > 50) { this.$emit('pulldown') } }) } // 是否 派發列表滾動 開始事件 if (this.beforeScroll) { this.scroll.on('beforeScrollStart', () => { this.$emit('beforeScroll') }) } }, disable() { // 代理better-scroll的disable方法 this.scroll && this.scroll.disable() }, enable() { // 代理better-scroll的enable方法 this.scroll && this.scroll.enable() }, refresh() { // 代理better-scroll的refresh方法 this.scroll && this.scroll.refresh() }, scrollTo() { // 代理better-scroll的scrollTo方法 this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments) }, scrollToElement() { // 代理better-scroll的scrollToElement方法 this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments) }, }, watch: { // 監聽數據變化,延時XX時間後刷新better-scroll的效果,保證滾動效果正常 data () { setTimeout(() => { this.refresh() }, this.refreshDelay) } }}</script><style></style>複製代碼
那麼,下小編這裏仍是須要再談到一下slot的使用,由於它的靈活性讓程序編碼確實很方便,只要你父組件須要給組件填充內容使用它特別方便。
此外,這裏還使用到了 debounce的小知識,爲啥吶?固然時爲了防止你不斷的下拉,形成屢次的數據請求,由於你意圖就是加載一次數據嗎,並且他也會減緩服務器的壓力。debounce實現邏輯以下:
export function debounce (func, delay) { let timer return function (...args) { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { func.apply(this, args) }, delay) }}複製代碼
緊接着,咱們來看下拉加載數據的思路: 在父組件中的使用better-scroll的封裝組件上,綁定data數據源和scroll、beforeScroll方法。每當你向下拉頁面超過某個高度時,利用發佈-訂閱者模式的長處,經過scroll事件再給父組件派發this.$emit('scrol')方法。此時,父組件中編寫其對應的綁定的方法,去請求數據並更新頁面的數據。具體實現以下:
<template>
...此處省略一萬行 <v-scroll class="content-scroll" ref="contentScroll" :listenScroll="listenScroll" :data="result" :beforeScroll="beforeScroll" @scrol="searchMore"> <ul class="content-article-list"> <!-- loading --> <v-load class="loading-wrapper" v-show="display"></v-load> <li class="article-summary" v-for="(article, index) in result" :key="index"> <h3 class="article-title">{{article.content}}</h3> <div class="article-author">來源:{{article.hashId}}</div> <img :src="article.url" alt="" class="pic"> <!-- 尾部組件:用於點贊、分享、踩 --> </li> </ul> </v-scroll>
...此處省略一萬行
<script>import scroll from '@/components/scroll'...此處省略一萬行
methods: { _getNewImg () { const params = { key: '991792145f62460bac35b1c92ee50cdb', page: this.page, rows: 20 } api.NewImg(params) .then((res) => { // console.log(res) if (res.error_code === 0) { this.result = [...res.result, ...this.result] this.display = false // this._checkMore(res.result) console.log(this.result) } }) }, _checkMore (data) { if (data.length < 10) { this.display = false } }, searchMore () { this.display = true this.page++ this._getNewImg() } }, mounted () { this._getNewImg() setTimeout(() => { this.show = false }, 1500) }}</script>
複製代碼
最後,須要注意的時組件v-scroll中有一個watch須要注意以下:
watch: { // 監聽數據變化,延時XX時間後刷新better-scroll的效果,保證滾動效果正常 data () { setTimeout(() => { this.refresh() }, this.refreshDelay) } }複製代碼
其目的就是,爲了解決,當數據更新時,better-scroll從新渲染。
在階段三中須要學習的東西比較少,可能須要講解的知識點就只有,數據請求接口的key須要更好掉;在vue項目中怎麼進行url頁面跳轉了;以及還有一個css3函數編寫須要注意。
其一
數據請求key更換,爲啥要提,由於在阿凡達雲數據平臺,不一樣的數據類型有不一樣的key值,你須要找到你記得key值,以及請求數據的關鍵路徑。配置以下:
其二
怎麼在vue項目中進行url跳轉,確實很簡單,你回一下h5項目的頁面跳轉js如何調用了瀏覽器的href。詳情以下:
<button class="item-button" @click="detail(headline.url)">查看詳情</button>...此處省略一萬行
detail (url) { window.location.href = url },
...此處省略一萬行
複製代碼
順便再提一下,vue項目的頁面切換直接再js中怎麼跳轉吶?簡單,直接調用this.$router.push,具體以下:
<button class="item-button" @click="detail(headline.key)">查看詳情</button>
detail (id) { this.$router.push({path: '/detail', query: {id: key}}) },複製代碼
其三
css3函數編寫,其意圖是爲了解決統一管理樣式主題樣式,以及減小重複性css代碼作到css的封裝複用。具體編寫以下(此處是css的stylus寫法):
px2rem($px) return ($px / 37.5px)remborder-1px($color) border-top 1px solid $color複製代碼
小編已經明白你們的想法了,是否是看源碼吶!在此,小編放個GitHub鏈接,掘友們多多使用大家的小星星哦✿ヽ(°▽°)ノ✿,點贊加關注,繼續咱們的技術學習旅程!🎉
但願小編的Y式分享能幫助到你,傳播知識,分享快樂,與大家一塊兒共同進步~😄