JavaScript 命名规则转换 (case convert)

483 阅读3分钟

JavaScript 中,我们习惯使用 camelCase 命名规则。而实际开发中,可能会处理一些用其他命名规则命名(比如snake_case, UPPER_CASE)的东西,比如外来数据。

camelcasedecamelize 两个工具库,就是用于字符串命名规则转换的,它们的 npm 周下载量都在千万级。

当然也存在其他可用的工具库。这里仅介绍这两个工具库。

  • camelcase : Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: foo-barfooBar
  • decamelize:Convert a camelized string into a lowercased one with a custom separator Example: unicornRainbowunicorn_rainbow

两个库的用法很简单,都接受一个字符串和一个选项对象作为参数。

import camelCase from 'camelcase'
import decamelize from 'decamelize'

camelCase('foo-bar')
//=> 'fooBar'

camelCase('foo_bar')
//=> 'fooBar'

camelCase('Foo-Bar')
//=> 'fooBar'

decamelize('unicornRainbow')
//=> 'unicorn_rainbow'

decamelize('unicornRainbow', {separator: '-'})
//=> 'unicorn-rainbow'

更详细的使用请参考:

对于中大型公司,后端接口通常是有规范的。而中小型公司可能更多地依赖前后端约定。我们通过一个实际的场景,对后端接口数据进行命名规则的转换,来看一下实际的应用可以是什么样子。

如果你所在项目刚好约定接口的 json 数据命名规则为 shake_case 格式,比如有一个这样的接口:

// 伪代码 
const post = await getPostApi({ post_id: 22, is_published: true })

调用后返回下面的数据

{
  code: 100000,
  message: '',
  data: {  // post
    id: 22,
    title: 'JavaScript case convert',
    content: 'This is a post...',
    head_img: 'https://caseconvert.com/assets/headimg.jpg'
    author: {
      id: 22039,
     	poster_level: 1,
      post_count: 12
    }
    create_at: 890732423,
    update_at: 890732423
  }
}

而前端如果要使用这些数据,代码中就出现了 shake-case 格式的代码,比如:

const headImg = post.head_img

// Vue template
<img :src="post.head_img" />

示例接口的调用的实参 { post_id: 22 }和返回数据 post 都用了 snake_case 的形式。

然而我们希望前端代码统一使用 camelCase 的命名规则,接口调用的时候这样传参数:

// 伪代码 
const post = await getPostApi({ postId: 22, isPublished: true })

在真正发送 http 请求时,发出的 data 数据是 { post_id: 22, is_published: true }。并且我们希望返回的 json 数据在消费之前被转换成下面的样子:

{
  code: 100000,
  message: '',
  data: {  // post
    id: 22,
    title: 'JavaScript case convert',
    content: 'This is a post...',
    headImg: 'https://caseconvert.com/assets/headimg.jpg'
    author: {
      id: 22039,
     	posterLevel: 1,
      postCount: 12
    }
    createAt: 890732423,
    updateAt: 890732423
  }
}

这样,在任何地方就能够类似这样消费返回的 json 数据了

const headImg = post.headImg

// Vue template
<img :src="post.headImg" />

那么需要做的就是下面两个点:

  • http 请求发送之前,将 { postId: 22, isPublished: true } 转换成 { post_id: 22, is_published: true }。即,将 camelCase 的键转换成 shake_case的格式。
  • 在消费服务端请求数据之前,将snake_case 的键转换成 camelCase 的格式。

我们需要两个工具函数,toCamelCase() 用于转换成 camelCase格式,toSnakeCase() 用于转换成 snake_case格式。并且它们能转换对象或数组嵌套情况下的键。

caseConvert.js

import camelcase from 'camelcase'
import decamelize from 'decamelize'
import { isObject, isArray, isString } from './valType'

export function toCamelCase(val) {
  if (isObject(val)) {
    const result = {}
    for (const key in val) {
      const convertedKey = camelcase(key)
      result[convertedKey] = toCamelCase(val[key])
    }
    return result
  }
  
  if (isArray(val)) {
    return val.map((item) => toCamelCase(item))
  }
  
  return val
}

function toDecamelCase(val, separator) {
  if (isObject(val)) {
    const result = {}
    for (const key in val) {
      const convertedKey = decamelize(key, {
        preserveConsecutiveUppercase: true,
      })
      result[convertedKey] = toDecamelCase(val[key], separator)
    }
    return result
  }
  
  if (isArray(val)) {
    return val.map((item) => toDecamelCase(item, separator))
  }
  
  return val
}

export const toSnakeCase = (val) => toDecamelCase(val, '_')

isObject()isArray() 实现方式很多,下面的示例仅做参考。

valType.js

const objectToString = Object.prototype.toString
export const toTypeString = (value) =>
  objectToString.call(value).slice(8, -1).toLowerCase()
export const isObject = (value) => toTypeString(value) === 'object'
export const isArray = (value) => toTypeString(value) === 'array'

现在转换工具函数有了,我们需要在请求发送之前调用 toSnakeCase(data),在请求成功返回数据被消费之前调用 toCamelCase(data) 。假如我们使用 axios 用于接口调用的话,axios 拦截器就是做这些事情很好的位置。

request.js

import axios from 'axios'
const request = axios.create()

request.interceptors.request.use((config) => {
  config.data = toSnakeCase(config.data)
  return config
})

request.interceptors.response.use((response) => {
  return toCamelCase(response.data)
})

export default request

实际开发中,还有其他场景可能需要做命名规则转换。camelcasedecamelize 作为基础工具库,也用在很多库中,它们的源码也很精炼,有兴趣的可以读一读,学习一下。