我是靠谱客的博主 成就海燕,这篇文章主要介绍React复习(12),现在分享给大家,希望可以做个参考。

(1)SPA应用

复制代码
1
2
3
4
5
6
7
8
9
10
11
单页面web应用(Single Page Web Application) 是一种网站模型,可以动态重写当前页面,而不需要重新加载整个页面 相对于传统的Web应用它能做到前后端分离,后端只负责处理数据提供接口 页面逻辑和渲染都交给前端 SPA的一个重要实现就是改变路由时页面不刷新 实现这个功能通常有两种方式:windows.history对象或者location.hash 路由分类 (1)后端路由:node服务器端路由,用来处理客户端提交的请求并返回一个响应数据 (2)前端路由:浏览器端路由,当请求的是路由path时,浏览器端前没有发送http请求 但界面会更新显示对应的组件

(2)Hash模式

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//原理:利用改变#后面的值不触发网页重载,在客户端实现路由切换 import React from "react"; const Home = () => <div>Home</div> const About = () => <div>About</div> const Inbox = () => <div>Inbox</div> class App extends React.Component { state = { path: window.location.hash.substr(1) } render() { let Content switch (this.state.path) { case '/home': Content = Home break; case '/about': Content = About break; case '/inbox': Content = Inbox break; default: break; } return ( <div> <div>Header</div> <div> <ul> <li><a href="#/home">Home</a></li> <li><a href="#/about">About</a></li> <li><a href="#/inbox">Inbox</a></li> </ul> </div> <div> {/*根据变量内容与组件名相匹配来实时刷新*/} <Content/> </div> </div> ) } componentDidMount() { //监听hash值,发生变化则执行回调函数 window.addEventListener('hashchange', () => { const path = window.location.hash.substr(1) this.setState({ path }) }) } } export default App;

(3)History模式

