vue實戰 | vue移動端專案架構設計(附demo)

linmu發表於2019-12-28

引言

記錄 vue 專案中所使用的技術細節,此文著重使用和封裝層面,原理性的東西會附上參考文章連結。

建議 clone 下來程式碼看文章:vue-template-project

麻煩動動小手點個 star 哦。

專案初始化

技術選型

結合vue生態,此移動端專案模板使用如下技術:

  • 前端框架——vue
  • vue 狀態管理——vuex
  • vue 路由管理——vue-router
  • 請求方式——axios
  • 樣式管理——less
  • 包管理——npm/cnpm

vue-cli4搭建專案

vue-cli 工具更新很快,我們現在專案中仍使用的是 vue-cli2 ,專案中如需更新腳手架工具,按照以下步驟更新即可。

安裝 Vue CLI

安裝

npm install -g @vue/cli
複製程式碼

如果存在舊版本的 vue-cli ,需先解除安裝再安裝:

npm uninstall vue-cli -g
複製程式碼

檢測版本:

vue --version
#OR
vue -V
複製程式碼

建立專案

執行以下命令建立一個專案:

vue create vue-webapp-template
// vue-webapp-template是你建立的專案名稱
複製程式碼

新的腳手架工具也給提供了視覺化介面建立和管理專案,如需使用視覺化工具搭建專案可參考——從零使用vue-cli+webpack4搭建專案

vue實戰 | vue移動端專案架構設計(附demo)

vue實戰 | vue移動端專案架構設計(附demo)

使用命令列建立專案時,會讓你選擇預設或手動配置,我選的第二個手動配置(Manually),因為在專案裡有輕微的強迫症,沒有用到的就沒有選,具體每個部分、每個目錄是做什麼的,這個文章講的比較清楚——從零使用vue-cli+webpack4搭建專案

移動端元件庫選型

其實,元件庫是可選可不選的,如果團隊中都是大牛,而且有專門的 UI 團隊設計複用元件,專案中所用之處皆已封裝為元件或外掛,我認為這樣的團隊完全不需使用外部的元件庫,團隊本身都代表著效率,為什麼還要參考別人的效率工具

如果不是上面那種團隊,我覺得還是謙虛些,選一個元件庫支援基礎開發更為穩妥。畢竟,業務繁忙時,效率至上

對比了好多個移動端元件庫,對比結果如下:

vue實戰 | vue移動端專案架構設計(附demo)

在這裡我們選用 vant

專案初始化後執行:

// 安裝
npm i vant -S
// 安裝外掛
npm i babel-plugin-import -D
// 在.babelrc 中新增配置
// 注意:webpack 1 無需設定 libraryDirectory
{
  "plugins": [
    ["import", {
      "libraryName": "vant",
      "libraryDirectory": "es",
      "style": true
    }]
  ]
}

// 對於使用 babel7 的使用者,可以在 babel.config.js 中配置
module.exports = {
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
};
// 接著你可以在程式碼中直接引入 Vant 元件
// 外掛會自動將程式碼轉化為方式二中的按需引入形式
import { Button } from 'vant';
複製程式碼

基本知識

vuex資料管理

在這裡只介紹專案中如何使用 vuex 進行資料管理,具體知識點請檢視 官網

store 目錄的設計參考官網推薦購物車案例

vuex 執行的流程圖如下:

vue實戰 | vue移動端專案架構設計(附demo)

接下來展示在元件中如何呼叫 state、getters、actions、mutations。

state&&getters

import { mapState, mapGetters } from 'vuex'
export default {
  computed: {
    // 使用物件展開運算子將此物件混入到外部物件中
    // home代表是store中的哪一個模組
    ...mapState('home', {
      // 箭頭函式可使程式碼更簡練
      home1: state => state.home1
    }),
    ...mapGetters('home', {
      home1Getter: 'home1'
    })
  }
}
複製程式碼

actions&&mutations

import { mapMutations, mapActions } from 'vuex'
export default {
  methods: {
    ...mapActions('home', {
      handleActions: 'getExample'
    }),
    ...mapMutations('home', {
      handleMutations: 'handleMutations'
    }),
  }
}
複製程式碼

vue-router路由管理

路由管理方面採用的是一個主檔案和各個模組的路由檔案的方式,這樣維護起來會稍微舒心一些,不至於當你接到一個專案時,幾千行程式碼在一起,看著也不是很舒服。基礎目錄如下:

--router
  --index.js
  --home.js
  --my.js
複製程式碼

index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import home from './home'
import my from './my'

Vue.use(VueRouter)

const routes = [...home, ...my]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router
複製程式碼

在 index.js 裡面可以加一些全域性路由守衛的東西。

例如可以在路由的 meta 中加入每個頁面的 title,然後當使用者進入每個頁面前,判斷這個元件是否有 title 屬性,如果有的話,就按照你定義的 title 進行展示。

// 設定頁面title
router.beforeEach((to, from, next) => {
  if (to.meta.title) {
    document.title = to.meta.title
  }
  next()
})
複製程式碼

最常用的應該是當使用者未登陸時,如果想進入某個頁面,需跳轉至登陸頁面。

實現思路:加一個全域性的登陸態,並利用前端儲存儲存在本地,如果未登陸跳轉到登陸的頁面,登陸後進入本想進入的頁面。

mixin

混入 (mixin) 提供了一種非常靈活的方式,來分發 Vue 元件中的可複用功能。一個混入物件可以包含任意元件選項。當元件使用混入物件時,所有混入物件的選項將被“混合”進入該元件本身的選項。

如果你有一些公用的資料和方法,不想在另一個元件裡面再寫一遍,就可以寫一個 mixin 的 js 檔案,類似:

const homeMixin = {
  // 在不止一個檔案用到的資料
  data () {
    return {
      homeMixin: 'test-homeMixin'
    }
  },
  // 在不止一個檔案用到的方法
  methods: {
    one () {

    },
    two () {

    }
  }
}

export default homeMixin
複製程式碼

在元件中如何使用呢?

// 引入
import homeMixin from 'components/common/home.js'
// 使用
export default {
    mixins:[homeMixin]
}
複製程式碼

工具封裝

axios封裝(請求攔截,響應攔截)

在我這個搭建的模板專案中只是簡單的做了一點封裝,之後我自己用到這個模板後,也可以有更多的操作性。

import Vue from 'vue'
import axios from 'axios'
import { Toast } from 'vant'
Vue.use(Toast)
axios.defaults.headers['content-Type'] = 'application/json;charset=UTF-8' // 'Content-Type': 'application/x-www-form-urlencoded'

// 請求攔截
axios.interceptors.request.use(function (config) {
  if (config.method === 'post') {

  } else if (config.method === 'get') {

  }
  return config
}, function (error) {
  return Promise.reject(error)
})

// 響應攔截
axios.interceptors.response.use(res => res, err => {
  if (err && (err.toString().indexOf('500') > -1 || err.toString().indexOf('502') > -1 || err.toString().indexOf('404') > -1)) {
    Toast('網路或介面異常')
    return Promise.reject('網路或介面異常')
  } else {
    return Promise.reject(err)
  }
})
複製程式碼

api統一管控

api 是按照每個模組建立的檔案。

--service
----api.js  // axios封裝
----homeApi.js // home模組所有的請求
----myApi.js  // my模組所有的請求
複製程式碼

homeApi.js

import './api.js'
import axios from 'axios'
/**
 * get 案例
 * @param options
 */
export const getExample = options => {
  return axios.get('mock/home1.json', { params: options })
}

/**
 * post 案例
 * @param options
 * @returns {*}
 */
export const postExample = options => {
  return axios.post('mock/home2.json', options)
}
複製程式碼

更為具體的 axios 封裝和 api 統一管控的內容可以參考我的另一篇文章詳解vue中Axios的封裝與API介面的管理

