vue項目實踐@樹洞(三)

業務開發

業務邏輯這塊兒並就沒有什麼特別重要的東西要說,無非就是樣式、兼容,再加上vue的語法。這些都是基本功,移動端沒有IE,故而不會有太多讓人窒息的問題。若是說有什麼難點,那就是vuex,主要是它的幾個核心比較難理解,理解了也不難。javascript

目前只是一些基礎的業務邏輯,故只是提一下我在這個項目中遇到的一些問題。css

## Tabbar多米諾骨牌 ##html

使用Vant的Tabbar完成底部菜單,五個菜單項,中間項是一個建立消息的功能。相似於手機QQ空間底部菜單欄,這個功能須要功能須要登陸且是一個彈窗。那麼問題來了:第一,不能使用<router-link>,須根據是否已登陸執行相應的邏輯;第二,頁面跳轉到相應的連接,這個地址再回來對應的索引必須正確;第三,彈窗關閉,對應tabbar的索引也應該進行正確的指示;第四,這個建立圖標不能有指示效果,也就是說它只能有一種狀態;第五,不是全部頁面都有Tabbar,它相對於底部的間距須要處理。vue

先解決第五個問題,提取組件,全部樣式、邏輯集中處理,在須要的地方引用便可。這很容易想到,那麼底部菜單的間距怎麼作?這也容易想獲得,組件渲染完成,給body注入一個菜單高度的底部間距便可。別忘了,在組件結束要移除這個樣式。那麼,何時注入,在哪一個生命週期函數內注入?我在百度這個問題的時候,發現有人說beforeMount,這樣真的能夠嗎?看圖說話:java

紅線之上是第一次渲染,紅線之下是切換Tabbar以後的渲染,beforeMount在beforeDestroy以前先執行了,因此邏輯應該寫在mounted裏面。這就OK了嗎?vue這類框架設計出來的終極目的是不去操做DOM,讓虛擬DOM來作這個工做,因此這樣作是不科學的。這裏能夠綁定class屬性或者綁定style屬性來作這件事,body操做不了,只好對App.vue下手。綁定寫好了,怎樣觸發它呢?webpack

這就須要用到vuex,mounted觸發一次狀態值改變,beforeDestroy再觸發一次狀態值改變。這樣,只要使用這個組件都會觸發樣式更替,第五個問題完美解決。一樣利用vuex來解決第二和第三個問題,這兩個問題能夠併案處理。最初我想用參數來處理,後來以爲不科學,假如底部菜單架構改變會有很大改動。因而我改成映射,將路由地址存在數組裏面,解析地址得出索引。git

// 匹配路由是否存在於地址
function match(path) {
  return path ? (new RegExp(`${path}$`, 'ig')).test(document.location) : false
}
// 根據匹配得出路徑索引
function getPathIndex() {
  return ['/', '/chat', '', '/mail', '/centre'].findIndex(v => match(v)) || 0
}

假如一個頁面有多個路由的狀況,如今的方法是沒法完成的,好比首頁,能夠是/,也能夠是/home,也能夠是/index。還有一種路由地址衝突的狀況,也會致使這個方法失效。這個根據具體狀況作好映射就能夠了,個人邏輯比較簡單。獲得索引以後,觸發store.js裏面的值改變,獲得正確的顯示。Vant的Tabbar索引必須正確,不然Tabbar切換路由上就會報錯。由於建立消息是彈窗,彈窗不會觸發地址改變,那麼獲取到的索引不會改變。github

下面是地址攜帶索引參數的方式,則是經過路由參數來完成索引的識別,我的認爲這會讓地址變得很奇怪。web

// 解析地址參數
function parse(url) {
  return (url || document.location.search || document.location.hash).match(/\?(.*?)$/ig)
}
// 將參數轉爲對象
function getParamsByReg(url) {
  let params = parse(url)
  if(params) {
    params = params[0].replace(/^\?/g, '').replace(/(^|&)(.*?)=(.*?)/g, ',"$2":$3')
    params = JSON.parse(`${params.replace(/^,/, '{')}}`)
  }
  return params || {}
}

第一個問題和第四個問題很簡單,Vant的Tabbar默認不是路由模式,這個item不配置to屬性。在觸發切換的事件上根據須要進行邏輯判斷,第一個問題解決。第四個問題,Vant一樣給了開發者自由嵌入內容的選擇。這兩個問題看似不是什麼問題,其實否則,這兒用的是別人封裝好的庫,很簡單。提出這兩個問題的目的在於,這是思考問題和解決問題的過程,也是寫出優秀組件的必然過程。vuex

## 下拉加載 ##

