背景

用一个东西之前如果不了解它的原理那就是想相当于一个搬运工,我们拒绝做搬运工
那么他的原理、代码执行的流程、生命周期是什么呢?
咱们带着问题一探究竟

本博客是基于hexo框架,主题butterfly构建完成的

hexo执行过程

hexo 是基于nodejs

通过执行node hexo s就会生成可访问的静态文件,那么他的原理是什么呢?

  1. 首先你得了解nodeJs的npm

  2. hexo是一个npm模块,执行node hexo s就是执行该模块

  3. 模块初始化代码

    1
    2
    3
    4
    5
    6
    #!/usr/bin/env node

    'use strict';

    // hexo模块依赖了hexo-cli
    require('hexo-cli')();
  4. hexo模块依赖了hexo-cli,hexo-cli初始化代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    module.exports = entry;
    function entry(cwd = process.cwd(), args) {
    // process.argv = hexo 后面的参数
    args = camelCaseKeys(args || minimist(process.argv.slice(2), {string: ['_']}));

    // hexo临时的上下文
    let hexo = new Context(cwd, args);

    // findPkg: 首先在node_modules寻找package.json,找不到则从上层目录寻找,直到找到package.json
    return findPkg(cwd, args).then(path => {
    if (!path) return;

    // 此时的path就是当前hexo博客所处的位置
    hexo.base_dir = path;

    /**
    * loadModule:加载node_modules/hexo/lib/hexo/index.js,加载的伪代码如下
    * const resolve = require('resolve')
    * const modulePath = resolve.sync('hexo', { basedir: path });
    * const Hexo = require(modulePath);
    * return new Hexo(path, args);
    */
    return loadModule(path, args);
    }).then(mod => {
    // 真正的hexo
    if (mod) hexo = mod;

    // hexo是基于控制台交互的,所以优先加载console组件,注册几个默认的组件逻辑,比如help。。。避免连报错都无法提示
    require('./console')(hexo);

    /**
    * 加载node_modules/hexo/lib/hexo/index.js之后,执行init初始化方法
    * init 会往hexo.extend 注册很多组件以及组件的方法
    * 比如console组件是用来 和控制台交互的,那么会注册方法:help\server\generate\clean等
    */
    return hexo.init();
    }).then(() => {
    let cmd = 'help';

    // 从组件console控制台获取 hexo的命令,如server 或 generate等
    if (!args.h && !args.help) {
    const c = args._.shift();
    if (c && hexo.extend.console.get(c)) {
    cmd = c;
    }
    }

    // 监听退出方法,做一些清理的工作,比如说,如果是http服务则停止
    watchSignal(hexo);

    // 执行从console获取到的方法,如执行server或者clean或者help或者generate等
    return hexo.call(cmd, args).then(() => hexo.exit());
    });
    }
  5. hexo的设计思路和nodeJs的核心思想高度相似,都是注册很多事件,然后触发一个事件,每个事件都有自己的回调,这样使hexo扩展性极高

  6. 核心逻辑就是hexo.init以及hexo.call

  7. hexo.init

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    init() {
    // 注册内部的插件
    require('../plugins/console')(this); // 和控制台交互的组件

    // 以下的组件基本都是围绕着console组件中的命令展开的,比如generate命令:生成文章
    // 那么filter组件,在生成文章的前后对文章内容做一些处理,比如说替换
    require('../plugins/filter')(this);
    // 那么generator组件,就是用来生成页面
    require('../plugins/generator')(this);
    // helper
    require('../plugins/helper')(this);
    require('../plugins/injector')(this);
    require('../plugins/processor')(this);
    require('../plugins/renderer')(this);
    require('../plugins/tag')(this);

    // Load config
    return Promise.each([
    'update_package', // 升级hexo,package.json的hexo版本为^6.0.0,也就是说有更新时会自动更新package.json里面的version值
    'load_config', // 加载配置文件: _config.yml
    'load_theme_config', // 加载主题配置文件,如主题是butterfly,则加载hexoc.config.theme_config = _config.butterfly.yml
    // 加载扩展插件,优先resolve项目的package.json中的dependencies以hexo开头的依赖
    // 其次加载并执行用户scripts目录和hexo-theme/scriptes里面的脚本
    'load_plugins'
    ], name => require(`./${name}`)(this)
    ).then(() => this.execFilter('after_init', null, {context: this})
    ).then(() => {
    // Ready to go!
    this.emit('ready');
    });
    }
  8. hexo.call
    上一步init注册完组件以及加载完扩展脚本后,就要执行真正用户的指令了,比如说generate,或者gen,或者g

    缩写命令是node模块abbrev提供的转换,实际注册的名称为generate,经过abbrev转换之后,注册了递减的别名:generate\generat\genera\gener\gene\gen\ge\g

生成静态文件过程