常用函式封裝

utils.js

模板專案中封裝了一個日期格式化的函式,專案中可以根據自己的需要封裝幾個常用的函式。

/**
 * 日期格式化 new Date(...).format('yyyy-MM-dd hh:mm:ss')
 * @param fmt
 * @returns {*}
 */
window.Date.prototype.format = function (fmt) {
  let o = {
    'M+': this.getMonth() + 1,
    'd+': this.getDate(),
    'h+': this.getHours(),
    'm+': this.getMinutes(),
    's+': this.getSeconds(),
    'q+': Math.floor((this.getMonth() + 3) / 3),
    'S': this.getMilliseconds()
  }
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
  }
  for (var k in o) {
    if (new RegExp('(' + k + ')').test(fmt)) {
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
    }
  }
  return fmt
}
複製程式碼

元件化思想

什麼是元件化

元件化並不是前端所特有的,一些其他的語言或者桌面程式等,都具有元件化的先例。確切的說,只要有UI層的展示,就必定有可以元件化的地方。簡單來說,元件就是將一段UI樣式和其對應的功能作為獨立的整體去看待,無論這個整體放在哪裡去使用,它都具有一樣的功能和樣式,從而實現複用,這種整體化的細想就是元件化。不難看出,元件化設計就是為了增加複用性,靈活性,提高系統設計,從而提高開發效率。

簡而言之:一個 .vue 檔案就是一個元件

slot擴充套件元件

Vue 實現了一套內容分發的 API,這套 API 的設計靈感源自 Web Components 規範草案,將 <slot> 元素作為承載分發內容的出口。

它允許你像這樣合成元件:

<home-header title="home標題">
    <p>這是slot</p>
</home-header>
複製程式碼

然後你在 <home-header> 的模板中可能會寫為:

<div id="header">
  <div class="left">{{title}}</div>
  <slot></slot>
</div>
複製程式碼

install封裝外掛

如果重複業務很多的話,相較於元件化,外掛化無疑是更能加快開發效率的方式。

開發外掛

Vue.js 的外掛應該暴露一個 install 方法。這個方法的第一個引數是 Vue 構造器,第二個引數是一個可選的選項物件:

MyPlugin.install = function (Vue, options) {
  // 1. 新增全域性方法或屬性
  Vue.myGlobalMethod = function () {
    // 邏輯...
  }

  // 2. 新增全域性資源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 邏輯...
    }
    ...
  })

  // 3. 注入元件選項
  Vue.mixin({
    created: function () {
      // 邏輯...
    }
    ...
  })

  // 4. 新增例項方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 邏輯...
  }
}
複製程式碼

使用外掛

通過全域性方法 Vue.use() 使用外掛。它需要在你呼叫 new Vue() 啟動應用之前完成:

// 呼叫 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)

new Vue({
  // ...元件選項
})
複製程式碼

也可以傳入一個可選的選項物件:

Vue.use(MyPlugin, { someOption: true })
複製程式碼

Vue.use 會自動阻止多次註冊相同外掛,屆時即使多次呼叫也只會註冊一次該外掛。

Vue.js 官方提供的一些外掛 (例如 vue-router) 在檢測到 Vue 是可訪問的全域性變數時會自動呼叫 Vue.use()。然而在像 CommonJS 這樣的模組環境中,你應該始終顯式地呼叫 Vue.use()

// 用 Browserify 或 webpack 提供的 CommonJS 模組環境時
var Vue = require('vue')
var VueRouter = require('vue-router')

// 不要忘了呼叫此方法
Vue.use(VueRouter)
複製程式碼

效率工具

rem佈局——cssrem+flexble.js

移動端最常用的佈局無非有這幾種:響應式佈局、vw + vh 佈局、rem 佈局、vm + rem 佈局。

這裡採用的是 rem 佈局,使用的是淘寶出品的 Flexible.js 。

