React前端基础实例教程

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

10.完结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

问渠科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值