React进阶(十):React项目结构启动原理详解

举报
SHQ5785 发表于 2020/12/29 23:50:08 2020/12/29
【摘要】 React调用顺序: index.html → index.js → components/组件 一般项目创建好后会有二个文件:index.html、index.js。 my-app文件夹下的public/index.html 和src/index.js的源码,可以在这里编写项目代码,但是注意 public/index.html 是启动http服务器的首页,src/...

React调用顺序: index.htmlindex.jscomponents/组件

一般项目创建好后会有二个文件:index.html、index.js

my-app文件夹下的public/index.htmlsrc/index.js的源码,可以在这里编写项目代码,但是注意 public/index.html 是启动http服务器的首页,src/index.js是编译的入口文件,只能叫index这个名字,改别的名字不行(除非你改配置文件,继续往下看)。

注:运行npm run eject可使其暴露webpack等配置文件,注意该操作不可逆。

有关 npm run eject 命令的具体使用原理,可详参其他博文。

在利用脚手架工具create-react-app创建项目后,

其中:
1.node_modules是各个插件存放位置;
2.public用于放置静态资源,里面的资源不会参与构建;
3.src是源码文件,一般做开发就在这个文件夹,会参与打包构建;

重点来了:
package.json中:
只有三个依赖,分别是reactreact-dom,react-scripts,依赖为什么这么少,是因为像webpackbabel等等都是被creat react app封装到了react-scripts这个项目当中,包括基本启动命令都是通过调用react-scripts这个依赖下面的命令进行启动的,creat react app搭建出来的项目默认支持这4种命令:

  • start以开发模式启动项目;
  • build将整个项目进行构建;
  • test进行测试
  • eject,会将原本creat react appwebpackbabel等相关配置的封装弹射出来,如果要将creat react app配置文件进行修改,现有目录下是没有地方修改的。此时,就可以通过eject命令将原本被封装到脚手架当中的命令弹射出来,然后就可以在项目的目录下看到很多配置文件。

建议:
安装完毕后,
首先git add .
然后git commit -m “init”
然后再npm run eject

利用脚手架工具create-react-app创建并启动项目后,打开 http://localhost:3000F12查看网页源码,会看到

<script type="text/javascript" src="/static/js/bundle.js"></script>

  
 
  • 1

在项目my-app是看不到/static/js/bundle.js这个文件路径的,也没有写配置文件webpack.config.js

http服务器配置, 自动代开浏览器窗口, react, es6语法编译, babel-core, webpack等等这些 都没下载、配置。其实,这些活,react-scripts 都帮做了。

通过npm run start命令启动,运行项目。

打开my-app\package.json

"scripts": { "start": "react-scripts start", ...
  }

  
 
  • 1
  • 2
  • 3
  • 4

所以执行的是 react-scripts start

打开你的my-app\node_modules\react-scripts这个文件夹下的bin文件夹下的react-scripts.js文件

const scriptIndex = args.findIndex(
  x => x === 'build' || x === 'eject' || x === 'start' || x === 'test'
);
const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];

