Vue 项目常用点总结,一文带你走进实践

492 阅读4分钟

今年年初由于工作调动,从 Vue 转到了 React,由于对其深深的眷念,抽空总结下从 2016 年到现在基于 Vue 的实际项目在开发中的必备点。

I love Vue and use react just for work

Vue 官方配套文档很全,建议细读;同时建议使用 Vue CLI,不建议自行写 Webpack 等配置文件,若需要复写配置可使用 vue.config.js

起手式

Node 版本要求

Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 最新)。你可以使用 nvmnvm-windows 在同一台电脑中管理多个 Node 版本。

安装:

npm install -g @vue/cli
# OR
yarn global add @vue/cli

创建一个项目:

vue create my-project
# OR
vue ui

创建新项目的原型的过程不再赘述,根据实际需要选择即可。项目结构、文件信息这里不细说,根据命名即可知道代表的意思,下面说下在实际项目中需要额外添加的或注意的点。

常用点

Mock、环境区分、网络请求、骨架屏

Mock

实际开发中为提升开发效率、解决对 API 的依赖或实现离线开发,这些均会使用到 Mock。实现 Mock 的方式有很多,如:Webpack Server (拦截器/路由)Mock.js、使用外部的 Mock Serve ,如 SSRJSON Server 等。这里介绍基于 Webpack Server (拦截器/路由) 的实现方式。

新建 mock 目录

// mock/a.js
module.exports = {
  // 地址
  api: '/a',
  // 返回数据
  response: function(req, res, fs) {
    res.json({ data: '这是mock数据' })
  }
}
// vue.config.js
const path = require('path')
const fs = require('fs')
const mockDirPath = path.resolve(__dirname, './mock')
function mockProxy(app, mockDir) {
  fs.readdirSync(mockDir).forEach(function(file) {
    let filePath = path.resolve(mockDir, file)
    if (fs.statSync(filePath).isDirectory()) {
      mockProxy(app, filePath)
    } else {
      let mock = require(filePath)
      app.all(mock.api, (req, res) => {
        mock.response(req, res)
      })
    }
  })
}

module.exports = {
  devServer: {
    open: true,
    before: function(app) {
      mockProxy(app, mockDirPath)
    }
  }
}

原理:对 express 使用一个类似于拦截器的方式,对网络请求进行处理。 缺点:mock 文件无法热加载

环境区分

实际项目的环境不仅仅只是简单的 developmentproduction,还存在前后端对接联调阶段(前后端并行开发)、E2E 环境、测试环境等。可参考环境变量和模式

