上一篇文章寫了vue和typescript的整合,發現很多小夥伴對vue-cli構建出來的專案很感興趣,所以今天打算寫寫怎麼在vue-cli3.0的架子上,在進一步完善,整合出具備基礎功能的前端架子,主要包括以下幾個功能點:
- webpack 打包擴充套件
- css:sass支援、normalize.css
- rem佈局
- 路由設計:懶載入、前置檢查、合法性校驗
- api 設計
- 請求體設計-防重複提交
- vuex狀態管理
webpack 打包擴充套件
vue-cli3 最大的特點就是零配置
,腳手架把webpack相關的配置都隱藏在@vue\preload-webpack-plugin中,預設的配置可以滿足大部分應用場景,優點是我們可以節省很多折騰配置的時間,webpack對於新手來說,還是有點門檻的,這樣一來,新人上手可以更關注於vue的編碼上。缺點也很明顯,對於想自己進行自定義配置的時候,就會稍微麻煩些。
檢視當前webpack的詳細配置
使用 vue inspect
可以檢視到詳細的配置列表
擴充套件webpack配置
當我們想要修改或者擴充套件webpack配置項時,可以在根目錄下新增 vue.config.js
檔案,列舉個我自己寫的簡單小栗子
// webpack 擴充套件
module.exports = {
baseUrl: 'production' === process.env.NODE_ENV ?
'/production-sub-path/' :
'/',
chainWebpack: config => {
config.module
.rule('images')
.use('url-loader')
.tap(options => Object.assign(options, { limit: 500 }));
},
devServer: {
open: 'darwin' === process.platform,
// host: '0.0.0.0',
port: 8088,
https: false,
hotOnly: false,
// proxy: 'https://api.douban.com' // string | Object
proxy: 'http://localhost:3000' // string | Object
},
lintOnSave: false
};
複製程式碼
官網Vue.js 開發的標準工具 的介紹非常詳細,而且還有中文版,非常易懂,
sass支援
- 元件中這麼寫
<style lang="scss"></style>
就可以支援scss語法 - 在元件中import別的scss檔案,寫法如下
<style lang="scss">
@import "./assets/style/app";
</style>
複製程式碼
- 在元件中使用自定義的 functions 和 mixin,我暫時沒找到全域性引用的辦法,只能在需要使用的元件檔案中手動引用,如下
<style lang="scss">
@import "../assets/style/functions";
@import "../assets/style/mixin";
.rem {
height: px2rem(187.5px); //自定義的函式
}
.mimi {
@include clearfix(); //自定義的mixin
}
</style>
複製程式碼
- 為了抹平各個瀏覽器間的差異,我們需要引入 normalize.css
// app.scss
@import "./node_modules/normalize.css/normalize"; //引用第三方normalize
@import "custom_normalize"; // 自定義的normalize
複製程式碼
rem佈局
在移動端下使用rem佈局是個不錯的選擇,既然我們使用裡的scss,那麼可以使用函式來簡化我們的重複計算的工作。設計給到的通常是2倍圖,寬為750px,那麼我們可以將基準設為
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
然後寫個轉換函式,如下:
// _functions.scss
@function px2rem($px) {
$rem: 75px;
@return ($px/$rem) + rem;
}
複製程式碼
我們在使用的時候,就可以這麼寫
.rem {
height: px2rem(300px); // 2倍圖下的寬是300px,
}
複製程式碼
轉換成css就是
.rem {
height: 4rem;
}
複製程式碼
路由設計
主要包括路由懶載入、路由前置檢查、合法性校驗邏輯,以下是我寫的一個簡單路由
import Vue from 'vue';
import Router from 'vue-router';
// 路由懶載入
const getComponent = (name: string) => () => import(`./views/${name}.vue`);
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
name: 'home',
component: getComponent('home')
},
{
path: '/about',
name: 'about',
component: getComponent('about'),
meta: {
auth: true
}
},
{
path: '*',
name: 'not_fount',
component: getComponent('notFount')
}
]
});
/**
* 路由前置檢查
*/
router.beforeEach((to, from, next) => {
// 合法性校驗
if (to.meta.auth) {
console.log('into auth');
next();
}
next();
});
export default router;
複製程式碼
api 設計
新建service
資料夾用於存放api指令碼,根據業務模組來劃分檔案,如使用者相關的api一個檔案、購買相關的一個檔案,api.ts
是各模組api的集合,如下
// service/api.ts
export { userApi } from './user';
export { buyApi } from './buy';
// service/user.ts
export const userApi = {
/**
* 獲取使用者資料
*/
userInfo: '/node_api/read/userInfo'
};
// service/buy.ts
export const buyApi = {
/**
* 購買
*/
shoping: '/node_api/shop/buy'
};
複製程式碼
這麼劃分,是為了專案結構和業務結構都足夠清晰,同時可以避免單檔案過長的問題。
HTTP請求二次封裝
傳送http我使用的是非常流行的axios
,我在其基礎上,稍微進行簡單的封裝,然後暴露 request
物件供呼叫。二次封裝主要是為了解決以下幾個問題
-
簡化引數,把一些常用引數都賦預設值,簡化外部的使用,使得更加通用和利於排查問題。
-
返回報文統一處理,我們通常需要對些高頻的場景做相同的處理,如錯誤碼、未登入等場景,可以在它提供的返回響應攔截器中,統一處理。
-
防止重複提交,因為網路、後端處理的因素,有時介面響應會較慢,那麼使用者可能會在非常短的時間內,反覆點選按鈕,在第一次請求未返回的情況下,會再次發起新的請求,那麼我們可以在axios提供的前置攔截器中搞點事情。關於防止重複請求這東東,我在以前的一篇文章有寫過,前端防止使用者重複提交-js 感興趣的小夥伴可以看看。
根據以上幾點,下面是我封裝的request檔案,思路都比較簡單,就不多說啦
import axios from 'axios';
import qs from 'qs';
const Axios = axios.create({
baseURL: '/',
timeout: 10000,
responseType: 'json',
withCredentials: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
}
});
const CancelToken = axios.CancelToken;
const requestMap = new Map();
// 請求前置攔截器
Axios.interceptors.request.use(
config => {
// 防重複提交
const keyString = qs.stringify(Object.assign({}, { url: config.url, method: config.method }, config.data));
if (requestMap.get(keyString)) {
// 取消當前請求
config.cancelToken = new CancelToken((cancel) => {
cancel('Please slow down a little');
});
}
requestMap.set(keyString, true);
Object.assign(config, { _keyString: keyString });
if (config.method === 'post' || config.method === 'put' || config.method === 'delete') {
// 序列化
config.data = qs.stringify(config.data);
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 返回響應攔截器
Axios.interceptors.response.use(
res => {
// 重置requestMap
const config: any = res.config;
requestMap.set(config._keyString, false);
if (res.status === 200) {
return res.data;
}
// todo 彈窗提示等
console.log(`request error:${res}`);
},
error => {
return {
code: -1
};
}
);
/**
* @description
* 請求
* @param url
* @param data
* @param method
*/
const request = (url: string, data = {}, method = 'post') => {
return Axios({
method,
url,
data,
params: method.toUpperCase() === 'GET' && data
});
};
export { request };
複製程式碼
vuex狀態管理
這裡我根據業務模組來劃分檔案結構,如下圖
分為首頁模組和使用者模組,每個模組都有自己獨立的 state mutations 等,在store.ts
中,引入各模組的檔案,如下
import Vue from 'vue';
import Vuex from 'vuex';
import index from './indexModule/index';
import user from './userModule/user';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
user,
index
}
});
複製程式碼
大家注意到這裡有個 store_types.ts
檔案,這個檔案主要是為了搭配ts使用的,檔案內容如下
export enum UserType {
/**
* 模組名稱
*/
'MODULE_NAME' = 'user',
/**
* 增加次數
*/
'ADD_COUNT' = 'addCount',
/**
* 計算屬性-獲取十倍的值
*/
'GET_TEM_COUNT' = 'getTenCount'
}
複製程式碼
在看下元件中的使用方式:
<script lang="ts">
import { UserType } from '@/store/store_types';
import { Component, Prop, Vue, Watch,Emit } from 'vue-property-decorator';
import {
Action,
Getter,
Mutation,
namespace,
State
} from 'vuex-class';
@Component
export default class Test extends Vue {
@State(state => state[UserType.MODULE_NAME].count) public fff!: number;
@Getter(`${UserType.MODULE_NAME}/${UserType.GET_TEM_COUNT}`) public tenCount!: number;
@Mutation(`${UserType.MODULE_NAME}/${UserType.ADD_COUNT}`) public addCount!: any;
}
</script>
複製程式碼
雖然這麼寫的確有點繞,但有個好處,我們可以通過註釋清晰知道方法和屬性的說明
小結
以上是我根據自己工作中常見的場景來設計的,希望能對小夥伴能有幫助,其中設計不當的地方,歡迎小夥伴們在留言區一起探討哈~