了解create-react-app初始化脚手架的原理

前言:

  1. 本来一直是知道create-react-app可以命令初始化项目,因此就想让我自己的脚手架也能达到这个目标,自己的实现计划如下:
  • 希望将脚手架放在npm上,成为可以安装的依赖包;
  • 然后希望可以实现和create-react-app一样的方式来初始化一个react项目,有如下三种让方式:
`npx create-react-app my-app`

`npm init react-app my-app` // 拓展:如果是项目目录,一定会存在一个package.json文件

`yarn create react-app my-app`

// 以上可以理解为等价于以下两个步骤,create-react-app可以作为命令,然后执行其内部一系列代码
// yarn global add create-react-app
// create-react-app my-app
  1. 然后自己实现了如何将一个项目发布到npm上面(24小时内可以删除npm unpublish [npm package] --force,超过则不能删除)。
    脑子想到的问题:
    执行create-react-app my-app后,是怎样创建了my-app项目目录,并是怎样将自己搭建好的脚手架项目克隆下来到my app文件里的(当时是这样想的,其实真正的实现并不是这样子的)

  2. 然后后面我找到了一个文档(深度解析create-react-app源码),是阐述create-react-app命令初始化项目的源码,看了之后,就想着只要理解了初始化项目的原理就行了,自己实现这个功能太麻烦了。
    如下是create-react-app命令包的一些依赖,后期可以不断学习了解这些包:

"dependencies": {
    "chalk": "2.4.2", // 这个库的作用是改变命令行中输出信息的样式,颜色等
    "commander": "2.20.0", // Node命令接口,也就是可以用它代管Node命令
    "cross-spawn": "6.0.5", // 用来执行node进程
    "envinfo": "7.3.1", // 可以打印当前操作系统的环境和指定包的信息
    "fs-extra": "7.0.1", // 外部依赖,Node自带文件模块的外部扩展模块
    "hyperquest": "2.1.3", // 这个用于将http请求流媒体传输
    "inquirer": "6.5.0",
    "semver": "6.3.0", // 用于比较Node版本
    "tar-pack": "3.4.1",
    "tmp": "0.0.33",
    "validate-npm-package-name": "3.0.0" // 检查包名是否合法
}
// 还有其他node的api
`child_process`
execSync:引用自child_process.execSync,用于执行需要执行的子进程

总结:

  • 自己之前认识上的误区更正:
    (1)create-react-app放在npm上的git地址就是这个大包的地址,并不是自身脚手架的地址。
    (2)create-react-app my-app初始化的项目,脚手架真正的内部文件什么的,都是通过react-scripts里面的模板添加进去的,即重点是react-scripts文件包。
  • 重点都在react-scripts这个文件夹下面,对这个文件夹做了很多的处理,估计后面我没事了还要多看看。
  • create-react-app my-app初始化项目后,通过针对输入的name(my-app)以及其他的参数进行判断和函数的执行,再到将react-script里的package.jsontemplate添加进my-app项目中,再进行install安装依赖,即完成。

create-react-app是如何统一更新第三方依赖包:

实现:只需要更新react-script这个包即可,查看(react-scripts版本)。不过前提是项目没有进行npm run eject

// 比如,将当前版本升级到3.1.0:
`yarn add --exact react-scripts@3.1.0`
`npm install --save --save-exact react-scripts@3.1.0`

原因:
(1)首先,没有eject之前,项目package.json中除了react、react-dom、react-script依赖外,没有其他的依赖包;

// eject前

{
  "name": "test-my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-scripts": "3.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

// eject后

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@babel/core": "7.2.2",
    "@svgr/webpack": "4.1.0",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "9.0.0",
    "babel-jest": "23.6.0",
    "babel-loader": "8.0.5",
    "babel-plugin-named-asset-import": "^0.3.1",
    "babel-preset-react-app": "^7.0.2",
    "bfj": "6.1.1",
    "case-sensitive-paths-webpack-plugin": "2.2.0",
    "css-loader": "1.0.0",
    "dotenv": "6.0.0",
    "dotenv-expand": "4.2.0",
    "eslint": "5.12.0",
    "eslint-config-react-app": "^3.0.8",
    "eslint-loader": "2.1.1",
    "eslint-plugin-flowtype": "2.50.1",
    "eslint-plugin-import": "2.14.0",
    "eslint-plugin-jsx-a11y": "6.1.2",
    "eslint-plugin-react": "7.12.4",
    "file-loader": "2.0.0",
    "fs-extra": "7.0.1",
    "html-webpack-plugin": "4.0.0-alpha.2",
    "identity-obj-proxy": "3.0.0",
    "jest": "23.6.0",
    "jest-pnp-resolver": "1.0.2",
    "jest-resolve": "23.6.0",
    "jest-watch-typeahead": "^0.2.1",
    "mini-css-extract-plugin": "0.5.0",
    "optimize-css-assets-webpack-plugin": "5.0.1",
    "pnp-webpack-plugin": "1.2.1",
    "postcss-flexbugs-fixes": "4.1.0",
    "postcss-loader": "3.0.0",
    "postcss-preset-env": "6.5.0",
    "postcss-safe-parser": "4.0.1",
    "react": "^16.8.6",
    "react-app-polyfill": "^0.2.2",
    "react-dev-utils": "^8.0.0",
    "react-dom": "^16.8.6",
    "resolve": "1.10.0",
    "sass-loader": "7.1.0",
    "style-loader": "0.23.1",
    "terser-webpack-plugin": "1.2.2",
    "url-loader": "1.1.2",
    "webpack": "4.28.3",
    "webpack-dev-server": "3.1.14",
    "webpack-manifest-plugin": "2.0.4",
    "workbox-webpack-plugin": "3.6.3"
  },
  "scripts": {
    "start": "node scripts/start.js",
    "build": "node scripts/build.js",
    "test": "node scripts/test.js"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ],
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx,ts,tsx}",
      "!src/**/*.d.ts"
    ],
    "resolver": "jest-pnp-resolver",
    "setupFiles": [
      "react-app-polyfill/jsdom"
    ],
    "testMatch": [
      "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
      "<rootDir>/src/**/?(*.)(spec|test).{js,jsx,ts,tsx}"
    ],
    "testEnvironment": "jsdom",
    "testURL": "http://localhost",
    "transform": {
      "^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
      "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
      "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
    },
    "transformIgnorePatterns": [
      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
      "^.+\\.module\\.(css|sass|scss)$"
    ],
    "moduleNameMapper": {
      "^react-native$": "react-native-web",
      "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
    },
    "moduleFileExtensions": [
      "web.js",
      "js",
      "web.ts",
      "ts",
      "web.tsx",
      "tsx",
      "json",
      "web.jsx",
      "jsx",
      "node"
    ],
    "watchPlugins": [
      "/Users/jojo/learn-code/my-app/node_modules/jest-watch-typeahead/filename.js",
      "/Users/jojo/learn-code/my-app/node_modules/jest-watch-typeahead/testname.js"
    ]
  },
  "babel": {
    "presets": [
      "react-app"
    ]
  }
}

(2)其次,webpack的配置也没有出现,确保了后面更新依赖包后,eject后,webpack配置也会随之更新。


eject前

eject后

ok,终于解决了我的疑问!!!

PS说明:

create-react-app大包git地址:https://github.com/facebook/create-react-app
包含的两个重要的包:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354