其他章节请看:

七天接手react项目 系列

react 脚手架创建项目

前面我们一直通过 script 的方式学习 react 基础知识,而真实项目通常是基于脚手架进行开发。

本篇首先通过 react 脚手架创建项目,分析其目录结构,接着编写第一个组件、解决样式覆盖,最后配置代理 proxy 以及通过消息发布与订阅解决兄弟组件之间的通信问题。

Tip:我们要接手的 react 项目是:spug_web

使用 react 脚手架创建项目 react-cli-demo

前面我们学习 vue 脚手架 vue-cli 创建一个项目是这样:

> vue create vue-hello-world

在 react 中创建项目是这样:

$ npx create-react-app react-cli-demo

Creating a new React app in exercise\react-cli-demo.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template... added 1368 packages in 2m 169 packages are looking for funding
run `npm fund` for details Initialized a git repository. Installing template dependencies using npm...
npm WARN deprecated source-map-resolve@0.6.0: See https://github.com/lydell/source-map-resolve#deprecated added 38 packages in 9s 169 packages are looking for funding
run `npm fund` for details
Removing template package using npm... removed 1 package, and audited 1406 packages in 4s 169 packages are looking for funding
run `npm fund` for details 6 moderate severity vulnerabilities To address all issues (including breaking changes), run:
npm audit fix --force Run `npm audit` for details. Created git commit. Success! Created react-cli-demo at exercise\react-cli-demo
Inside that directory, you can run several commands: npm start
Starts the development server. npm run build
Bundles the app into static files for production. npm test
Starts the test runner. npm run eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can't go back! We suggest that you begin by typing: cd react-cli-demo
npm start Happy hacking!

Create React App 是一个用于学习 React 的舒适环境,也是用 React 创建新的单页应用最佳方式 —— 官网-Create React App

$ cd react-cli-demo/

本地启动项目:

$ npm start

> react-cli-demo@0.1.0 start
> react-scripts start (node:3880) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:3880) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
Starting the development server... Compiled successfully! You can now view react-cli-demo in the browser. Local: http://localhost:3000
On Your Network: http://192.168.85.1:3000 Note that the development build is not optimized.
To create a production build, use npm run build. assets by path static/ 1.49 MiB
asset static/js/bundle.js 1.48 MiB [emitted] (name: main) 1 related asset
asset static/js/node_modules_web-vitals_dist_web-vitals_js.chunk.js 6.93 KiB [emitted] 1 related asset
asset static/media/logo.6ce24c58023cc2f8fd88fe9d219db6c6.svg 2.57 KiB [emitted] (auxiliary name: main)
asset index.html 1.67 KiB [emitted]
asset asset-manifest.json 546 bytes [emitted]
cached modules 1.37 MiB (javascript) 31.3 KiB (runtime) [cached] 122 modules
webpack 5.69.1 compiled successfully in 1867 ms

自动打开网页:

react-cli-demo 目录结构分析

exercise\react-cli-demo> dir

Mode                 LastWriteTime         Length Name
---- ------------- ------ ----
d----- 2022/3/3 17:50 node_modules
d----- 2022/3/3 17:48 public
d----- 2022/3/18 19:22 src
-a---- 1985/10/26 16:15 310 .gitignore
-a---- 2022/3/3 17:49 1120931 package-lock.json
-a---- 2022/3/3 17:49 817 package.json
-a---- 1985/10/26 16:15 3359 README.md

一级目录结构很简单,我们主要分析一下 publicsrc 目录

public 目录

public> dir

Mode                 LastWriteTime         Length Name
---- ------------- ------ ----
-a---- 1985/10/26 16:15 3870 favicon.ico
-a---- 2022/3/4 16:06 1966 index.html
-a---- 1985/10/26 16:15 5347 logo192.png
-a---- 1985/10/26 16:15 9664 logo512.png
-a---- 1985/10/26 16:15 492 manifest.json
-a---- 1985/10/26 16:15 67 robots.txt

从中我们猜测主要文件应该是 index.html。内容如下:

