dva-原始碼解析

opt_bbt發表於2018-10-08

dva-cli

dva 的命令列工具 原理就是當執行 dva new my-app 時,dva-cli 就將其專案中的boilerplates資料夾拷貝到process.cwd()目錄下。並且執行npm install安裝專案依賴。

檢視 package.json 入口在bin/dva

// Notify update when process exits
const updater = require('update-notifier');
const pkg = require('../package.json');
updater({ pkg: pkg }).notify({ defer: true });

// dva -v
if (process.argv.slice(2).join('') === '-v') {
  // ...
}

program
  .usage('<command> [options]')
  .on('--help', printHelp)
  .parse(process.argv);

const aliases = {
  g: 'generate',
};
const args = process.argv.slice(3);

// new、generate
let subcmd = program.args[0];
if (aliases[subcmd]) subcmd = aliases[subcmd];

if (!subcmd) {
  program.help();
} else {
  const bin = executable(subcmd);
  if (bin) {
    // 執行檔案
    wrap(spawn(bin, args, {stdio: 'inherit', customFds: [0, 1, 2]}));
  } else {
    program.help();
  }
}

function wrap(sp) {
  sp.on('close', function(code) {
    process.exit(code);
  });
}

// 檢查是不是有相應 subcmd 的執行檔案
function executable(subcmd) {
  var file = join(__dirname, 'dva-' + subcmd);
  if (exists(file)) {
    return file;
  }
}
複製程式碼

bin/dva 是dva-cli的入口檔案,其中定義了-v命令以及執行相應的subcmd命令。如果你輸入了dva new my-app,那麼將會執行bin/dva-new,下面看下bin/dva-new檔案

// 沒有定義專案名稱
if (!program.args[0]) {
  program.help();
} else {
  const dest = join(process.cwd(), program.args[0]);
  // 如果已經存在該專案
  if (existsSync(dest)) {
    console.error(error('Existing directory here, please run new command for an empty folder!'));
    process.exit(1);
  }
  // 建立目錄
  mkdirpSync(dest);
  // 進入目錄
  process.chdir(dest);
  require('../lib/init')(program);
}
複製程式碼

bin/dva-new 檢查了命令的有效性,並且建立、進入專案目錄,執行dva-init初始化專案,下面去看一下bin/dva-init

program
  .option('--demo', 'Generate dva project for demo')
  .option('--no-install', 'Install dependencies after boilerplate, default: true')
  .parse(process.argv);

require('../lib/init')(program);
複製程式碼

定義了兩個配置項,然後又呼叫了lib/init,由於lib資料夾是打包出來的,先去看一下src/init檔案吧

function init({ demo, install }) {
  const type = demo ? 'demo' : 'app';
  // __dirname 為執行檔案所在的目錄
  const cwd = join(__dirname, '../boilerplates', type);
  const dest = process.cwd();
  const projectName = basename(dest);

  if (!emptyDir(dest)) {
    error('Existing files here, please run init command in an empty folder!');
    process.exit(1);
  }

  console.log(`Creating a new Dva app in ${dest}.`);
  console.log();

  // vfs:檔案流處理工具,將boilerplates中的檔案拷貝到process.cwd目錄下,
  // 如果install為true的話,則執行src/install
  vfs.src(['**/*', '!node_modules/**/*'], {cwd: cwd, cwdbase: true, dot: true})
    .pipe(template(dest, cwd))
    .pipe(vfs.dest(dest))
    .on('end', function() {
      info('rename', 'gitignore -> .gitignore');
      renameSync(join(dest, 'gitignore'), join(dest, '.gitignore'));
      if (install) {
        info('run', 'npm install');
        require('./install')(printSuccess);
      } else {
        printSuccess();
      }
    })
    .resume();

  function printSuccess() {
    //...
  }
}
複製程式碼

src/install 會去根據系統安裝的依賴管理工具進行install操作

// 好吧 竟然沒有yarn
function findNpm() {
  var npms = process.platform === 'win32' ? ['tnpm.cmd', 'cnpm.cmd', 'npm.cmd'] : ['tnpm', 'cnpm', 'npm'];
  for (var i = 0; i < npms.length; i++) {
    try {
      which.sync(npms[i]);
      console.log('use npm: ' + npms[i]);
      return npms[i];
    } catch (e) {
    }
  }
  throw new Error('please install npm');
}
export default function (done) {
  const npm = findNpm();
  // 因為boilerplates/package.json 中沒有dva,所以在執行install之後,再安裝dva,why?
  // 為什麼不乾脆將dva寫在boilerplates/package.json裡呢,
  // 因為這樣就可以保證每次安裝的dva都是最新版本啊 騷年。
  runCmd(which.sync(npm), ['install'], function () {
    runCmd(which.sync(npm), ['install', 'dva', '--save'], function () {
      console.log(npm + ' install end');
      done();
    });
  });
};
複製程式碼

在上面我們已經分析了dva new的相關命令,其實dva還支援generate操作(文件上是這麼說的),不過。。。我們來看下generate檔案

//...
console.log(chalk.red('?  dva generate is disabled since we don\'t have enough time currently.'));
process.exit(0);

require('../lib/generate')(program, {
  cwd: process.cwd(),
});
複製程式碼

四個字,暫未開放... 這個文件更新有點落後啊。dva-generate中又依賴了dva-ast 其中主要使用了 facebook/jscodeshift這個庫,作用是解析js,將js內容解析成 AST 語法樹,然後提供一些便利的操作介面,方便我們對各個節點進行更改,比如更改所有的屬性名之類的。感興趣的同學可以自己去研究一下,就送到這裡啦~ dva-cli就分析到這裡了,接下來我會去分析dva。

相關文章