基于 vue-cli4 實(shí)現(xiàn)的移動端 H5 開發(fā)模板

vue-cli4-vant

簡介

這是基于 vue-cli4 實(shí)現(xiàn)的移動端 H5 開發(fā)模板屠升,其中包含項(xiàng)目常用的配置及組件封裝歌逢,可供快速開發(fā)使用衫生。

技術(shù)棧:vue-cli4 + webpack4 + vant + axios + less + postcss-px2rem

// 安裝依賴
npm install

// 本地啟動
npm run dev

// 生產(chǎn)打包
npm run build

配置 vant

vant 是一套輕量媳溺、可靠的移動端 Vue 組件庫洞渤,非常適合基于 vue 技術(shù)棧的移動端開發(fā)况既。在過去很長的一段時間內(nèi)这溅,本人用的移動端 UI 框架都是 vux。后來由于 vux 不支持 vue-cli3棒仍,就轉(zhuǎn)用了 vant悲靴,不得不說,無論是在交互體驗(yàn)上莫其,還是代碼邏輯上癞尚,vant 都比 vux 好很多,而且 vant 的坑比較少乱陡。

對于第三方 UI 組件浇揩,如果是全部引入的話,比如會造成打包體積過大憨颠,加載首頁白屏?xí)r間過長的問題胳徽,所以按需加載非常必要积锅。vant 也提供了按需加載的方法。babel-plugin-import 是一款 babel 插件养盗,它會在編譯過程中將 import 的寫法自動轉(zhuǎn)換為按需引入的方式缚陷。

1、安裝依賴

npm i babel-plugin-import -D

2往核、配置 .babelrc 或者 babel.config.js 文件

// 在.babelrc 中添加配置
{
  "plugins": [
    ["import", {
      "libraryName": "vant",
      "libraryDirectory": "es",
      "style": true
    }]
  ]
}

// 對于使用 babel7 的用戶蹬跃,可以在 babel.config.js 中配置
module.exports = {
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
};

3、按需引入

你可以在代碼中直接引入 Vant 組件铆铆,插件會自動將代碼轉(zhuǎn)化為方式二中的按需引入形式

import Vue from 'vue'
import { Button } from 'vant'

Vue.use(Button)

rem 適配

移動端適配是開發(fā)過程中不得不面對的事情蝶缀。在此,我們使用 postcss 中的 px2rem-loader薄货,將我們項(xiàng)目中的 px 按一定比例轉(zhuǎn)化 rem翁都,這樣我們就可以對著藍(lán)湖上的標(biāo)注寫 px 了。

我們將 html 字跟字體設(shè)置為 100px谅猾,很多人選擇設(shè)置為 375px柄慰,但是我覺得這樣換算出來的 rem 不夠精確,而且我們在控制臺上調(diào)試代碼的時候無法很快地口算得出它本來的 px 值税娜。如果設(shè)置 1rem=100px坐搔,這樣我們看到的 0.16rem,0.3rem 就很快得算出原來是 16px敬矩,30px 了概行。

具體步驟如下;

1弧岳、安裝依賴

npm install px2rem-loader --save-dev

2凳忙、在 vue.config.js 進(jìn)行如下配置

  css: {
    // css預(yù)設(shè)器配置項(xiàng)
    loaderOptions: {
      postcss: {
        plugins: [
          require('postcss-px2rem')({
            remUnit: 100
          })
        ]
      }
    }
  },

3、在 main.js 設(shè)置 html 跟字體大小

function initRem() {
  let cale = window.screen.availWidth > 750 ? 2 : window.screen.availWidth / 375
  window.document.documentElement.style.fontSize = `${100 * cale}px`
}

window.addEventListener('resize', function() {
  initRem()
})

axios 請求封裝

1禽炬、設(shè)置請求攔截和響應(yīng)攔截

const PRODUCT_URL = 'https://xxxx.com'
const MOCK_URL = 'http://xxxx.com'
let http = axios.create({
  baseURL: process.env.NODE_ENV === 'production' ? PRODUCT_URL : MOCK_URL,
})
// 請求攔截器
http.interceptors.request.use(
  (config) => {
    // 設(shè)置token涧卵,Content-Type
    var token = sessionStorage.getItem('token')
    config.headers['token'] = token
    config.headers['Content-Type'] = 'application/json;charset=UTF-8'
    // 請求顯示loading效果
    if (config.loading === true) {
      vm.$loading.show()
    }
    return config
  },
  (error) => {
    vm.$loading.hide()
    return Promise.reject(error)
  }
)
// 響應(yīng)攔截器
http.interceptors.response.use(
  (res) => {
    vm.$loading.hide()
    // token失效,重新登錄
    if (res.data.code === 401) {
      //  重新登錄
    }
    return res
  },
  (error) => {
    vm.$loading.hide()
    return Promise.reject(error)
  }
)

2腹尖、封裝 get 和 post 請求方法

function get(url, data, lodaing) {
  return new Promise((resolve, reject) => {
    http
      .get(url)
      .then(
        (response) => {
          resolve(response)
        },
        (err) => {
          reject(err)
        }
      )
      .catch((error) => {
        reject(error)
      })
  })
}

function post(url, data, loading) {
  return new Promise((resolve, reject) => {
    http
      .post(url, data, { loading: loading })
      .then(
        (response) => {
          resolve(response)
        },
        (err) => {
          reject(err)
        }
      )
      .catch((error) => {
        reject(error)
      })
  })
}

export { get, post }

3柳恐、把 get,post 方法掛載到 vue 實(shí)例上热幔。

// main.js
import { get, post } from './js/ajax'
Vue.prototype.$http = { get, post }

工具類函數(shù)封裝

1乐设、添加方法到 vue 實(shí)例的原型鏈上

export default {
  install (Vue, options) {
    Vue.prototype.util = {
      method1(val) {
        ...
      },
      method2 (val) {
       ...
      },
  }
}

2、在 main.js 通過 vue.use()注冊

import utils from './js/utils'
Vue.use(utils)

本文提供以下函數(shù)封裝

  • 獲取 url 參數(shù)值
  • 判斷瀏覽器類型
  • 判斷 IOS/android
  • 校驗(yàn)手機(jī)號碼
  • 檢驗(yàn)車牌號
  • 校驗(yàn)車架號
  • 檢驗(yàn)身份證號碼
  • 日期格式化
  • 時間格式化
  • 城市格式化
  • 壓縮圖片
  • 圖片轉(zhuǎn)成 base64

vue-router 配置

平時很多人對 vue-router 的配置可配置了 path 和 component断凶,實(shí)現(xiàn)了路由跳轉(zhuǎn)即可伤提。其實(shí) vue-router 可做的事情還有很多,比如

  • 路由懶加載配置
  • 改變單頁面應(yīng)用的 title
  • 登錄權(quán)限校驗(yàn)
  • 頁面緩存配置

路由懶加載配置

Vue 項(xiàng)目中實(shí)現(xiàn)路由按需加載(路由懶加載)的 3 中方式:

// 1认烁、Vue異步組件技術(shù):
{
    path: '/home',
    name: 'Home',
    component: resolve => reqire(['../views/Home.vue'], resolve)
}

// 2肿男、es6提案的import()
{
  path: '/',
  name: 'home',
  component: () => import('../views/Home.vue')
}

// 3介汹、webpack提供的require.ensure()
{
    path: '/home',
    name: 'Home',
    component: r => require.ensure([],() =>  r(require('../views/Home.vue')), 'home')
}

本項(xiàng)目采用的是第二種方式,為了后續(xù) webpack 打包優(yōu)化舶沛。

改變單頁面應(yīng)用的 title

由于單頁面應(yīng)用只有一個 html嘹承,所有頁面的 title 默認(rèn)是不會改變的,但是我們可以才路由配置中加入相關(guān)屬性如庭,再在路由守衛(wèi)中通過 js 改變頁面的 title

router.beforeEach((to, from, next) => {
  document.title = to.meta.title
})

登錄權(quán)限校驗(yàn)

在應(yīng)用中叹卷,通常會有以下的場景,比如商城:有些頁面是不需要登錄即可訪問的坪它,如首頁骤竹,商品詳情頁等,都是用戶在任何情況都能看到的往毡;但是也有是需要登錄后才能訪問的蒙揣,如個人中心,購物車等开瞭。此時就需要對頁面訪問進(jìn)行控制了懒震。

此外,像一些需要記錄用戶信息和登錄狀態(tài)的項(xiàng)目嗤详,也是需要做登錄權(quán)限校驗(yàn)的个扰,以防別有用心的人通過直接訪問頁面的 url 打開頁面。

此時葱色。路由守衛(wèi)可以幫助我們做登錄校驗(yàn)递宅。具體如下:

1、配置路由的 meta 對象的 auth 屬性

const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/Home.vue'),
    meta: { title: '首頁', keepAlive: false, auth: false },
  },
  {
    path: '/mine',
    name: 'mine',
    component: () => import('../views/mine.vue'),
    meta: { title: '我的', keepAlive: false, auth: true },
  },
]

