吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【上】


吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【上】


前言

之前使用過 Vue 開發後臺、中臺項目,也做過移動端 H5,弄過一點小的前端架構。每做一個項目都會收穫了不一樣的經驗和理解。下面我把這些點點滴滴的經驗總結下來,做一個系列的文章分享和階段性的總結。

常規操作,先點贊後觀看哦!你的點贊是我創作的動力之一!

概覽

吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【上】

問題

我將從 16 個方面來論述 vue 開發過程中的一些技巧和原理。當然由於篇幅有限,先論述前 8 個問題,下節將完成全系列內容。

本篇文章將圍繞下列問題進行論述:

  • 如何規範你的 git 提交,並自動生成並提交日誌?
  • 如何配置和使用 Sass 和 PUG 提升你的編碼效率?
  • 如何處理你的代碼風格問題,以及如何使用 perttier 與 eslint 解決效率風格兩難問題?
  • 如何管理頁面的路由,如何編寫異步路由?
  • 如何編寫組件,引入組件庫?
  • 如何管理你的資源,如何引入圖標,樣式?
  • 如何封裝你的 axios,管理你的api?
  • 如何使用 mock 模擬你的數據,實現真正意義的前後端分離?

實踐

實踐之前:我希望你有如下準備,或者知識儲備。

瞭解 npm/yarn/git/sass/pug/vue/vuex/vue-router/axios/mock/ssr/jest 的使用和原理。

當然上面知識不瞭解也沒關係哈哈哈,文章中會提到大致用法和作用。

如何規範 git 提交

代碼提交記錄是一個很好的代碼修改日誌。規範的代碼提交記錄,不管在平時代碼開發維護過程中,還是在定位 bug 或者回退版本來說都是極為重要。

原理

兩種做法:

  • 自己手動規範 git 的提交原則或者團隊統一制定。這個靠自覺,好習慣養成之後就沒問題來
  • 使用插件規範,比如下面這種

為了規範提交,我使用瞭如下插件:

  • commitizen
  • conventional-changelog
  • cz-conventional-changelog
  • conventional-changelog-cli

解決方案

安裝系列插件依賴

<code>yarn add -D commitizen conventional-changelog cz-conventional-changelog
複製代碼/<code>

安裝依賴時,要注意是否是生產環境需要的。顯然 commitizen 只在開發環境中使用。-D 只在 dev 環境使用

配置依賴路徑

在 package.json 中添加配置

<code>{
//...
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}
複製代碼/<code>

在命令行中輸入

<code>git add -A
git-cz
複製代碼/<code>

出現了交互輸入方式,規範你的 commit 輸入格式

吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【上】

生成 CHANGELOG

<code>npm i -g conventional-changelog-cli
複製代碼/<code>

增加一個npm 命令,快速生成日誌

<code>"genlog": "conventional-changelog -p angular -i .github/CHANGELOG.md -s" 

複製代碼/<code>

使用yarn命令生成日誌

<code>yarn genlog
複製代碼/<code>

自動生成的log

<code># 0.1.0 (2019-12-27)

