• mkdirpSync方法可以递归创建目录

    var fs = require('fs-extra');
    fs.mkdirpSync('/tmp/foo/bar/baz')

    参考 https://github.com/jprichardson/node-fs-extra/blob/HEAD/docs/ensureDir-sync.md


  • const https = require('https');
    const app = require("./lib/app")
    
    const options = {
        hostname: 'gw.open.1688.com',
        port: 443,
        path: `/openapi/param2/1/system.oauth2/getToken/${app.appKey}?grant_type=refresh_token&client_id=${app.appKey}&client_secret=${app.appSecret}&refresh_token=${app.refresh_token}`,
        method: 'POST'
    };
    
    const req = https.request(options, (res) => {
        res.on('data', (d) => {
            process.stdout.write(d);
        });
    });
    
    req.on('error', (e) => {
        console.error(e);
    });
    req.end();

  • const crypto = require("crypto")
    const app = require("./app")
    
    let gw = 'http://gw.open.1688.com/openapi'
    
    module.exports = (code_arr, apiInfo) => {
    
        //将token加入参数列表
        code_arr.access_token = app.access_token
    
        //将第一个签名因子 urlPath 加上 appkey
        apiInfo += app.appKey
    
        //构造第二个签名因子 排序
        let aliParams = []
    
        Object.keys(code_arr).forEach((val) => {
    
            aliParams.push(val + code_arr[val])
    
        })
    
        aliParams.sort()
    
        let sign_str = aliParams.join('')
    
        // 得出要签名的字符串
        sign_str = apiInfo + sign_str
        //签名
        let code_sign = (crypto.createHmac('sha1', app.appSecret).update(sign_str).digest('hex')).toUpperCase()
        
        //得出url中的参数
        let urlParams = ''
    
        Object.keys(code_arr).forEach((val) => {
            urlParams += `${val}=${code_arr[val]}&`
        })
        
        // 生成url
        let url = `${gw}/${apiInfo}?${urlParams}_aop_signature=${code_sign}`
    
        return url
    
    }

    code_arr 为入参,apiinfo为要请求的api接口


  • EJS —— hexo默认使用的模板,使用尖括号<>包括语法,语法与JS类似,基本可以在编辑器中找到高亮插件(.ejs文件),部分语法错误在发生解析错误时会打印出来,之前尝试去使用hexo就是因为觉得这种语法看上去很复杂(相对我一直使用的jekyll liquid来说)才放弃了

    pug —— 原jade,express默认模板,类haml写法,完全放弃了html标签,改用缩进表示结构,我讨厌一切用缩进表示结构的语法(没错就是python了

    swig —— 支持多种脚本语言,我在php中使用过,大括号写法

    mustache,handlebars —— 这两者语法类似,都是大括号写法,也支持多种语言,辅以#号,结束标签用反斜杠/表示,没试过

    nunjucks —— mozilla出品,语法继承jinja2,使用大括号写法,然而我第一次满怀期待的去使用的时候,不知道哪里错误只返回 null,不会提示哪里错误,找了半天找不到错误的地方,放弃~

    上面列举了一些使用人数较多的,但其实还有很多模板语言,比如原腾讯团队开发的art-template,支持两种写法,号称速度最快,还有一些与上述提到的语法都不同,例如sodajs,则是写在html标签里的类似ng的写法,还有的其实像jsx以及vue的模板语法其实也算是一种模板引擎了…

    当然大部分都大同小异,选一个顺手的即可,


  • 如果你之前使用WordPress等不支持markdown的博客程序,然后想转到一个其它使用 markdown 的程序或者像Jekyll这种静态博客程序,你可以找到导出工具,但是导出来的格式却是HTML,如果你也像我一样是个强迫症,肯定是不允许自己写的文章里又有HTML格式的又有md格式的。
    例如我想转到 typecho ,我先使用 wordpress to typecho 的工具转到数据库,然后去修改即可,这里用到一个将html转为md的工具 turndown ,依赖于nodejs,在线转换地址 Turndown Demo

    // For Node.js
    var TurndownService = require('turndown')
    var turndownService = new TurndownService()
    var markdown = turndownService.turndown('<h1>Hello world!</h1>')
    

    然后它可以自定义解析后的格式,markdown 语法有很多种形式,例如代码块既可以用“`,也可以用缩进表示;列表可以用-符号,也可以用*符号。
    下面是按照我的使用习惯定义的参数

    const turndownService = new TurndownService({
      headingStyle: "atx",
      hr: "---",
      bulletListMarker: "-",
      codeBlockStyle: "fenced",
      emDelimiter: "*"
    });
    

    你可以直接在 nodejs 上操作数据库,但是这样好像又涉及到了异步问题,为了省心,我直接在phpmyadmin里将数据表导出成json格式,然后改好之后,发现phpmyadmin不支持json格式的导入,只能先将json转为sql了,这里我用到一个在线将json转为sql的工具 Easily convert files into SQL Databases | SQLizer ,但是这玩意有点坑,具体就不多说了,也很好解决,你去用用就知道了,或者你完全可以自己转换一下格式,也不算麻烦。


  • nvm全称nodejs version manager即nodejs的版本管理器,它可以帮助你快速安装和切换多个不同的nodejs版本,如果你经常需要在不同的nodejs版本中进行测试,那么这款工具可以给你提供很大的帮助。

    卸载原有的Nodejs、NPM和安装的包

    卸载掉旧的nodejs:

    sudo apt-get remove nodejs
    sudo apt-get remove npm
    

    删除原来安装的包:

    rm -rf ~/.npm
    sudo rm -rf /usr/local/lib/node_modules/
    

    如果你之前有安装过nvm,则删除

    rm -rf ~/.nvm
    

    安装NVM

    nvm的Github仓库在这里

    安装命令:

    wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
    

    如果命令失效或不是最新版本,请参考官方仓库的README文件

    接着,将如下内容添加到你的~/.bash_profile, ~/.zshrc或者.bashrc(具体看你使用什么Shell), ~/.profile文件里:

    export NVM_DIR="$HOME/.nvm"
    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
    

    然后刷新一下刚才添加的文件

    source ./.bash_profile
    source ~/.zshrc
    source ~/.profile
    

    接着你就可以运行nvm命令看到了,可以看到很多使用参数和方法。

    这里有举了几个例子:

    nvm install 8.0.0                     安装指定的Nodejs版本
    nvm use 8.0                           使用最新的 8.0.x 版本
    nvm run 6.10.3 app.js                 使用Nodejs 6.10.3运行app.js
    nvm exec 4.8.3 node app.js            设置运行node app.js命令时默认使用Nodejs 4.8.3
    nvm alias default 8.1.0               设置在整个Shell中的默认Nodejs版本为8.1.0版本
    nvm alias default node                设置在整个Shell中的默认Nodejs版本为最新版本
    

    更多的使用方法可以使用nvm help查看

    在不同的版本中所安装的包不是共通的。


  • 实现代码很简单,我们知道有highlight.js可以直接在浏览器中给pre标签中的代码加入高亮标签的样式,然后引入对于的高亮样式CSS文件即可,我们可以在使用任何程序的网站中使用这个库。
    但是highlight.js经过压缩后还是有几十kb,同样,在浏览器中渲染HTML标签也会耗时耗资源,所以干脆在服务端(生成页面的时候)就给代码加上高亮样式,我这里举两个例子,分别是两个Markdown生成器的使用方法:

    MarkdownIt

    这是我现在在用的解析器,在生成md实例的时候可以直接启用highlight.js插件。

    const MarkdownIt = require('markdown-it');
    const hljs = require('highlight.js');
    const md = new MarkdownIt({
      highlight: function(str, lang) {
        if (lang && hljs.getLanguage(lang)) {
          try {
            return '<pre><code class="hljs ' + lang + '">' +
              hljs.highlight(lang, str, true).value +
              '</code></pre>';
          } catch (__) {}
        }
        return '<pre><code>' + md.utils.escapeHtml(str) + '</code></pre>';
      }
    });
    

    然后使用md.render()方法解析MD时就会自动加上hljs的高亮样式了。

    marked

    这是另一款非常受欢迎的markdown解析器,我曾经也尝试使用过,相比markdownit,我认为它配置起来更加简单,同样,它也可以直接启用highlight.js拓展

    const marked = require('marked');
    const hljs = require('highlight.js');
    marked.setOptions({
      highlight: (code) => hljs.highlightAuto(code).value,
    })
    marked(markdown)
    

  • art-template 是一个简约、超快的模板引擎。它采用作用域预声明的技术来优化模板渲染速度,从而获得接近 JavaScript 极限的运行性能,并且同时支持 NodeJS 和浏览器。

    一个静态站点生成器自然要用到模板语言,如jade,ejs,handlebars,mustache等,这里我使用的是国人开发的art-template,据说速度是相当的快。

    其实我在之前一直是不知道怎么使用模板语言,我原先对于模板文件的工作方式是这样理解的:

    由JS获取要应用到模板文件的数据然后传入模板文件生成文件

    而其实真正的方式是这样的:

    由JS读取模板文件并将模板需要的数据作为参数传给模板函数然后得到生成好的数据,这一切的过程其实都在JS中实现,并不是我以为的要由模板文件来进行编译。

    为什么要选择artTemplate呢

    • 可以使用传统模板语法,即双大括号!(这是我最喜欢的语法)

    • 查询文档很方便(有中文版,因为是由国人开发的)

    • 速度快,这点其实并没有太大的感觉

    使用方法

    使用方法非常简单,可以参考其中文文档

    我这里简单说下如何将它与现有代码即这个博客生成器结合起来

    Markdown解析器

    这里我选择markdown-it,类似的还有marked、showdown等

    var md = require('markdown-it')();
    var result = md.render('# markdown-it rulezz!');
    console.log(result) // <h1>markdown-it rulezz!</h1>
    

    因为之前我们使用front-matter对markdown文件进行了解析,得到的文章内容应该是content.body,然后使用markdown-it将这个值解析为html即可。

    模板编译

    我们这里只需要用到一个核心方法:

    template.render(source, data, options);
    

    其中我们将模板文件作为source传进去,然后data应该为一个对象,额外的选项暂时不需要。

    模板变量

    接着来看一下一个博客生成器中应该用到哪些变量是必须得:

    站点信息(site对象):
      标题(title):config.json文件定义
      链接(url):config.json文件定义
      所有文章(posts数组):posts.json中的posts值
      所有分类(categories对象):config.json文件定义,应为键对值对象
    页面信息(page对象):
      标题(title):由front-matter解析得到的content.attributes.title
      日期(date):由front-matter解析得到的content.attributes.date(只有文章有)
      所属分类(category):由front-matter解析得到的content.attributes.content
    

    然后将这些值全部放入一个对象中供template.render调用,为了方便复用,需要判断当前的页面是页面还是文章或者是分类页,所以我将它写成函数,判断当前是什么页面来进行解析并返回一个对象即可。

    具体data.js的实现方式在这里是代码

    编辑模板

    然后就可以来编写模板了(主题)

    可以这样来循环所有的文章

    <ul>
      {{each site.posts}}
      <li>
        <a href="{{$value.path}}">{{$value.title}}</a>
        <span>{{$value.date}}</span>
      </li>
      {{/each}}
    </ul>
    

    得到所有的分类

    {{each site.categories}}
    <li class="pure-menu-item">
      <a class="pure-menu-link" href="{{site.url}}/{{$value.path}}">{{$value.title}}</a>
    </li>
    {{/each}}
    

    对于单个分类页的变量储存方式,我像Jekyll那样将当前分类页的名称作为分类变量的第一项,然后将当前分类下的所有文章作为一个数组存到当前分类数组的第二项,所以可以这样来输出:

    <h1>{{category[0]}}</h1> <!-- 分类页标题 -->
    <!-- 文章 -->
    {{each category[1]}}
    <li>
      <a href="{{$value.path}}">{{$value.title}}</a>
      <span>{{$value.date}}</span>
    </li>
    {{/each}}
    

    跟Jekyll很像是吧


  • 自己用Node.js写了一个非常简单的静态博客生成器,也就是现在这个网站,源代码在Github,这里记录一下开发过程

    读取所有的Markdown文件

    首先这个博客生成器是将Markdown文件生成为HTML,所以第一步要做的是读取所有的Markdown源文件,然后将其内容和信息都存入一个JSON文件中。

    处理文件

    rd模块可以遍历文件夹下所有的文件。

    我们只需要使用readFileSync(dir)方法就可以返回文件夹下所有的文件的一个数组,然后使用forEach方法来遍历数组中每一个元素(即每个文件的路径),接着读取每一个文件的内容,使用fs模块的readFileSync方法来读取这个文件的内容。

    let mdContent = fs.readFileSync(filePath, 'utf-8');
    

    然后我们写一个函数来返回这个文件的文件名

    const formatFileName = filePath => path.basename(filePath).slice(0, -3).replace(/ /g, '-').toLocaleLowerCase();
    

    我们这里将文件中所有的空格替换成-符号,然后将所有的大写转换为小写。

    然后这里我们使用到一个front-matter模块来读取这个md文件中的头信息,就像Jekyll和Hexo那样,我们需要在文章或页面的文件头部需要定义这篇文章的信息,如标题、时间、分类等。

    var fs = require('fs');
    var fm = require('front-matter');
    fs.readFile('./example.md', 'utf8', function(err, data){
      if (err) throw err
      var content = fm(data)
      console.log(content)
    })
    

    fm会解析文件内容并返回一个对象:

    {
        attributes: { // 头信息
            title: '',
            category: ''
        },
        body: '' // 内容主体
    }
    

    存入‘数据库’

    接着我们将读取到的所有文件的内容和信息存入‘数据库’中,这里我们用一个JSON文件来代替数据库储存文章内容。

    我们这里使用lowdb这个库,具体使用方法可以浏览这里,我这里对一个JSON文件初始化一个posts数组来储存所有文章。

    _postsDb.defaults({posts: []})
      .write();
    

    接着写一个方法,然后再之前遍历所有文章的时候将文章信息传入这个方法

    this.appendPostsDb = currentDb => {
        _postsDb.get('posts')
          .push(
            {
              path: currentDb.path,
              title: currentDb.title,
              date: currentDb.date(),
              category: currentDb.category,
              content: currentDb.conetnt
            }
          )
          .write();
        // console.log("write db")
      };
    

    然后,在每次遍历到文章的时候,使用fm或者其它方法得到这个文章的相关信息并存入一个对象,之后再将这个对象传给appendPostsDb()函数。

    const post = {
      title: fContent.attributes.title,
      date: f => moment(fContent.attributes.date).format(f || 'YYYY-MM-DD'),
      category: fContent.attributes.category,
      conetnt: md.render(fContent.body),
      path: postLink(fileName)
    };
    writeDb.appendPostsDb(post);
    

    对数据库进行排序

    这样直接存入数据库后,是没有顺序的,这样很不方便以后我们直接读取所有文章,所有我们这里按照日期先后来给这个JSON排序一下。

    function formatDate(item) {
      return parseInt(item.replace(/-/g, ''));
    }
    let posts = dbPosts.sort((a, b) => formatDate(b.date) - formatDate(a.date));
    

    排序方法也是非常简单,直接将日期转换为一个数字字符串然后直接sort比较即可。