Vant封裝了List組件,上滑加載更多,默認要預先加載一屏的內容,並且彷佛只對異步有效。它的原理大概是監聽滾動的距離,再根據視窗的高度和內容的高度計算距離底部的位置進行加載。這些邏輯不算複雜,須要注意的是加完事件監聽,在組件銷燬的時候應該對對應的引用進行銷燬,不然很容易形成內存泄漏。

除了使用系統的滾動監聽外,還能夠本身編寫滾動效果,這樣計算會更加方便,這方面的邏輯能夠參考iScroll。若是本身懶得動手,能夠藉助別人寫好的庫,目前我還沒在vue中使用過,暫無推薦。any-touch支持vue,better-scroll是改寫的iScroll,具體效果怎樣,只有使用以後才知道。

## 修改<title> ##

不少人立馬想到JS動態修改,先在路由裏定義好title,而後在路由跳轉的時候進行修改。

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      meta: {
        title: '森林'
      },
      component: () => import('./views/Home.vue')
    }
  ]
})
router.beforeEach((to, from, next) => {
  if (to.meta.title) {
    document.title = to.meta.title
  }
  next()
})

假如title須要讀取文章的標題,每次都不同,這種方法就有問題了。本身擼代碼就沒必要了(想鍛鍊一下本身也何嘗不可),有現成的車軲轆vue-wechat-title。

# 安裝
npm i -D vue-wechat-title
// 全局引入
import Vue from 'vue'
import Title from 'vue-wechat-title'

Vue.use(Title)
<template>
  <!-- 使用 -->
  <div id="app" v-wechat-title="$route.meta.title" img-set=" ">
    <router-view/>
  </div>
</template>

v-wechat-title屬性換成變量,就能實現title動態改變了。img-set屬性用來修改圖標,默認值是一個base64圖標會有警告,由於尚未圖標,因此我用了一個空格佔位。

## vue-lazyload ##

vant是集成了vue-lazyload,個人引入方式是所有導入,在使用圖片懶加載的時候卻告訴我沒有指令。所以,我只好另外安裝vue-lazyload,而後全局引入,也就是如今代碼庫的樣子。後來仔細看了官方文檔,默認的是vant輸出,集成的組件不在vant中,改爲這樣就能夠了。

import Vant, { Lazyload } from 'vant'
import 'vant/lib/index.css'

Vue.use(Lazyload)
Vue.use(Vant)

## 不存在的路由 ##

當用戶請求了一個不存在的地址,由於匹配不到對應的路由,這時就是一片空白,對這樣的路由要進行攔截。

router.beforeEach((to, from, next) => {
  if (to.matched.length === 0) {
    // 跳轉到404頁面
    next({ path: '/404', replace: true })
  } else {
    next()
  }
})

打包測試

當引入vant以後,再一次跑項目其實很快就會發現,編譯速度特別慢。事實上vue-cli 3在編譯上作了優化,速度應該比vue-cli 2快纔對,怎麼會感受慢呢?!先打包一下再看看什麼效果!

兩個警告,說的都是同一個問題——資源過大。別急,複製第一個警告去問下度娘怎麼處理。度娘給了咱們不少答案,答案都差很少,歸結起來就兩點:第一,關閉警告,我不聽你的警告;第二,調節閾值,將警惕線調高。而後呢,而後可愛的小夥伴們就按着他的意思去作了。若是是一兩篇文章這麼說情有可原,一連串的都這麼說,誤人子弟啊。我前面的文章就提到過,不少人本身壓根兒就沒搞清楚就去複製別人的文章,搞得滿世界都是差很少文章。他還不服,轉載標識都捨不得給原創。

爲何說誤人子弟?腳疼就把腳給據了,手疼把手砍了,頭疼頭部如下截肢:這不是瞎胡鬧嗎?閉上眼,看不見,問題就解決了嗎?爲何webpack給的上限建議是244KiB?這個值其實已經很大了,這麼大的文件會影響到資源的加載!咱們不能捂着眼睛就當作看不見了,而應該去優化資源。

VUE CLI自身給了圖片壓縮,這個且無論了,進入vue.config.js作一些調整。生產環境不須要排錯,先關閉map。

productionSourceMap: false

代碼的壓縮工具換成uglifyjs-webpack-plugin,能夠利用環境變量來判斷是否去除打印。將它按照到devDependencies裏面,在.env.debug文件增長一個值NPM_CONFIG_REPORT,利用這個值判斷是測試服務器仍是正式服務器。

NODE_ENV=production
VUE_APP_HOST=https://www.debug.com
NPM_CONFIG_REPORT=true

