一、文件加载顺序
React
调用顺序: index.html
→ index.js
→ components
/组件
一般项目创建好后会有二个文件:index.html、index.js
。
my-app 文件夹下的public/index.html
和src/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
中:只有三个依赖,分别是react
,react-dom
,react-scripts
,依赖为什么这么少,是因为像webpack
,babel
等等都是被creat react app
封装到了react-scripts
这个项目当中,包括基本启动命令都是通过调用react-scripts
这个依赖下面的命令进行启动的,creat react app
搭建出来的项目默认支持这 4 种命令:
建议:安装完毕后,首先git add .
然后git commit -m “init”
然后再npm run eject
利用脚手架工具create-react-app
创建并启动项目后,打开 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 命令解析
通过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.config
与 paths.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 },
/**
* 省略其他代码
*/
}
复制代码
三、拓展阅读
评论