if (['build', 'eject', 'start', 'test'].includes(script)) {
  const result = spawn.sync( 'node', nodeArgs .concat(require.resolve('../scripts/' + script)) .concat(args.slice(scriptIndex + 1)), { stdio: 'inherit' }
  );
  if (result.signal) { if (result.signal === 'SIGKILL') { console.log( 'The build failed because the process exited too early. ' + 'This probably means the system ran out of memory or someone called ' + '`kill -9` on the process.' ); } else if (result.signal === 'SIGTERM') { console.log( 'The build failed because the process exited too early. ' + 'Someone might have called `kill` or `killall`, or the system could ' + 'be shutting down.' ); } process.exit(1);
  }
  process.exit(result.status);
} else {
  .......

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

上面代码中 script 的变量值是start
在这里插入图片描述
所以执行 my-app\node_modules\react-scripts\scripts 文件夹下的 start.js 文件代码

var webpack = require('webpack');

var WebpackDevServer = require('webpack-dev-server');  // 启动http服务器

var paths = require('../config/paths');  //要编译的文件路径与生成路径等

const configFactory = require('../config/webpack.config');

const createDevServerConfig = require('../config/webpackDevServer.config');

//这就是为什么端口号不是8080 ,而是 3000 的原因,在这里可以改成8080,
// 重新npm run start 生效 
var DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; 

detect(DEFAULT_PORT).then(port => { if (port === DEFAULT_PORT) { run(port); //这里开始运行 return; }

......

function run(port) { 

// 这里可以设置 http协议, 或者可以在 npm run start 之前 cmd命令窗口中执行
// set HTTPS=true&&npm start 改成https 安全协议 var protocol = process.env.HTTPS === 'true' ? "https" : "http"; var host = process.env.HOST || 'localhost'; setupCompiler(host, port, protocol);  // 编译源码 ,生成路径 runDevServer(host, port, protocol);  //启动 http服务器

}

//配置http服务器

function runDevServer(host, port, protocol) { var devServer = new WebpackDevServer(compiler, { compress: true, clientLogLevel: 'none', contentBase: paths.appPublic,   //根据导入的paths 指定应用根目录(即index.html所在目录) hot: true, publicPath: config.output.publicPath, //根据导入的 config 变量,指定 虚拟目录, 自动指向path编译目录(/assets/ => /build/js/)。html中引用js文件时, //必须引用此虚拟路径(但实际上引用的是内存中的文件,既不是/build/js/也不是/assets/)。 quiet: true, watchOptions: { ignored: /node_modules/ }, // Enable HTTPS if the HTTPS environment variable is set to 'true' https: protocol === "https", host: host }); /** * 省略其他代码 */ // 打开浏览器向服务器发送请求 openBrowser(protocol + '://' + host + ':' + port + '/'); });

}

function setupCompiler(host, port, protocol) {
  //  根据导入的config 变量指向的 webpack.config.dev 配置文件运行
  compiler = webpack(config, handleCompile); /** * 省略其他代码 */

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

start.js 文件代码中导入了my-app\node_modules\react-scripts\config文件夹下的 webpack.configpaths.js

paths.js 代码节选如下:

 // 获取npm run start 运行所在的路径
var appDirectory = fs.realpathSync(process.cwd()); function resolveApp(relativePath) { return path.resolve(appDirectory, relativePath);

}

module.exports = { appPath: resolveApp('.'), ownPath: resolveApp('node_modules/react-scripts'), appBuild: resolveApp('build'), appPublic: resolveApp('public'), // 这就是一开始我们的项目要使用public/index.html作为默认首页 
  appHtml: resolveApp('public/index.html'), // 这里写什么文件名,项目中就要使用什么文件名  包括 也要有public文件夹
  // 编译的入口文件的文件名  项目中要包括src文件夹
  appIndexJs: resolveApp('src/index.js'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), yarnLockFile: resolveApp('yarn.lock'), testsSetup: resolveApp('src/setupTests.js'), appNodeModules: resolveApp('node_modules'), // this is empty with npm3 but node resolution searches higher anyway: ownNodeModules: resolveOwn('node_modules'), nodePaths: nodePaths, publicUrl: getPublicUrl(resolveApp('package.json')), servedPath: getServedPath(resolveApp('package.json'))

};

 /** * 省略其他代码 */

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

webpack.config 代码节选如下:

var paths = require('./paths');  //也导入了同文件夹下的 paths.js

module.exports = {  
entry: [ require.resolve('react-dev-utils/webpackHotDevClient'), require.resolve('./polyfills'), paths.appIndexJs // 编译的入口文件  ], 
output: {
	path: paths.appBuild, pathinfo: true, 
	// 只是编译后生成的目标文件,这就是一开始我们 打开浏览器按f12看到的
	filename: 'static/js/bundle.js', 
publicPath: publicPath  },

 /** * 省略其他代码 */
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

文章来源: shq5785.blog.csdn.net,作者:No Silver Bullet,版权归原作者所有,如需转载,请联系作者。

原文链接:shq5785.blog.csdn.net/article/details/105564564

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。