Higher - Order Components:在原有组件基础之上加工后新生成得到的新组件。【高阶组件】
const NewComponent = HOC(YourComponent)
通俗的来讲,高阶组件
就相当于手机壳,通过包装组件,增强组件功能。
HOC实现步骤:
创建一个函数
指定函数参数,参数应该以大写字母开头
在函数内部创建一个类组件,提供复用的状态(如有)及逻辑代码,并返回
在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件(可选,如有)
调用该高阶组件方法,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面
比如,我们想要我们的组件通过自动注入一个版权信息:
import React, { Component, Fragment } from "react";
const withCopyright = (Cmp) => {
return class Hoc extends Component {
render() {
return (
<Fragment>
<Cmp></Cmp>
<div>© 2020 千峰教育</div>
</Fragment>
);
}
};
};
export default withCopyright;
// Fragment是一个伪标签,渲染的时候是不会显示在页面中的,因此也不会影响视图显示
使用方式:
import React, { Component } from "react";
// 引入HOC函数
import Hoc from './Hoc/Hoc_copyright'
class App extends Component {
render() {
return (
<div>
<h1>网站首页</h1>
</div>
);
}
}
export default Hoc(App);
案例:?
import React, { Component } from "react";
import { Fragment } from "react";
/**
* 高阶组件: 把组件包装 扩展原有组件的功能
* 实现步骤
* 1.创建一个函数
* 2.函数参数为组件形参
* 3.在函数内部创建一个类组件,提供**复用**的状态(如有)及逻辑代码,并返回
* 4.在返回的类组件找中将传入的参数组件进行加工并渲染
*
*/
class App extends Component {
render() {
return <div>App</div>;
}
}
// 第二步 Cmp 参数 为 组件参数
const WithCopy = (Cmp) => {
// 第一步 创建一个WithCopy的函数
return class Hoc extends Component {
// 第三步 在内部生成一个类组件 并返回
render() {
return (
// 写法一
// <div>
// <Cmp></Cmp>
// <div>版权信息</div>
// </div>
// 写法二
<Fragment>
<Cmp></Cmp>
<div>版权信息</div>
</Fragment>
);
}
};
};
const WithColor = (Cmp) => {
return class Hoc extends Component {
render() {
return (
<div style={{ backgroundColor: "aqua" }}>
<Cmp></Cmp>
</div>
);
}
};
};
export default WithColor(WithCopy(App)); // 第四步 进行组件合并
这样只要我们有需要用到版权信息的组件,都可以直接使用withCopyright这个高阶组件包裹即可。
当然,也可以使用
ES7的装饰器(ES7的一个新语法,它可以对一些对象进行装饰包装然后返回一个被包装过的对象,可以装饰的对象包括:类,属性,方法等)
来实现高阶组件的效果,需要在package.json文件中增加如下配置:"plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ] ]
①默认情况下装饰器语法在react中是不被支持的,如果直接用会报错
Support for the experimental syntax 'decorators-legacy' isn't currently enabled
②开启装饰器的支持(需要配置babel),需要解压react封装在react-scripts项目中配置
git add .
git commit -m xxx
npm run eject
③需要在package.json文件中给babel增加如下配置(加完之后需要重启项目)
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
?④装饰器语法
@方法名
案例:
import React, { Component } from "react";
import { Fragment } from "react";
/**
* 高阶组件: 把组件包装 扩展原有组件的功能
* 实现步骤
* 1.创建一个函数
* 2.函数参数为组件形参
* 3.在函数内部创建一个类组件,提供**复用**的状态(如有)及逻辑代码,并返回
* 4.在返回的类组件找中将传入的参数组件进行加工并渲染
*
*/
// 第二步 Cmp 参数 为 组件参数
const WithCopy = (Cmp) => {
// 第一步 创建一个WithCopy的函数
return class Hoc extends Component {
// 第三步 在内部生成一个类组件 并返回
render() {
return (
// 写法一
// <div>
// <Cmp></Cmp>
// <div>版权信息</div>
// </div>
// 写法二
<Fragment>
<Cmp></Cmp>
<div>版权信息</div>
</Fragment>
);
}
};
};
const WithColor = (Cmp) => {
return class Hoc extends Component {
render() {
return (
<div style={{ backgroundColor: "aqua" }}>
<Cmp></Cmp>
</div>
);
}
};
};
// @ 装饰器语法 扩展原有类功能
@WithColor
@WithCopy
export default class App extends Component {
render() {
return <div>App</div>;
}
}
CSS-in-JS是一种技术,而不是一个具体的库实现。简单来说CSS-in-JS就是将应用的CSS样式写在JavaScript文件里面,而不是独立为一些css,scss或less之类的文件,这样你就可以在CSS中使用一些属于JS的诸如模块声明,变量定义,函数调用和条件判断等语言特性来提供灵活的可扩展的样式定义。CSS-in-JS在React社区的热度是最高的,这是因为React本身不会管用户怎么去为组件定义样式的问题,而Vue有属于框架自己的一套定义样式的方案。
在js文件中写css就是css-in-js技术
好处:
支持一些js的特性
继承
变量
函数
支持框架的特性
传值特性
styled-components
应该是CSS-in-JS最热门的一个库,通过styled-components
,你可以使用ES6的标签模板字符串语法,为需要styled的Component定义一系列CSS属性,当该组件的JS代码被解析执行的时候,styled-components会动态生成一个CSS选择器(比较随意的),并把对应的CSS样式通过style标签的形式插入到head标签里面。动态生成的CSS选择器会有一小段哈希值来保证全局唯一性来避免样式发生冲突。
通过ES6里面的模版字符串形式写css样式(遵循之前css样式代码的写法)
每个样式选择器都会在编译之后自动被添加上一个hash值(全局唯一)
使用styled-components
前需要安装,安装的命令如下:
npm i -S styled-components
由于css后期会在模版字符串中编写,默认情况下vscode是没有css样式代码片段的(写样式的时候是没有代码提示的),为了提高css代码在模版字符串中编写的效率,此处强烈建议安装一个vscode的扩展:vscode-styled-components。
在React中写样式的方式一共有:
import "xxx.css"
styled-components
行内标签style属性
index.html中Link标签
index.html的style标签
定义
import styled from "styled-components";
const Title = styled.div`
? ?font-size: 110px;
? ?color: pink;
? ?font-family: 华文行楷;
? ?background-color: black;
`;
export { Title };
使用
在使用的时候成员会被当作组件去使用(首字母大写)
import React, { Component, Fragment } from "react";
// 就像使用常规 React 组件一样使用 Title
import { Title } from "./assets/style/style";
?
class App extends Component {
? ?render() {
? ? ? ?return (
? ? ? ? ? ?<Fragment>
? ? ? ? ? ? ? ?<Title>桃之夭夭,灼灼其华。</Title>
? ? ? ? ? ?</Fragment>
? ? ? );
? }
}
?
export default App;
在styled-components
中也可以使用样式的继承,其继承思想与react
的组件继承相似:
继承父的样式
重载父的样式
import styled from "styled-components";
const Button = styled.button`
font-size: 20px;
border: 1px solid red;
border-radius: 3px;
`;
// 一个继承 Button 的新组件, 重载了一部分样式
// 继承会合并与父的样式,但是如果遇到样式冲突(相同),会以自己的为准
const Button2 = styled(Button)`
color: blue;
border-color: yellow;
`;
export { Button, Button2 };
?使用
import React, { Component, Fragment } from "react";
import { Button, Button2 } from "./assets/style/style";
class App extends Component {
render() {
return (
<Fragment>
<Button>我是第1个按钮</Button>
<Button2>我是第2个按钮</Button2>
</Fragment>
);
}
}
export default App;
import React, { Component } from "react";
import "./assets/css/App.css"; // 引入外部样式
// 引入 style-component
import styled from "styled-components";
// 方法三
const LineOne = styled.div`
color: aqua;
`;
export default class App extends Component {
render() {
return (
<div>
{/* 方法一 style 属性 */}
<div style={{ fontSize: 24 + "px", color: "aqua" }}>第一行</div>
{/* 方法二 外部引入 若是外部子组件类名相同 则样式相同 不存在作用域限制 存在污染情况*/}
<div className="lineOne">第二行</div>
{/* 方法三 安装 styled-components 在组件内部使用 存在隔离 解决全局样式污染*/}
<LineOne>
<div>第二行</div>
</LineOne>
</div>
);
}
}
?
属性传递:样式值的动态传参(组件传值)
基于css-in-js
的特性,在styled-components
中也允许我们使用props
(父传子),这样一来,我们可以对部分需要的样式进行传参,很方便的动态控制样式的改变。
import styled from "styled-components";
// 参数传递
const Input = styled.input`
color: ${(props) => props.inputColor || "red"};
`;
export { Input };
import React, { Component, Fragment } from "react";
import { Input } from "./assets/style/style";
class App extends Component {
render() {
return (
<Fragment>
<Input defaultValue="are you ok?" inputColor="blue"></Input>
</Fragment>
);
}
}
export default App;
案例(styled-components):
import React, { Component } from "react";
// 引入 style-component
import styled from "styled-components";
// 方法三
const LineOne = styled.div`
color: aqua;
`;
// 1.样式继承
const Line4 = styled(LineOne)`
background-color: blue;
`;
// 属性传递
const MyColor = styled.div`
color: ${(props) => props.color || "red"};
`;
export default class App extends Component {
render() {
return (
<div>
{/* 方法三 安装 styled-components 在组件内部使用 存在隔离 解决全局样式污染*/}
<LineOne>
<div>第二行</div>
</LineOne>
{/* styled-components高级写法 */}
{/* 1.样式继承 */}
<Line4>
第四行
</Line4>
{/* 2.动态传递参数 */}
<MyColor color="pink">
第五行
</MyColor>
</div>
);
}
}
React Router官网:Home v6.21.1 | React Router
使用用React Router前需要先进行安装:
npm i react-router-dom@5.3.0
React Router现在的主版本是5,思想:一切皆组件。
如前面介绍里说的,自Router 4之后的思想是一切皆组件
,所以在正式开始学习React路由前需要先对几个组件要有所掌握:
Router组件(别名,真实是不存在的,为了简写路由模式的组件名称):包裹整个应用(单个具体的组件/根组件),一个React应用只需要使用一次
注意:在react中,不存在类似于vue的路由配置文件,对于前端路由模式的选择,我们可以通过该组件完成
Router类型: HashRouter和BrowserRouter
HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)
BrowserRouter:使用H5的history API实现(localhost:3000/first)
区别:
两者在开发阶段,除了地址栏上的表现形式不一样以外,其它没区别
两者在生产阶段,hash路由可以直接上生产,无需做任何配置,而history模式则上生产需要配置的,配置服务器环境,否则项目是不能刷新的,一刷新会404
Link组件:用于指定导航链接(a标签)就是做声明式导航的(类似于vue中的router-link组件
)
最终Link会编译成a标签,而to属性会被编译成 a标签的href属性
Route组件:指定路由展示组件相关信息(组件渲染)【路由规则】{path: xx,component:xxx}
path属性:路由规则,这里需要跟Link组件里面to属性的值一致
component属性:展示的组件
语法:<Route path="路径" component={组件}></Route>
该组件除了具备定义路由规则功能外,还有类似于vue中router-view
的功能
各个组件之间的关系
注意:Link
和Route
组件必须被Router
组件给包裹,否则报错。
案例:
import React, { Component } from "react";
/**
* React-router 中一切皆组件
* 跳转标签 Link to url 地址
* NavLink 跳转标签 会有一个选择css类名
* Route 渲染容器 切换的组件渲染显示的地方 路由映射关系
* Router BrowserRouter 历史路由
* HashRouter hash路由
*/
import { Link, Route ,NavLink} from "react-router-dom";
import pageA from "./pages/pageA";
import pageB from "./pages/pageB";
export default class App extends Component {
render() {
return (
<div>
<ul>
<li>
<Link to="/a">去A页面</Link>
</li>
<li>
<NavLink to="/b">去B页面</NavLink>
</li>
</ul>
<hr />
<Route path="/a" component={pageA}></Route>
<Route path="/b" component={pageB}></Route>
</div>
);
}
}