// 注释已全部删除

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head> <body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body> </html>

index.html核心就是 <div id="root"></div>,即挂载的根元素

Tiprobots.txtrobots协议,只是约定俗成的,所以并不能保证网站的隐私

src 目录

src> dir

Mode                 LastWriteTime         Length Name
---- ------------- ------ ----
d----- 2022/3/4 16:56 components
-a---- 1985/10/26 16:15 564 App.css
-a---- 2022/3/18 19:22 553 App.js
-a---- 1985/10/26 16:15 246 App.test.js
-a---- 1985/10/26 16:15 366 index.css
-a---- 1985/10/26 16:15 500 index.js
-a---- 1985/10/26 16:15 2632 logo.svg
-a---- 1985/10/26 16:15 362 reportWebVitals.js
-a---- 1985/10/26 16:15 241 setupTests.js
index.js 和 App.js

哪个是入口文件?App.js 还是 index.js?我们先看一下这两个文件的内容:

// index.js 已删除注释

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals'; ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
); reportWebVitals();
// App.js
import logo from './logo.svg';
import './App.css'; function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
} export default App;

index.js 引用了 App.js

我们在回忆一下 vue-cli 生成的项目,也有 App.js 文件,不过是被 main.js 引用。内容如下:

// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store' Vue.config.productionTip = false new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
// App.js
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
</div>
</template>

对比发现,都是将 App 组件挂载到 dom 上 —— react 是 #root,vue 是 #app

至此,我们知道 index.js入口文件,而 App 应该是根组件。

App.css

上面我们启动 react-cli-demo 项目,网页中有这么一句话:

Edit src/App.js and save to reload.

编辑 src/App.js 并保存以重新加载。

App.js 中有 import './App.css';,笔者尝试修改一下 App.css

.App-header {
- background-color: #282c34;
+ background-color: orange;
...
}

保存后,发现页面背景自动变成橙色

于是我们知道 App.css 应该是 App 组件的样式。而 vue 中样式、html和 js 都在一个 .vue 文件中。

第一个组件 HelloWorld

组件创建有两种方式:函数组件以及类组件。

这里的 App.js 使用的是函数组件,我们也用函数的方式创建组件,然后让 App 加载。请看示例:

// App.js
// webpack 中就可以省略 .js
import HelloWorld from './HelloWorld.js'; export default function App() {
return (
<div className="App">
< HelloWorld />
</div >
);
}
// HelloWorld.js
export default function HelloWorld() {
return <div>hello world!</div>
}

页面显示 hello world!

对应类组件的实现:

import { Component } from 'react'
export default class HelloWorld extends Component {
render() {
return <div>hello world!</div>
}
}

后缀js/jsx

我们写的组件 HelloWorld 其实是 jsx 语法,所以可以将 HelloWorld.js 改为 HelloWorld.jsx,引入的后缀名也同步一下即可。

import HelloWorld from './HelloWorld.jsx';

Tip:纯逻辑的 js 可以用 .js 或小写(例如 http.js),组件用 Http.jsHttp.jsx

Create React App 在内部使用 webpack,而 webpack 能够使用户在引入模块时不带扩展:

import File from '../path/to/file';

经测试,无论是 HelloWorld.js 还是 HelloWorld.jsx,都可以省略扩展名引入:

import HelloWorld from './HelloWorld';

components

src/App.js 作为组件的根组件(或组件壳子),现在我们的HelloWorld 组件和它是同一目录,倘若以后组件变多了,岂不是不好管理,所以我们可以将组件放在 src/components 文件夹中。请看实现:

将 HelloWorld 组件代码移至 index.js

// src/components/HelloWorld/index.js

export default function HelloWorld() {
return <div>hello world!</div>
}

App.js 中修改引入组件的代码:

// 默认会去加载 HelloWorld 文件夹中的 index.js或 index.jsx
import HelloWorld from './components/HelloWorld'; ...

样式覆盖

