标签归档:Javascript

black and yellow printed paper

warehouse — 一个简单易用的 JSON 数据库

在 Hexo 的 Github 组织下,有一个不明显,但却很有用的仓库 —— warehouse。

warehouse 是一个 JSON 数据库,基于 JSON 实现了各种类似于 SQL 的查询,可以帮助我们基于一个 JSON 文件来进行查询。在 Hexo 的静态生成过程中,warehouse 帮了大忙。

用官方的话来说,warehouse 就是 A JSON database with Models, Schemas, and a flexible querying interface.

在实际使用上,warehouse 确实如他所说的那边方便(虽然某些方法没有,但依然不影响他的使用很方便)。

Example

比如,如下代码就定义了 一个 Post 模型和对应的表。并实现了在这个表中插入一个新的数据。

var Database = require('warehouse');
var db = new Database();

var Post = db.model('posts', {
  title: String,
  created: {type: Date, default: Date.now}
});

Post.insert({
  title: 'Hello world'
}).then(function(post){
  console.log(post);
});

如果你需要将这些数据保存为一个单独的文件,只需要修改初始化的参数,并执行 save 方法,就可以将 JSON 导出到指定的文件中

var db = new Database({
  path: "./test.json", // 将数据存储在 test.json 当中
});
db.save();

类似的,如果数据已经构建好了,也只需要执行 load 方法,就可以加载数据。

var db = new Database({
  path: "./test.json", // 将数据存储在 test.json 当中
});
db.load();

场景

如果你希望在内存当中对于 JSON 有一个更好的操作方式,那么 warehouse 是个不错的选择,不需要另外单独安装数据库,就可以实现类似于数据库的查询方式,体验还是非常好的。

如果你想了解更多,可以查看

computer screen displaying website home page

在 Next.js 中直接引入 SVG

随着 SVG 的兴起,过去使用雪碧图、IconFont 作为界面中图标用法已经过时。大家会更倾向于加载更加简单、空间占用更小、编写更容易的 SVG 作为图标的解决方案。

而 Next.js 同样对于 SVG 提供了支持。

最简单的加载方式 —— URL 加载

如果不希望对应用程序做任何的改进,一个最简单的方式是直接使用路径进行加载。只需要使用一个 img 标签,并设定 src 为 svg 文件的路径就可以了。

<img src="/logo.svg" alt="Logo SVG" />

不过,使用 URL 来加载的问题是,当你的文件夹路径和结构比较复杂的时候,你需要使用一个较长的路径来加载 SVG,比较麻烦。

当然, 你也可以简单的优化这个方案 —— 比如自行封装一个 svg component,支持传入文件名 & alt 文字和其他参数。这样在调用的时候只需要输入文件名即可。

最舒服的加载方式 —— 编程加载

在 Next.js 当中,如果你希望更加舒服的引入 SVG,那比较好的方案是使用 import 标签来进行引入。不过,由于 SVG 本身并不是一个标准的 React 组件,你需要在 Next.js 上安装插件来支持对 SVG 的解析。

加入对 SVG 的解析需要安装 @svgr/webpack 插件。执行如下命令安装插件。

# yarn
yarn add @svgr/webpack
# npm
npm install --save @svgr/webpack

安装完成后,修改 next.config.js添加相关的解析规则。

module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: ["@svgr/webpack"]
    }); // 针对 SVG 的处理规则

    return config;
  }
};

修改后保存,便可以直接使用 import 语法来处理

import Logo from "../assets/logo.svg";


// usage
<Logo />

这样,你就可以直接将 SVG 作为一个 Component 引入,并使用你熟悉的 React Componet Props 来修改这个 Component 的各项属性。

总结

在这篇文章中,分享了两种在 Next.js 的方法,你可以根据自己的实际情况来决定选择具体使用什么样的方式来引入 SVG。

text

如何解决 JavaScript 中 RegExp 不幂等的问题?

在对 WXMarkdown 进行改造的时候,我准备使用 Vercel 提供的 cloudFunction 来实现。

在执行时,出现了一个奇怪的问题,当我使用 RegExp 进行匹配的时候,会出现匹配成功和匹配失败的情况。

当我回溯代码时,发现问题出现在

import type { NextApiRequest, NextApiResponse } from "next";
const URL = require("url");
const rule = /BV([0-9a-zA-Z]{1,10})/g;
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<MDResponse>
) {
    const { url } = req.body;
    const result = rule.exec(url);
}

result 的匹配结果不是每次都是可用的,会交替一次 null,一次有结果。

搜索后了解到,这是由于 RegExp 本身是具有状态的,在这种情况下,由于后一次的调用复用了之前的请求,所以出现了一次有结果,一次没有结果。

在这种情况下,有两种解决方案:

  1. 在使用 Rule 之前,对其进行重置状态,即在进行匹配之前,先设定 rule.lastIndex 为 0
  2. 另一种解决方案是将 rule 的定义放置在导出的函数内部,这样就可以在后续执行的过程中,重新初始化。

Reference

https://stackoverflow.com/questions/38910334/regular-expression-exec-function-does-not-work-multiple-times

实现密码生成器的思路

在项目中有这样一个需求:需要生成一组随机密码,确保安全性。因此,我去翻了一个项目的代码。这里是具体的代码

function GeneratePassword() {
    if (parseInt(navigator.appVersion) <= 3) {
        alert("Sorry this only works in 4.0+ browsers");
        return true;
    }
    var length=8;
    var sPassword = "";
    length = document.aForm.charLen.options[document.aForm.charLen.selectedIndex].value;
    var noPunction = (document.aForm.punc.checked);
    var randomLength = (document.aForm.rLen.checked);
    if (randomLength) {
        length = Math.random();
        length = parseInt(length * 100);
        length = (length % 7) + 6
    }
    for (i=0; i < length; i++) {
        numI = getRandomNum();
        if (noPunction) { while (checkPunc(numI)) { numI = getRandomNum(); } }
        sPassword = sPassword + String.fromCharCode(numI);
    }
    document.aForm.passField.value = sPassword
    return true;
}
function getRandomNum() {
    // between 0 - 1
    var rndNum = Math.random()
    // rndNum from 0 - 1000
    rndNum = parseInt(rndNum * 1000);
    // rndNum from 33 - 127
    rndNum = (rndNum % 94) + 33;
    return rndNum;
}
function checkPunc(num) {
    if ((num >=33) && (num <=47)) { return true; }
    if ((num >=58) && (num <=64)) { return true; }
    if ((num >=91) && (num <=96)) { return true; }
    if ((num >=123) && (num <=126)) { return true; }
    return false;
}

上完代码,接下来来分析一下这段代码

首先,这段代码中有三个函数,其关系如下图所示

密码生成器各函数关系图

其中 getRandomNum 生成了一个范围在 33 – 127 的数字。这里是由于在后续生成随机字符串时,用的是 String.fromCharCode,当数字在 33~127 之间时,能够确保生成的数字在输入时比较简单。

String.fromCharCode 的测试结果

checkPunc 则是检测输出的文字是否可以直接发音,这涉及到记忆的难度的问题,在一个长的字符串中,记忆 ABCD 肯定比记忆逗号、破折号、句号等符号要简单一些,毕竟字母肯定要好记忆点,可以自编语句来实现。

接下来我们来看最核心的 GeneratorPassword 函数:如果将其中的 Dom 操作都忽略掉,并设置一个值。并进行配置后,代码如下:

function GeneratePassword() {
    // 检测浏览器支持
    if (parseInt(navigator.appVersion) <= 3) {
        alert("Sorry this only works in 4.0+ browsers");
        return true;
    }
    var length=8;
    var sPassword = "";
    length = 10 // 此处代码原为使用 dom 函数获取表单数据的代码,来实现自定义长度的功能。
    var noPunction = false// 此处代码原为获取表单数据的代码,控制是否检测密码的可读性
    var randomLength = false // 此处代码原为获取表单数据的代码,控制是否使用随机长度。
    // 如果使用随机长度的密码,则进入循环
    if (randomLength) {
        length = Math.random();
        length = parseInt(length * 100); // 生成 0 ~ 100 之间的数字
        length = (length % 7) + 6 // 对生成的长度取余,则最短为6,最长为12
    }
    // 根据长度进行循环
    for (i=0; i < length; i++) {
        numI = getRandomNum();  // 获取一个随机数
        if (noPunction) { while (checkPunc(numI)) { numI = getRandomNum(); } } // 如果需要判断发音,进行发音判断,判断失败就重新生成,直到成功。
        sPassword = sPassword + String.fromCharCode(numI); // 将生成的字符串加入到原有的字符串最后
    }
    // 设置代码到 Dom 元素中
    return true;
}

其代码逻辑图如下

密码生成器的代码逻辑图

使用 JavaScript 来导出 CSV

由于要做一个 UserScript 用到了这个功能,便顺便记录下来。
数组结构

var data = [
{
"title" :value,
"href" : value
},
...
]

生成 CSV 字符串

function convertArrayOfObjectsToCSV(args) {
        var result, ctr, keys, columnDelimiter, lineDelimiter, data;
        data = args.data || null;
        if (data == null || !data.length) {
            return null;
        }
        columnDelimiter = args.columnDelimiter || ',';
        lineDelimiter = args.lineDelimiter || '\n';
        keys = Object.keys(data[0]);
        result = '';
        result += keys.join(columnDelimiter);
        result += lineDelimiter;
        data.forEach(function(item) {
            ctr = 0;
            keys.forEach(function(key) {
                if (ctr > 0) result += columnDelimiter;
                result += item[key];
                ctr++;
            });
            result += lineDelimiter;
        });
        return result;
    }

生成 CSV 文件,并下载

function downloadCSV(args) {
        var data, filename, link;
        var csv = convertArrayOfObjectsToCSV({
            data: data
        });
        if (csv == null) return;
        filename = args.filename || 'export.csv';
        if (!csv.match(/^data:text\/csv/i)) {
            csv = 'data:text/csv;charset=utf-8,' + csv;
        }
        data = encodeURI(csv);
        link = document.createElement('a');
        link.setAttribute('href', data);
        link.setAttribute('download', filename);
        link.click();
    }

 
 
代码来源:https://halistechnology.com/2015/05/28/use-javascript-to-export-your-data-as-csv/

批量删除 QQ 空间说说的 JS 代码

var delay = 1000;function del() {    document.querySelector('.app_canvas_frame').contentDocument.querySelector('.del_btn').click();    setTimeout("yes()", delay);}function yes() {    document.querySelector('.qz_dialog_layer_btn').click();    setTimeout("del()", delay);}del();

在 Chrome 的调试控制台内执行即可。