2冬筒、在路由首頁進(jìn)行判斷恐锣。當(dāng)to.meta.authtrue(需要登錄),且不存在登錄信息緩存時舞痰,需要重定向去登錄頁面

router.beforeEach((to, from, next) => {
  document.title = to.meta.title
  const userInfo = sessionStorage.getItem('userInfo') || null
  if (!userInfo && to.meta.auth) {
    next('/login')
  } else {
    next()
  }
})

頁面緩存配置

項(xiàng)目中,總有一些頁面我們是希望加載一次就緩存下來的诀姚,此時就用到 keep-alive 了响牛。keep-alive 是 Vue 提供的一個抽象組件,用來對組件進(jìn)行緩存赫段,從而節(jié)省性能呀打,由于是一個抽象組件,所以在 v 頁面渲染完畢后不會被渲染成一個 DOM 元素糯笙。

1贬丛、通過配置路由的 meta 對象的 keepAlive 屬性值來區(qū)分頁面是否需要緩存

const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/Home.vue'),
    meta: { title: '首頁', keepAlive: false, auth: false },
  },
  {
    path: '/list',
    name: 'list',
    component: () => import('../views/list.vue'),
    meta: { title: '列表頁', keepAlive: true, auth: false },
  },
]

2、在 app.vue 做緩存判斷

<div id="app">
  <router-view v-if="!$route.meta.keepAlive"></router-view>
  <keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
  </keep-alive>
</div>

多環(huán)境變量配置

首先我們先來了解一下環(huán)境變量给涕,一般情況下我們的項(xiàng)目會有三個環(huán)境豺憔,本地環(huán)境(development)额获,測試環(huán)境(test),生產(chǎn)環(huán)境(production)恭应,我們可以在項(xiàng)目根目錄下建三個配置環(huán)境變量的文件.env.development抄邀,.env.test,.env.production

環(huán)境變量文件中只包含環(huán)境變量的“鍵=值”對:

NODE_ENV = 'production'
VUE_APP_ENV = 'production' // 只有VUE_APP開頭的環(huán)境變量可以在項(xiàng)目代碼中直接使用

除了自定義的 VUEAPP*變量之外昼榛,還有兩個可用的變量:

  • NODE_ENV : "development"境肾、"production" 或 "test"中的一個。具體的值取決于應(yīng)用運(yùn)行的模式胆屿。
  • BASE_URL : 和 vue.config.js 中的 publicPath 選項(xiàng)相符奥喻,即你的應(yīng)用會部署到的基礎(chǔ)路徑。

下面開始配置我們的環(huán)境變量

1非迹、在項(xiàng)目根目錄中新建.env.*

  • .env.development 本地開發(fā)環(huán)境配置
NODE_ENV='development'
VUE_APP_ENV = 'development'
  • env.staging 測試環(huán)境配置
NODE_ENV='production'
VUE_APP_ENV = 'staging'
  • env.production 正式環(huán)境配置
NODE_ENV='production'
VUE_APP_ENV = 'production'

為了在不同環(huán)境配置更多的變量环鲤,我們在 src 文件下新建一個 config/index