结合前面的 Mock ,假设需要 开发环境(mock) 和 联调环境(dev 下直接调用 API,假设地址为 http://localhost:8080/),实现方式如下:

修改package.json

  <!-- package.json -->
  "scripts": {
    ...
    "dev": "vue-cli-service serve --mode mock",
    "sys": "vue-cli-service serve"
    ...
  }

新增 .env.mock文件

VUE_APP_MOCK=true

修改vue.config.js

// vue.config.js
const path = require('path')
const fs = require('fs')
const mockDirPath = path.resolve(__dirname, './mock')

function mockProxy(app, mockDir) {
  fs.readdirSync(mockDir).forEach(function(file) {
    let filePath = path.resolve(mockDir, file)
    if (fs.statSync(filePath).isDirectory()) {
      mockProxy(app, filePath)
    } else {
      let mock = require(filePath)
      app.all(mock.api, (req, res) => {
        mock.response(req, res)
      })
    }
  })
}

const isMock = process.env.VUE_APP_MOCK
const isDev = process.env.NODE_ENV === 'development'

if (isDev) {
  if (isMock) {
    console.log(
      '\x1B[33m%s\x1b[0m',
      '===================== development mock ==================='
    )
  } else {
    console.log(
      '\x1B[33m%s\x1b[0m',
      '===================== development joint debugging ==================='
    )
  }
}
// API 地址
const configProxy = 'http://localhost:8080/'

module.exports = {
  devServer: {
    open: true,
    port: 9000,
    before: function(app) {
      if (process.env.VUE_APP_MOCK) {
        mockProxy(app, mockDirPath)
      }
    },
    proxy: isDev && !isMock ? configProxy : ''
  }
}

若需要其它区分环境,可参考实现

网络请求

这里简单讲下官方推荐使用的 Axios,其它库的封装大同小异。以下方式仅供参考。

  • 自行封装处理,添加个性化配置(如全局 Loading、异常拦截等)

    新建 http.js ,封装 Axios

    // http.js
    import Axios from 'axios'
    
    const install = Vue => {
      if (install.installed) {
        return
      }
    
      Axios.defaults.headers = {
        'Content-Type': 'application/json; charset=utf-8'
      }
      // Axios.defaults.timeout = 20000
      // 添加一个请求拦截器
      Axios.interceptors.request.use(
        config => {
          return config
        },
        error => {
          return Promise.reject(error)
        }
      )
    
      // 添加一个响应拦截器
      Axios.interceptors.response.use(
        response => {
          return response.data
        },
        error => {
          return Promise.reject(error)
        }
      )
      Vue.http = Axios
      Vue.prototype.$http = Axios
    }
    
    // auto install
    if (typeof window !== 'undefined' && window.Vue) {
      install(window.Vue)
    }
    
    export default install
    

    是否将 http 实现为 插件 自行决定。

    可在 interceptors 里添加 Loading 动画,这样虽可一劳永逸,但若出现短时间内发起连续的网络请求(如实现某个操作需要发起多次请求)会出现闪烁情况。

    可在 response 里进行全局的简单异常处理,如根据返回错误码进行提示或跳转

    src/main.js 引入

    // src/main.js
    ...
    import http from './http'
    Vue.use(http)
    ...
    
  • 使用第三方

    如使用 vue-axios

    import Vue from 'vue'
    import axios from 'axios'
    import VueAxios from 'vue-axios'
    
    Vue.use(VueAxios, axios)
    
    // 使用方式
    Vue.axios.get(api).then(response => {
      console.log(response.data)
    })
    
    this.axios.get(api).then(response => {
      console.log(response.data)
    })
    
    this.$http.get(api).then(response => {
      console.log(response.data)
    })
    

骨架屏

引入骨架屏避免首屏长时间白屏现象,原理是先在 html 模板的 <div id="app"></div> 中插入骨架,当首屏的 js 文件加载完成,会被替换为实际内容。此处使用 vue-skeleton-webpack-plugin 实现。

新增 Skeleton.vue 文件

<!-- src/Skeleton.vue -->
<template>
  <div class="skeleton-wrapper">
    <header class="skeleton-header"></header>
    <div>
      <div class="skeleton-body"></div>
      <div class="skeleton-footer"></div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'skeleton'
  }
</script>

<style scoped>
  .skeleton-header {
    height: 52px;
    background: #3a89c9;
    width: 100%;
  }

  .skeleton-body {
    background: #f6f6f6;
    margin: 20px 0 20px 0;
    height: 15vh;
    border-radius: 10px;
  }

  .skeleton-footer {
    border-radius: 10px;
    background: #f6f6f6;
    min-height: 60vh;
  }
</style>

新增 skeleton.js 文件

// src/skeleton.js
import Vue from 'vue'
import Skeleton from './Skeleton.vue'

export default new Vue({
  components: {
    Skeleton
  },
  render: h => h(Skeleton)
})

修改 vue.config.js 文件

// vue.config.js
const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')
module.exports = {
  chainWebpack: config => {
    config.when(!isDev, config => {
      config.plugin('skeleton').use(SkeletonWebpackPlugin, [
        {
          webpackConfig: {
            entry: {
              app: path.join(__dirname, './src/skeleton.js')
            }
          },
          minimize: true,
          quiet: true
        }
      ])
    })
  }
}

若出现 vue-server-renderervue-skeleton-webpack-plugin 中使用了) 与项目中 Vue 版本不匹配 可自行安装 vue-server-renderer 进行版本覆盖