假如我在 App 中引入两个组件,每个组件有自己的样式,让若发生冲突怎么办?请看示例:

// App.js
import HelloWorld from './components/HelloWorld'
import HelloWorld2 from './components/HelloWorld2' export default function App() {
return (
<div className="App">
< HelloWorld />
< HelloWorld2 />
</div >
);
}

组件1 的文字是蓝色

import './index.css'

export default function HelloWorld() {
return <div className="title">hello world!</div>
}
.title{color:blue}

组件2与组件1相同,唯一区别是文字颜色为红色

.title{color:red}

最终,页面中两个组件的文字颜色都是红色

页面有如下代码:

<style>.title{color:blue}
</style>
<style>.title{color:red}
</style> <div class="title">hello world!</div>
<div class="title">hello world!</div>

由此我们知道后者样式将前者给覆盖了。

样式模块化

我们可以使用样式模块化来修复样式覆盖的问题。

比如我要将 HelloWorld 组件样式模块化,只需要两步:

首先重命名样式文件。在名字和 css 之间增加 module

component/HelloWorl/index.css

// 重命名后
component/HelloWorl/index.module.css

然后使用样式的方式也得调整。就像这样:

import helloWorld from './index.module.css'

export default function HelloWorld() {
return <div className={helloWorld.title}>hello world!</div>
}

最终,页面中两个组件的文字颜色分别是蓝色红色,与预期相符。

页面有如下代码:

<style>.HelloWorld_title__kRYA7{color:blue}
</style>
<style>.title{color:red}
</style> <div class="HelloWorld_title__kRYA7">hello world!</div>
<div class="title">hello world!</div>

Tip:效果其实和 vue 中 Scoped Css 类似

less

我们还可以使用 less 这类 css 预处理语言来避免样式冲突。就像这样:

.HelloWorld {
.title{color: blue}
}
.HelloWorld2 {
.title{color: red}
}

代理 Proxy

在 vue-cli 中我们曾使用 proxy 做过一个需求:新建一个页面,里面有 2 个按钮,点击按钮能发出相应的请求,一个是非跨域请求,一个是跨域请求。

这里我们也实现一下。无需按钮,之间在组件中发请求即可。

在 React 开发中,你能使用任何你喜欢的 AJAX 库,比如社区比较流行的 Axios,jQuery AJAX,或者是浏览器内置的 window.fetch —— 官网-如何在 React 中发起 AJAX 请求?

Tip:React 和 Vue 将注意力集中保持在核心库,而将其他功能如ajax、路由和全局状态管理交给相关的库。

axios

我们曾在 vue-loader 扩展 这里将 axios 集成到项目中。这里却无需那么复杂,只需能发出 ajax 请求。

根据 axios 官网 介绍,简单使用只需两步:

首先下载依赖包:

react-cli-demo> npm i axios

added 1 package, and audited 1407 packages in 7s

169 packages are looking for funding
run `npm fund` for details 6 moderate severity vulnerabilities To address all issues (including breaking changes), run:
npm audit fix --force Run `npm audit` for details.

在 HelloWorld 组件中通过 axios 发起一个 ajax 请求:

// src/components/HelloWorld/index.jsx

const axios = require('axios');

export default function HelloWorld() {
axios.get('/index.html')
.then(function (response) {
console.log(response.data);
}) return <div>hello world!</div>
}

浏览器控制台输出:

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<title>React App</title>
<script defer src="/static/js/bundle.js"></script></head> <body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body> </html>

请求将 react-cli-demo/public/index.html 的内容返了回来,于是我们知道本地服务器的根是 public 目录。

setupProxy.js

spug_web 中有个叫 setupProxy.js 的文件,内容如下:

// src/setupProxy.js
const proxy = require('http-proxy-middleware'); module.exports = function (app) {
app.use(proxy('/api/', {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
ws: true,
headers: {'X-Real-IP': '1.1.1.1'},
pathRewrite: {
'^/api': ''
}
}))
};

Tiphttp-proxy-middleware - http 代理中间件。

