謹慎使用toLocaleString!!!

hsabalaaaC發表於2019-04-21

¿

美麗的API都是帶刺的玫瑰

1. toLocaleString相關背景

掘金 - 想偷懶的話,toLocaleString 瞭解一下?

MDN - Date​.prototype​.toLocale​String()

MDN - Number​.prototype​.toLocale​String()

其實就是在說: 這個API可以省很多事兒, 方便的對時間/數字按照一定格式進行轉換, 不用你寫正則, 不用你拖庫

1.1 常用時間轉換

以常用時間格式: YYYY-MM-DD HH:mm:ss為例, 通常用庫day.js或者moment.js來完成

format('YYYY-MM-DD HH:mm:ss')
複製程式碼

使用toLocaleString的話

new Date()
  .toLocaleString('zh', { hour12: false }) // "2019/4/21 13:00:09"
  .replace(/\//g, '-') // "2019-4-21 13:00:23"
複製程式碼

1.2 常用數字轉換

例如: 整數部分每三位加一個逗號, 通常用正則完成

const number = 123456789
number.toString().replace(/\B(?=(\d{3})+\b)/g, ',') // "12,345,678"
複製程式碼

使用toLocaleString的話

const number = 123456789
number.toLocaleString() // "12,345,678"
複製程式碼

2. 坑

上面的一切看著都很美好, 雖然它有瀏覽器相容問題, 但是我覺得在node.js中使用的話, 應該是可以把這份美好延續下去的

但是在一次提交中, 升級了node版本, 涉及到toLocaleString的地方都出現了很多問題

FROM node:8.9-alpine

// 更改為
FROM node:lts-alpine
複製程式碼

打破了這份美好, 測試程式碼如下

// index.js 
console.log("en:   " + new Date().toLocaleString('en', { hour12: false }))
console.log("zh:   " + new Date().toLocaleString('zh', { hour12: false }))


// Dockerfile
FROM node:8.9-alpine
COPY . /app
CMD ["node", "/app/index.js"]

複製程式碼

映象node:8.9-alpine輸出

en:   4/21/2019, 06:42:03
zh:   2019-4-21 06:42:03  (預期)
複製程式碼

映象node:10.15-alpine輸出

en:   4/21/2019, 06:43:13
zh:   4/21/2019, 06:43:13
複製程式碼

映象node:11.14-alpine輸出

en:   4/21/2019, 06:43:59
zh:   4/21/2019, 06:43:59
複製程式碼

映象node:11.14輸出

en:   4/21/2019, 06:46:54
zh:   4/21/2019, 06:46:54
複製程式碼

本地node.js 11.13輸出, 怪不得本地除錯是好的....

en:   4/21/2019, 14:49:27
zh:   2019-4-21 14:49:27
複製程式碼

Chrome瀏覽器

en:   4/21/2019, 14:51:33
zh:   2019/4/21 14:51:33
複製程式碼

3. 具體原因: 國際化支援

Node.js / JavaScript的許多功能是提供國際化支援的, 例如

  • String.prototype.normalize()

  • String.prototype.toLowerCase()

  • Date.prototype.toLocaleString()

  • require('buffer').transcode()

  • ...略

Node.js(及其底層V8引擎)使用ICU實現這些功能

但是要支援世界上所有的語言環境需要一份非常大的ICU資料檔案, 而大多數使用者僅會使用ICU功能的一小部分, 因此預設情況下Node.js僅提供完整ICU資料集的子集

3.1 構建Node.js的ICU選項

  • --with-intl=none/--without-intl: 禁用所有國際化功能

  • --with-intl=system-icu: 取決於系統, 大多數linux發行版都安裝了ICU

  • --with-intl=small-icu(預設): 完整ICU的一部分, 基本等價於: 僅限英文

  • --with-intl=full-icu: 完整的支援

3.2 在執行時提供ICU資料

  1. npm模組: full-icu: 安裝完整的ICU資料
  • 通過npm i full-icu, 資料檔案將會拷貝在./node_modules/full-icu,
  1. 通過設定環境變數NODE_ICU_DATA指定執行時的ICU選項
  • env NODE_ICU_DATA=/some/directory node

3.3 測試

  • npm install full-icu, 確保package.json中有full-icu

  • 編寫如下的Dockerfile

FROM node:lts-alpine

WORKDIR /app

COPY package.json /app

RUN yarn --registry=https://registry.npm.taobao.org

COPY . /app

ENV NODE_ICU_DATA /app/node_modules/full-icu

CMD ["node", "/app/index.js"]
複製程式碼

輸出

en:   4/21/2019, 09:13:13
zh:   2019/4/21 09:13:13
複製程式碼

雖然和預期2019-4-21 09:13:13有點區別... 還是能說明有效吧- -

4. 小結

  1. 對於toLocaleString這個API, 瀏覽器環境下不相容問題更多, Node環境下也會出現不一致的問題, 但可以通過設定ICU來解決

  2. 從開發角度來說: API 雖然酷炫, 但是相比之下還是day.js / momemnt.js更穩定

  3. 涉及Node國際化支援的相關知識, 這部分可以參考官方文件Internationalization Support

相關文章