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

牧云@^-^@ 提交于 2020-04-17 16:42:19

React调用顺序: index.html → index.js → components/组件

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

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

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

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

在你的项目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",

      ...

  }

所以执行的是 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 {
  .......

上面代码中 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);

     /**

   * 省略其他代码

   */

}

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'))

};

 /**

   * 省略其他代码

   */

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  },

 /**

   * 省略其他代码

   */
}
发布了657 篇原创文章 · 获赞 2692 · 访问量 2480万+
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!