文章目录
React前端基础实例教程
1.建立项目
环境搭建
安装node.js(详细安装过程及配置,百度一下)
开始建立项目
# create-react-app建立项目
npx create-react-app react-demo
# 运行项目
yarn start
# react路由
yarn add react-router-dom
antd
# antd
yarn add antd
# 引入样式 index.js
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
antd按需加载
# antd 的 JS 代码默认支持基于 ES modules 的 tree shaking。
2.整理项目目录结构
src
|--assest # 资源目录
|--fonts # 自定义图标
|--components # 组件目录
|--AuthRoute.js # 权限组件
|--Child.js # 子组件
|--pages # 页面目录
|--About.js
|--Admin.js
|--Home.js
|--Login.js
|--routes # 路由目录
|--adminRoutes.js # Admin路由
|--publicRoutes.js # 公共路由
|--servics # 服务目录
|--admin.js # 后台服务
|--store # store目录
|--context.js # context
|--reducer.js # reducer
|--store.js # store
|--utils # utils目录
|--request.js # axios封装
App.css
App.js
index.js
插件搜索es7安装(非必须,可不安装插件)
ES7 React/Redux/GraphQL/React-Native snippets
3.权限路由配置
components/AuthRoute.js
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
function AuthRoute(props) {
const { role: routeRole, backUrl, ...otherProps } = props
const userRole = localStorage.getItem('role')
// 如果用户有权限,就渲染对应的路由
if (userRole && userRole.indexOf(routeRole) > -1) {
return <Route {...otherProps} />
} else {
// 如果没有权限,返回配置的默认路由
return <Redirect to={backUrl} />
}
}
export default AuthRoute
routes/publicRoutes.js
import Login from '../pages/Login'
import Home from '../pages/Home'
import About from '../pages/About'
const publicRoutes = [
{
path: '/login',
component: Login,
exact: true,
},
{
path: '/',
component: Home,
exact: true,
},
{
path: '/about',
component: About,
exact: true,
},
]
export default publicRoutes
routes/adminRoutes.js
import Admin from '../pages/Admin'
const adminRoutes = [
{
path: '/admin',
component: Admin,
exact: true,
role: 'admin',
backUrl: '/login',
},
]
export default adminRoutes
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
)
App.js
import React from 'react'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import publicRoutes from './routes/publicRoutes'
import adminRoutes from './routes/adminRoutes'
import AuthRoute from './components/AuthRoute'
function App() {
return (
<Router>
<Switch>
{publicRoutes.map(({ path, component, ...route }) => (
<Route
key={path}
path={path}
{...route}
render={(routeProps) => {
const Component = component
return <Component {...routeProps} />
}}
/>
))}
{adminRoutes.map((route) => (
<AuthRoute key={route.path} {...route} />
))}
</Switch>
</Router>
)
}
export default App
4.pages页面
关于我们页面About.js
import React from 'react'
import { Link } from 'react-router-dom'
function About() {
return (
<>
<h1>关于我们页面</h1>
<Link to="/">回首页</Link>
</>
)
}
export default About
管理员页面Admin.js
import React from 'react'
import { Link } from 'react-router-dom'
function Admin() {
return (
<>
<h1>管理员页面</h1>
<Link to="/">回首页</Link>
</>
)
}
export default Admin
Home页面Home.js
import React from 'react'
import { Link } from 'react-router-dom'
function Home() {
return (
<>
<h1>首页</h1>
<ul>
<li>
<Link to="/login">登录</Link>
</li>
<li>
<Link to="/admin">管理员</Link>
</li>
</ul>
</>
)
}
export default Home
登录页面Login.js
import React from 'react'
import { Link } from 'react-router-dom'
function Login(props) {
const { history } = props
const adminLoginHandler = () => {
localStorage.setItem('role', 'admin')
history.replace('/admin') // 登录后跳转管理员页面
}
return (
<>
<h1>登录页</h1>
<button onClick={adminLoginHandler}>管理员登录</button>
<br />
<br />
<Link to="/">回首页</Link>
</>
)
}
export default Login
5.实现全局状态管理
userReducer,useContext,useEffect 三者配合
建立context文件
/src/store/context.js
import { createContext } from 'react'
const Context = createContext(null)
export default Context
建立reducer文件
/src/store/reducer.js
const reducer = (state, action) => {
switch (action.type) {
case 'SET_ADMIN':
return { ...state, admin: action.admin }
default:
return state
}
}
export default reducer
建立store文件
/src/store/store.js
import React, { createContext, useContext, useReducer, useEffect } from 'react'
import reducer from './reducer'
const StoreContext = createContext()
export const StoreProvider = ({ children }) => {
const initialState = {admin: {}}
const [state, dispatch] = useReducer(reducer, initialState)
return (
<StoreContext.Provider value={{ state, dispatch }}>
{children}
</StoreContext.Provider>
)
}
export const useStore = () => useContext(StoreContext)
修改index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { StoreProvider } from './store/store'
ReactDOM.render(
<StoreProvider>
<React.StrictMode>
<App />
</React.StrictMode>
</StoreProvider>,
document.getElementById('root'),
)
页面刷新admin数据就丢失了,但我们又需要admin数据,所有就要将admin数据持久化
修改store.js
import React, { useContext, useReducer, useEffect } from 'react'
import reducer from './reducer'
import Context from './context'
export const StoreProvider = ({ children }) => {
let stateStorage = JSON.parse(localStorage.getItem('state'))
if (stateStorage === null) {
localStorage.setItem('state', JSON.stringify({}))
stateStorage = JSON.parse(localStorage.getItem('state'))
} else {
localStorage.setItem('state', JSON.stringify(stateStorage))
}
// eslint-disable-next-line react-hooks/exhaustive-deps
const initialState = {
admin: stateStorage.admin === undefined ? {} : stateStorage.admin,
}
const [state, dispatch] = useReducer(reducer, initialState)
useEffect(() => {
if (JSON.stringify(state.admin) !== '{}') {
initialState.admin = state.admin
}
let stateStorage = JSON.parse(localStorage.getItem('state'))
stateStorage.admin = initialState.admin
localStorage.setItem('state', JSON.stringify(stateStorage))
}, [initialState, state])
return (
<Context.Provider value={{ state, dispatch }}>{children}</Context.Provider>
)
}
export const useStore = () => useContext(Context)
修改login.js
import React from 'react'
import { useStore } from '../store/store'
import { loginApi } from '../services/admin'
import { setToken } from '../utils/auth'
import { Row, Col, Form, Input, Button } from 'antd'
const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 20 },
}
const tailLayout = {
wrapperCol: { offset: 4, span: 20 },
}
function Login(props) {
const { state, dispatch } = useStore()
const { history } = props
const onFinish = (values) => {
loginApi({
username: values.username,
password: values.password,
})
.then((res) => {
console.log(res)
if (res.code === 2000) {
setToken(res.data.token)
localStorage.setItem('role', 'admin')
dispatch(
{
type: 'SET_ADMIN',
admin: res.data.admin,
},
[state],
)
history.push('/admin') // 登录后跳转管理员页面
} else {
alert('账号密码错误')
}
})
.catch((err) => {
alert('账号密码错误')
})
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo)
}
return (
<Row>
<Col span={9}></Col>
<Col span={6}>
<Form
{...layout}
name="basic"
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
style={{ marginTop: '100px' }}
>
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit" style={{ width: '100%' }}>
登录
</Button>
</Form.Item>
</Form>
</Col>
<Col span={9}></Col>
</Row>
)
}
export default Login
6.封装axios
# 安装axios
yarn add axios
项目目录 src/utils/auth.js
/**
* 获取token
* @returns
*/
export function getToken() {
return localStorage.getItem('token')
}
/**
* 持久化token
* @param {*} token
*/
export function setToken(token) {
localStorage.setItem('token', token)
}
/**
* 清空token
*/
export function clearToken() {
localStorage.removeItem('token')
}
项目目录 src/utils/request.js
import axios from 'axios'
import { getToken } from './auth'
// 服务地址
var baseUrl = 'http://114.215.80.22:9527'
export function getBaseUrl() {
return baseUrl
}
const instance = axios.create({
baseURL: baseUrl,
timeout: 5000,
})
// 添加请求拦截器
instance.interceptors.request.use(
function (config) {
// 在发送请求之前做些什么
config.headers['authorization'] = 'Bearer ' + getToken()
//config.headers.Authorization = getToken()
return config
},
function (error) {
// 对请求错误做些什么
return Promise.reject(error)
},
)
// 添加响应拦截器
instance.interceptors.response.use(
function (response) {
// 对响应数据做点什么
return response.data
},
function (error) {
// 对响应错误做点什么
return Promise.reject(error)
},
)
export function get(url, params) {
return instance.get(url, {
params,
})
}
export function post(url, data) {
return instance.post(url, data)
}
7.后台接口
/src/services/admin.js
import { get, post, getBaseUrl } from '../utils/request'
/**
* 获取服务地址
*/
export function getServerUrl() {
return getBaseUrl()
}
/**
* 管理员登录
* @param {*} user
*/
export function loginApi(user) {
return post('/api/admin/login', user)
}
8.传值
全局状态传值(第5节)
Link传值
# 传值
<Link to={{ pathname: '/about', state: { name: 'chinaLink' } }}>
# 接收
{props.location.state.name}
history.push 传值
# 传值
history.push({ pathname: '/about', state: { name: 'chinaHistory' } })
# 接收
{props.location.state.name}
useContext进行父子组件传值
父传子
# 父组件
<Context.Provider value={count}>
<Child />
</Context.Provider>
# 子组件
let count = useContext(Context)
子传父
# 父组件
<Context.Provider value={count}>
<Child changeValue={changeValue} />
</Context.Provider>
# 子组件
const { changeValue } = props
修改Home.js
import React, { useState } from 'react'
import { Link } from 'react-router-dom'
import { useStore } from '../store/store'
import { Button, Space } from 'antd'
import Context from '../store/context'
import Child from '../components/Child'
function Home(props) {
const [count, setCount] = useState(0)
const [childValue, setChildValue] = useState(0)
const { state } = useStore()
const { history } = props
const about = () => {
history.push({ pathname: '/about', state: { name: 'chinaHistory' } })
}
const changeCount = () => {
setCount(count + 1)
}
const changeValue = (value) => {
setChildValue(value)
}
return (
<>
<h2>首页</h2>
<h2>管理员:{state.admin ? state.admin.username : null}</h2>
<Space>
<Link to="/login">登录</Link>
<Link to="/admin">管理员</Link>
<Link to={{ pathname: '/about', state: { name: 'chinaLink' } }}>
关于我们
</Link>
<Button type="primary" onClick={about}>
关于我们
</Button>
<Button type="primary" onClick={changeCount}>
更改Count
</Button>
<h2>{count}</h2>
<h2>子组件传过来的值:{childValue}</h2>
</Space>
<Context.Provider value={count}>
<Child changeValue={changeValue} />
</Context.Provider>
</>
)
}
export default Home
修改Admin.js
import React from 'react'
import { Link } from 'react-router-dom'
import { useStore } from '../store/store'
import { clearToken } from '../utils/auth'
import { Button, Space } from 'antd'
function Admin(props) {
const { state, dispatch } = useStore()
const changeStateHandler = () => {
dispatch(
{
type: 'SET_ADMIN',
admin: { username: '555' },
},
[state],
)
}
const logout = () => {
clearToken()
localStorage.removeItem('role')
dispatch(
{
type: 'SET_ADMIN',
admin: null,
},
[state],
)
props.history.push('/login')
}
return (
<>
<h2>管理员页面</h2>
<h2>管理员:{state.admin.username}</h2>
<Space>
<Link to="/">回首页</Link>
<Button type="primary" onClick={changeStateHandler}>
更改state
</Button>
<Button type="danger" onClick={logout}>
退出登录
</Button>
</Space>
</>
)
}
export default Admin
修改About.js
import React from 'react'
const About = (props) => {
console.log(JSON.stringify(props))
return (
<div>
<h2>传值:{props.location.state.name}</h2>
</div>
)
}
export default About
修改Login.js
import React from 'react'
import { useStore } from '../store/store'
import { loginApi } from '../services/admin'
import { setToken } from '../utils/auth'
import { Row, Col, Form, Input, Button } from 'antd'
const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 20 },
}
const tailLayout = {
wrapperCol: { offset: 4, span: 20 },
}
function Login(props) {
const { state, dispatch } = useStore()
const { history } = props
const onFinish = (values) => {
loginApi({
username: values.username,
password: values.password,
})
.then((res) => {
console.log(res)
if (res.code === 2000) {
setToken(res.data.token)
localStorage.setItem('role', 'admin')
dispatch(
{
type: 'SET_ADMIN',
admin: res.data.admin,
},
[state],
)
history.push('/admin') // 登录后跳转管理员页面
} else {
alert('账号密码错误')
}
})
.catch((err) => {
alert('账号密码错误')
})
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo)
}
return (
<Row>
<Col span={9}></Col>
<Col span={6}>
<Form
{...layout}
name="basic"
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
style={{ marginTop: '100px' }}
>
<Form.Item
label="账号"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit" style={{ width: '100%' }}>
登录
</Button>
</Form.Item>
</Form>
</Col>
<Col span={9}></Col>
</Row>
)
}
export default Login
/src/components/Child.js
import React, { useContext } from 'react'
import Context from '../store/context'
import { Button } from 'antd'
const Child = (props) => {
let count = useContext(Context)
const { changeValue } = props
const changeFatherValue = () => {
//将999传递给父组件Home
changeValue(999)
}
return (
<div>
<h2>子组件</h2>
<h2>父组件传过来的值:{count}</h2>
<Button type="primary" onClick={changeFatherValue}>
更改父组件的值
</Button>
</div>
)
}
export default Child
8.整合iconfont阿里图标库
1.建立/src/assest/fonts文件夹
2.将iconfont生成的项目图标库下载
3.将下载的图标库解压后内容复制到/src/assest/fonts文件夹
4.项目中引用
import '../../../assest/fonts/iconfont.css'
5.使用iconfont图标
<i className="iconfont iconshangpin" style={{ fontSize: '18px' }} />
9.打包部署
# 项目打包
yarn build
ubuntu 部署
nginx安装配置
# nginx下载
wget http://nginx.org/download/nginx-1.19.6.tar.gz
# nginx解压
tar zxvf nginx-1.19.6.tar.gz
nginx.conf配置
{
......
#部署react-demo
server {
# 监听端口为8008
listen 8008;
# 定义网站域名,可以写多个,用空格分隔。
server_name localhost;
#在server{}里有很多location配置段
location / {
#定义网站根目录,目录可以是相对路径也可以是绝对路径。
root /var/www/react-demo;
#定义站点的默认页。
index index.html index.htm;
}
}
......
}
将打包后的文件(项目目录下build文件夹里的所有文件)拷贝到/var/www/react-demo/里
启动nignx
# 进入nginx目录
cd /usr/local/nginx/sbin
# 启动nginx
nginx
# 重新载入配置文件
nginx -s reload
# 重启 Nginx
nginx -s reopen
# 停止 Nginx
nginx -s stop
访问 http://localhost:8008