网站首页 > 技术文章 正文
最近因为公司业务的调整,项目需要开发大量的业务组件、高复用逻辑提供给客户使用。当各类组件、代码多了以后,加上团队内几个成员书写习惯、开发思想的不同,出现了好多问题。尤其两个问题最严重:
- 大量的业务组件/业务逻辑需要通过查源代码的方式,或者问写组件的人,才能知道组件是否有自己需要的属性/钩子方法
- 有些组件因为产品需求 + 口头沟通 + 需求妥协,只能应用于某一个特定的情况下,其他人看设计图或者逻辑差不多相似就直接拿过来用,结果发现用不了/各种问题
为了解决这两个问题,就开始要求组员在开发业务组件的同时,必须写对应的开发文档/代码注释。一开始还好,中后期开发文档的更新明显跟不上组件的迭代,逐渐地又回到了靠嘴问的情况,第2个问题也是随着时间推移又回到了起点。
某天通过VS Code调试代码的时候忽然发现,用鼠标在原生语法和react的方法上悬浮几秒钟,就会出现一个提示框,里面有一些节点/组件/方法的简单介绍,参数等。
对,这就是我想要的效果!
原生语法 (如document.getElementById):
react的方法(如useState):
通过ctrl + 鼠标左键点开类型定义,发现提示框里的内容其实是相关代码上方的注释。
按照类型定义里面的注释,我在代码里输入/**的时候出现了如下图的提示。
拿着关键词我去VS Code的官网搜索了一番,在官网搜到了答案(点击此处)。
VS Code understands many standard JSDoc annotations, and uses these annotations to provide rich IntelliSense.
VS Code 可以理解标准的JSDoc代码注释,并使用这些注释提供丰富的智能感知(如智能代码完成,悬停信息和签名信息)
而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能够加强这方面的优化吧。
作者丨飞灰同学
https://www.cnblogs.com/fei-hui/p/14426794.html
猜你喜欢
- 2024-11-08 大厂必问 · 如何防止订单重复? 防止订单重复提交与高并发
- 2024-11-08 使用JS快速读取TXT文件 js怎么读取文件
- 2024-11-08 开发必懂的文件加解密 开发必懂的文件加解密怎么弄
- 2024-11-08 安卓手机如何快速刷完青年大学习 安卓如何快速完成青年大学
- 2024-11-08 DNF:健康系统最简单的修改信息方法
- 2024-11-08 这个传染病各年龄人群都易感,该如何预防?戳→
- 2024-11-08 文字跳动特效表白网页 字幕滚动表白软件
- 2024-11-08 如何访问Web存储 web项目怎么访问本地文件
- 2024-11-08 智能科技时代的事件驱动编程:艺术与算法的完美交融
- 2024-11-08 收藏!全网最新首末班车时刻表来啦
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-