zoukankan      html  css  js  c++  java
  • create-react-app源码解读之为什么不搞个山寨版的create-react-app呢?

    最近把 vue-cli@2.x 和 create-react-app 的源码都看了一遍。由于现在官方推荐使用 vue-cli@3.0 ,改动比较大,所以就不写关于 vue-cli 的了(据说是因为 vue-cli@2.x 创建项目时操作有点太复杂了,于是犹雨溪大大就借鉴了 create-react-app 的思想,搞出了个零配置的 vue-cli@3.0 ,有兴趣的小伙伴可以去自己看一下哈)。这篇随笔只讲解 create-react-app 的实现,但是,因为 create-react-app源码 加上注释总共800多行代码,这里也不打算对它的源码进行逐一解读了,如果想要对全部源码解读的,可以先把这篇文章看完、再去看源码,应该会容易明白很多。

    前面说了这么多废话,现在该进入正题了。那 create-react-app 到底是什么东东叱?这里还是引用官方 readme 文件的第一句话解释:

    Create React apps with no build configuration.

    嗯,这解释得很清楚了:creact-react-app可以让你零配置创建一个React应用!为什么要强调零配置呢?因为我们都知道,React是分模块的组件化的框架,这需要配置webpack打包吧?还有使用了JSX和高大上的ES6新特性,这需要配置bable了啊?另外还需要对代码做静态检查,需要配置eslint吧?对于一个新手来说,能够成功的配置一个能运行React的环境,真的很有可能需要一两天时间的。所以零配置的意义就在于让小萌新在不懂配置的情况下,也能迅速的编写自己的第一个 react-hello-world,这是很有成就感的!

    如果之前没有使用过create-react-app也没有关系 ,这里是它最简单的用法:

    create-react-app my-app
    

    等待数分钟,就会在当前目录下创建一个my-app的项目,然后进入这个根目录npm start就可以启动一个React项目了。记得要先全局安装好create-react-app

    介绍了create-react-app是什么,以及他的最简单的用法。现在我们就一起动手实现一个create-react-app山寨版吧。因为我们实现的是一个简化版的,去除了环境检查、版本检测、离线包安装等功能,代码就剩下100行左右,暂且就叫做simple-create-react-app

    在实现代码之前我们先梳理一上思路:

    1. 通过commander获取项目名称;
    2. 如果项目名称为空(实际上还要对包名进行有效性检查的,这里暂且忽略),则退出进程,并提示用户项目名称不能为空,否则进行步骤3;
    3. 在当前目录下创建一个子目录,目录名称就是用户输入的项目名,并在里面初始化一个package.json文件;
    4. 进入项目的根目录,安装react, react-domreact-scripts三个依赖;
    5. 依赖安装完成后,调用react-scriptsinit方法初始化项目;
    6. 结束;

    按照上面的思路,开始编码吧!

    先引入一些必要的依赖,对于这些依赖有什么作用这里就不展开了。 以及定义一个用来存放项目名称的变量projectName

    const commander = require('commander');
    const chalk = require('chalk');
    const spawn = require('cross-spawn'); 
    const fs = require('fs-extra');
    const path = require('path');
    const os = require('os');
    
    const packageJson = require('./package.json');
    
    let projectName; // 项目名称,通过命令行参数获取
    

    接下来,就创建一个Commander的实例,获取用户输入的项目名称, 并判断是否为空。如果是空,则提示用户,并退出进程。

    const program = new commander.Command(packageJson.name)
    .version(packageJson.version)
    .arguments('<project-directory>')
    .usage(`${chalk.green('<project-directory>')} [options]`)
    .action(name => {
      projectName = name;
    })
    .parse(process.argv) // 格式化参数,必须要的
    
    // 如果没有输入项目名称,则给出提示,并退出进程
    if(typeof projectName === 'undefined') {
      console.error('please specify the project directory');
      console.log();
      console.log('For examaple: ')
      console.log(`    ${chalk.cyan(program.name())} ${chalk.green('my-react-app')}`)
      console.log();
      process.exit(1);
    }
    

    如果项目名称不为空,则开始创建一个空的项目,并且初始化一个packgae.json文件:

    // 开始创建项目
    createApp(projectName);
    
    function createApp(name) {
      const root = path.resolve(name);
      fs.ensureDirSync(root); // 创建项目空目录
      console.log(`Creating a new React app in ${chalk.green(root)}.`);
    
      // 创建新项目的package.json
      const packageJson = {
        name: name,
        version: '0.1.0',
        private: true
      };
      fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(packageJson, null, 2) + os.EOL);
    
      // 将当前目录的路径存下来。因为下一步我们就要进入到新项目的目录了
      // 后面可能还会用到当前的路径
      const originalDirectory = process.cwd();
    
      // 进入新创建的项目里面
      process.chdir(root);
    
      run(root, originalDirectory);
    }
    

    创建新项目之后,通过process.chdir(root);让进程的工作目录进入到新项目里面。然后开始安装依赖,等数分钟之后,安装依赖完成后,开始调用create-react-appinit方法初始化项目:

    function run(root, originalDirectory) {
      const allDependencies = ['react', 'react-dom', 'react-scripts'];
      console.log('Installing packages. This migth take a couple of minutes...');
      console.log(`Installing ${chalk.cyan('react')}, ${chalk.cyan('react-dom')}, and ${chalk.cyan('react-scripts')}...`);
      console.log();
    
      install(root, allDependencies)
      .then(() => {
        console.log();
        console.log('Installing is success!');
        console.log();
    
        // 执行react-scripts模块下的init方法进行初始化项目
        const scriptsPath = path.resolve(
          process.cwd(),
          'node_modules',
          'react-scripts',
          'scripts',
          'init.js'
        )
        const init  = require(scriptsPath);
        init(root, projectName, null, originalDirectory);
      })
      .catch(reason => {
        console.log();
        console.log('Aborting installation.');
        if(reason.command) {
          console.log(`    ${chalk.cyan(reason.command)} has failed.`);
        } else {
          console.log(chalk.red('Unexpected error!'), reason);
        }
      })
    }
    
    // 在指定目录下安装npm依赖
    function install(root, dependencies) {
      return new Promise((resolve, reject) => {
        let command = 'yarnpkg';
        const args = ['add'];
        [].push.apply(args, dependencies);
        let child = spawn(command, args, {stido: 'inherit'});
        child.on('close', code => {
          if(code !== 0) {
            reject({
              command: `${command} ${args.join(' ')}`
            });
            return;
          }
          resolve();
        })
      });
    }
    

    数了一下,代码总共100多行,就这么简单就实现了create-react-app的核心功能了。当然,实际上,还有环境检测、版本检测、离线安装等,我们这里忽略了的,如果有兴趣的,可以自己看一下官方的源码。

    关于create-reate-app就写这么多了,源码可以到我的github进行下载,如果喜欢的欢迎star一下哈~

  • 相关阅读:
    一段获取windows环境变量的代码
    尝试word2007的blog发布
    如何求一表所有行所有字段实际占用的空间
    如何查询全表扫描SQL
    Oracle 33个等待事件
    如何使用RMAN
    如何使用Diagnostics工具监控应用服务器
    如何查询每行数据的数据块信息
    如何理解recursive calls,db block gets和consistent gets
    如何编写批处理与SQLPLUS(例子)
  • 原文地址:https://www.cnblogs.com/yugege/p/9360304.html
Copyright © 2011-2022 走看看