React学习(四)----- 基于React脚手架的React应用
1、React脚手架
- 什么是脚手架???
用来帮助程序员快速创建一个基于React库的模板项目
1) 包含了所有需要的配置(语法检查、jsx编译、devServer…)
2) 下载好了所有相关的依赖
3) 可以直接运行一个简单效果
4) 项目的整体技术架构为: react + webpack + es6 + eslint
5) 使用脚手架开发项目的特点: 模块化, 组件化, 工程化
- 脚手架的安装
cnpm install create-react-app -g
- 创建项目
1、创建项目 create-react-app hello-react 2、进入项目目录 cd hello-react 3、启动项目 npm start
- React脚手架项目结构
- public ----- 静态资源文件夹
- favicon.ico ----- 网站页签图标
- index.html ----- 主页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <!-- %PUBLIC_URL%代表public文件夹的路径 相当于./favicon.ico --> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <!-- 开启理想视口,用于做移动端网页的适配 --> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- 用于配置浏览器页签 + 地址栏的颜色 ,只针对安卓手机浏览器(兼容性不好,有些机型无效果),不支持ios--> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <!-- 只支持ios手机:用于指定网页添加到手机主屏幕后的图标 --> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <!-- 应用加壳的配置文件 --> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>React App</title> </head> <body> <!-- 当浏览器不支持js时展示的提示信息 --> <noscript>You need to enable JavaScript to run this app.</noscript> <!-- 容器,挂载虚拟DOM的地方,也就是ReactDOM.render方法的第二个参数 --> <div id="root"></div> </body> </html>
- robots.txt ----- 爬虫协议文件
- src ----- 源码文件夹
- App.css ----- App组件的样式
- App.js ----- App组件
- App.test.js ----- 用于给App做测试
- index.css ----- 样式
- index.js ----- 入口文件:React.StrictMode 用于检查App和App的子组件的写法是否合理
- logo.svg ----- logo图
- reportWebVitals.js ----- 页面性能分析文件(需要web-vitals库的支持)
- setupTests.js ----- 组件单元测试的文件(主要jest-dom库的支持)
- public ----- 静态资源文件夹
2、一个简单的Hello组件
- vscode中react插件的安装
ES7 React/Redux/GraphQL/React-Native snippets
- 样式的模块化
- 组件的文件目录
-
- 将组件的css文件命名为:index.module.css,在组件jsx中的引入方式为用一个变量接收,并且定义的时候使用差值表达式的方式定义,这样不会造成样式污染;
import { Component } from "react"; import hello from './index.module.css' export default class Hello extends Component { render() { return <h1 className={hello.title}>Hello,React!!!!</h1>; } }
不然结果就是:同名的class标签只会使用后引入的css文件
- 将组件的css文件命名为:index.module.css,在组件jsx中的引入方式为用一个变量接收,并且定义的时候使用差值表达式的方式定义,这样不会造成样式污染;
- 功能界面的组件化编码流程
1. 拆分组件: 拆分界面,抽取组件 2. 实现静态组件: 使用组件实现静态页面效果 3. 实现动态组件 3.1 动态显示初始化数据 3.1.1 数据类型 3.1.2 数据名称 3.1.3 保存在哪个组件? 3.2 交互(从绑定事件监听开始)
3、组件的组合使用(TodoList)
- 效果图
- 知识点
- 步骤
-
- 生成唯一的id值得库
cnpm i uuid/nanoid -D//uuid库比较大,建议选择nanoid
- 非父子组件之间的传值
1、子传父、父传子 即Header传给App,App传给List (1)子传父 子组件在父组件当做标签使用 1) 子组件this.props.sendData(event.target.value) 2) 父组件中的子组件绑定数据 <Header sendData={this.receiveData} /> 3) 父组件中的方法 receiveData = (data) => { console.log(data); }; (2)父传子 子组件在父组件当做标签使用 1) 父组件中的子组件绑定数据 <Header {...this.state}} /> 2) 在子组件的render方法中使用this.props进行接收
- checkbox的相关问题
1、checked属性必须与onChange事件一起使用; 2、defaultChecked属性只有第一次会生效,后效修改将无效;
- 生成唯一的id值得库
4、React Ajax
- 为什么要引入Ajax???
1.React本身只关注于界面, 并不包含发送ajax请求的代码 2.前端应用需要通过ajax请求与后台进行交互(json数据) 3.react应用中需要集成第三方ajax库(或自己封装)
- 常用的Ajax请求库
1.jQuery: 比较重, 直接操作DOM,会有回调函数地域的问题,如果需要另外引入不建议使用 2.axios: 轻量级, 建议使用 1)封装XmlHttpRequest对象的ajax 2)promise风格 3)可以用在浏览器端和node服务器端
- 脚手架配置代理(客户端解决,服务器的话使用cors)
- 跨域原因
http://localhost:3000 -----> http://localhost:5000/students 原因:由于Ajax引擎的限制,请求可以从3000发送到5000端口,但是响应的时候被引擎拦截,这个时候我们可以配置代理 (设置一个中间人服务器,也在3000端口运行,而中间服务器没有ajax引擎的影响,且服务器之间无同源策略的影响,可以正常接收响应,接收到响应数据后,由于端口都是
3000,客户端直接收到数据)
本地3000端口运行这一个脚手架,一个服务器,name脚手架和服务器由于协议、域名、端口号完全一致,所以不会产生跨域,而本地代理的服务器与访问服务器之间不受同源策略的影响
因此也可以正常的接收到数据,代理服务器收到数据后再返回给客户端,因此解决跨域问题!!! - 第一种方式:代理一个服务器的情况下,多个不允许这么配置
- 步骤
1、修改package.json "proxy": "http://localhost:5000" 2、重启 npm start 3、修改请求接口 http://localhost:5000/students -----> http://localhost:3000/students
- 说明
1. 优点:配置简单,前端请求资源时可以不加任何前缀。 2. 缺点:不能配置多个代理。 3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
- 步骤
- 第二种方式:代理多个服务器
- 步骤
1. 第一步:创建代理配置文件 在src下创建配置文件:src/setupProxy.js 2. 编写setupProxy.js配置具体代理规则: // 使用commonjs的语法,引入一个内置插件 无需安装 const proxy = require('http-proxy-middleware') module.exports = function(app) { app.use( proxy('/api1', { //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000) target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址) changeOrigin: true, //控制服务器接收到的请求头中host字段的值 /* changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000 changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000 changeOrigin默认值为false,但我们一般将changeOrigin值设为true */ pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置) 重写请求路径 }), proxy('/api2', { target: 'http://localhost:5001', changeOrigin: true, pathRewrite: {'^/api2': ''} }) ) }
- 说明
1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。 2. 缺点:配置繁琐,前端请求资源时必须加前缀。
- 步骤
- 跨域原因
- github搜索案例
- 效果图
-
- 知识点
- 连续解构赋值
const obj = {a:{b:{c:1}}} console.log(obj.a.b.c) //1 const { a: { b: { c } } } = obj console.log(c) //1
- 连续解构赋值
- 知识点
5、发布-订阅机制(适用于任意组件的通信)
- 安装
cnpm i pubsub-js -S
- 使用:Search组件传递给-----List组件(兄弟组件之间的传值)
- List组件订阅消息
import PubSub from 'pubsub-js' componentDidMount(){ // 订阅消息 this.token = PubSub.subscribe("updateAppState",(msg,data)=>{ console.log("list") console.log(msg,data)//msg是updateAppState,即事件的名称,data是发布的消息 this.setState(data) }) } //取消订阅 componentWillUnmount(){ PubSub.unsubscribe(this.token) }
- Search组件发布消息
import PubSub from 'pubsub-js' PubSub.publish('updateAppState',{ isFirst: false,isLoading:true })
- List组件订阅消息
6、前后端交互的方式
- jquery/axios
- 特点
axios是一个基于Promise,用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征: 1)从浏览器中创建 XMLHttpRequest 2)从 node.js 发出 http 请求 3)支持 Promise API 4)拦截请求和响应 5)转换请求和响应数据 6)自动转换JSON数据, 7)客户端支持防止CSRF/XSRF
- 示例
import axios from 'axios' axios.get(`http://localhost:3000/api/search/users?q=${inputValue}`).then((res) => { // 请求成功后通知App更新状态 updateAppState({isLoading:false,users:res.data.items}) }).catch(err => { updateAppState({isLoading:false,err:err.message}) })
- 特点
- fetch ----- 无需安装,浏览器内置
- 特点
fetch:返回的是一个未处理的方法集合,我们可以通过这些方法得到我们想要的数据类型。如果我们想要json格式,就执行res.json(),如果我们想要字符串就res.text() 1)关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里 2)更加底层,提供的API丰富(request, response) 3)脱离了XHR,是ES规范里新的实现方式 4)fetch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理 5)fetch默认不会带cookie,需要添加配置项 6)fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费 7)fetch没有办法原生监测请求的进度,而XHR可以
- 请求格式
默认是get请求,如果需要向后端发送数据,则直接在地址后面做拼接 当请求post方式时: fetch(url, { method:"post", body: JSON.stringify(obj), headers : {//必须要写 "Content-type" : "application/json"; "credentials": 'include'//携带cookie进行提交 } }).then((res)=>res.json()).then((data)=>{})
- 示例:默认为get请求
fetch(`http://localhost:3000/api/search/users?q=${inputValue}`) .then( (res) => { console.log('联系服务器成功了', res)//是一个综合各种方法的对象,并不是请求的数据 return res.json() }, (error) => { console.log('联系服务器失败了', error) return new Promise(()=>{})//为了防止断网走了上一行后,接着走下一个then中的第一个回调,加上只走上面一行 }) .then( (res) => { console.log("获取数据成功了",res) }, (error) => { console.log('获取数据失败了',error) })
- 简化代码后
fetch(`http://localhost:3000/api/search/users?q=${inputValue}`) .then( (res) => { console.log('联系服务器成功了', res)//是一个综合各种方法的对象,并不是请求的数据 return res.json() } ) .then( (res) => { console.log("获取数据成功了",res) } ).catch( (error) => { console.log('请求出错',error) } )
- 使用async和await
try { let response = await fetch(`http://localhost:3000/api/search/users?q=${inputValue}`) let result = await response.json(); console.log(result) } catch (e) { console.log(e) }
- 特点
7、其他问题
暂无
北栀女孩儿