01.create-react-app的基础操作
安装脚手架
npm i create-react-app -g
create-react-app --version 检查版本
创建React工程化项目
create-react-app 项目名称
命名规范:使用数字、小写字母、下划线
react和react-dom和react-native和react-scripts
react:React框架的核心
react-dom:React视图渲染的核心(基于React构建WebApp----HTML页面)
react-native:构建和渲染App的
react-scripts:把webpack打包的规则及相关插件或loader都隐藏到node_modules目录下,react-scripts就是脚手架的中自己对打包命令的一种封装,会调用node_modules中给的webpack等进行处理
02.脚手架进阶应用
处理跨域
-
在src目录中新建setupProxy.js文件
-
const { createProxyMiddleware } = require('http-proxy-middleware') module.exports = function (app) { app.use( createProxyMiddleware("/jian", { target: "https://www.jianshu.com/asimov", changeOrigin: true, ws: true, pathRewrite: { "^/jian": "" } }) ) app.use( createProxyMiddleware("/zhi", { target: "https://www.news-at.zhihu.com/api/4", changeOrigin: true, ws: true, pathRewrite: { "^/zhi": "" } }) ) }
03.jsx语法细节
vscode支持jsx语法
后缀名改为jsx
构建视图
获取页面中#root的容器,作为根容器(不能用body或html):
const root = ReactDOM.createRoot(document.getElementById('root'));
基于render方法渲染我们编辑的视图,渲染后的内容全部插入到#root中进行渲染
root.render(
<div>fjl学习react</div>
);
每一个构建的视图,只能有一个根节点!!!
可以用<></>作为根节点,减少层级---->React.Fragment空文档标记标签,类似于vue的template
胡子语法
number / string:值是啥就渲染啥
boolean / null / undefined / Symbol / BigInt:渲染的内容为空
不支持渲染:普通对象
数组对象:把数组的每一项都分别拿出来渲染(不是变为字符串,没有逗号)
函数对象:不支持在{ }中渲染,但是可以作为函数组件
虚拟DOM对象(用React.createElement创建的)可以
给元素设置样式
行内样式要用对象写(驼峰命名),不能直接写字符串
<div style={{
color: 'red',
fontSize: '18px'
}}>fjl学习react</div>
let styObj = {
color:'red',
fontSize:'16px'
}
<h2 className="title" style={styObj}></h2>
给元素设置class
要用className,不能直接用class
条件渲染
{flag ? <button>按钮2</button> : null}
列表渲染
没有类似于v-for的语法,使用数组的map进行列表渲染,key一定要写!
let data = [
{
id: 1,
title: "列表渲染",
},
{
id: 2,
title: "列表渲染2",
},
{
id: 3,
title: "列表渲染3",
},
];
root.render(
<>
<h2 className="title">今日新闻</h2>
<ul className="news-box">
{data.map((item, index) => {
return (
<li key={item.id}>
<em>{item.id}</em>
<span>{item.title}</span>
</li>
);
})}
{/* 单独渲染5次 */}
{new Array(5).fill(null).map((_, index) => {
return <button key={index}>按钮{index + 1}</button>;
})}
</ul>
</>
);
04.jsx底层渲染机制
步骤
-
把编写的jsx语法编译为虚拟DOM对象
- 基于babel-preset-react-app,把jsx编译为React.createElement(ele,props,…children)这种格式(ele:标签,props:标签内的属性,…children:子元素)
- 把React.createElement方法执行,返回虚拟DOM(或称为JSX元素、JSX对象)
-
把构建的虚拟DOM渲染为真实DOM(基于ReactDOM中的render方法处理)
重写render方法(v16):
// 封装对象迭代方法 // 如果基于传统的forin循环(性能较差,只能迭代可枚举、非symbol类型的属性) // 解决思路:获取对象所有的私有属性(私有的、不论是否可枚举、不论属性) // Object.getOwnPropertyNames(arr) -> 获取对象的非Symbol类型的私有属性 // Object.getOwnPropertySymbols(arr) -> 获取Symbol类型的私有属性 // 还可以用ES6中给的Reflect.ownKeys代替上述操作 const each = function each(obj, callback) { if (obj === null || typeof obj !== "object") { throw new TypeError("obj is not a object"); } if (typeof callback !== "function") { throw new TypeError("callback is not a function"); } let keys = Reflect.ownKeys(obj); keys.forEach((key) => { let value = obj[key]; // 每一次迭代都把回调函数执行 callback(value, key); }); }; // render方法:把虚拟DOM渲染为真实DOM // V16 export function render(virtualDOM, container) { let { type, props } = virtualDOM; // 存储的是标签名(动态创建标签) if (typeof type === "string") { let ele = document.createElement(type); // 为标签设置相关的属性 & 子节点 each(props, (value, key) => { // className处理:value存储的是类名 if (key === "className") { ele.className = value; return; } // style处理:value存储的是样式对象 if (key === "style") { each(value, (value, key) => { ele.style[key] = value; }); return; } // children处理: if (key === "children") { let children = value; if (!Array.isArray(children)) { children = [children]; } children.forEach((child) => { // 子节点是文本节点:直接插入即可 if (typeof child === "string") { ele.appendChild(document.createTextNode(child)); return; } // 子节点是虚拟DOM:递归处理 render(child, ele); }); return; } ele.setAttribute(key, value); }); // 把新增的标签,增加到指定容器container中 container.appendChild(ele); } }
视图更新时
第一次渲染完毕后,会把创建的虚拟DOM缓存起来,当数据改变时,JSX创建全新的虚拟DOM,使用diff算法,与之前缓存的虚拟DOM进行比对,生成Patch补丁包(不同的部分),最后只渲染补丁包
05.函数组件
使用方法
在src目录中,创建一个xxx.jsx的文件,就是要创建一个组件
在文件中创建一个函数,让函数返回JSX视图(虚拟DOM对象)
组件名首字母大写!!!!
// 参数props是一个对象,是接受的属性
const DemoOne = function DemoOne(props) {
console.log(props);
return (
<div className="demo-box">
我是DEMO-ONE
<div className="title">{props.title}</div>
</div>
);
};
export default DemoOne;
调用:
import DemoOne from "./views/DemoOne";
root.render(
<>
<DemoOne title='这是title'></DemoOne> // 双闭合(可以放子节点)
<DemoOne/> // 单闭合
</>
)