若是是正式服務器就去除打印,若是是測試服務器就容許打印,這樣就沒必要爲了調試產生的代碼煩惱。不少人認爲console.log無所謂,畢竟在手機端是看不到的。這是錯誤的想法,console.log會影響性能。能夠作個測試,用canvas繪製一個1920*1080的壁紙,而後console.log輸出base64,感覺效果。

// 配置工具
configureWebpack: config => {
    if(process.env.NODE_ENV === 'production') {
      if(!process.env.NPM_CONFIG_REPORT) {
        // 壓縮代碼
        config.plugins.push(new UglifyJsPlugin({
          uglifyOptions: {
            compress: {
              drop_debugger: true,
              drop_console: true,
              pure_funcs: ['console.log']
            }
          },
          sourceMap: false,
          parallel: true
        }))
      }
    }
  }

稍微作了下優化,再次打包,沒什麼做用嘛,依舊包很大。爲何呢?安裝webpack-bundle-analyzer到devDependencies,修改vue.config.js。

// 引入工具
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

// 配置工具
chainWebpack: config => {
    // 其餘代碼
    // ...

    if(process.env.NODE_ENV === 'production') {
      // 打包分析
      if(process.env.NPM_CONFIG_REPORT) {
        config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin).end()
        config.plugins.delete('prefetch')
      }
    }
  }

我將分析工具配置在debug環境下運行,用debug環境打包,看下到底是什麼佔據這麼大空間。

vant最大,其次是vue。整個vant都在裏面,必然會增長包的體積,因此在引入的時候我就表示這種引入方式不科學。前面我講廢話時也曾吐槽過將jQuery引入vue,就是這個緣由。用document.getElementById解決的事,恰恰去引入一個jQuery,這是看不起這個時代呀?

廢話少說,有沒有辦法壓縮它呢?答案是確定的。看下打包時控制檯的打印,三個字段File、Size、Gzipped。第三個字段就是壓縮包的大小,這須要服務端配合。並且解壓須要一個過程,這確定不是優先的解決辦法。既然它們合併在一塊兒會幹壞事,可不能夠將它們分離?首先想到的就是cdn,經過人工去解決,很常見的一個辦法。但是,這個操做實際上是機械化的事情,若是每一個項目都這麼作,不就不符合工程化思想了嗎?

固然,cdn並非很差的思路,爲了分流,這仍是必然的。我在想,有沒有自動化的方式來分離代碼,這些資源幾乎是不變的,何不提取公共庫呢?利用webpack-cli,將須要提取的庫分離出來,而後將公共庫插入到html中。具體操做參考《vue-cli3 DllPlugin 提取公用庫》,我就再也不多說。配置好了,先跑一下公共庫,再次打包。

什麼?公共庫的包又超標了,它讓我去webpack官網看看代碼分割!這個超標的庫只有vant,還能繼續分割嗎?我也沒有答案,因而我試着將vant的總體引入改成按需引入,很明顯包的體積一會兒就下來了。不過,在上傳的代碼中我並無按需引入,主要是方便使用。在實際開發中,只會用到部分組件,按需引用是最佳解決方式。

剛剛webpack提示已經提示了分割代碼,在開發中還能夠利用這個辦法來減輕包的體積。VUE CLI是用的webpack打包,所以,學習webpack也是一個必修的功課,更多優化能夠參考《Vue Cli3 項目打包優化》

最後

由於這個項目缺乏產品角色,一開始並無架構好,想法不成熟。我一邊搭建項目,一邊思考業務的時候,發現這個項目有點大,牽扯的業務甚多。加上各類緣由,最後並無按着理想的進度完成,被迫停止。這個系列暫且會告一段落,計劃中還有一個服務器渲染的章節。

服務器渲染是創建在服務器基礎之上,若是隻是簡單的業務邏輯就不必拿出來講,看官方文檔就行了,這必定得和服務器一塊兒纔有意義。一般,在實際工做中接觸不到服務器,絕大部分人就是碼農而已。我認爲知道服務器部署對服務器渲染會理解更多一點,這對於我來講也是一個全新的課題。

服務器渲染沒有數據的話也是沒有意義的,在停止的這段時間我會從新梳理@樹洞的業務,從最核心的地方入手,精簡項目,確保項目完整。也但願再更新的時候能給你們帶來更多有用的東西。

## 代碼倉庫 ##

https://gitee.com/IanLew/tree-hole.git

## @樹洞系列 ##

vue項目實踐@樹洞(一)

vue項目實踐@樹洞(二)

vue項目實踐@樹洞(三)

相關文章
相關標籤/搜索