// 根據(jù)環(huán)境引入不同配置 process.env.NODE_ENV
const config = require('./env.' + process.env.VUE_APP_ENV)
module.exports = config

在同級目錄下新建 env.development.js,env.test.js彻秆,env.production.js楔绞,在里面配置需要的變量。
以 env.development.js 為例

module.exports = {
  baseUrl: 'http://localhost:8089', // 項(xiàng)目地址
  baseApi: 'https://www.mock.com/api', // 本地api請求地址
}

2唇兑、配置打包命令

package.json 里的 scripts 不同環(huán)境的打包命令

  • 通過 npm run serve 啟動本地
  • 通過 npm run test 打包測試
  • 通過 npm run build 打包正式
"scripts": {
  "dev": "vue-cli-service serve",
  "build": "vue-cli-service build",
  "test": "vue-cli-service build --mode test",
}

vue.config.js 配置

vue-cli3 開始酒朵,新建的腳手架都需要我們在 vue.config.js 配置我們項(xiàng)目的東西。主要包括

  • 打包后文件輸出位置
  • 關(guān)閉生產(chǎn)環(huán)境 souecemap
  • 配置 rem 轉(zhuǎn)化 px
  • 配置 alias 別名
  • 去除生產(chǎn)環(huán)境 console
  • 跨域代理設(shè)置

此外扎附,還有很多屬于優(yōu)化打包的配置蔫耽,后面會一一道來。

module.exports = {
  // 部署應(yīng)用包時的基本URL留夜,默認(rèn)為'/'
  publicPath: './',

  // 將構(gòu)建好的文件輸出到哪里匙铡,本司要求
  outputDir: 'dist/static',

  // 放置生成的靜態(tài)資源(js、css碍粥、img鳖眼、fonts)的目錄。
  assetsDir: 'static',

  // 指定生成的 index.html 的輸出路徑
  indexPath: 'index.html',

  // 是否使用包含運(yùn)行時編譯器的 Vue 構(gòu)建版本嚼摩。
  runtimeCompiler: false,

  // 默認(rèn)情況下 babel-loader 會忽略所有 node_modules 中的文件钦讳。如果你想要通過 Babel 顯式轉(zhuǎn)譯一個依賴,可以在這個選項(xiàng)中列出來枕面。
  transpileDependencies: [],

  // 如果你不需要生產(chǎn)環(huán)境的 source map愿卒,可以將其設(shè)置為 false 以加速生產(chǎn)環(huán)境構(gòu)建。
  productionSourceMap: false,

  // 配置css
  css: {
    // 是否使用css分離插件 ExtractTextPlugin
    extract: true,
    sourceMap: true,
    // css預(yù)設(shè)器配置項(xiàng)
    loaderOptions: {
      postcss: {
        // options here will be passed to postcss-loader
        plugins: [
          require('postcss-px2rem')({
            remUnit: 100,
          }),
        ],
      },
    },
    // 啟用 CSS modules for all css / pre-processor files.
    modules: false,
  },

  // 是一個函數(shù)潮秘,允許對內(nèi)部的 webpack 配置進(jìn)行更細(xì)粒度的修改琼开。
  chainWebpack: (config) => {
    // 配置別名
    config.resolve.alias
      .set('@', resolve('src'))
      .set('assets', resolve('src/assets'))
      .set('components', resolve('src/components'))
      .set('views', resolve('src/views'))

    config.optimization.minimizer('terser').tap((args) => {
      // 去除生產(chǎn)環(huán)境console
      args[0].terserOptions.compress.drop_console = true
      return args
    })
  },

  // 是否為 Babel 或 TypeScript 使用 thread-loader。該選項(xiàng)在系統(tǒng)的 CPU 有多于一個內(nèi)核時自動啟用枕荞,僅作用于生產(chǎn)構(gòu)建柜候。
  parallel: require('os').cpus().length > 1,

  devServer: {
    host: '0.0.0.0',
    port: 8088, // 端口號
    https: false, // https:{type:Boolean}
    open: false, // 配置自動啟動瀏覽器  open: 'Google Chrome'-默認(rèn)啟動谷歌

    // 配置多個代理
    proxy: {
      '/api': {
        target: 'https://www.mock.com',
        ws: true, // 代理的WebSockets
        changeOrigin: true, // 允許websockets跨域
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },
}

基礎(chǔ)組件封裝

在開發(fā)項(xiàng)目過程中搞动,通常會用到很多功能和設(shè)計相類似的組件,toast 和 dialog 組件基本是每一個移動端項(xiàng)目都會用到的改橘。為了更好匹配自己公司的 UI 設(shè)計風(fēng)格滋尉,我們沒有直接用 vant 的 toast 和 dialog 組件,而是自己封裝了類似的組件飞主,可供直接調(diào)用狮惜,如:

this.$toast({ msg: '手機(jī)號碼不能為空' })

this.$toast({
  msg: '成功提示',
  type: 'success',
})

this.$dialog({
  title: '刪除提示',
  text: '是否確定刪除此標(biāo)簽?',
  showCancelBtn: true,
  confirmText: '確認(rèn)',
  confirm(content) {
    alert('刪除成功')
  },
})

效果圖如下
<img src="./static/toast.png">

toast 傳入?yún)?shù)