### Features
* **git:** 增加commitizen工具規範提交 ([58e3937](https://github.com/suoyuesmile/suo-design-pro/commit/58e39370aa838fd99312f73b37d092ffadc85990))
複製代碼/<code>

如何管理代碼風格

較統一的代碼風格利於閱讀,也利於協作。

原理與解決方案

使用 eslint 約束基本風格和語法,使用 prettier 自動格式化你的代碼。

實踐

安裝 eslint 依賴

<code>{
"eslint": "^5.16.0",
"eslint-config-standard": "^6.2.1",
"eslint-friendly-formatter": "^2.0.7",
"eslint-loader": "^2.1.2",
"eslint-plugin-html": "^2.0.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^2.3.1",
"eslint-plugin-vue": "^5.0.0"
}
複製代碼/<code>

使用兩個插件,一個 plugin:vue/essential,一個是 standard。 vue/essential 為了在 vue 裡面也可以生效。另一個是 standard。 standard 標準文檔

使用 recommend 也可以,採用推薦 lint,更加輕量化

<code>module.exports = {
root: true,
env: {
node: true
},
extends: ['plugin:vue/essential', 'standard'],
rules: {
quotes: ['error', 'single'],
indent: ['error', 2, { MemberExpression: 'off' }],
'arrow-parens': 0,
'no-loop-func': 2,
'space-before-function-paren': ['error', 'never'],
indent: ['error', 2, { SwitchCase: 1 }]
},
parserOptions: {
parser: require.resolve('babel-eslint'),
ecmaVersion: 2018,
sourceType: 'module'
}
}
複製代碼/<code>

可以自定義 rules 的規則

rules 的規則 { 規則名:[是否關閉/規則等級,配置的值,只對部分配置] } indent: ['error', 2, { SwitchCase: 1 }] 兼容 prettier,prettier 會將代碼格式化成 eslint 報錯的情況。 規則等級:0 關閉 1 警告 2 報錯

使用 prettier

配置 prettier 文件

<code>{ 

"printWidth": 150,
"singleQuote": true,
"trailingComma": "none",
"semi": false,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"proseWrap": "preserve",
"overrides": [
{
"files": ["*.json", ".eslintrc", ".tslintrc", ".prettierrc", ".tern-project"],
"options": {
"parser": "json",
"tabWidth": 2
}
},
{
"files": "*.{css,sass,scss,less}",
"options": {
"parser": "css",
"tabWidth": 2
}
},
{
"files": "*.ts",
"options": {
"parser": "typescript"
}
},
{
"files": "*.vue",
"options": {
"parser": "vue"
}
},
{
"files": "*.md",
"options": {
"parser": "markdown"
}
}
]
}
複製代碼/<code>

開啟 vscode 自動格式化

<code>{
// prettier
"prettier.singleQuote": true,
"prettier.semi": false,
"prettier.tabWidth": 2,
"[javascript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
複製代碼/<code>

如何提升編碼效率

原理與解決方案

我主要從 3 個方面來做一些編碼效率上的改進

  • 升級你的 vue-cli 減少 webpack 配置的成本
  • 使用 sass,利用裡面函數、mixins、變量提升 css 文件的複用
  • 使用 pug,減少 html 的代碼編寫量

實踐

vue-cli3+、vue-cli4+ 相比於 vue-cli2+ 最大的改變就是將約定俗稱的配置,全部公共化了,也就是做了一次二次封裝。這樣的好處在於,我們不必要在繁多的配置代碼中尋找需要的配置。

簡單新建一個配置入口就能操作我們大多數想要的功能。在 root 目錄下新建一個 vue.config.js 文件,作為我們 webpack 的配置文件。

初始化 vue 配置

<code>const autoprefixer = require('autoprefixer')

module.exports = {
publicPath: process.env === 'production' ? '' : '/',
outputDir: 'dist',
assetsDir: 'static',
filenameHashing: true,
lintOnSave: true,
runtimeCompiler: false,
transpileDependencies: [/\\/node_modules\\/vue-echarts\\//, /\\/node_modules\\/resize-detector\\//],
productionSourceMap: false
}
複製代碼/<code>

簡單的配置完成後,我們引入一個 sass 工具用於編寫 sass文件

使用 Sass

安裝與使用

<code>yarn add -D sass sass-loader
複製代碼/<code>

如何處理樣式

在 assets 目錄中建立一個 styles 文件專門來存放樣式文件,新增入口index.scss文件,便於 JavaScript 引入,增加 utils.scss、reset.scss、varibles 文件。

這些樣式工具都是為了提升我們 scss 開發效率,具有暢快的開發體驗!

使用 varibles 變量文件

為了提升我們代碼的可讀性,複用性。使用 sass 變量必不可少。

還有一點就是利於全局修改樣式,如果需要更換皮膚這個功能,我們只需要更改全局的主題色,即可更換主題,那樣更加方便。

<code>// 主題色
$color-red: #ff3333;
$color-purple: #ff33a9;
$color-orange: #ff8833;
$color-blue: #3377ff;

// 文字色
$color-black: #000;
$color-dark: #333;
$color-deep: #555;
$color-pl: #999999;
$color-weak: #B3B3B3;
$color-white: #fff;

// 背景色
$bg-bar: #F9F9F9;
$bg-page: #F3F3F3;
$bg-page-light: #F9F9F9;
複製代碼/<code>

使用變量之後,sass 文件不會直接生效,至少在 vue 文件 裡面是訪問不到的。 需要在 vue.config.js 裡面增加如下配置。

<code>module.exports = {
// ...
css: {
sourceMap: true,
loaderOptions: {
sass: {
prependData: `
@import "@/assets/styles/variable.scss";
`
}
}
}
}
複製代碼/<code>

覆蓋默認樣式

常規操作, 引入 reset.scss 將默認樣式覆蓋掉

<code>/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
\tmargin: 0;
\tpadding: 0;
\tborder: 0;
\tfont-size: 100%;
\tfont: inherit;
\tvertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
\tdisplay: block;
}
body {
\tline-height: 1;
}
ol, ul {
\tlist-style: none;
}
blockquote, q {
\tquotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
\tcontent: '';
\tcontent: none;

}
table {
\tborder-collapse: collapse;
\tborder-spacing: 0;
}

html, body {
width: 100%;
height: 100%;
overflow: auto;
margin: 0;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
複製代碼/<code>

使用樣式工具集

有時候我們發現,光是引入變量還不夠,變量工具只能允許我們在 css 類文件中使用。假如我想著模版中直接使用樣式,有沒有更快的方案呢?

當然有的,我們可以自定義一個常用的樣式工具集。設置一些背景顏色、字體顏色、盒子模型中的常規操作。

要是有設計規範更好哦,我也是常常向設計師提出要求,一定要制定出一套產品的設計規範。

<code>// utils 工具
// 顏色
.bg-red {background-color: $color-red!important;}
.bg-purple {background-color: $color-purple!important;}
.bg-orange {background-color: $color-orange!important;}
.bg-blue {background-color: $color-blue!important;}
.color-red {color: $color-red!important;}
.color-purple {color: $color-purple!important;}
.color-orange {color: $color-orange!important;}
.color-blue {color: $color-blue!important;}

.text-black {color: #000;}
.text-dark {color: #333;}
.text-deep {color: #555;}
.text-weak {color: #B3B3B3;}
.text-white {color: #fff;}

// 字體
.f10 {font-size: 10px;}
.f12 {font-size: 12px;}
.f14 {font-size: 14px;}
.f15 {font-size: 15px;}
.f17 {font-size: 17px;}
.f20 {font-size: 20px;}
.f24 {font-size: 24px;}

// 文字對齊
.tl {text-align: left;}
.tc {text-align: center;}
.tr {text-align: right;}

// 浮動與清除浮動
.fl {float: left;}
.fr {float: right;}
.fix {*zoom: 1;}
.fix:after{display:table; content:''; clear:both;}

// 顯示
.dn{display:none;}
.di{display:inline;}
.db{display:block;}
.dib{display:inline-block;}
.dt{display:table;}
div.dib{*display:inline; *zoom:1;}
.vm {vertical-align: middle;}
.vib {display:inline-block; vertical-align: middle;}

// 定位
.pr {position: relative;}
.pa {position: absolute;}
.pf {position: fixed;}

// 盒子模型
.ml4 {margin-left: 4px;}
.mr4 {margin-right: 4px;}
.mt4 {margin-top: 4px;}
.mb4 {margin-bottom: 4px;}
.ml8 {margin-left: 8px;}
.mr8 {margin-right: 8px;}

.mt8 {margin-top: 8px;}
.mb8 {margin-bottom: 8px;}
.ml12 {margin-left: 12px;}
.mr12 {margin-right: 12px;}
.mt12 {margin-top: 12px;}
.mb12 {margin-bottom: 12px;}
.ml16 {margin-left: 16px;}
.mr16 {margin-right: 16px;}
.mt16 {margin-top: 16px;}
.mb16 {margin-bottom: 16px;}
.ml20 {margin-left: 20px;}
.mr20 {margin-right: 20px;}
.mt20 {margin-top: 20px;}
.mb20 {margin-bottom: 20px;}
.ml24 {margin-left: 24px;}
.mr24 {margin-right: 24px;}
.mt24 {margin-top: 24px;}
.mb24 {margin-bottom: 24px;}

.ml10 {margin-left: 10px;}
.mr10 {margin-right: 10px;}
.mt10 {margin-top: 10px;}
.mb10 {margin-bottom: 10px;}
.ml15 {margin-left: 15px;}
.mr15 {margin-right: 15px;}
.mt15 {margin-top: 15px;}
.mb15 {margin-bottom: 15px;}

// 按鈕禁用
.disabled{outline:0 none; cursor:default!important; opacity:.4; filter:alpha(opacity=40); -ms-pointer-events:none; pointer-events:none;}
複製代碼/<code>

增加樣式入口文件

最後一步,新建一個入口文件,將樣式工具類全部導入進來,供主程序引入。

<code>// index.scss 文件
// @import './varibles.scss'
@import './reset.scss';
@import './utils.scss';
複製代碼/<code>

varibles.scss vue 配置中引入,這裡無需引入

在 main.js 中直接引入 index.scss

<code>import '@/assets/styles/index.scss'
複製代碼/<code>

vue 中寫樣式要注意哪些方面,有哪些技巧呢?

避免全局汙染

在頁面中寫 css/scss 加上 scoped,scoped 的功能就是使頁面的樣式是局部的,不讓影響其他頁面的樣式。

bem 規範

我們大多數人時候會遇到問題,樣式嵌套太多了怎麼命名

BEM是塊(block)、元素(element)、修飾符(modifier)的簡寫,由 Yandex 團隊提出的一種前端 CSS 命名方法論。

名字太長易讀性太差

<code>.cardbox {
.cardbox-card {
.cardbox-card-wrapper
.cardbox-card-wrapper-header {
.cardbox-card-wrapper-header-title {
// ...
}
}
.cardbox-card-wrapper-body{
.cardbox-card-item {
.cardbox-card-item-title {
// ...
}
}
}

}
}
}
複製代碼/<code>

bem 使用方式

block-name__element-name--color

  • 區分塊,子元素,修飾元素
  • 塊,頁面中獨立的單元
  • 子元素,塊裡面的兒子 card__item 使用 __ 連接
  • 子元素長命名使用 - 連接
  • 修飾(易變的)card__item--warning 使用 --

我們使用 bem 改造樣式

<code>.cardbox {
&__header {
&__title {
//...
}
}
&__body {
&__item {
&__title {
//...
}
}
}
}
複製代碼/<code>

bem 一般推薦子元素嵌套儘量在2-3層以內

但是我們發現樣式子元素嵌套有點多,使用了兩重子元素嵌套。

大致原理是嘗試分離父子元素的關係,把卡片本身當作一個塊看待。

下面來試著去減少嵌套:

<code>.cardbox {
&__header {
&__title {
//...
}
}
&__body {
.card {
&__title {
//...
}
}
}
}
複製代碼/<code>

現在編寫樣式效率提高也更加規範了,那麼編寫 HTML 也是有很多累贅的代碼。

比如大多數標籤都是前開後閉的。通過 pug 我們可以省略很多字符的敲打,下面我們談談如何使用 pug 編寫模版。

當然喜歡哪種 HTML 編寫風格見人見智啦,我自己更加傾向 pug,那種縮進和簡潔的表達,有種在寫 scss 的感覺。

如何使用 pug

類似 sass,首先安裝 pug 和 pug 的 loader

<code>yarn add -D pug pug-html-loader pug-plain-loader
複製代碼/<code>

完成配置

<code>module.exports = {
// ...
chainWebpack: (config) => {
config.module
.rule('pug')
.test(/\\.pug$/)
.use('pug-html-loader')
.loader('pug-html-loader')
.end()
}
}
複製代碼/<code>

編寫 pug 代碼

使用 scss 工具與 pug 完美搭配,少寫很多代碼

<code>// 登陸
<template>
.login
h1.login__title.ml15 註冊/登陸
.login__form.mt15.ml15
van-field.login__form__input(placeholder="輸入手機號" v-model="phone")
.login__form__protocol.mt15
.login__form__protocol__tips.dib.text-weak 註冊或登錄即表示同意
.login__form__protocol__name.dib.color-orange 《用戶協議》
app-button.mt15(size="large"
theme="orange"
:disabled="phone.length !== 11"
@click="handleSubmit"
) 下一步
/<template>
複製代碼/<code>

我們已經引入了樣式,接下來我將談談其他資源的引入

如何管理你的資源

原理與解決方案

我暫時把資源分為下面幾種

  • 字體
  • ICON
  • 圖片
  • 樣式 把他們各自新建一個目錄,都放在 assets 目錄下面分門別類,供其他地方調用。 使用 alias 更好重命名,使之更便捷的訪問到。

增加 vue.config.js 配置 ,設置assets別名

<code>const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
//...
chainWebpack: (config) => {
config.resolve.alias.set('@', resolve('src')).set('@assets', resolve('src/assets'))
}
}
複製代碼/<code>

ICON

引入 iconfont

  1. iconfont 阿里圖標項目中下載,將整個項目圖標包一起下載下來
吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【上】

  1. 引入 iconfont 樣式 需要下面四個文件
  • iconfont.eot
  • iconfont.ttf
  • iconfont.woff
  • iconfont.woff2
  1. 項目中引入 iconfont 讓 icon 組件化
<code><template>

/<template>



<style>.iconfont {<br> font-size: 16px;<br> color: $color-icon;<br>}/<style>
複製代碼/<code>

引入圖片作為 ICON

讓圖片組件化,我們再來寫一個 img 組件

<code><template>
img(
:class="lazy" data-original="require(`@/assets/images/${name}.png`)"
v-bind="$attrs"
v-on="$listeners"
:style="{'width': width ? width + 'px' : size + 'px', 'height': height ? height + 'px' : size + 'px' }")
/<template>

複製代碼/<code>

如何管理你的路由

原理與解決方案

使用 vue-router,使用import() 生成異步路由,只有在訪問時候才會加載模塊。

為什麼使用 import() 會異步加載模塊?

MDN:在您希望按照一定的條件或者按需加載模塊的時候,動態import() 是非常有用的。而靜態型的 import 是初始化加載依賴項的最優選擇,使用靜態 import 更容易從代碼靜態分析工具和 tree shaking 中受益。

說白了就是起到一個按需加載的目的。現在大多數實現的按需加載,基本都是依賴 import() 這個方法。

安裝 vue-router

<code>yarn add vue-router
複製代碼/<code>

安裝完 router,在編寫 router 先創建頁面

新建一個空頁面

src 目錄下新增 views 目錄存放頁面文件。創建 index 目錄和 home 頁面

<code><template>
.home 首頁
/<template>


<style><br>/<style>
複製代碼/<code>

編寫路由

<code>const routes = [
{
// 首頁
path: '/',
name: 'index',
redirect: '/home',
component: App,
children: [
{
// 首頁
path: 'home',
name: 'home',
// 路由懶加載
component: () =>

import(
/* webpackChunkName: "index" */ '../views/index/home.vue'
)
}
]
}
]
Vue.use(VueRouter)

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

export default router
複製代碼/<code>

為了消除 # 顯得路徑更加好看簡潔,我們採用 history 模式,但是 history 模式有個問題就是,異步路由沒有緩存在頁面中,第一次進入頁面會找不到

在 vue.config.js 中增加配置,開發環境可以訪問到,恢復正常

<code>module.exports = {
// ...
devServer: {
historyApiFallback: true
}
}
複製代碼/<code>

關於路由還有很多可以研究到地方,可以自行研究哦!

組件化開發

原理與解決方案

一般來說,我們根據組件的複用度,分給基礎(公共)組件和業務組件。

為了節省時間,快速開發,這裡基礎組件大部分引用開源組件。當然不能直接就用哦。

一般要進行二次封裝,也就是高階組件開發。

  1. 通過修改和覆蓋當前組件的樣式來達到修改樣式的作用。
  2. 通過攔截事件來更改js的邏輯。

下面我們先引入 vant 組件

實踐

引入 vant

<code>yarn add vant
複製代碼/<code>

對基礎組件進行二次封裝和改造

下面 7 步來寫好一個公共組件

  1. 新建一個 components 目錄來存放基礎組件
  2. 基礎組件命名為 app-xxx 或 appXxx,新建一個 app-button 目錄,新建 index.vue
  3. 根據設計稿設計和編寫組件

編寫組件之前首先要設計組件,根據組件的不變性和可變性原則編寫。不變性是組件的核心,可變性根據參數對組件對相關部分進行調節,實現可選擇的功能。

  1. 實現組件
<code><template>
div.dib
van-button.btn(
@click="$emit('click')"
:class="getClass" v-bind="$attrs"
:style="{'width': size === 'large' ? '345px': '', 'backgroundColor': getBgColor, borderColor: getBgColor, color: getBgColor}")
slot
/<template>



<style><br>.app-btn {<br> ::v-deep &--primary {<br> padding: 8px 30px;<br> height: 40px;<br> border-radius: 4px;<br> font-size: 15px;<br> font-weight: 400;<br> line-height: 19px;<br> color: white!important;<br> }<br> ::v-deep &--minor {<br> padding: 5px 10px;<br> height: 26px;<br> border-radius: 14px;<br> font-size: 12px;<br> font-weight: 400;<br> line-height: 16px;<br> background-color: #fff!important;<br> }<br> ::v-deep &--rect {<br> padding: 5px px;<br> height: 49px;<br> font-size: 14px;<br> color: white!important;<br> }<br>}<br>/<style>
複製代碼/<code>

::v-deep 樣式深覆蓋,scoped 情況下覆蓋組件樣式,不改變其樣式

  1. 寫好基礎組件 README,為什麼要寫文檔呢?如果多人開發同一個項目,基礎組件會被其他人引用。方便其他人使用,所以要寫文檔。

一句話言:只要可能被其他人用的公共方法和組件,註釋或文檔是很重要的,對自己的代碼負責哈。

其他用法參照 vant

  1. 全局引用,基礎組件許多頁面都會用到,將其設置成全局組件,其他地方就不必再引用了哦。

新建一個文件 global 存放全局組件註冊,在 main.js 引入

<code>import Vue from 'vue'
import appButton from '@/components/app-button'
Vue.component('app-button', appButton)
複製代碼/<code>
  1. 寫好 demo,即使暫時不寫單元測試,也要寫好一個 demo,使之能正常的運行

添加 demo 頁面和路由

<code><template>
div(class="base")
// 按鈕組件
app-button.mt4(theme="blue") 確認支付
app-button(theme="orange") 確認支付
app-button(theme="purple") 確認支付
app-button.mt4(theme="red") 確認支付
app-button(theme="grey") 確認支付
app-button.mt4(theme="blue" size="large") 修改地址
app-button.mt4(theme="orange" size="large") 修改地址
app-button.mt4(theme="purple" size="large") 修改地址
app-button.mt4(theme="red" size="large") 修改地址
app-button.mt4(theme="grey" size="large") 修改地址
app-button.mt4(theme="blue" type="minor") 確認收貨
app-button(theme="orange" type="minor") 確認收貨

app-button(theme="purple" type="minor") 確認收貨
app-button(theme="red" type="minor") 修改地址
app-button(theme="grey" type="minor") 修改地址
app-button.mt4(theme="blue" type="rect") 確認收貨
app-button(theme="orange" type="rect") 確認收貨
app-button(theme="purple" type="rect") 確認收貨
app-button(theme="red" type="rect") 修改地址
app-button(theme="grey" type="rect") 修改地址
/<template>

<style><br>/<style>
複製代碼/<code>

實現效果

吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【上】

如何封裝請求

原理與解決方案

基本上就是對 axios 的封裝,封裝主要有兩個目的。

  • 修改一些基礎的配置:請求地址,超時,其他的雜七雜八的
  • 統一操作:統一處理錯誤,統一處理請求參數和格式,響應參數和格式。統一處理 message,統一攔截掛載等等。

網上已經有很多類似的文章了, 我這裡給出我常用的封裝方案。

實踐

根據不同環境設置請求地址

<code>// .env-default.js 文件
// 不同環境訪問不同的路徑
const api = {
develop: 'http://xxxx:8080',
mock: 'http://xxxx',
feature: 'http://xxxx',
test: 'http://xxxx',
production: 'http://xxxx'
}

export const baseURL = api[process.env.NODE_ENV || 'dev']
複製代碼/<code>

因為每個人開發環境,mock環境不一定相同,這個文件建議 gitignore忽略掉。模版可以寫在 readme 文檔中,啟動項目時加入文件。

新建一個 utils 工具

我們現在將 axios 封裝成我們自己需要的配置,然後定義四個常用的請求方法供調用

<code>// utils/http.js 文件
import axios from 'axios'
import { baseURL } from '../../.env-defalut.js'

// axios 配置
const defaultBaseUrl = 'http://localhost:8080/'
// 默認超時時間
axios.defaults.timeout = 15000
// 數據接口域名統一配置.env
axios.defaults.baseURL = baseURL || defaultBaseUrl

// http request 攔截器
axios.interceptors.request.use(
(config) => {
config.headers = {
}
return config
},
(err) => {
return Promise.reject(err)
}
)

// http response 攔截器
axios.interceptors.response.use(
(response) => {
return response
},
(error) => {
const data = error.response.data
return Promise.reject(data || error)
}
)

export default axios

/**
* fetch 請求方法

* @param {*} url
* @param {*} params
*/
export function fetch(url, params = {}) {
return new Promise((resolve, reject) => {
axios
.get(url, {
params: params
})
.then((response) => {
resolve(response.data)
})
.catch((err) => {
reject(err)
})
})
}

/**
* post 請求方法,發送數據格式 json
* @param {*} url
* @param {*} data
*/
export function post( url,
data = {},
config = {
transformRequest: [
function(fData, headers) {
headers['Content-Type'] = 'application/json'
return JSON.stringify(fData)
}
]
}
) {
return new Promise((resolve, reject) => {
axios.post(url, data, config).then(
(response) => {
resolve(response.data)
},
(err) => {
reject(err)
}
)
})
}

/**
* patch 請求方法,發送數據格式 json

* @param {*} url
* @param {*} data
*/
export function patch(url, data = {}) {
return new Promise((resolve, reject) => {
axios
.patch(url, data, {
transformRequest: [
function(fData, headers) {
headers['Content-Type'] = 'application/json'
return JSON.stringify(fData)
}
]
})
.then(
(response) => {
resolve(response.data)
},
(err) => {
reject(err)
}
)
})
}

export function del(url, data) {
return new Promise((resolve, reject) => {
axios.delete(url, { data }).then(
(response) => {
resolve(response.data)
},
(err) => {
reject(err)
}
)
})
}
複製代碼/<code>

如何管理 api

原理

首先要制定一個 api 的原則 我的原則一般是這些:

  • 乾淨純粹
  • 儘量不要處理數據
  • 獨立單一不要互相依賴

好處在於:不在 api 裡面處理數據,api裡面的接口和接口文檔上一樣。避免別人引用我的api,還要去看代碼,只需要看文檔就好了。

例子:想象這樣一種情況,別人引用了我的 api,突然發現響應數據不對。首先它排查到頁面數據沒更改。看了api文檔,數據也沒問題,最後發現我在寫 api 的時候進行了處理,這個 api 呢又不能改動,改了影響我自己的模塊。只能它在重新寫一個api,這樣顯得很繁雜了,不夠乾淨優雅。

<code>import { fetch, post } from '@/utils/http'

// 用戶登陸
export const login = data => post('/user/login', data)

// 獲取用戶信息
export const getUserInfo = (data) => fetch('/api/user/info', data)
複製代碼/<code>

如果需要處理數據,要麼使用一箇中間工具處理,要麼在頁面裡面處理。當然還是實際問題實際分析。

如何使用mock模擬數據

原理與解決方案

一般就是兩種方案,一是模擬後端,使用遠程在線 JSON 服務器。另外一種搭建本地 JSON 或者 使用現成的 Node 服務器攔截請求。

這兩種方式各有千秋,沒有優劣之分,適合就是最好的。

遠程在線mock

我用過的遠程在線mock

  • apizza:好用,功能齊全,喜歡他的文件展開目錄api,免費版只支持 2 個人共同編輯
  • swagger:開源免費,api管理太凌亂,
  • rap/rap2:開源免費,可以搭建本地api,需要自己搭建

使用遠程mock的優點:

  • 不需要在項目內部增加mock
  • 功能更加全面完善
  • 可以在接口文檔基礎上mock,與接口文檔放在一起查看更加方便。

缺點:需要自己另外搭建服務器,只支持靜態的mock,不能與單元測試結合使用

本地JSON mock

  • 使用 webpack 內部 mock 配置
<code>devServer: {
// 接口未實現情況下,使用mock
before: require('./mock')
}
複製代碼/<code>

基本原理:主要是使用 node 讀取文件,轉換成 JSON 格式,使用mock.js 模擬數據,最後 webpack 攔截請求生成json響應數據

<code>const Mock = require('mockjs')

module.exports = (app) => {
function getJsonFile (filePath) {
var json = fs.readFileSync(path.resolve(__dirname, filePath), 'utf-8')
return JSON.parse(json)
},
const returnMock = (datafile, res, req) => {
setTimeout(() => {
var json
if (/\\.json$/.test(datafile)) {
// json文件暴露的是mock的模板
json = getJsonFile(datafile)
} else if (/\\.js$/.test(datafile)) {
json = require(datafile)(req.query)
}
res.json(Mock.mock(json))
}, 500)
}
}
複製代碼/<code>
  • 使用 json-server 搭建 主要分為下面幾步
  1. npm 安裝 json-server
  2. 編寫 npm 腳本命令,引入 mock 配置文件
  3. 編寫 mock 路由匹配規則

比較簡單這裡不詳細描述了!

本地的缺點在於需要

  • 前端需要根據api文檔寫mock數據格式
  • 功能沒有遠程mock那麼完善,支持restful需要去研究下
  • 也是需要配置相關mock工具

優點在於

  • 不用查看編輯api文檔
  • 在代碼中就可以更改和查看mock數據
  • 支持使用JavaScipt動態處mock,可以與單元測試結合使用

總結

本篇文章耗費作者一個多星期的業餘時間,存手工敲打 6000+字,同時收集,整理之前很多技巧和邊寫作邊

思考總結。如果能對你有幫助,便是它最大的價值。都看到這裡還不點贊,太過不去啦!

由於技術水平有限,文章中如有錯誤地方,請在評論區指出,感謝!

文中大多數代碼將在suo-design-pro 中更新

項目有時間會盡量完善

寫實踐總結性文章真的很耗費時間。如何文章中有幫到你的地方分享下唄,讓更多人看到!

下節內容預告

  • 如何編寫原生組件,以及組件編寫的思考與原則?
  • 如何使用vuex 以及它的應用場景和原理
  • 如何使用過濾器,編寫自己的過濾器
  • 如何使用 Jest 測試你的代碼?TDD 與 BDD 的比較


分享到:


相關文章: