通過對項目結構 / 開發方式 / 技術架構的研究,再次提出部分能夠優化的點。css
剛加入新公司以後,在學習來新公司的開發技術以後,就火燒眉毛的想要將本身過往的開發方式和技巧融合到如今的項目中,下面是針對目前項目的中一些技術點的改進思路html
此方案主要由以下幾點組成:前端
方案在不斷的編寫過程當中,其餘內容還需你們共同努力發現,整理。vue
良好的代碼編寫規範能夠大幅度提升項目開發效率,增長代碼的可讀性/可維護性/可測試性,減小開發/重構等場景的複雜耗時程度。node
代碼規範能夠劃分爲構建規範/編寫規範等方面,例如項目結構的搭建,說明文檔的維護/模塊的構建與命名/代碼中註釋與業務代碼編寫方式等方面都應該考慮到並設立統一的/較優的規則來進行約束,下面就進行簡單說明:ios
項目結構構建過程當中,應該不斷維護《目錄結構說明文檔》,而且創建項目結構版本,如:1.0.0,而且應該標註以下幾點:ajax
根據目前已經處於開發過程當中的項目,整理部分規則以下:算法
通常目錄命名採用lowercase方式,,目錄由多個單詞組件則利用"-"鏈接,如:npm
router,json
store,
components,
store/modules,
utils/dygraph-plugin
views中路由視圖目錄採用大駝峯形式命名,如:
views/Algorithms
views/Events
views/Monitor
工具模塊, 基礎業務模塊文件採用lowercase方式,目錄由多個單詞組件則利用"-"鏈接,如:
bin/babel-external-helpers.js (babel中的命名方式)
store/modules/event.js
組件命名採用大駝峯方式命名,如:
AlgorithmsCenter.vue
AlgorithmsDdetail.vue
具體命名方式能夠參考Vue.js風格指南 。
代碼編寫過程當中應遵照基本的開發規範準則,在這裏只是給出部分建議。
BEM(Block-Element-Modifier)結構命名方式
HTML編寫DOM結構並標時儘可能遵照BEM命名方式,具體請參考文獻:BEM--前端命名規範介紹,BEM —— 源自Yandex的CSS 命名方法論。
注意:不必真的在每一個地方都用上它,當某個節點不屬於任何一個BEM範疇的時候,按照常規命名方式就能夠。
OOCSS(Object Oriented CSS)編寫方式
OOCSS(面向對象CSS編寫)並非什麼新奇的技術,它想代表的其實就是咱們能夠將樣式與固定的dom解耦,將一個或多個細小的css樣式添加到須要它們的dom節點身上,目的其實仍是爲了提升代碼的複用率,而且代碼可讀性也會有很大的提升,在項目中特別推薦使用BEM + OOCSS的開發方式,網上有不少結合使用指南,這裏給你們提供一個很是簡潔的CSS的組件化方案:OOCSS + BEM。
Javascript規範能夠參考Eslint中更多詳細的規則,Google Javascript規範也是不錯的選擇,好比:
1. 拒絕var
2. 使用空格代替tab
3. 優先使用箭頭函數
4. 使用模版字符串代替鏈接字符串
5. ...
複製代碼
這裏提供給你們來自阿里的Kissy 最佳編碼實踐。
註釋方式
對於註釋其實應該更加看重,好的註釋能夠大幅度提升代碼的可讀性。
HTML文檔中應該對頁面每個Block的開始和結束進行註釋說明,單獨的Element也要標明,例如:
<div class="events-center">
<!-- 頭部圖表顯示 start -->
<div class="top-chart">
<stacked-bar-chart :counts="eventCounts" :color="eventColor" @click="handleBarClick"></stacked-bar-chart>
</div>
<!-- 頭部圖表顯示 end -->
<!-- 搜索與操做模塊 start -->
<div class="toolbar horizontal background">
<!-- 搜索功能組件 -->
<cv-search :placeholder="helloWord" v-model="query"></cv-search>
<!-- 設置按鈕 -->
<div v-if="!filterToggle" @click="openFilter" class="filter-icon"><icon-settings /></div>
<!-- 模式切換按鈕 -->
<div v-if="!filterToggle" @click="switchMode" class="filter-icon"><icon-mode /></div>
<!-- 操做按鈕組 start-->
<cv-button v-if="filterToggle" kind="secondary" @click="resetFilter">重置</cv-button>
<cv-button v-if="filterToggle" kind="secondary" @click="cancelFilter">取消</cv-button>
<cv-button v-if="filterToggle" @click="confirmFilter">確認</cv-button>
<!-- 操做按鈕組 end-->
</div>
<!-- 搜索與操做模塊 end -->
</div>
複製代碼
CSS中也應該根據所編寫的代碼劃分結構片斷後註釋,儘量使用多行註釋方式(/**/),以下:
/* css reset start*/
*{
box-sizing: border-box
}
body {
margin: 0;
background-color: #FFF;
font-family: MicrosoftYaHei, PingFangSC, Helvetica, Arial, sans-serif, "宋體";
}
// ...
/* elemnt-ui table 樣式重置 */
.el-table tr {
background-color: #f3f3f3;
cursor: pointer;
}
// ...
複製代碼
JS中的註釋但願能採用JSDoc推薦的方式,這樣更有利於開發到某一階段時,利用JSDoc工具直接生成關於該JS模塊的文檔,具體能夠參考文檔:JSDoc在線文檔,JSDoc中文文檔
/**
* 爲了演示JSDoc的示例模塊.
* @module utils/jsdoc
* @see module:main.js
*/
/** 暴露 name */
export const name = 'mixer';
/**
* 錯誤處理方法.
* @param {object} self - 調用方法的vue實例.
* @param {object} error - 錯誤對象.
* @param {string} errorMessage - 出錯提醒信息.
* @return {string} 處理後的錯誤信息文本.
* @example
* errorTip(this, e, '登錄失敗')
*/
export function errorTip (self, error, errorMessage) {
// 處理後的錯誤文本信息
let message = translate(error.message || error.msg || '出錯了,請重試')
console.log(error, message)
// 調用vue實例的消息提示方法
self.$message({
type: 'error',
message: errorMessage || message,
center: true,
duration: (error.message && error.message.indexOf('Network Error') !== -1) ? 8000 : 3000
})
return message
}
/**
* 錯誤文本信息處理方法.
* @param {string} message - 原始的error對象中的報錯信息.
* @return {string} 處理後的錯誤信息.
*/
function translate (message) {
if (message.indexOf('timeout') !== -1) {
return '請求超時'
} else if (message.indexOf('Network Error') !== -1) {
return '網絡錯誤'
} else {
return '出錯了,請重試'
}
}
複製代碼
生成文檔的方式:
下載jsdoc-to-markdown 能夠將文檔輸出爲markdown
npm install --save-dev jsdoc-to-markdown
複製代碼
配置scripts
{
"scripts": {
"docs": "jsdoc2md lib/*.js > api.md"
}
}
複製代碼
執行任務
npm run docs
複製代碼
便可將lib下全部的js文件根據jsdoc規範生成到api.md中。
針對axios進行了更高效的封裝,好比設置了攔截器,在數據請求回來後就能夠根據請求結果的狀態進行失敗處理等等。
config/axios.config.js:
/**
* axios 配置模塊
* @module config/axios.config
* @see utils/request
*/
import qs from 'qs';
/**
* axios具體配置對象
* @description 包含了基礎路徑/請求先後對數據對處理,自定義請求頭的設置等
*/
const axiosConfig = {
baseURL: process.env.RESTAPI_PREFIX,
// 請求前的數據處理
transformRequest: [function (data) {
return data;
}],
// 請求後的數據處理
transformResponse: [function (data) {
return data;
}],
// 自定義的請求頭
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded'
},
// 查詢對象序列化函數
paramsSerializer: function (params) {
return qs.stringify(params);
},
// 超時設置s
timeout: 10000,
// 跨域是否帶Token 項目中加上會出錯
// withCredentials: true,
// 自定義請求處理
// adapter: function(resolve, reject, config) {},
// 響應的數據格式 json / blob /document /arraybuffer / text / stream
responseType: 'json',
// xsrf 設置
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
// 下傳和下載進度回調
onUploadProgress: function (progressEvent) {
Math.round(progressEvent.loaded * 100 / progressEvent.total);
},
onDownloadProgress: function (progressEvent) {
Math.round(progressEvent.loaded * 100 / progressEvent.total);
},
// 最多轉發數,用於node.js
maxRedirects: 5,
// 最大響應數據大小
maxContentLength: 2000,
// 自定義錯誤狀態碼範圍
validateStatus: function (status) {
return status >= 200 && status < 300;
},
// 用於node.js
// httpAgent: new http.Agent({ keepAlive: true }),
// httpsAgent: new https.Agent({ keepAlive: true })
};
/** 導出配置模塊 */
export default axiosConfig;
複製代碼
utils/request.js(目前使用的是restapi.js):
/**
* 業務中使用的ajax請求工具模塊
* @module utils/request
* @see main.js
*/
import axios from 'axios';
import config from '../config/axios.config';
import qs from 'querystring'
import Vue from 'vue'
import { errorTip } from 'utils/common'
// 用來調用errorTip的vue實例
const vueInstance = new Vue()
// 構建得的請求對象
const request = axios.create(config);
// 返回狀態判斷(添加響應攔截器)
request.interceptors.response.use(
res => {
// 若是數據請求失敗
if ( res.data.code > 300 ) {
errorTip(vueInstance, res.data)
return Promise.reject(res.data);
}
return res.data.data;
},
error => {
return Promise.reject(error);
}
);
request.interceptors.request.use((config) => {
let allowMethods = ['post'];
if (allowMethods.indexOf(config.method) !== -1) {
config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
config.data = qs.stringify(config.data);
}
return config;
}, (error) => {
return Promise.reject(error);
});
// 對axios的實例從新封裝成一個plugin ,方便 Vue.use(xxxx)
export default request;
複製代碼
在src開發目錄中創建api目錄結構,在其中能夠集中構建全部請求動做的模塊,以便調用。
api/index.js:
/**
* api接口調用維護模塊
* @module api
* @see main
*/
import request from 'utils/request'
import Vue from 'vue'
import { errorTip } from 'utils/common'
// 用來調用errorTip的vue實例
const vueInstance = new Vue()
/**
* 分頁獲取算法列表
* @param {Object} [p] pageInfo 頁碼相關信息
* @param {number} [p.page] page 頁數
* @param {number} [p.size] size 每頁數據個數
* @param {string} [p.labels] labels 標籤
* @param {string} [p.query] query 查詢條件
* @return {Promise} request 請求動做promise
*/
export let apiGetList = ({page = 1, size = 10, labels = 'anomaly', query = ''} = {}) => {
return request.get(`/algorithm?page=${page}&labels=${labels}&size=${size}&query=${query}`).catch(error => errorTip(vueInstance, error))
}
複製代碼
利用async await對請求做出優化處理,在須要調用請求的地方利用async/await來規避回調函數的嵌套。
store/modules/setting.js:
import { apiGetList } from 'api'
// ...
actions: {
// 分頁獲取算法列表 當前寫法
getList: ({ commit }, {page = 1, size = 10, labels = 'anomaly', query = ''} = {}) => {
return new Promise((resolve, reject) => {
restapi.request({
method: 'get',
url: `/algorithm?page=${page}&labels=${labels}&size=${size}&query=${query}`,
success: sdata => {
commit('LIST_ALGORITHMS', sdata)
resolve(sdata)
},
error: reject
})
})
},
// 分頁獲取算法列表 重構後寫法
getListTest: async ({ commit }, params) => {
let sdata = await apiGetList(params)
commit('LIST_ALGORITHMS', sdata)
}
// ...
}
複製代碼
視圖中調用方式基本區別不大,不須要再作請求錯誤處理。
views/Algorithms/AlgorithmsCenter.vue
methods: {
...mapActions('algorithm', [
'getList',
'getListTest',
'getCount'
]),
// ...
getAlgorithmsList ({start = 1, length = 10} = {}) {
// this.getList({page: Math.ceil(start / length), size: length, labels: this.algoType}).catch(error => errorTip(this, error)) // 當前調用方式
this.getListTest({page: Math.ceil(start / length), size: length, labels: this.algoType})// 重構後調用方式
}
// ...
}
複製代碼
mock數據的搭建有利於在先後端開發進度不一樣步的狀況下進行模擬數據請求,就能夠根據請求完成前端的交互邏輯,合理的配置能夠在有真實接口後稍做更改就能夠調用線上真實接口,處理手法更平滑。
Mock的搭建有不少種方式,在這裏推薦使用json-server的使用方式,本質就是利用json-server快捷的啓動一個mock服務器,而後在config/index/dev/proxyTable中代理請求到mock服務器便可。
具體配置過程能夠參考 json-server配置詳解
其實EasyMock這個網站也提供來便利的mock數據的方式,再也不須要本身去搭建mock環境,簡直是很是棒了