最近由於公司業務的調整,項目須要開發大量的業務組件、高複用邏輯提供給客戶使用。當各種組件、代碼多了之後,加上團隊內幾個成員書寫習慣、開發思想的不一樣,出現了好多問題。尤爲兩個問題最嚴重:javascript
爲了解決這兩個問題,就開始要求組員在開發業務組件的同時,必須寫對應的開發文檔/代碼註釋。一開始還好,中後期開發文檔的更新明顯跟不上組件的迭代,逐漸地又回到了靠嘴問的狀況,第2個問題也是隨着時間推移又回到了起點。html
某天經過VS Code
調試代碼的時候突然發現,用鼠標在原生語法和react
的方法上懸浮幾秒鐘,就會出現一個提示框,裏面有一些節點/組件/方法的簡單介紹,參數等。vue
對,這就是我想要的效果!java
原生語法 (如document.getElementById
):
react
react
的方法(如useState
):
ios
經過ctrl + 鼠標左鍵
點開類型定義,發現提示框裏的內容實際上是相關代碼上方的註釋。
按照類型定義裏面的註釋,我在代碼裏輸入/**
的時候出現了以下圖的提示。
json
拿着關鍵詞我去VS Code
的官網搜索了一番,在官網搜到了答案(點擊此處)。axios
VS Code understands many standard JSDoc annotations, and uses these annotations to provide rich IntelliSense.後端
VS Code 能夠理解標準的JSDoc代碼註釋,並使用這些註釋提供豐富的智能感知(如智能代碼完成,懸停信息和簽名信息)api
而JSDoc
的語法也很是簡單,只須要保證註釋的開頭是/**
便可,其餘與多行註釋沒有什麼差異。(更多語法:點擊此處)
/** 這樣便建立了一個代碼提醒 */ function remind() {}
上手寫個組件試試效果!
import React, { useEffect, useState } from 'react' interface KeywordInterface { /** * 關鍵詞 */ keyword?: string; /** * 高亮顯示的顏色,支持hex、hsl、rgba、keywords */ color?: string; children?: string; } /** * 關鍵詞高亮組件 * * @example <LightKeyword keyword="hello">Hello World</LightKeyword> * * @param { string } keyword - 關鍵詞 * @param { string } color - 高亮顯示的顏色 */ const LightKeyword: React.FC<KeywordInterface> = ({ color = '', keyword = '', children = '' }) => { const [ context, setContext ] = useState('') useEffect(() => { // 當關鍵詞爲空時,無需對內容作高亮顯示 if( !keyword ) { return setContext(children) } const pattern = new RegExp(keyword, 'gi') // 經過正則把關鍵詞過濾出來並增長HTML節點 const allword = (children as string).replace(pattern, (word) => `<i class="light-keyword-item" ${ color && `style="color: ${ color }"` }>${ word }</i>`) setContext(allword) }, [ keyword, color, children ]) return ( <span className="light-keyword" dangerouslySetInnerHTML={{ __html: context }}></span> ) } export default LightKeyword
效果展現:
當鼠標懸浮在組件上時:
當數據懸浮在組件屬性上時:
完美!這樣只要按格式寫好註釋,就能夠不用那麼麻煩地去查文檔了。(前提是得寫)
那若是是業務邏輯呢?所以我寫了一段基於業務封裝的異步請求代碼。
import qs from 'qs' import { message } from 'antd' import axios, { AxiosRequestConfig } from 'axios' interface configInterface { /** * 請求地址 */ url: string; /** * 請求方式 */ method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; /** * 請求參數 */ data?: any; /** * 其餘配置參數 * * @param { Object } headers 請求頭配置 * @param { boolean } errorMessage 是否啓用錯誤提醒 * @param { string } responseType 請求類型,默認爲json * @param { boolean } withCredentials 是否攜帶跨域憑證 */ options?: { /** * 請求頭配置 */ headers?: any; /** * 是否啓用錯誤提醒 */ errorMessage?: boolean; /** * 請求類型,默認爲json */ responseType?: 'json' | 'arraybuffer' | 'blob' | 'document' | 'text' | 'stream'; /** * 是否攜帶跨域憑證 */ withCredentials?: boolean } } // axios全局配置 const $axios = axios.create({ // 請求接口地址 baseURL: 'https://demo.com', // 超時時間 timeout: 60 * 1000 }) /** * 異步請求 * * @description 基於現有業務封裝,自動處理GET請求序列化/錯誤碼處理反饋/跨域配置等操做 * @example useRequest<T>({ url: 'api/weather', method: 'GET', data: { date: '2021-02-30' }, options: {} }) * @typedef requestConfig 請求參數 * @param { string } requestConfig.url 請求地址 * @param { string } requestConfig.method 請求方式 * @param { any } requestConfig.data 請求參數 * @param { object } requestConfig.options 其餘配置參數 */ const useRequest = async <T>(requestConfig: configInterface): Promise<T> => { const requestOptions = requestConfig.options || {} const axiosConfig: AxiosRequestConfig = { url: requestConfig.url, method: requestConfig.method || 'GET', headers: requestOptions.headers || {}, responseType: requestOptions.responseType || 'json', withCredentials: requestOptions.withCredentials !== false } // 請求方式爲GET時,對參數進行序列化處理 if( axiosConfig.method === 'GET' ) { axiosConfig.params = requestConfig.data || {} axiosConfig.paramsSerializer = (params) => qs.stringify(params, { arrayFormat: 'brackets' }) } else { axiosConfig.data = requestConfig.data || {} } try { const { data: response } = await $axios(axiosConfig) // 如後端返回錯誤碼,將錯誤推入catch句柄執行 if( response.code !== 0 ) { // 錯誤提醒 if( requestOptions.errorMessage !== false ) { message.error(response.message || '未知錯誤') } return Promise.reject(response) } return Promise.resolve(response) } catch(e) { // 錯誤提醒 if( requestOptions.errorMessage !== false ) { message.error('請求錯誤,請稍後重試') } return Promise.reject(e) } } export default useRequest
實際效果:
(基本用法及參數提醒)
(額外配置提醒)
配合Typescript
,幾乎就是把文檔寫進了代碼裏!!!
然而當我興致勃勃地搭建vue 3
的開發環境,想嘗試一下vue
的智能提示。通過多輪測試,JSDoc
的智能提示只支持在js/ts/tsx
這幾類的文件,並不支持.vue
格式的文件。
(vue
文件不支持jsdoc
的智能提示)
若是但願在vue
文件中也有相似的智能提示,能夠經過VS Code
安裝vetur
插件,而後在項目根目錄下建立名爲vetur
的文件夾,並新建tags.json
和attributes.json
兩個文件,而後在package.json
中引入二者的路徑 。
// package.json { "name": "demo", "version": "0.1.0", "vetur": { "tags": "./vetur/tags.json", "attributes": "./vetur/attributes.json" } } // vetur/tags.json { "light-keyword": { "attributes": ["keyword", "content", "color"], "description": "關鍵詞高亮組件" } } // vetur/attributes.json { "color": { "type": "string", "description": "高亮顯示的顏色,支持hex、hsl、rgba、keywords" }, "content": { "type": "string", "description": "文本內容" }, "keyword": { "type": "string", "description": "關鍵詞" } }
最後的實現效果
(組件智能提示)
(組件描述)
(屬性描述)
好處是不受vue
版本的限制,2和3均可以用;壞處是json
文件的限制,沒有辦法像JSDoc
同樣顯示豐富的格式和代碼片斷,但願vetur
可以增強這方面的優化吧。