一個巧合,我把文檔寫進了代碼裏

最近由於公司業務的調整,項目須要開發大量的業務組件、高複用邏輯提供給客戶使用。當各種組件、代碼多了之後,加上團隊內幾個成員書寫習慣、開發思想的不一樣,出現了好多問題。尤爲兩個問題最嚴重:javascript

  1. 大量的業務組件/業務邏輯須要經過查源代碼的方式,或者問寫組件的人,才能知道組件是否有本身須要的屬性/鉤子方法
  2. 有些組件由於產品需求 + 口頭溝通 + 需求妥協,只能應用於某一個特定的狀況下,其餘人看設計圖或者邏輯差很少類似就直接拿過來用,結果發現用不了/各類問題

爲了解決這兩個問題,就開始要求組員在開發業務組件的同時,必須寫對應的開發文檔/代碼註釋。一開始還好,中後期開發文檔的更新明顯跟不上組件的迭代,逐漸地又回到了靠嘴問的狀況,第2個問題也是隨着時間推移又回到了起點。html

某天經過VS Code調試代碼的時候突然發現,用鼠標在原生語法和react的方法上懸浮幾秒鐘,就會出現一個提示框,裏面有一些節點/組件/方法的簡單介紹,參數等。vue

對,這就是我想要的效果!java

原生語法 (如document.getElementById):
document.getElementByIdreact

react的方法(如useState):
useStateios

經過ctrl + 鼠標左鍵點開類型定義,發現提示框裏的內容實際上是相關代碼上方的註釋。
類型定義
按照類型定義裏面的註釋,我在代碼裏輸入/**的時候出現了以下圖的提示。
JSDOC提示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文件不支持jsdoc的智能提示)

若是但願在vue文件中也有相似的智能提示,能夠經過VS Code安裝vetur插件,而後在項目根目錄下建立名爲vetur的文件夾,並新建tags.jsonattributes.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可以增強這方面的優化吧。

相關文章
相關標籤/搜索