;(function(win, lib) {
    var doc = win.document;
    var docEl = doc.documentElement;
    var metaEl = doc.querySelector('meta[name="viewport"]');
    var flexibleEl = doc.querySelector('meta[name="flexible"]');
    var dpr = 0;
    var scale = 0;
    var tid;
    var flexible = lib.flexible || (lib.flexible = {});
    
    if (metaEl) {
        console.warn('將根據已有的meta標籤來設定縮放比例');
        var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
        if (match) {
            scale = parseFloat(match[1]);
            dpr = parseInt(1 / scale);
        }
    } else if (flexibleEl) {
        var content = flexibleEl.getAttribute('content');
        if (content) {
            var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
            var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
            if (initialDpr) {
                dpr = parseFloat(initialDpr[1]);
                scale = parseFloat((1 / dpr).toFixed(2));    
            }
            if (maximumDpr) {
                dpr = parseFloat(maximumDpr[1]);
                scale = parseFloat((1 / dpr).toFixed(2));    
            }
        }
    }

    if (!dpr && !scale) {
        var isAndroid = win.navigator.appVersion.match(/android/gi);
        var isIPhone = win.navigator.appVersion.match(/iphone/gi);
        var devicePixelRatio = win.devicePixelRatio;
        if (isIPhone) {
            // iOS下,對於2和3的屏,用2倍的方案,其餘的用1倍方案
            if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
                dpr = 3;
            } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
                dpr = 2;
            } else {
                dpr = 1;
            }
        } else {
            // 其他裝置下,仍舊使用1倍的方案
            dpr = 1;
        }
        scale = 1 / dpr;
    }

    docEl.setAttribute('data-dpr', dpr);
    if (!metaEl) {
        metaEl = doc.createElement('meta');
        metaEl.setAttribute('name', 'viewport');
        metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
        if (docEl.firstElementChild) {
            docEl.firstElementChild.appendChild(metaEl);
        } else {
            var wrap = doc.createElement('div');
            wrap.appendChild(metaEl);
            doc.write(wrap.innerHTML);
        }
    }

    function refreshRem(){
        var width = docEl.getBoundingClientRect().width;
        if (width / dpr > 540) {
            width = 540 * dpr;
        }
        var rem = width / 10;
        docEl.style.fontSize = rem + 'px';
        flexible.rem = win.rem = rem;
    }

    win.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 300);
    }, false);
    win.addEventListener('pageshow', function(e) {
        if (e.persisted) {
            clearTimeout(tid);
            tid = setTimeout(refreshRem, 300);
        }
    }, false);

    if (doc.readyState === 'complete') {
        doc.body.style.fontSize = 12 * dpr + 'px';
    } else {
        doc.addEventListener('DOMContentLoaded', function(e) {
            doc.body.style.fontSize = 12 * dpr + 'px';
        }, false);
    }
    

    refreshRem();

    flexible.dpr = win.dpr = dpr;
    flexible.refreshRem = refreshRem;
    flexible.rem2px = function(d) {
        var val = parseFloat(d) * this.rem;
        if (typeof d === 'string' && d.match(/rem$/)) {
            val += 'px';
        }
        return val;
    }
    flexible.px2rem = function(d) {
        var val = parseFloat(d) / this.rem;
        if (typeof d === 'string' && d.match(/px$/)) {
            val += 'rem';
        }
        return val;
    }

})(window, window['lib'] || (window['lib'] = {}));
複製程式碼

事實上 flexible.js 做了下面三件事:

  • 動態改寫標籤
  • <html>元素新增data-dpr屬性,並且動態改寫data-dpr的值
  • <html>元素新增font-size屬性,並且動態改寫font-size的值

因為我使用的是 vsCode 編輯器,在這裡介紹一下使用 vsCode 時,如何快速的將 px-->rem。

  1. 下載 cssrem 外掛

    vue實戰 | vue移動端專案架構設計(附demo)

  2. 開啟 vsCode 編譯器————檔案————首選項————設定————搜尋 cssrem ————進行設定

vue實戰 | vue移動端專案架構設計(附demo)

