一个巧合,我把文档写进了代码里

最近由于公司业务的调整,项目须要开发大量的业务组件、高复用逻辑提供给客户使用。当各种组件、代码多了之后,加上团队内几个成员书写习惯、开发思想的不一样,出现了好多问题。尤为两个问题最严重: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可以增强这方面的优化吧。

相关文章
相关标签/搜索