对比在 vue 中做 proxy 代理,代码非常相似:

// vue.config.js
module.exports = {
devServer: {
proxy: {
// 只有 /pengjiali 的请求会被代理
'/pengjiali': {
target: 'https://www.cnblogs.com/',
// changeOrigin: true
},
}
}
}

接下来我们就依葫芦画瓢:

新建 setupProxy.js

// src/setupProxy.js

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function (app) {
app.use(
// 将原来的 proxy 改为 createProxyMiddleware
createProxyMiddleware(
'/pengjiali',
{
target: 'https://www.cnblogs.com/',
changeOrigin: true
}
)
)
}
// src/components/HelloWorld/index.jsx

const axios = require('axios');

export default function HelloWorld() {
axios.get('/pengjiali/p/14561119.html')
.then(function (response) {
// handle success
console.log(response.data);
}).catch(function (error) {
// handle error
console.log(error);
}) return <div>hello world2!</div>
}

重启服务,控制台输出博文内容。

http-proxy-middleware 有两点和 spug_web 不同:

  • 笔者这版的react脚手架默认已有 http-proxy-middleware,所以我们无需在下载。

    • vscode 中将鼠标移至 http-proxy-middleware 就会显示该文件路径。
  • 用法上从 proxy 改为 createProxyMiddleware
    • 我们的版本是 2.0.4,也是此刻官网最新版,其用法使用的就是 createProxyMiddleware
// 本地版本
react-cli-demo/node_modules/http-proxy-middleware (master)
$ cat package.json |head -n 5
{
"name": "http-proxy-middleware",
"version": "2.0.4",
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
"main": "dist/index.js",

消息订阅与发布

在 vue 中我们可以使用中央事件总线(或称 bus)来解决兄弟组件之间的通信。bus 相当于一个中介,组件可以在其上订阅消息,当触发时就会将消息通知到订阅者。其原理其实就是消息订阅与发布。

react 可以通过 pubsub-js 包来实现组件之间通信。

Tip:PubSubJS 是一个用 JavaScript 编写的基于主题的发布/订阅库 —— pubsub-js

下面我们定义两个组件,组件1订阅消息,组件2发布消息。请看实现:

首先按照依赖包:

react-cli-demo> npm i pubsub-js

added 1 package, and audited 1408 packages in 5s

169 packages are looking for funding
run `npm fund` for details 6 moderate severity vulnerabilities To address all issues (including breaking changes), run:
npm audit fix --force Run `npm audit` for details.

接着定义两个组件:

// 组件1订阅消息
// src/components/HelloWorld/index.jsx import PubSub from 'pubsub-js' export default function HelloWorld() {
// 订阅 message1
PubSub.subscribe('msg1', function (msg, data) {
console.log(msg, data);
}); return <div>hello world!</div>
}
// 组件2发布消息
// src/components/HelloWorld2/index.jsx import PubSub from 'pubsub-js' export default function HelloWorld() {
PubSub.publish('msg1', '旅游去');
return <div>hello world2!</div>
}

页面控制台显示:msg1 旅游去

其他章节请看:

七天接手react项目 系列