因為我們的設計稿尺寸是375px的,自動轉換時除以37.5得到應有的 rem 值。

vue實戰 | vue移動端專案架構設計(附demo)

解決300ms延遲

移動裝置上的瀏覽器預設會在使用者點選螢幕大約延遲300毫秒後才會觸發點選事件。

原因: 移動端的雙擊會縮放導致click判斷延遲。

安裝FastClick

npm i fastclick -S
複製程式碼

呼叫

//jquery
<script type='application/javascript' src='/path/to/fastclick.js'></script>
$(function() {
    FastClick.attach(document.body);
});

//原生js
if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', function() {
        FastClick.attach(document.body);
    }, false);
}

//vue 
import FastClick from 'fastclick'
FastClick.attach(document.body);
複製程式碼

移動端測試工具v-console

平時在 web 應用開發過程中,我們可以 console.log 去輸出一些資訊,但是在移動端,也就是在手機上, console.log 的資訊我們是看不到的。

這種情況下,可以選擇使用 alert 彈出一些資訊,但是這種方法不怎麼方便,也會阻斷 JS 執行緒,導致後面的執行緒都不執行,影響除錯體驗。

那麼,如果將console.log應用到移動端呢? 需要藉助第三方外掛:vConsole

安裝

npm install vconsole
複製程式碼

在main.js引入

import Vconsole from 'vconsole';
new Vconsole();
複製程式碼

在需要的地方

console.log('內容')
複製程式碼

vue實戰 | vue移動端專案架構設計(附demo)

專案優化

使用README.md記錄每次更新的內容

在我目前的團隊,人員流動還是比較大的,而且我們的專案耦合度很低,可能幾個頁面就是一個專案,所以造成專案很多。為了其他人更快的接手一個專案,所以在開發時制定了一個規範,即在 README.md 檔案中列出每個檔案中需要共享的部分,以減少溝通成本。

> 專案相關備註

- 相關人員 `有多人情況下全部列出`
  + 業務分析師:
  + 前端開發人員:
  + 後臺開發人員:

- 環境地址 `有更多環境依次補全, 以下詳情有則補充`
  * 測試環境
    + 測試環境頁面訪問地址:
    + 測試環境介面地址:
    + 測試環境部署方式:

  * 生產環境
    + 生產環境頁面訪問地址:
    + 生產環境介面地址:
    + 生產環境部署方式:

- 補充說明:

- 迭代說明:
    - v1.0
    ......
複製程式碼

分環境打包

當我們在實際開發時,最少會分三個環境:開發環境(用於本地開發)、測試環境(模擬生產環境,上線前的測試)、生產環境。

  • package.json
  "scripts": {
    "serve": "vue-cli-service serve --open --mode development",
    "build": "vue-cli-service build",
    "test": "vue-cli-service build --mode test"
  },
複製程式碼

具體請參看:詳解vue-cli4環境變數與分環境打包方法

mock資料

在前端開發過程中,有後臺配合是很必要的。但是如果自己測試開發,或者後臺很忙,沒時間,那麼我們需要自己提供或修改介面。下面提供兩種方式,第二種更簡單,個人推薦第二種。

mock檔案

  1. 安裝
npm i mockjs -D
複製程式碼
  1. 在 src 目錄下新建 mock 目錄

vue實戰 | vue移動端專案架構設計(附demo)

  1. index.js 內容如下
const Mock = require('mockjs')

Mock.mock('/test/get', 'get', require('./json/testGet'))
Mock.mock('/test/post', 'post', require('./json/testPost'))
複製程式碼
  1. json 檔案內容如下,以 testGet.json 為例:
{
  "result": "success",
  "data": {
    "sex": "man",
    "username": "前端林木--get",
    "age": 0,
    "imgUrl": ""
  },
  "msg": ""
}
複製程式碼
  1. 在main.js入口檔案中引入mock資料
if (env === 'DEV') {
  require('./mock') // 引入mock資料
}
複製程式碼
  1. vue 中封裝,然後呼叫即可
