基於vue-cli3.0構建功能完善的前端架子

上一篇文章寫了vue和typescript的整合,發現不少小夥伴對vue-cli構建出來的項目很感興趣,因此今天打算寫寫怎麼在vue-cli3.0的架子上,在進一步完善,整合出具有基礎功能的前端架子,主要包括如下幾個功能點:javascript

  1. webpack 打包擴展
  2. css:sass支持、normalize.css
  3. rem佈局
  4. 路由設計:懶加載、前置檢查、合法性校驗
  5. api 設計
  6. 請求體設計-防重複提交
  7. vuex狀態管理

webpack 打包擴展

vue-cli3 最大的特色就是零配置,腳手架把webpack相關的配置都隱藏在@vue\preload-webpack-plugin中,默認的配置能夠知足大部分應用場景,優勢是咱們能夠節省不少折騰配置的時間,webpack對於新手來講,仍是有點門檻的,這樣一來,新人上手能夠更關注於vue的編碼上。缺點也很明顯,對於想本身進行自定義配置的時候,就會稍微麻煩些。css

查看當前webpack的詳細配置

使用 vue inspect 能夠查看到詳細的配置列表html

擴展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 開發的標準工具 的介紹很是詳細,並且還有中文版,很是易懂,vue

sass支持

  1. 組件中這麼寫 <style lang="scss"></style> 就能夠支持scss語法
  2. 在組件中import別的scss文件,寫法以下
<style lang="scss">
@import "./assets/style/app";
</style>
複製代碼
  1. 在組件中使用自定義的 functions 和 mixin,我暫時沒找到全局引用的辦法,只能在須要使用的組件文件中手動引用,以下
<style lang="scss">
@import "../assets/style/functions";
@import "../assets/style/mixin";
.rem {
    height: px2rem(187.5px); //自定義的函數
}
.mimi {
    @include clearfix(); //自定義的mixin
}
</style>
複製代碼
  1. 爲了抹平各個瀏覽器間的差別,咱們須要引入 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'; 而後寫個轉換函數,以下:java

// _functions.scss
@function px2rem($px) {
    $rem: 75px;
    @return ($px/$rem) + rem;
}
複製代碼

咱們在使用的時候,就能夠這麼寫node

.rem {
    height: px2rem(300px); // 2倍圖下的寬是300px,
}
複製代碼

轉換成css就是webpack

.rem {
    height: 4rem;
}
複製代碼

路由設計

主要包括路由懶加載、路由前置檢查、合法性校驗邏輯,如下是我寫的一個簡單路由ios

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的集合,以下web

// 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對象供調用。二次封裝主要是爲了解決如下幾個問題

  1. 簡化參數,把一些經常使用參數都賦默認值,簡化外部的使用,使得更加通用和利於排查問題。

  2. 返回報文統一處理,咱們一般須要對些高頻的場景作相同的處理,如錯誤碼、未登陸等場景,能夠在它提供的返回響應攔截器中,統一處理。

  3. 防止重複提交,由於網絡、後端處理的因素,有時接口響應會較慢,那麼用戶可能會在很是短的時間內,反覆點擊按鈕,在第一次請求未返回的狀況下,會再次發起新的請求,那麼咱們能夠在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>
複製代碼

雖然這麼寫的確有點繞,但有個好處,咱們能夠經過註釋清晰知道方法和屬性的說明

小結

以上是我根據本身工做中常見的場景來設計的,但願能對小夥伴能有幫助,其中設計不當的地方,歡迎小夥伴們在留言區一塊兒探討哈~

相關文章
相關標籤/搜索