最近把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多行代码,这里也不打算对它的源码进行逐一解读了,若是想要对所有源码解读的,能够先把这篇文章看完、再去看源码,应该会容易明白不少。vue
前面说了这么多废话,如今该进入正题了。那create-react-app
究竟是什么东东叱?这里仍是引用官方readme
文件的第一句话解释:node
Create React apps with no build configuration.
复制代码
嗯,这解释得很清楚了:creact-react-app
可让你零配置建立一个React
应用!为何要强调零配置呢?由于咱们都知道,React
是分模块的组件化的框架,这须要配置webpack
打包吧?还有使用了JSX
和高大上的ES6新特性
,这须要配置babel
吧?另外还须要对代码风格进行检查,须要配置eslint
吧?对于一个新手来讲,可以成功的配置一个能运行React
的环境,真的颇有可能须要一两天时间的。因此零配置的意义就在于让小萌新在不懂配置的状况下,也能迅速的编写本身的第一个react-hello-world
,这是颇有成就感的!react
若是以前没有使用过create-react-app
也没有关系 ,这里是它最简单的用法:webpack
create-react-app my-app
复制代码
等待数分钟,就会在当前目录下建立一个my-app
的项目,而后进入这个根目录npm start
就能够启动一个React
项目了。记得要先全局安装好create-react-app
。git
介绍了create-react-app
是什么,以及他的最简单的用法。如今咱们就一块儿动手实现一个create-react-app
山寨版吧。由于咱们实现的是一个简化版的,去除了环境检查、版本检测、离线包安装等功能,代码就剩下100行左右,暂且就叫作simple-create-react-app
。github
在实现代码以前咱们先梳理一上思路:web
commander
获取项目名称;package.json
文件;react
, react-dom
和react-scripts
三个依赖;react-scripts
的init
方法初始化项目(主要是复制模板);按照上面的思路,开始编码吧!vue-cli
先引入一些必要的依赖,对于这些依赖有什么做用这里就不展开了。 以及定义一个用来存放项目名称的变量projectName
:npm
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
的实例,获取用户输入的项目名称, 并判断是否为空。若是是空,则提示用户,并退出进程。json
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);
让进程的工做目录进入到新项目里面。而后开始安装依赖,等数分钟以后,安装依赖完成后,开始调用react-scripts
(这是create-react-app
的一个子模块,它包含了为你的项目加载其它插件、解析最终的 webpack 配置这里也不展开了, 有兴趣的能够点击这里 )的init
方法初始化项目(主要是复制模板到新项目里面):
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
一下哈~