git仓库地址:https://github.com/TaleLin/lin-cms-koa
目录
一、从入口文件开始
查看package.json,可知入口文件为项目根目录下的index.js文件:
require('@babel/register'); require('./app/starter');
言简意赅,两行代码。第一行引用了@babel/register这个node包,查阅其官网(https://babeljs.io/docs/en/babel-register)可知这个库会修改node提供的require方法,当node程序去require对应后缀名(.es6、.es、.jsx、.mjs和.js)的文件(默认情况下不会去编译node_modules目录里文件)时,这个库会先把库给编译掉再提供给node使用(见下图,往exports对象上绑定了register函数,在register函数里通过hookExtensions函数对指定拓展名的文件添加自定义的compileHook,其中添加hook的addHook方法是从pirates包中导入的)。
compileHook的内容很简单,就是判断当前没有在编译的话就把代码编译一下:
let compiling = false; function compileHook(code, filename) { if (compiling) return code; try { compiling = true; return compile(code, filename); } finally { compiling = false; } }
现在我们看下核心的compile函数:
function compile(code, filename) { // merge in base options and resolve all the plugins and presets relative to this file const opts = new OptionManager().init( // sourceRoot can be overwritten { sourceRoot: path.dirname(filename) + path.sep, ...deepClone(transformOpts), filename, }, ); // Bail out ASAP if the file has been ignored. if (opts === null) return code; let cacheKey = `${JSON.stringify(opts)}:${babel.version}`; const env = babel.getEnv(false); if (env) cacheKey += `:${env}`; let cached = cache && cache[cacheKey]; if (!cached || cached.mtime !== mtime(filename)) { cached = babel.transform(code, { ...opts, sourceMaps: opts.sourceMaps === undefined ? "both" : opts.sourceMaps, ast: false, }); if (cache) { cache[cacheKey] = cached; cached.mtime = mtime(filename); } } if (cached.map) { if (Object.keys(maps).length === 0) { installSourceMapSupport(); } maps[filename] = cached.map; } return cached.code; }
这里的主要逻辑是判断是否有缓存了的编译结果,如果有的话就直接使用之前缓存的编译结果没必要重新编译了,如果没有对应的缓存则通过babel.transform方法对源码进行编译处理。所以,项目中其他源码文件里我们就可以使用import/export关键字替代require来进行模块的导入导出了。
二、应用的启动app/starter.js、app/app.js
作用:加载一堆配置文件,然后启动web应用(添加数据解析、跨域支持、静态服务、日志打印等)。代码如下:
'use strict'; const fs = require('fs'); const path = require('path'); const { config } = require('lin-mizar/lin/config'); /** * 获取配置 */ function applyConfig () { const cwd = process.cwd(); const files = fs.readdirSync(path.resolve(`${cwd}/app/config`)); for (const file of files) { config.getConfigFromFile(`app/config/${file}`); } // 加载其它配置文件 config.getConfigFromFile('app/extensions/file/config.js'); } const run = async () => { applyConfig(); const { createApp } = require('./app'); const app = await createApp(); const port = config.getItem('port'); app.listen(port, () => { console.log(`listening at http://localhost:${port}`); }); }; // 启动应用 run();
这里的createApp函数很核心,其内部实现直接看源码中的中文注释即可,见下图: