最近把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
一下哈~