Math.random()
通常情况下我们使用Math.random()
来生成伪随机数,在大部分情况下可以很方便的使用。比如
生成混合随机字符
Math.random().toString(36).substr(2);
生成指定范围的随机数
const randomNumber = (min, max) => Math.floor(Math.random() * (max - min) + min);
但如果涉及稍微严谨的一些场景中,Math.random()
随机分布不够平均,这时候可以使用浏览器提供的更安全的随机数生成接口Crypto.getRandomValues()
Crypto.getRandomValues()
Crypto
看着会比较陌生,这是浏览器提供了基本的密码学的操作接口。
其中提供了一个随机数生成方法getRandomValues
,Math.random()
的随机数分布不够平均,虽然大部分情况下不会有冲突,但确实有隐患。而且getRandomValues
兼容性也还是不错的。
生成随机数首先要提供一个TypedArray
对象,生成的随机数会将其里面的对象重写。
window.crypto.getRandomValues(new Uint32Array(5))
Crypto.getRandomValues()
直接使用还是比较原始需要自己再次处理数据,网上也有提供可直接用的生成代码:
生成[0, 1)
之间的随机数(与Math.random()
一致)
const cryptoRand = () => {
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
return ( randomBuffer[0] / (0xffffffff + 1) ); // 0xFFFFFFFF = uint32.MaxValue (+1 because Math.random is inclusive of 0, but not 1)
}
生成指定范围随机数
const randomNumber = (min, max) => {
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
const number = randomBuffer[0] / (0xffffffff + 1);
return Math.floor(number * (max - min) + min);
}
生成ID
const generateId = (len) => {
const typeArray = new Uint8Array((len || 40) / 2)
window.crypto.getRandomValues(typeArray)
return Array.from(typeArray, dec=>dec.toString(16).padStart(2, "0")).join('')
}
或是
const uuidv4 = () => ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
需要注意的是,为了性能crypto.getRandomValues
生成随机数的时候并没有真正的随机数生成器,而是使用了有足够熵的伪随机数生成器。如果需要加密场景,就需要用户自己提供足够熵源的种子。
或者使用现成的随机数生成库nanoid
。
Nano ID
Nano ID是一个小巧、安全、URL友好、唯一的 JavaScript 字符串ID生成器。支持浏览器、Node.js、React Native,以及提供了各种其他语言的版本。
安装
npm install --save nanoid
安全的生成随机数:
同步
import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"
异步
import { nanoid } from 'nanoid/async'
await nanoid()
如果更关心性能的话,可以降低安全性使用非安全的随机数生成器
import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"
当然也可以自定义随机字符和大小,具体的方法可以查看文档。