七天接手react项目 系列 —— react 脚手架创建项目的更多相关文章

  1. 七天接手react项目 系列 —— react 路由

    其他章节请看: 七天接手react项目 系列 react 路由 本篇首先讲解路由原理,接着以一个基础路由示例为起点讲述路由最基础的知识,然后讲解嵌套路由.路由传参,最后讲解路由组件和一般组件的区别,以 ...

  2. react实战系列 —— React 中的表单和路由的原理

    其他章节请看: react实战 系列 React 中的表单和路由的原理 React 中的表单是否简单好用,受控组件和非受控是指什么? React 中的路由原理是什么,如何更好的理解 React 应用的 ...

  3. Vue.js用脚手架创建项目

    安装全局脚手架 cnpm install vue-cli -g vue --version 用脚手架创建项目 创建项目 运行项目 停止项目:Ctrl+C 修改端口 config - index.js ...

  4. Koa 脚手架创建项目

    Koa 脚手架创建项目 通过应用 koa 脚手架生成工具 可以快速创建一个基于 koa2 的应用的骨架 全局安装koa npm install koa-generator -g //必须安装到全局 创 ...

  5. react实战 系列 —— React 的数据流和生命周期

    其他章节请看: react实战 系列 数据流和生命周期 如何处理 React 中的数据,组件之间如何通信,数据在 React 中如何流动? 常用的 React 生命周期方法以及开源项目 spug 中使 ...

  6. React学习之路之创建项目

    React 开发环境准备 IDE工具 visual studio code 开发环境 开发环境需要安装nodejs和npm,nodejs工具包含了npm. nodejs下载官网:https://nod ...

  7. react实战系列 —— react 的第一个组件

    react 的第一个组件 写了 react 有一个半月,现在又有半个月没写了,感觉对其仍旧比较陌生. 本文分两部分,首先聊一下 react 的相关概念,然后不使用任何语法糖(包括 jsx)或可能隐藏底 ...

  8. React(九)create-react-app创建项目 + 按需加载Ant Design

    (1)create-react-app如何创建项目我前面第一章介绍过了,这里就不过多写了, (2)我们主要来说说按需加载的问题 1. 引入antd npm install antd --save 2. ...

  9. 使用vue-cli脚手架创建项目

    ue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目. GitHub地址是:https://github.com/vuejs/vue-cli 一.安 ...

随机推荐

  1. 高德地图定位api以及导航和定位 位置的偏差

    <script type="text/javascript" src="http://webapi.amap.com/maps?v=1.4.2&key=37 ...

  2. 大前端JS篇之搞懂【Set】

    我认为前端生态很大,犹如一片汪洋大海,很难短时间内窥其全貌,在这里我们不谈其他,只聊聊 Set Set是 es6 新提出的一个对象,也是一种数据结构,为什么es6要提出这样一个新对象呢,无非就是丰富j ...

  3. python基础语法_9-0函数概念

    http://www.runoob.com/python3/python3-function.html 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代 ...

  4. Solution -「CF 1056G」Take Metro

    \(\mathcal{Description}\)   Link.   有 \(n\) 个站台在一个圆环上,顺时针编号 \(1\sim n\),其中 \(1\sim m\) 号站台只能乘坐顺时针转的环 ...

  5. Solution -「CF 1391E」Pairs of Pairs

    \(\mathcal{Description}\)   Link.   给定一个 \(n\) 个点 \(m\) 条边的无向图,在其上找到一条包括不少于 \(\lceil\frac{n}2\rceil\ ...

  6. Dump Lsass内存转储新旧方法

      之前看到一篇关于Lsass内存dump的文章,学习记录一下.   lsass.exe(Local Security Authority Subsystem Service)进程空间中,存有着机器的 ...

  7. Linux CentOS 搭建SVN并用钩子自动实现同步到Web目录

    linux安装配置SVN并设置钩子   安装说明 系统环境:CentOS-6.3安装方式:yum install (源码安装容易产生版本兼容的问题)安装软件:系统自动下载SVN软件 检查已安装版本 # ...

  8. 在超算系统上使用sbatch提交MXNet分布式训练任务

    在超算系统上运行MXNet分布式训练任务时,面临着一个IP地址相关的问题.我们在提交MXNet的分布式任务时,需要知道各个GPU节点的IP地址,把这些IP地址放到一个hosts文件中,以供分布式训练使 ...

  9. 关于Linux操作系统的命令行文件拷贝

    关于Linux操作系统的命令行文件拷贝 起因:服务器的加密狗秘钥过期导致无法使用服务,需要将服务器里面的秘钥文件发送给授权人员.本以为十分容易,打开服务器,图形界面点击发送即可.没想到服务器的界面是命 ...

  10. python3发微信脚本

    企业微信发微信脚本 #!/usr/bin/env python # -*- coding: utf-8 -*- #GuoYabin import requests,json,sys,imp imp.r ...