export const getExample = options => {
  return axios.get('/test/get', { params: options })
}
複製程式碼

第三方介面 eolinker

  1. 官網介面地址:www.eolinker.com/#/home/proj…

需登入,沒註冊過的小夥伴,註冊一個賬號吧。

  1. 註冊好後有一個預設介面,當然我們要做自己的專案。

  2. 新建專案

vue實戰 | vue移動端專案架構設計(附demo)

  1. 新增介面

vue實戰 | vue移動端專案架構設計(附demo)

  1. 自定義介面

vue實戰 | vue移動端專案架構設計(附demo)

  1. 使用介面

vue實戰 | vue移動端專案架構設計(附demo)

vue實戰 | vue移動端專案架構設計(附demo)

  1. 前端專案中,後臺 url 地址,有開發版,測試版,本地版等多個版本,建議大家把開發的的URL換成 mock 的地址。

vue實戰 | vue移動端專案架構設計(附demo)

webpack 優化專案

如何提高 webpack 的打包速度

  1. 優化 Loader

    對於 Loader 來說,影響打包效率首當其衝必屬 Babel 了。因為 Babel 會將程式碼轉為字串生成 AST,然後對 AST 繼續進行轉變最後再生成新的程式碼,專案越大,轉換程式碼越多,效率就越低。當然了,我們是有辦法優化的。

    首先我們可以優化 Loader 的檔案搜尋範圍

module.exports = {
  module: {
    rules: [
      {
        // js 檔案才使用 babel
        test: /\.js$/,
        loader: 'babel-loader',
        // 只在 src 資料夾下查詢
        include: [resolve('src')],
        // 不會去查詢的路徑
        exclude: /node_modules/
      }
    ]
  }
}

複製程式碼

對於 Babel 來說,我們肯定是希望只作用在 JS 程式碼上的,然後 node_modules 中使用的程式碼都是編譯過的,所以我們也完全沒有必要再去處理一遍。

當然這樣做還不夠,我們還可以將 Babel 編譯過的檔案快取起來,下次只需要編譯更改過的程式碼檔案即可,這樣可以大幅度加快打包時間

loader: 'babel-loader?cacheDirectory=true'

複製程式碼
  1. HappyPack

    受限於 Node 是單執行緒執行的,所以 Webpack 在打包的過程中也是單執行緒的,特別是在執行 Loader 的時候,長時間編譯的任務很多,這樣就會導致等待的情況。

    HappyPack 可以將 Loader 的同步執行轉換為並行的,這樣就能充分利用系統資源來加快打包效率了

module: {
  loaders: [
    {
      test: /\.js$/,
      include: [resolve('src')],
      exclude: /node_modules/,
      // id 後面的內容對應下面
      loader: 'happypack/loader?id=happybabel'
    }
  ]
},
plugins: [
  new HappyPack({
    id: 'happybabel',
    loaders: ['babel-loader?cacheDirectory'],
    // 開啟 4 個執行緒
    threads: 4
  })
]

複製程式碼
  1. DllPlugin

    DllPlugin 可以將特定的類庫提前打包然後引入。這種方式可以極大的減少打包類庫的次數,只有當類庫更新版本才有需要重新打包,並且也實現了將公共程式碼抽離成單獨檔案的優化方案。

    接下來我們就來學習如何使用 DllPlugin

// 單獨配置在一個檔案中
// webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    // 想統一打包的類庫
    vendor: ['react']
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].dll.js',
    library: '[name]-[hash]'
  },
  plugins: [
    new webpack.DllPlugin({
      // name 必須和 output.library 一致
      name: '[name]-[hash]',
      // 該屬性需要與 DllReferencePlugin 中一致
      context: __dirname,
      path: path.join(__dirname, 'dist', '[name]-manifest.json')
    })
  ]
}

複製程式碼

然後我們需要執行這個配置檔案生成依賴檔案,接下來我們需要使用 DllReferencePlugin 將依賴檔案引入專案中

