詳解基于vue-cli3.0如何構(gòu)建功能完善的前端架子
上一篇文章寫了vue和typescript的整合,發(fā)現(xiàn)很多小伙伴對(duì)vue-cli構(gòu)建出來的項(xiàng)目很感興趣,所以今天打算寫寫怎么在vue-cli3.0的架子上,在進(jìn)一步完善,整合出具備基礎(chǔ)功能的前端架子,主要包括以下幾個(gè)功能點(diǎn):
- webpack 打包擴(kuò)展
- css:sass支持、normalize.css
- rem布局
- 路由設(shè)計(jì):懶加載、前置檢查、合法性校驗(yàn)
- api 設(shè)計(jì)
- 請(qǐng)求體設(shè)計(jì)-防重復(fù)提交
- vuex狀態(tài)管理
webpack 打包擴(kuò)展
vue-cli3 最大的特點(diǎn)就是 零配置
,腳手架把webpack相關(guān)的配置都隱藏在@vue\preload-webpack-plugin中,默認(rèn)的配置可以滿足大部分應(yīng)用場(chǎng)景,優(yōu)點(diǎn)是我們可以節(jié)省很多折騰配置的時(shí)間,webpack對(duì)于新手來說,還是有點(diǎn)門檻的,這樣一來,新人上手可以更關(guān)注于vue的編碼上。缺點(diǎn)也很明顯,對(duì)于想自己進(jìn)行自定義配置的時(shí)候,就會(huì)稍微麻煩些。
查看當(dāng)前webpack的詳細(xì)配置
使用 vue inspect
可以查看到詳細(xì)的配置列表
擴(kuò)展webpack配置
當(dāng)我們想要修改或者擴(kuò)展webpack配置項(xiàng)時(shí),可以在根目錄下新增 vue.config.js
文件,列舉個(gè)我自己寫的簡(jiǎn)單小栗子
// webpack 擴(kuò)展 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 };
官網(wǎng)Vue.js 開發(fā)的標(biāo)準(zhǔn)工具 的介紹非常詳細(xì),而且還有中文版,非常易懂,
sass支持
<style lang="scss"></style>
<style lang="scss"> @import "./assets/style/app"; </style>
在組件中使用自定義的 functions 和 mixin,我暫時(shí)沒找到全局引用的辦法,只能在需要使用的組件文件中手動(dòng)引用,如下
<style lang="scss"> @import "../assets/style/functions"; @import "../assets/style/mixin"; .rem { height: px2rem(187.5px); //自定義的函數(shù) } .mimi { @include clearfix(); //自定義的mixin } </style>
為了抹平各個(gè)瀏覽器間的差異,我們需要引入 normalize.css
// app.scss @import "./node_modules/normalize.css/normalize"; //引用第三方normalize @import "custom_normalize"; // 自定義的normalize
rem布局
在移動(dòng)端下使用rem布局是個(gè)不錯(cuò)的選擇,既然我們使用里的scss,那么可以使用函數(shù)來簡(jiǎn)化我們的重復(fù)計(jì)算的工作。設(shè)計(jì)給到的通常是2倍圖,寬為750px,那么我們可以將基準(zhǔn)設(shè)為 document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
然后寫個(gè)轉(zhuǎn)換函數(shù),如下:
// _functions.scss @function px2rem($px) { $rem: 75px; @return ($px/$rem) + rem; }
我們?cè)谑褂玫臅r(shí)候,就可以這么寫
.rem { height: px2rem(300px); // 2倍圖下的寬是300px, }
轉(zhuǎn)換成css就是
.rem { height: 4rem; }
路由設(shè)計(jì)
主要包括路由懶加載、路由前置檢查、合法性校驗(yàn)邏輯,以下是我寫的一個(gè)簡(jiǎn)單路由
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) => { // 合法性校驗(yàn) if (to.meta.auth) { console.log('into auth'); next(); } next(); }); export default router;
api 設(shè)計(jì)
新建 service
文件夾用于存放api腳本,根據(jù)業(yè)務(wù)模塊來劃分文件,如用戶相關(guān)的api一個(gè)文件、購(gòu)買相關(guān)的一個(gè)文件, api.ts
是各模塊api的集合,如下
// service/api.ts export { userApi } from './user'; export { buyApi } from './buy'; // service/user.ts export const userApi = { /** * 獲取用戶數(shù)據(jù) */ userInfo: '/node_api/read/userInfo' }; // service/buy.ts export const buyApi = { /** * 購(gòu)買 */ shoping: '/node_api/shop/buy' };
這么劃分,是為了項(xiàng)目結(jié)構(gòu)和業(yè)務(wù)結(jié)構(gòu)都足夠清晰,同時(shí)可以避免單文件過長(zhǎng)的問題。
HTTP請(qǐng)求二次封裝
發(fā)送http我使用的是非常流行的 axios
,我在其基礎(chǔ)上,稍微進(jìn)行簡(jiǎn)單的封裝,然后暴露 request
對(duì)象供調(diào)用。二次封裝主要是為了解決以下幾個(gè)問題
- 簡(jiǎn)化參數(shù),把一些常用參數(shù)都賦默認(rèn)值,簡(jiǎn)化外部的使用,使得更加通用和利于排查問題。
- 返回報(bào)文統(tǒng)一處理,我們通常需要對(duì)些高頻的場(chǎng)景做相同的處理,如錯(cuò)誤碼、未登錄等場(chǎng)景,可以在它提供的返回響應(yīng)攔截器中,統(tǒng)一處理。
- 防止重復(fù)提交,因?yàn)榫W(wǎng)絡(luò)、后端處理的因素,有時(shí)接口響應(yīng)會(huì)較慢,那么用戶可能會(huì)在非常短的時(shí)間內(nèi),反復(fù)點(diǎn)擊按鈕,在第一次請(qǐng)求未返回的情況下,會(huì)再次發(fā)起新的請(qǐng)求,那么我們可以在axios提供的前置攔截器中搞點(diǎn)事情。關(guān)于防止重復(fù)請(qǐng)求這東東,我在以前的一篇文章有寫過, 前端防止用戶重復(fù)提交-js 感興趣的小伙伴可以看看。
根據(jù)以上幾點(diǎn),下面是我封裝的request文件,思路都比較簡(jiǎn)單,就不多說啦
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(); // 請(qǐng)求前置攔截器 Axios.interceptors.request.use( config => { // 防重復(fù)提交 const keyString = qs.stringify(Object.assign({}, { url: config.url, method: config.method }, config.data)); if (requestMap.get(keyString)) { // 取消當(dāng)前請(qǐng)求 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); } ); // 返回響應(yīng)攔截器 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 * 請(qǐng)求 * @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狀態(tài)管理
這里我根據(jù)業(yè)務(wù)模塊來劃分文件結(jié)構(gòu),如下圖
分為首頁(yè)模塊和用戶模塊,每個(gè)模塊都有自己獨(dú)立的 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 } });
大家注意到這里有個(gè) store_types.ts
文件,這個(gè)文件主要是為了搭配ts使用的,文件內(nèi)容如下
export enum UserType { /** * 模塊名稱 */ 'MODULE_NAME' = 'user', /** * 增加次數(shù) */ 'ADD_COUNT' = 'addCount', /** * 計(jì)算屬性-獲取十倍的值 */ '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>
雖然這么寫的確有點(diǎn)繞,但有個(gè)好處,我們可以通過注釋清晰知道方法和屬性的說明
小結(jié)
以上是我根據(jù)自己工作中常見的場(chǎng)景來設(shè)計(jì)的,希望能對(duì)小伙伴能有幫助,其中設(shè)計(jì)不當(dāng)?shù)牡胤?,歡迎小伙伴們?cè)诹粞詤^(qū)一起探討哈~也希望大家多多支持腳本之家。
- vue-cli3.0 特性解讀
- 詳解如何配置vue-cli3.0的vue.config.js
- 一份超級(jí)詳細(xì)的Vue-cli3.0使用教程【推薦】
- vue-cli3.0使用及部分配置詳解
- vue-cli3.0 腳手架搭建項(xiàng)目的過程詳解
- vue-cli3.0配置及使用注意事項(xiàng)詳解
- vue-cli3.0+element-ui上傳組件el-upload的使用
- vue-cli3.0 環(huán)境變量與模式配置方法
- 如何基于vue-cli3.0構(gòu)建功能完善的移動(dòng)端架子
- 詳解在vue-cli3.0中自定css、js和圖片的打包路徑
- 使用Vue-cli3.0創(chuàng)建的項(xiàng)目 如何發(fā)布npm包
- vue-cli3.0實(shí)現(xiàn)一個(gè)多頁(yè)面應(yīng)用的歷奇經(jīng)歷記錄總結(jié)
相關(guān)文章
Vue判斷數(shù)組內(nèi)是否存在某一項(xiàng)的兩種方法
這篇文章主要介紹了Vue判斷數(shù)組內(nèi)是否存在某一項(xiàng),今天給大家分享兩種方法,分別是findIndex()和 indexOf()方法,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07vue 數(shù)據(jù)雙向綁定的實(shí)現(xiàn)方法
這篇文章主要介紹了vue 數(shù)據(jù)雙向綁定的實(shí)現(xiàn)方法,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下2021-03-03vue實(shí)現(xiàn)移動(dòng)端touch拖拽排序
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)移動(dòng)端touch拖拽排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07vue使用動(dòng)態(tài)組件實(shí)現(xiàn)TAB切換效果
這篇文章主要介紹了vue使用動(dòng)態(tài)組件實(shí)現(xiàn)TAB切換效果的方法,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下2021-05-05vue3中使用VueParticles實(shí)現(xiàn)粒子動(dòng)態(tài)背景效果
為了提高頁(yè)面展示效果,特別類似于登錄界面內(nèi)容比較單一的,粒子效果作為背景經(jīng)常使用到,vue工程中利用vue-particles可以很簡(jiǎn)單的實(shí)現(xiàn)頁(yè)面的粒子背景效果,本文給大家分享vue粒子動(dòng)態(tài)背景效果實(shí)現(xiàn)代碼,需要的朋友參考下吧2022-05-05