Props

name type default description
msg String '' 彈窗提示語
type String '' 彈窗類型:success(成功提示),fail(失敗提示),warning(警告),loading(加載)

dialog 傳入?yún)?shù)

Props

name type default description
title String '' 標(biāo)題
text String '' 文本內(nèi)容
type String '' 默認(rèn)純文本碌识,input(輸入框)
maxlength Number 20 輸入的最多字?jǐn)?shù)
confirmText String 確定 右邊按鈕
cancelText String 取消 左邊按鈕

Events

name params description
confirm null 選擇后的回調(diào)
cancel ull 取消后的回調(diào)

webpack 可視化分析

從這里開始碾篡,我們開始進(jìn)行 webpack 優(yōu)化打包。首先我們來分析一下 webpack 打包性能瓶頸筏餐,找出問題所在开泽,然后才能對癥下藥。此時就用到 webpack-bundle-analyzer 了魁瞪。
1穆律、安裝依賴

npm install webpack-bundle-analyzer -D

2、在 vue.config.js 配置

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
configureWebpack: (config) => {
  if (process.env.NODE_ENV === 'production') {
    config.plugins.push(new BundleAnalyzerPlugin())
  }
}

打包后导俘,我們可以看到這樣一份依賴圖
<img src="./static/cdn前.png">

從以上的界面中峦耘,我們可以得到以下信息:

  • 打包出的文件中都包含了什么,以及模塊之間的依賴關(guān)系
  • 每個文件的大小在總體中的占比旅薄,找出較大的文件辅髓,思考是否有替換方案,是否使用了它包含了不必要的依賴少梁?
  • 是否有重復(fù)的依賴項(xiàng)洛口,對此可以如何優(yōu)化?
  • 每個文件的壓縮后的大小凯沪。

CDN 資源優(yōu)化

CDN 的全稱是 Content Delivery Network第焰,即內(nèi)容分發(fā)網(wǎng)絡(luò)。CDN 是構(gòu)建在網(wǎng)絡(luò)之上的內(nèi)容分發(fā)網(wǎng)絡(luò)妨马,依靠部署在各地的邊緣服務(wù)器樟遣,通過中心平臺的負(fù)載均衡、內(nèi)容分發(fā)身笤、調(diào)度等功能模塊,使用戶就近獲取所需內(nèi)容葵陵,降低網(wǎng)絡(luò)擁塞液荸,提高用戶訪問響應(yīng)速度和命中率。CDN 的關(guān)鍵技術(shù)主要有內(nèi)容存儲和分發(fā)技術(shù)脱篙。

隨著項(xiàng)目越做越大娇钱,依賴的第三方 npm 包越來越多伤柄,構(gòu)建之后的文件也會越來越大。再加上又是單頁應(yīng)用文搂,這就會導(dǎo)致在網(wǎng)速較慢或者服務(wù)器帶寬有限的情況出現(xiàn)長時間的白屏适刀。此時我們可以使用 CDN 的方法,優(yōu)化網(wǎng)絡(luò)加載速度煤蹭。

1笔喉、將 vue、vue-router硝皂、vuex常挚、axios 這些 vue 全家桶的資源,全部改為通過 CDN 鏈接獲取稽物,在 index.html 里插入 相應(yīng)鏈接奄毡。

<body>
  <div id="app"></div>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
  <script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script>
  <script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script>
  <script src="https://cdn.bootcss.com/element-ui/2.6.1/index.js"></script>
</body>

2、在 vue.config.js 配置 externals 屬性

module.exports = {
 ···
    externals: {
      'vue': 'Vue',
      'vuex': 'Vuex',
      'vue-router': 'VueRouter',
      'axios':'axios'
    }
  }

3贝或、卸載相關(guān)依賴的 npm 包

npm uninstall  vue vue-router vuex axios

此時啟動項(xiàng)目運(yùn)行就可以了吼过。我們在控制臺就能發(fā)現(xiàn)項(xiàng)目加載了以上四個 CDN 資源。

不過現(xiàn)在有不少聲音說咪奖,vue 全家桶加載 CDN 資源其實(shí)作用并不大盗忱,而且公共的 CDN 資源也沒有 npm 包那么穩(wěn)定,這個就見仁見智了赡艰。所以我在源碼時新建的分支做這個優(yōu)化售淡。當(dāng)項(xiàng)目較小的就不考慮 CDN 優(yōu)化了。

當(dāng)然慷垮,當(dāng)引入其他較大第三方資源揖闸,比如 echarts,AMAP(高德地圖)料身,采用 CDN 資源還是很有必要的汤纸。

gZip 加速優(yōu)化

所有現(xiàn)代瀏覽器都支持 gzip 壓縮,啟用 gzip 壓縮可大幅縮減傳輸資源大小芹血,從而縮短資源下載時間贮泞,減少首次白屏?xí)r間,提升用戶體驗(yàn)幔烛。

gzip 對基于文本格式文件的壓縮效果最好(如:CSS啃擦、JavaScript 和 HTML),在壓縮較大文件時往往可實(shí)現(xiàn)高達(dá) 70-90% 的壓縮率饿悬,對已經(jīng)壓縮過的資源(如:圖片)進(jìn)行 gzip 壓縮處理令蛉,效果很不好。

const CompressionPlugin = require('compression-webpack-plugin')
configureWebpack: (config) => {
  if (process.env.NODE_ENV === 'production') {
    config.plugins.push(
      new CompressionPlugin({
        // gzip壓縮配置
        test: /\.js$|\.html$|\.css/, // 匹配文件名
        threshold: 10240, // 對超過10kb的數(shù)據(jù)進(jìn)行壓縮
        deleteOriginalAssets: false, // 是否刪除原文件
      })
    )
  }
}

首頁添加骨架屏

隨著 SPA 在前端界的逐漸流行,單頁面應(yīng)用不可避免地給首頁加載帶來壓力珠叔,此時良好的首頁用戶體驗(yàn)至關(guān)重要蝎宇。很多 APP 采用了“骨架屏”的方式去展示未加載內(nèi)容,給予了用戶煥然一新的體驗(yàn)祷安。

所謂的骨架屏姥芥,就是在頁面內(nèi)容未加載完成的時候,先使用一些圖形進(jìn)行占位汇鞭,待內(nèi)容加載完成之后再把它替換掉凉唐。在這個過程中用戶會感知到內(nèi)容正在逐漸加載并即將呈現(xiàn),降低了“白屏”的不良體驗(yàn)虱咧。

本文采用 vue-skeleton-webpack-plugin 插件為單頁面應(yīng)用注入骨架屏熊榛。

1、在 src 的 common 文件夾下面創(chuàng)建了 Skeleton1.vue腕巡,Skeleton2.vue玄坦,具體的結(jié)構(gòu)和樣式自行設(shè)計,此處省略一萬字绘沉。煎楣。。车伞。

2择懂、在同級目錄下新建 entry-skeleton.js

import Vue from 'vue'
import Skeleton1 from './Skeleton1'
import Skeleton2 from './Skeleton2'

export default new Vue({
  components: {
    Skeleton1,
    Skeleton2,
  },
  template: `
    <div>
      <skeleton1 id="skeleton1" style="display:none"/>
      <skeleton2 id="skeleton2" style="display:none"/>
    </div>
  `,
})

在 vue.config.js 下配置插件

const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')
configureWebpack: (config) => {
  config.plugins.push(
    new SkeletonWebpackPlugin({
      webpackConfig: {
        entry: {
          app: path.join(__dirname, './src/common/entry-skeleton.js'),
        },
      },
      minimize: true,
      quiet: true,
      router: {
        mode: 'hash',
        routes: [
          { path: '/', skeletonId: 'skeleton1' },
          { path: '/about', skeletonId: 'skeleton2' },
        ],
      },
    })
  )
}

此時重新加載頁面就可以看到我們的骨架屏了。注意:一定要配置樣式分離 extract: true

使用 SvgIcon 組件

svg 優(yōu)點(diǎn):

  • 圖標(biāo)易于實(shí)時修改另玖,可以帶動畫
  • 可以使用標(biāo)磚的 prop 和默認(rèn)值來將圖標(biāo)保持在一個典型的尺寸并隨時按需改變他們
  • 圖標(biāo)是內(nèi)聯(lián)的困曙,所以不需要額外的 HTTP 請求
  • 可以動態(tài)地使得圖標(biāo)可訪問

通常我們項(xiàng)目都是使用 iconfont 阿里巴巴圖標(biāo)矢量庫,但是操作比較麻煩谦去,每次更新都要重新下載鏈接慷丽。另外我們可以使 svg-sprite-loader 實(shí)現(xiàn)。

1鳄哭、新增 SvgIcon 組件

<template>
  <svg class="svg-icon" aria-hidden="true">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true,
    },
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`
    },
  },
}
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

2要糊、全局注冊組件并導(dǎo)入 svg

import SvgIcon from './SvgIcon.vue'
import Vue from 'vue'

// 注冊到全局
Vue.component('svg-icon', SvgIcon)

const requireAll = (requireContext) => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
requireAll(req)

3、在 main.js 中引入

import './components/icon'

4妆丘、配置 vue.config.js

module.exports = {
  chainWebpack: (config) => {
    const svgRule = config.module.rule('svg')
    svgRule.uses.clear()
    svgRule.exclude.add(/node_modules/)
    svgRule
      .test(/\.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]',
      })

    const imagesRule = config.module.rule('images')
    imagesRule.exclude.add(resolve('src/icons'))
    config.module.rule('images').test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
  },
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锄俄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子勺拣,更是在濱河造成了極大的恐慌奶赠,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門竹祷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人羊苟,你說我怎么就攤上這事塑陵。” “怎么了蜡励?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵令花,是天一觀的道長。 經(jīng)常有香客問我凉倚,道長兼都,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任稽寒,我火速辦了婚禮扮碧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杏糙。我一直安慰自己慎王,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布宏侍。 她就那樣靜靜地躺著赖淤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谅河。 梳的紋絲不亂的頭發(fā)上咱旱,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機(jī)與錄音绷耍,去河邊找鬼吐限。 笑死,一個胖子當(dāng)著我的面吹牛锨天,可吹牛的內(nèi)容都是我干的毯盈。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼病袄,長吁一口氣:“原來是場噩夢啊……” “哼搂赋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起益缠,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤脑奠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后幅慌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宋欺,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了齿诞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酸休。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖祷杈,靈堂內(nèi)的尸體忽然破棺而出斑司,到底是詐尸還是另有隱情,我是刑警寧澤但汞,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布宿刮,位于F島的核電站,受9級特大地震影響私蕾,放射性物質(zhì)發(fā)生泄漏僵缺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一踩叭、第九天 我趴在偏房一處隱蔽的房頂上張望磕潮。 院中可真熱鬧,春花似錦懊纳、人聲如沸揉抵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冤今。三九已至,卻和暖如春茂缚,著一層夾襖步出監(jiān)牢的瞬間戏罢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工脚囊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留龟糕,地道東北人。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓悔耘,卻偏偏與公主長得像讲岁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子衬以,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容