// webpack.conf.js
module.exports = {
  // ...省略其他配置
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      // manifest 就是之前打包出來的 json 檔案
      manifest: require('./dist/vendor-manifest.json'),
    })
  ]
}

複製程式碼
  1. 程式碼壓縮

    在 Webpack3 中,我們一般使用 UglifyJS 來壓縮程式碼,但是這個是單執行緒執行的,為了加快效率,我們可以使用 webpack-parallel-uglify-plugin 來並行執行 UglifyJS,從而提高效率。

    在 Webpack4 中,我們就不需要以上這些操作了,只需要將 mode 設定為 production 就可以預設開啟以上功能。程式碼壓縮也是我們必做的效能優化方案,當然我們不止可以壓縮 JS 程式碼,還可以壓縮 HTML、CSS 程式碼,並且在壓縮 JS 程式碼的過程中,我們還可以通過配置實現比如刪除 console.log 這類程式碼的功能。

  2. 一些小的優化點

    我們還可以通過一些小的優化點來加快打包速度

    • resolve.extensions:用來表明檔案字尾列表,預設查詢順序是 ['.js', '.json'],如果你的匯入檔案沒有新增字尾就會按照這個順序查詢檔案。我們應該儘可能減少字尾列表長度,然後將出現頻率高的字尾排在前面
    • resolve.alias:可以通過別名的方式來對映一個路徑,能讓 Webpack 更快找到路徑
    • module.noParse:如果你確定一個檔案下沒有其他依賴,就可以使用該屬性讓 Webpack 不掃描該檔案,這種方式對於大型的類庫很有幫助

如何用 webpack 來優化前端效能

⽤webpack優化前端效能是指優化webpack的輸出結果,讓打包的最終結果在瀏覽器執行快速⾼效。

  • 壓縮程式碼:刪除多餘的程式碼、註釋、簡化程式碼的寫法等等方式。可以利⽤ webpack 的 UglifyJsPlugin 和 ParallelUglifyPlugin 來壓縮 JS ⽂件, 利⽤ cssnano (css-loader?minimize)來壓縮 css。
  • 利⽤CDN加速: 在構建過程中,將引⽤的靜態資源路徑修改為CDN上對應的路徑。可以利⽤webpack對於 output 引數和各loader的 publicPath 引數來修改資源路徑。
  • Tree Shaking: 將程式碼中永遠不會⾛到的⽚段刪除掉。可以通過在啟動webpack時追加引數 --optimize-minimize 來實現。
  • Code Splitting: 將程式碼按路由維度或者元件分塊(chunk),這樣做到按需載入,同時可以充分利⽤瀏覽器快取。
  • 提取公共第三⽅庫: SplitChunksPlugin 外掛來進⾏公共模組抽取,利⽤瀏覽器快取可以⻓期快取這些⽆需頻繁變動的公共程式碼。

骨架屏展示

專案 demo 中使用的是 vant 中的 Skeleton 骨架屏

// 引入
import Vue from 'vue';
import { Skeleton } from 'vant';

Vue.use(Skeleton);
複製程式碼
// 展示子元件
// 將loading屬性設定成false表示內容載入完成,此時會隱藏佔點陣圖,並顯示Skeleton的子元件

<van-skeleton
  title
  avatar
  :row="3"
  :loading="loading"
>
  <div>實際內容</div>
</van-skeleton>
export default {
  data() {
    return {
      loading: true
    }
  },
  mounted() {
    this.loading = false;
  }
};
複製程式碼

如果想自己搭建一個骨架屏,給大家幾個參考連結:

  1. Vue頁面骨架屏注入實踐
  2. 前端骨架屏方案小結
  3. 為vue專案新增骨架屏

總結

以上就是 vue 移動端整體大的專案架構設計了(webpack 有一部分沒做演示,有需要的童鞋要實踐一下哦),總結一篇文章好辛苦。

2020-1-10 0:55

晚安~


相關文章