复制代码
1
2
3
4
5
调用pushState可以在当前历史记录里添加一条url记录,但页面不会跳转 新增后若页面再发生一次跳转,那么再发生后腿时会跳转到新增url记录上 当前url状态对象可以通过history.state来访问 若要在前进或后退时访问历史记录对象则可以监听window.onpopstate事件 replaceState是替换历史记录中的最新的一条
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import React from 'react'; const Home = () => <div>Home Component</div>; const Page1 = () => <div>Page1 Component</div>; const Page2 = () => <div>Page2 Component</div>; const Page3 = () => <div>Page3 Component</div>; const Page4 = () => <div>Page4 Component</div>; const route = { '/': Home, '/page1': Page1, '/page2': Page2, '/page3': Page3, '/page4': Page4, } class App extends React.Component { constructor(props) { super(props) this.state = { //pathname就是请求地址,例如 /page1 path: window.location.pathname } this.initPushstate() } //定义方法,在每一次压栈时触发 initPushstate = () => { //保存原生的pushState let oldPushState = window.history.pushState; //重写pushState,添加回调功能 window.history.pushState = function (state, title, pathname) { // 执行原生pushState let result = oldPushState.apply(window.history, arguments); // window添加回调onpushstate if (typeof window.onpushstate === 'function') { window.onpushstate({ state, title, pathname, type: 'pushstate' }); } return result; } } push1 = () => { window.history.pushState({ page: 'page1' }, { title: 'page1' }, '/page1') console.log(window.history.length); } push2 = () => { window.history.pushState({ page: 'page2' }, { title: 'page2' }, '/page2') console.log(window.history.length); } push3 = () => { window.history.pushState({ page: 'page3' }, { title: 'page3' }, '/page3') console.log(window.history.length); } //把最新的历史记录第一条替换,完成重定向,不会触发onpushstate //例如现在在2,点击3,再点击replace,然后点击回退,这时会跳转到2 //因为把最新的历史记录3替换掉了 replace = () => { window.history.replaceState({ page: 'page4' }, { title: 'page4' }, '/page4') console.log(window.history.length); } forward = () => { //加载历史列表中的下一个 URL ,方法的效果等价于点击前进按钮或调用 history.go(1) window.history.forward() console.log(window.history.length); } back = () => { window.history.back() console.log(window.history.length); } go = () => { // window.history.go(1) window.history.go(-1) console.log(window.history.length) } render() { let path = this.state.path let Outlet = route[path] return ( <div> <h2>pathname:{this.state.route}</h2> <button onClick={this.push1}>pushstate1</button> <button onClick={this.push2}>pushstate2</button> <button onClick={this.push3}>pushstate3</button> {/*重定向*/} <button onClick={this.replace}>replace</button> <button onClick={this.forward}>forward</button> <button onClick={this.back}>back</button> <button onClick={this.go}>go</button> <Outlet /> </div> ) } componentDidMount() { console.log(window.location); console.log(window.history); //在window上加一个onpushstate函数 //模拟onpopstate相同的功能,在每一次压栈操作后触发回调函数 //利用setState把数据同步回去 window.onpushstate = (event) => { console.log('onpushstate'); this.setState({ //同步路径 path: window.location.pathname }) } //前进或后退是触发 window.onpopstate = (e) => { console.log('onpopstate'); this.setState({ //回退或者前进时能及时切换页面 path: window.location.pathname }) } } } export default App

 (4)react-router

复制代码
1
利用react-router实现的案例

 

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
先用BrowserRouter把app组件包裹上 link创建链接,route使链接和组件间建立关联,OutLet是路由出口 获取路由详情 在invoices的Link标签中的to方法请求链接,index.js里的Route标签生效,渲染出Invoices组件 渲染位置就在App.js的Outlet中 //index.js配置路由 import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { BrowserRouter, Route, Routes } from 'react-router-dom' import Expenses from './components/expenses' import Invoices from './components/invoices' import NoMatch from './components/noMatch' import Invoice from './components/invoice' ReactDOM.render( <BrowserRouter> <Routes> {/* 路由嵌套,在APP组件中渲染两个子组件的出口*/} <Route path='/' element={<App></App>}> <Route path='expenses' element={<Expenses></Expenses>}></Route> <Route path='invoices' element={<Invoices></Invoices>}> {/* 没有ID时展示默认信息 */} <Route index element={ <main style={{ padding: '5rem' }}> <p>请选择发票选项</p> </main> } /> {/* 根据ID显示单个订单信息 */} {/*invoiceId是占位符*/} <Route path=":invoiceId" element={<Invoice />} /> </Route> {/* 当上面所有的路由不匹配时*会拦截到 */} <Route path='*' element={<NoMatch></NoMatch>}></Route> </Route> </Routes> </BrowserRouter> , document.getElementById('root')); //App.js import {Link, Outlet} from 'react-router-dom' function App(){ return( <div> <h1>账本</h1> <nav style={{borderBottom:'solid 1px',paddingBottom:'1rem'}}> <Link to='/invoices'>发票</Link> {' || '} <Link to='/expenses'>花销</Link> </nav> {/* 路由出口 */} <Outlet></Outlet> </div> ) } export default App //data.js模拟数据 let invoices = [ { number: 1, name: 'phone1', amount: '&80', date: '06/07/2001' }, { number: 2, name: 'phone2', amount: '&90', date: '06/09/2001' }, { number: 3, name: 'iphone3', amount: '&70', date: '06/11/2001' }, { number: 4, name: 'iphone4', amount: '&70', date: '06/11/2001' }, { number: 5, name: 'apple5', amount: '&70', date: '06/11/2001' }, { number: 6, name: 'apple6', amount: '&70', date: '06/11/2001' }, ] export function getInvoices(){ return invoices } export function getInvoice(number) { return invoices.find( invoice => invoice.number === number ); } //expenses.js export default function Expenses(){ return( <main style={{padding:'1rem 0'}}> <h2>花销</h2> </main> ) } //invoice.js import { useParams } from "react-router-dom"; import { getInvoice } from "../data"; export default function Invoice() { //params就是一个对象,对象的键名就是index.js中path后的字符串,也就是invoiceId //键值就是invoices.js中to标签${}里对应的值,点击时能获取到 let params = useParams(); let invoice = getInvoice(parseInt(params.invoiceId, 10)); return ( <main style={{ padding: "1rem" }}> <h2>总额: {invoice.amount}</h2> <p> {invoice.name}: {invoice.number} </p> <p>日期: {invoice.date}</p> </main> ); } //invoices.js import { getInvoices } from "../data" import { Outlet, useSearchParams } from "react-router-dom" import { NavLink } from "react-router-dom" export default function Invoices() { let invoices = getInvoices() // useSearchParams返回值是一个数组,从中结构出这两个方法 let [searchParams, setSearchParams] = useSearchParams() return ( <div style={{ display: 'flex' }}> <nav style={{ borderRight: 'solid 1px', padding: '1rem' }} > {/* 搜索 */} <input //searchParams是搜索参数,从地址栏拿到值,设置为输入框默认值 //刷新页面时保证搜索框值还在 value={searchParams.get('filter') || ""} onChange={ (e) => { let value = e.target.value if (value) { //url设置搜索参数 setSearchParams({ filter: value }) } else { setSearchParams({}) } } } /> {/* 发票列表 */} { invoices .filter(invoice => { //过滤参数 let filter = searchParams.get('filter') //若没有filter则不过滤了,数据全都返回 if (!filter) return true //全转成大写 let name = invoice.name.toLocaleLowerCase() //startsWith 以...开头,返回布尔值 //若为true则留在invoices数组中 return name.startsWith(filter.toLocaleLowerCase()) }) .map(invoice => ( // NavLink是导航链接,能动态改变样式 // 点击NavLink是给style传参数 // style中的函数接收一个对象 // 其中包含isActive属性,是布尔类型,代表是否点击标签 // style返回值是对象 // 动态样式就是利用返回对象实现的 // <NavLink style={() => ({ // })} // > // </NavLink> // 从对象里结构出来isActive <NavLink style={({ isActive }) => ({ display: 'block', margin: '1rem 0', color: isActive ? "red" : "" })} //动态地址 to={`/invoices/${invoice.number}`} key={invoice.number} > {invoice.name} </NavLink> )) } </nav> <Outlet></Outlet> </div> ) } //noMatch.jsx function NoMatch(){ return( <main style={{padding:'1rem'}}> <p>404</p> </main> ) } export default NoMatch

(5)自定义路由组件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
改进前查询出新的列表后点击选项又会跳回该页面 这是因为点击选项时路由地址有变回去了 选项的路由地址没有拼接上搜索条件 所以我们给原有的<NavLink/>加强,重新定义一个组件 QueryNavLink.js import { NavLink, useLocation } from "react-router-dom" //把to解构出来,其他属性压回到props里 export function QueryLNavLink({to,...props}){ //useLocation返回的是url信息 let location = useLocation() console.log(location); //只改变to,其他属性正常传入 return <NavLink to={to+location.search} {...props}></NavLink> } 再把invoices.js的NavLink标签换成QueryNavLink就行了

最后

以上就是成就海燕最近收集整理的关于React复习(12)的全部内容,更多相关React复习(12)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部