项目准备
1. 项目介绍
本节目标:
了解项目的定位和功能
项目功能演示
登录、退出
首页
内容(文章)管理:文章列表、发布文章、修改文章
技术
React 官方脚手架 create-react-app
react hooks
状态管理:mobx
UI 组件库:antd
v4
ajax请求库:axios
路由:react-router-dom
以及 history
富文本编辑器:react-quill
CSS 预编译器:sass
2. 项目搭建
本节目标:
能够基于脚手架搭建项目基本结构
实现步骤
使用create-react-app生成项目 npx create-react-app geek-pc
进入根目录 cd geek-pc
启动项目 yarn start
调整项目目录结构
/src
/assets 项目资源文件,比如,图片 等
/components 通用组件
/pages 页面
/store mobx 状态仓库
/utils 工具,比如,token、axios 的封装等
App.js 根组件
index.css 全局样式
index.js 项目入口
保留核心代码
src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(
,
document.getElementById('root')
)
src/App.js
export default function App() {
return 根组件
}
3. 使用gitee管理项目
本节目标:
能够将项目推送到gitee远程仓库
实现步骤
在项目根目录打开终端,并初始化 git 仓库(如果已经有了 git 仓库,无需重复该步),命令:git init
添加项目内容到暂存区:git add .
提交项目内容到仓库区:git commit -m '项目初始化'
添加 remote 仓库地址:git remote add origin [gitee 仓库地址]
将项目内容推送到 gitee:git push origin master -u
4. 使用scss预处理器
本节目标:
能够在CRA中使用sass书写样式
SASS
是一种预编译的 CSS,作用类似于 Less。由于 React 中内置了处理 SASS 的配置,所以,在 CRA 创建的项目中,可以直接使用 SASS 来写样式
实现步骤
安装解析 sass 的包:yarn add sass -D
创建全局样式文件:index.scss
body {
margin: 0;
}
#root {
height: 100%;
}
5. 配置基础路由
本节目标:
能够配置登录页面的路由并显示到页面中
实现步骤
安装路由:yarn add react-router-dom
在 pages 目录中创建两个文件夹:Login、Layout
分别在两个目录中创建 index.js 文件,并创建一个简单的组件后导出
在 App 组件中,导入路由组件以及两个页面组件
配置 Login 和 Layout 的路由规则
代码实现
pages/Login/index.js
const Login = () => {
return login
}
export default Login
pages/Layout/index.js
const Layout = () => {
return layout
}
export default Layout
app.js
// 导入路由
import { BrowserRouter, Route, Routes } from 'react-router-dom'
// 导入页面组件
import Login from './pages/Login'
import Layout from './pages/Layout'
// 配置路由规则
function App() {
return (
}/>
}/>
)
}
export default App
6. 组件库antd使用
本节目标:
能够使用antd的Button组件渲染按钮
实现步骤
安装 antd 组件库:yarn add antd
全局导入 antd 组件库的样式
导入 Button 组件
在 Login 页面渲染 Button 组件进行测试
代码实现
src/index.js
import 'antd/dist/antd.min.css'
import './index.css'
pages/Login/index.js
import { Button } from 'antd'
const Login = () => (
Button
)
易错总结
在哪个文件中导入 antd 的样式文件?
antd 的样式文件和我们自己的全局样式文件的导入顺序?
7. 配置别名路径
本节目标:
能够配置@路径简化路径处理
自定义 CRA 的默认配置 craco 配置文档
CRA 将所有工程化配置,都隐藏在了 react-scripts
包中,所以项目中看不到任何配置信息
如果要修改 CRA 的默认配置,有以下几种方案:
通过第三方库来修改,比如,@craco/craco
(推荐)
通过执行 yarn eject
命令,释放 react-scripts
中的所有配置到项目中
实现步骤
安装修改 CRA 配置的包:yarn add -D @craco/craco
在项目根目录中创建 craco 的配置文件:craco.config.js
,并在配置文件中配置路径别名
修改 package.json
中的脚本命令
在代码中,就可以通过 @
来表示 src 目录的绝对路径
重启项目,让配置生效
代码实现
craco.config.js
const path = require ( 'path' )
module. exports = {
webpack : {
alias : {
'@' : path. resolve ( __dirname, 'src' )
}
}
}
package.json
"scripts" : {
"start" : "craco start" ,
"build" : "craco build" ,
"test" : "craco test" ,
"eject" : "react-scripts eject"
}
8. @别名路径提示
本节目标:
能够让vscode识别@路径并给出路径提示
实现步骤
在项目根目录创建 jsconfig.json
配置文件
在配置文件中添加以下配置
代码实现
{
"compilerOptions" : {
"baseUrl" : "./" ,
"paths" : {
"@/*" : [ "src/*" ]
}
}
}
vscode会自动读取jsconfig.json
中的配置,让vscode知道@就是src目录
9. 安装dev-tools调试工具
https://gitee.com/react-cp/react-pc-doc 这里找到dev-tools.crx文件
登录模块
1. 基本结构搭建
本节目标:
能够使用antd搭建基础布局
实现步骤
在 Login/index.js 中创建登录页面基本结构
在 Login 目录中创建 index.scss 文件,指定组件样式
将 logo.png 和 login.png 拷贝到 assets 目录中
代码实现
pages/Login/index.js
import { Card } from 'antd'
import logo from '@/assets/logo.png'
import './index.scss'
const Login = () => {
return (
{/* 登录表单 */}
)
}
export default Login
pages/Login/index.scss
.login {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
background: center/cover url('~@/assets/login.png');
.login-logo {
width: 200px;
height: 60px;
display: block;
margin: 0 auto 20px;
}
.login-container {
width: 440px;
height: 360px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 50px rgb(0 0 0 / 10%);
}
.login-checkbox-label {
color: #1890ff;
}
}
2. 创建表单结构
本节目标:
能够使用antd的Form组件创建登录表单
实现步骤
打开 antd Form 组件文档
找到代码演示的第一个示例(基本使用),点击<>
(显示代码),并拷贝代码到组件中
分析 Form 组件基本结构
调整 Form 组件结构和样式
代码实现
pages/Login/index.js
import { Form, Input, Button, Checkbox } from 'antd'
const Login = () => {
return (
我已阅读并同意「用户协议」和「隐私条款」
登录
)
}
3. 表单校验实现
本节目标:
能够为手机号和密码添加表单校验
实现步骤
为 Form 组件添加 validateTrigger
属性,指定校验触发时机的集合
为 Form.Item 组件添加 name 属性,这样表单校验才会生效
为 Form.Item 组件添加 rules
属性,用来添加表单校验
代码实现
page/Login/index.js
const Login = () => {
return (
我已阅读并同意「用户协议」和「隐私条款」
登录
)
}
4. 获取登录表单数据
本节目标:
能够拿到登录表单中用户的手机号码和验证码
实现步骤
为 Form 组件添加 onFinish
属性,该事件会在点击登录按钮时触发
创建 onFinish 函数,通过函数参数 values 拿到表单值
Form 组件添加 initialValues
属性,来初始化表单值
代码实现
pages/Login/index.js
// 点击登录按钮时触发 参数values即是表单输入数据
const onFinish = values => {
console.log(values)
}
5. 封装http工具模块
本节目标:
封装axios,简化操作
实现步骤
创建 utils/http.js 文件
创建 axios 实例,配置 baseURL,请求拦截器,响应拦截器
在 utils/index.js 中,统一导出 http
代码实现
utils/http.js
import axios from 'axios'
const http = axios. create ( {
baseURL : 'http://geek.itheima.net/v1_0' ,
timeout : 5000
} )
http. interceptors. request. use ( ( config ) => {
return config
} , ( error ) => {
return Promise. reject ( error)
} )
http. interceptors. response. use ( ( response ) => {
return response
} , ( error ) => {
return Promise. reject ( error)
} )
export { http }
utils/index.js
import { http } from './http'
export { http }
6. 配置登录Mobx
本节目标:
基于mobx封装管理用户登录的store
store/login.Store.js
import { makeAutoObservable } from "mobx"
import { http } from '@/utils'
class LoginStore {
token = ''
constructor ( ) {
makeAutoObservable ( this )
}
login = async ( { mobile, code } ) => {
const res = await http. post ( 'http://geek.itheima.net/v1_0/authorizations' , {
mobile,
code
} )
this . token = res. data. token
}
}
export default LoginStore
store/index.js
import React from "react"
import LoginStore from './login.Store'
class RootStore {
constructor ( ) {
this . loginStore = new LoginStore ( )
}
}
const StoresContext = React. createContext ( new RootStore ( ) )
export const useStore = ( ) => React. useContext ( StoresContext)
7. 实现登录逻辑
本节目标:
在表单校验通过之后通过封装好的store调用登录接口
实现步骤
使用useStore方法得到loginStore实例对象
在校验通过之后,调用loginStore中的login函数
登录成功之后跳转到首页
代码实现
import { useStore } from '@/store'
const Login = () => {
// 获取跳转实例对象
const navigate = useNavigate()
const { loginStore } = useStore()
const onFinish = async values => {
const { mobile, code } = values
try {
await loginStore.login({ mobile, code })
navigate('/')
} catch (e) {
message.error(e.response?.data?.message || '登录失败')
}
}
return (...)
}
8. token持久化
8.1 封装工具函数
本节目标:
能够统一处理 token 的持久化相关操作
实现步骤
创建 utils/token.js 文件
分别提供 getToken/setToken/clearToken/isAuth 四个工具函数并导出
创建 utils/index.js 文件,统一导出 token.js 中的所有内容,来简化工具函数的导入
将登录操作中用到 token 的地方,替换为该工具函数
代码实现
utils/token.js
const TOKEN_KEY = 'itcast_geek_pc'
const getToken = ( ) => localStorage. getItem ( TOKEN_KEY )
const setToken = token => localStorage. setItem ( TOKEN_KEY , token)
const clearToken = ( ) => localStorage. removeItem ( TOKEN_KEY )
export { getToken, setToken, clearToken }
8.2 持久化设置
本节目标:
使用token函数持久化配置
实现步骤
拿到token的时候一式两份,存本地一份
初始化的时候优先从本地取,取不到再初始化为控制
代码实现
store/login.Store.js
import { makeAutoObservable } from "mobx"
import { setToken, getToken, clearToken, http } from '@/utils'
class LoginStore {
token = getToken ( ) || ''
constructor ( ) {
makeAutoObservable ( this )
}
login = async ( { mobile, code } ) => {
const res = await http. post ( 'http://geek.itheima.net/v1_0/authorizations' , {
mobile,
code
} )
this . token = res. data. token
setToken ( res. data. token)
}
}
export default LoginStore
9. 请求拦截器注入token
本节目标:
把token通过请求拦截器注入到请求头中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HYd0RfEu-1657264065747)(assets/token.png)]
拼接方式:config.headers.Authorization = Bearer ${token}}
utils/http.js
http. interceptors. request. use ( config => {
const token = getToken ( )
if ( token) {
config. headers. Authorization = ` Bearer ${ token} `
}
return config
} )
10. 路由鉴权实现
本节目标:
能够实现未登录时访问拦截并跳转到登录页面
实现思路
自己封装 AuthRoute
路由鉴权高阶组件,实现未登录拦截,并跳转到登录页面
思路为:判断本地是否有token,如果有,就返回子组件,否则就重定向到登录Login
实现步骤
在 components 目录中,创建 AuthRoute/index.js 文件
判断是否登录
登录时,直接渲染相应页面组件
未登录时,重定向到登录页面
将需要鉴权的页面路由配置,替换为 AuthRoute 组件渲染
代码实现
components/AuthRoute/index.js
// 1. 判断token是否存在
// 2. 如果存在 直接正常渲染
// 3. 如果不存在 重定向到登录路由
// 高阶组件:把一个组件当成另外一个组件的参数传入 然后通过一定的判断 返回新的组件
import { getToken } from '@/utils'
import { Navigate } from 'react-router-dom'
function AuthRoute ({ children }) {
const isToken = getToken()
if (isToken) {
return <>{children}
} else {
return
}
}
//
// 登录:<>
// 非登录:
export {
AuthRoute
}
src/app.js
import { Router, Route } from 'react-router-dom'
import { AuthRoute } from '@/components/AuthRoute'
import Layout from '@/pages/Layout'
import Login from '@/pages/Login'
function App() {
return (
{/* 需要鉴权的路由 */}
} />
{/* 不需要鉴权的路由 */}
} />
)
}
export default App
Layout模块
1. 基本结构搭建
本节目标:
能够使用antd搭建基础布局
实现步骤
打开 antd/Layout 布局组件文档,找到示例:顶部-侧边布局-通栏
拷贝示例代码到我们的 Layout 页面中
分析并调整页面布局
代码实现
pages/Layout/index.js
import { Layout, Menu, Popconfirm } from 'antd'
import {
HomeOutlined,
DiffOutlined,
EditOutlined,
LogoutOutlined
} from '@ant-design/icons'
import './index.scss'
const { Header, Sider } = Layout
const GeekLayout = () => {
return (
} key="1">
数据概览
} key="2">
内容管理
} key="3">
发布文章
内容
)
}
export default GeekLayout
pages/Layout/index.scss
.ant-layout {
height: 100%;
}
.header {
padding: 0;
}
.logo {
width: 200px;
height: 60px;
background: url('~@/assets/logo.png') no-repeat center / 160px auto;
}
.layout-content {
overflow-y: auto;
}
.user-info {
position: absolute;
right: 0;
top: 0;
padding-right: 20px;
color: #fff;
.user-name {
margin-right: 20px;
}
.user-logout {
display: inline-block;
cursor: pointer;
}
}
.ant-layout-header {
padding: 0 !important;
}
2. 二级路由配置
本节目标:
能够在右侧内容区域展示左侧菜单对应的页面内容
使用步骤
在 pages 目录中,分别创建:Home(数据概览)/Article(内容管理)/Publish(发布文章)页面文件夹
分别在三个文件夹中创建 index.js 并创建基础组件后导出
在app.js中配置嵌套子路由,在layout.js中配置二级路由出口
使用 Link 修改左侧菜单内容,与子路由规则匹配实现路由切换
代码实现
pages/Home/index.js
const Home = () => {
return Home
}
export default Home
pages/Article/index.js
const Article = () => {
return Article
}
export default Article
pages/Publish/index.js
const Publish = () => {
return Publish
}
export default Publish
app.js
}>
{/* 二级路由默认页面 */}
} />
} />
} />
}>
pages/Layout/index.js
// 配置Link组件
} key="/">
数据概览
} key="/article">
内容管理
} key="/publish">
发布文章
// 二级路由对应显示
{/* 二级路由默认页面 */}
3. 菜单高亮显示
本节目标:
能够在页面刷新的时候保持对应菜单高亮
思路
Menu组件的selectedKeys属性与Menu.Item组件的key属性发生匹配的时候,Item组件即可高亮
页面刷新时,将当前访问页面的路由地址
作为 Menu 选中项的值(selectedKeys)即可
实现步骤
将 Menu 的 key
属性修改为与其对应的路由地址
获取到当前正在访问页面的路由地址
将当前路由地址设置为 selectedKeys
属性的值
代码实现
pages/Layout/index.js
import { useLocation } from 'react-router-dom'
const GeekLayout = () => {
const location = useLocation()
// 这里是当前浏览器上的路径地址
const selectedKey = location.pathname
return (
// ...
} key="/">
数据概览
} key="/article">
内容管理
} key="/publish">
发布文章
)
}
4. 展示个人信息
本节目标:
能够在页面右上角展示登录用户名
实现步骤
在store中新增userStore.js模块,在其中定义获取用户信息的mobx代码
在store的入口文件中组合新增的userStore模块
在Layout组件中调用action函数获取用户数据
在Layout组件中获取个人信息并展示
代码实现
store/user.Store.js
import { makeAutoObservable } from "mobx"
import { http } from '@/utils'
class UserStore {
userInfo = { }
constructor ( ) {
makeAutoObservable ( this )
}
async getUserInfo ( ) {
const res = await http. get ( '/user/profile' )
this . userInfo = res. data
}
}
export default UserStore
store/index.js
import React from "react"
import LoginStore from './login.Store'
import UserStore from './user.Store'
class RootStore {
constructor ( ) {
this . loginStore = new LoginStore ( )
this . userStore = new UserStore ( )
}
}
const StoresContext = React. createContext ( new RootStore ( ) )
export const useStore = ( ) => React. useContext ( StoresContext)
pages/Layout/index.js
import { useEffect } from 'react'
import { observer } from 'mobx-react-lite'
const GeekLayout = () => {
const { userStore } = useStore()
// 获取用户数据
useEffect(() => {
try {
userStore.getUserInfo()
} catch { }
}, [userStore])
return (
{/* 省略无关代码 */}
)
}
export default observer(GeekLayout)
5. 退出登录实现
本节目标:
能够实现退出登录功能
实现步骤
为气泡确认框添加确认回调事件
在store/login.Store.js
中新增退出登录的action函数,在其中删除token
在回调事件中,调用loginStore中的退出action
退出后,返回登录页面
代码实现
store/login.Store.js
class LoginStore {
loginOut = ( ) => {
this . token = ''
clearToken ( )
}
}
export default LoginStore
pages/Layout/index.js
// login out
const navigate = useNavigate()
const onLogout = () => {
loginStore.loginOut()
navigate('/login')
}
退出
6. 处理Token失效
本节目标:
能够在响应拦截器中处理token失效
说明:为了能够在非组件环境下拿到路由信息,需要我们安装一个history包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FM82Evvt-1657264065748)(assets/historyoutside.png)]
实现步骤
安装:yarn add history
创建 utils/history.js
文件
在app.js中使用我们新建的路由并配置history参数
通过响应拦截器处理 token 失效
代码实现
utils/history.js
import { createBrowserHistory } from 'history'
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom'
const history = createBrowserHistory ( )
export {
HistoryRouter,
history
}
app.js
import { HistoryRouter, history } from './utils/history'
function App() {
return (
...省略无关代码
)
}
export default App
utils/http.js
import { history } from './history'
http. interceptors. response. use (
response => {
return response. data
} ,
error => {
if ( error. response. status === 401 ) {
clearToken ( )
history. push ( '/login' )
}
return Promise. reject ( error)
}
)
7. 首页Home图表展示
本节目标:
实现首页echart图表封装展示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yeyIGoia-1657264065748)(assets/home.png)]
需求描述:
使用eharts配合react封装柱状图组件Bar
要求组件的标题title,横向数据xData,纵向数据yData,样式style可定制
代码实现
components/Bar/index.js
import * as echarts from 'echarts'
import { useEffect, useRef } from 'react'
function echartInit (node, xData, sData, title) {
const myChart = echarts.init(node)
// 绘制图表
myChart.setOption({
title: {
text: title
},
tooltip: {},
xAxis: {
data: xData
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: sData
}
]
})
}
function Bar ({ style, xData, sData, title }) {
// 1. 先不考虑传参问题 静态数据渲染到页面中
// 2. 把那些用户可能定制的参数 抽象props (1.定制大小 2.data 以及说明文字)
const nodeRef = useRef(null)
useEffect(() => {
echartInit(nodeRef.current, xData, sData, title)
}, [xData, sData])
return (
)
}
export default Bar
pages/Home/index.js
import Bar from "@/components/Bar"
import './index.scss'
const Home = () => {
return (
)
}
export default Home
pages/Home/index.scss
.home {
width: 100%;
height: 100%;
align-items: center;
}
内容管理
1. 筛选区结构
本节目标:
能够使用antd组件库搭建筛选区域结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4SxhpvU-1657264065748)(assets/search.png)]
重点关注
如何让RangePicker日期范围选择框选择中文
Select组件配合Form.Item使用时,如何配置默认选中项
代码实现
import { Link } from 'react-router-dom'
import { Card, Breadcrumb, Form, Button, Radio, DatePicker, Select } from 'antd'
import 'moment/locale/zh-cn'
import locale from 'antd/es/date-picker/locale/zh_CN'
import './index.scss'
const { Option } = Select
const { RangePicker } = DatePicker
const Article = () => {
return (
首页
内容管理
}
style={{ marginBottom: 20 }}
>
全部
草稿
待审核
审核通过
审核失败
Jack
Lucy
{/* 传入locale属性 控制中文显示*/}
筛选
)
}
export default Article
2. 表格区域结构
本节目标:
能够基于Table组件搭建表格结构
重点关注
通过哪个属性指定Table组件的列信息
通过哪个属性指定Table数据
通过哪个属性指定Table列表用到的key属性
代码实现
import { Link } from 'react-router-dom'
import { Table, Tag, Space } from 'antd'
import { EditOutlined, DeleteOutlined } from '@ant-design/icons'
import img404 from '@/assets/error.png'
const Article = () => {
const columns = [
{
title: '封面',
dataIndex: 'cover',
width:120,
render: cover => {
return
}
},
{
title: '标题',
dataIndex: 'title',
width: 220
},
{
title: '状态',
dataIndex: 'status',
render: data => 审核通过
},
{
title: '发布时间',
dataIndex: 'pubdate'
},
{
title: '阅读数',
dataIndex: 'read_count'
},
{
title: '评论数',
dataIndex: 'comment_count'
},
{
title: '点赞数',
dataIndex: 'like_count'
},
{
title: '操作',
render: data => {
return (
} />
}
/>
)
}
}
]
const data = [
{
id: '8218',
comment_count: 0,
cover: {
images:['http://geek.itheima.net/resources/images/15.jpg'],
},
like_count: 0,
pubdate: '2019-03-11 09:00:00',
read_count: 2,
status: 2,
title: 'wkwebview离线化加载h5资源解决方案'
}
]
return (
)
}
3. 渲染频道数据 本节目标:
使用接口数据渲染频道列表
实现步骤
使用axios获取数据
将使用频道数据列表改写下拉框组件
代码实现
pages/Article/index.js
// 获取频道列表
const [channels, setChannels] = useState([])
useEffect(() => {
async function fetchChannels() {
const res = await http.get('/channels')
setChannels(res.data.channels)
}
fetchChannels()
}, [])
// 渲染模板
return (
{channels.map(item => (
{item.name}
))}
)
4. 渲染表格数据 本节目标:
使用接口数据渲染表格数据
实现步骤
声明列表相关数据管理
声明参数相关数据管理
调用接口获取数据
使用接口数据渲染模板
代码实现
// 文章列表数据管理
const [article, setArticleList] = useState({
list: [],
count: 0
})
// 参数管理
const [params, setParams] = useState({
page: 1,
per_page: 10
})
// 发送接口请求
useEffect(() => {
async function fetchArticleList() {
const res = await http.get('/mp/articles', { params })
const { results, total_count } = res.data
setArticleList({
list: results,
count: total_count
})
}
fetchArticleList()
}, [params])
// 模板渲染
return (
)
5. 筛选功能实现 本节目标:
能够根据筛选条件筛选表格数据
实现步骤
为表单添加onFinish
属性监听表单提交事件,获取参数
根据接口字段格式要求格式化参数格式
修改params
触发接口的重新发送
代码实现
// 筛选功能
const onSearch = values => {
const { status, channel_id, date } = values
// 格式化表单数据
const _params = {}
// 格式化status
_params.status = status
if (channel_id) {
_params.channel_id = channel_id
}
if (date) {
_params.begin_pubdate = date[0].format('YYYY-MM-DD')
_params.end_pubdate = date[1].format('YYYY-MM-DD')
}
// 修改params参数 触发接口再次发起
setParams({
...params,
..._params
})
}
// Form绑定事件
return (
)
6. 分页功能实现 本节目标:
能够实现分页获取文章列表数据
实现步骤
为Table组件指定pagination属性来展示分页效果
在分页切换事件中获取到筛选表单中选中的数据
使用当前页数据修改params参数依赖引起接口重新调用获取最新数据
代码实现
const pageChange = (page) => {
// 拿到当前页参数 修改params 引起接口更新
setParams({
...params,
page
})
}
return (
)
7. 删除功能 本节目标:
能够实现点击删除按钮弹框确认
实现步骤
给删除文章按钮绑定点击事件
弹出确认窗口,询问用户是否确定删除文章
拿到参数调用删除接口,更新列表
代码实现
// 删除回调
const delArticle = async (data) => {
await http.delete(`/mp/articles/${data.id}`)
// 更新列表
setParams({
page: 1,
per_page: 10
})
}
const columns = [
// ...
{
title: '操作',
render: data => {
return (
} />
delArticle(data)}
okText="确认"
cancelText="取消"
>
}
/>
)
}
]
8. 编辑文章跳转 本节目标:
能够实现编辑文章跳转功能
代码实现
const columns = [
// ...
{
title: '操作',
render: data => (
}
onClick={() => history.push(`/home/publish?id=${data.id}`)}
/>
)
}
]
发布文章 1. 基本结构搭建 本节目标:
能够搭建发布文章页面的基本结构
实现步骤
使用Card、Form组件搭建基本页面结构
创建样式文件,对样式做出调整
代码实现
pages/Publish/index.js
import {
Card,
Breadcrumb,
Form,
Button,
Radio,
Input,
Upload,
Space,
Select
} from 'antd'
import { PlusOutlined } from '@ant-design/icons'
import { Link } from 'react-router-dom'
import './index.scss'
const { Option } = Select
const Publish = () => {
return (
)
}
export default Publish
pages/Publish/index.scss
.publish {
position: relative;
}
.ant-upload-list {
.ant-upload-list-picture-card-container,
.ant-upload-select {
width: 146px;
height: 146px;
}
}
2. 富文本编辑器 本节目标:
能够安装并初始化富文本编辑器
实现步骤
安装富文本编辑器:yarn add react-quill
导入富文本编辑器组件以及样式文件
渲染富文本编辑器组件
通过 Form 组件的 initialValues
为富文本编辑器设置初始值,否则会报错
调整富文本编辑器的样式
代码实现
pages/Publish/index.js
import ReactQuill from 'react-quill'
import 'react-quill/dist/quill.snow.css'
const Publish = () => {
return (
// ...
)
}
pages/Publish/index.scss
.publish-quill {
.ql-editor {
min-height: 300px;
}
}
3. 频道数据获取 本节目标:
实现频道数据的获取和渲染
实现步骤
使用useState初始化数据和修改数据的方法
在useEffect中调用接口并保存数据
使用数据渲染对应模块
代码实现
// 频道列表
const [channels, setChannels] = useState([])
useEffect(() => {
async function fetchChannels() {
const res = await http.get('/channels')
setChannels(res.data.channels)
}
fetchChannels()
}, [])
// 模板渲染
return (
{channels.map(item => (
{item.name}
))}
)
4. 上传封面实现 本节目标:
能够实现上传图片
实现步骤
为 Upload 组件添加 action 属性,指定封面图片上传接口地址
创建状态 fileList 存储已上传封面图片地址,并设置为 Upload 组件的 fileList 属性值
为 Upload 添加 onChange 属性,监听封面图片上传、删除等操作
在 change 事件中拿到当前图片数据,并存储到状态 fileList 中
代码实现
import { useState } from 'react'
const Publish = () => {
const [fileList, setFileList] = useState([])
// 上传成功回调
const onUploadChange = info => {
const fileList = info.fileList.map(file => {
if (file.response) {
return {
url: file.response.data.url
}
}
return file
})
setFileList(fileList)
}
return (
)
}
5.切换图片Type 本节目标:
实现点击切换图片类型
实现步骤
创建状态 maxCount
给 Radio 添加 onChange 监听单图、三图、无图的切换事件
在切换事件中修改 maxCount 值
只在 maxCount 不为零时展示 Upload 组件
代码实现
pages/Publish/index.js
const Publish = () => {
const [imgCount, setImgCount] = useState(1)
const changeType = e => {
const count = e.target.value
setImgCount(count)
}
return (
// ...
单图
三图
无图
{maxCount > 0 && (
)}
)
}
6. 控制最大上传数量 本节目标:
控制Upload组件的最大上传数量和是否支持多张图片
实现步骤
修改 Upload 组件的 maxCount(最大数量)
属性控制最大上传数量
控制multiple (支持多图选择)属性
控制是否支持选择多张图片
代码实现
pages/Publish/index.js
const Publish = () => {
return (
// ...
单图
三图
无图
{maxCount > 0 && (
1 }
>
)}
)
}
7. 暂存图片列表实现 本节目标:
能够实现暂存已经上传的图片列表,能够在切换图片类型的时候完成切换
问题描述
如果当前为三图模式,已经完成了上传,选择单图只显示一张,再切换到三图继续显示三张,该如何实现?
实现思路
在上传完毕之后通过ref存储所有图片,需要几张就显示几张,其实也就是把ref当仓库,用多少拿多少
实现步骤 (特别注意useState异步更新的巨坑)
通过useRef创建一个暂存仓库,在上传完毕图片的时候把图片列表存入
如果是单图模式,就从仓库里取第一张图,以数组的形式 存入fileList
如果是三图模式,就把仓库里所有的图片,以数组的形式 存入fileList
代码实现
const Publish = ( ) => {
const fileListRef = useRef ( [ ] )
const onUploadChange = info => {
fileListRef. current = imgUrls
}
const changeType = e => {
const count = e. target. value
setMaxCount ( count)
if ( count === 1 ) {
const firstImg = fileListRef. current[ 0 ]
setFileList ( ! firstImg ? [ ] : [ firstImg] )
} else if ( count === 3 ) {
setFileList ( fileListRef. current)
}
}
}
8. 发布文章实现 本节目标:
能够在表单提交时组装表单数据并调用接口发布文章
实现步骤
给 Form 表单添加 onFinish
用来获取表单提交数据
在事件处理程序中,拿到表单数据按照接口需要格式化数据
调用接口实现文章发布,其中的接口数据格式为:
{
channel_id : 1
content : "测试
"
cover : {
type : 1 ,
images : [ "http://geek.itheima.net/uploads/1647066600515.png" ]
} ,
type : 1
title : "测试文章"
}
代码实现
const Publish = () => {
const onFinish = async (values) => {
// 数据的二次处理 重点是处理cover字段
const { channel_id, content, title, type } = values
const params = {
channel_id,
content,
title,
type,
cover: {
type: type,
images: fileList.map(item => item.response.data.url)
}
}
await http.post('/mp/articles?draft=false', params)
}
}
9. 编辑文章-文案适配 本节目标:
能够在编辑文章时展示数据
实现步骤
通过路由参数拿到文章id
根据文章 id 是否存在判断是否为编辑状态
如果是编辑状态,展示编辑时的文案信息
代码实现
import { useSearchParams } from 'react-router-dom'
const Publish = () => {
const [params] = useSearchParams()
const articleId = params.get('id')
return (
首页
{articleId ? '修改文章' : '发布文章'}
}
>
// ...
{articleId ? '修改文章' : '发布文章'}
)
}
10.编辑文章-数据获取 本节目标:
使用id获取文章详情
判断文章 id 是否存在,如果存在就根据 id 获取文章详情数据
useEffect ( ( ) => {
async function getArticle ( ) {
const res = await http. get ( ` /mp/articles/ ${ articleId} ` )
}
if ( articleId) {
getArticle ( )
}
} , [ articleId] )
11. 编辑文章-回显Form 本节目标:
完成Form组件的回填操作
调用Form组件的实例对象方法 setFieldsValue
useEffect ( ( ) => {
async function getArticle ( ) {
const res = await http. get ( ` /mp/articles/ ${ articleId} ` )
const { cover, ... formValue } = res. data
form. setFieldsValue ( { ... formValue, type : cover. type } )
}
if ( articleId) {
getArticle ( )
}
} , [ articleId] )
12. 编辑文章-回显Upload相关
1.Upload回显列表 fileList 2. 暂存列表 cacheImgList 3. 图片数量 imgCount
核心要点:fileList和暂存列表要求格式统一
表单的赋值回显需要调用setFieldsValue
方法,其中图片上传upload组件的回显依赖的数据格式如下:
[
{ url : 'http://geek.itheima.net/uploads/1647066120170.png' }
...
]
代码实现
useEffect(() => {
async function getArticle () {
const res = await http.get(`/mp/articles/${articleId}`)
const { cover, ...formValue } = res.data
// 动态设置表单数据
form.setFieldsValue({ ...formValue, type: cover.type })
// 格式化封面图片数据
const imageList = cover.images.map(url => ({ url }))
setFileList(imageList)
setMaxCount(cover.type)
fileListRef.current = imageList
}
if (articleId) {
// 拉取数据回显
getArticle()
}
}, [articleId])
11. 编辑保存 本节目标:
能够在编辑文章时对文章进行修改
代码实现
const onFinish = async ( values ) => {
const { type, ... rest } = values
const data = {
... rest,
cover : {
type,
images : fileList. map ( item => item. url)
}
}
if ( articleId) {
await http. put ( ` /mp/articles/ ${ data. id} ?draft=false ` , data)
} else {
await http. post ( '/mp/articles?draft=false' , data)
}
}
项目打包 1. 项目打包 本节目标:
能够通过命令对项目进行打包
使用步骤
在项目根目录下打开终端,输入打包命令:yarn build
等待打包完成,打包生成的内容被放在根下的build文件夹中
2. 项目本地预览 本节目标:
能够在本地预览打包后的项目
使用步骤
全局安装本地服务包 npm i -g serve
该包提供了serve命令,用来启动本地服务
在项目根目录中执行命令 serve -s ./build
在build目录中开启服务器
在浏览器中访问:http://localhost:3000/
预览项目
3. 打包体积分析 本节目标:
能够分析项目打包体积
分析说明 通过分析打包体积,才能知道项目中的哪部分内容体积过大,才能知道如何来优化
使用步骤
安装分析打包体积的包:yarn add source-map-explorer
在 package.json 中的 scripts 标签中,添加分析打包体积的命令
对项目打包:yarn build
(如果已经打过包,可省略这一步)
运行分析命令:yarn analyze
通过浏览器打开的页面,分析图表中的包体积
核心代码 :
package.json 中:
"scripts" : {
"analyze" : "source-map-explorer 'build/static/js/*.js'" ,
}
4. 优化-配置CDN 本节目标:
能够对第三方包使用CDN优化
分析说明 :通过 craco 来修改 webpack 配置,从而实现 CDN 优化
核心代码
craco.config.js
const path = require ( 'path' )
const { whenProd, getPlugin, pluginByName } = require ( '@craco/craco' )
module. exports = {
webpack : {
alias : {
'@' : path. resolve ( __dirname, 'src' )
} ,
configure : ( webpackConfig ) => {
let cdn = {
js : [ ] ,
css : [ ]
}
whenProd ( ( ) => {
webpackConfig. externals = {
react : 'React' ,
'react-dom' : 'ReactDOM'
}
cdn = {
js : [
'https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js' ,
'https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js' ,
] ,
css : [ ]
}
} )
const { isFound, match } = getPlugin (
webpackConfig,
pluginByName ( 'HtmlWebpackPlugin' )
)
if ( isFound) {
match. userOptions. cdn = cdn
}
return webpackConfig
}
}
}
public/index.html
< body>
< div id = " root" > div >
<% htmlWebpackPlugin.userOptions.cdn.js.forEach(cdnURL => { %>
< script src = " <%= cdnURL %>" > script >
<% }) %>
body >
5. 优化-路由懒加载 本节目标:
能够对路由进行懒加载实现代码分隔
使用步骤
在 App 组件中,导入 Suspense 组件
在 路由Router 内部,使用 Suspense 组件包裹组件内容
为 Suspense 组件提供 fallback 属性,指定 loading 占位内容
导入 lazy 函数,并修改为懒加载方式导入路由组件
代码实现
App.js
import { Routes, Route } from 'react-router-dom'
import { HistoryRouter, history } from './utils/history'
import { AuthRoute } from './components/AuthRoute'
// 导入必要组件
import { lazy, Suspense } from 'react'
// 按需导入路由组件
const Login = lazy(() => import('./pages/Login'))
const Layout = lazy(() => import('./pages/Layout'))
const Home = lazy(() => import('./pages/Home'))
const Article = lazy(() => import('./pages/Article'))
const Publish = lazy(() => import('./pages/Publish'))
function App () {
return (
loading...
}
>
{/* 需要鉴权的路由 */}
}>
{/* 二级路由默认页面 */}
} />
} />
} />
{/* 不需要鉴权的路由 */}
} />
)
}
export default App
查看效果
我们可以在打包之后,通过切换路由,监控network面板资源的请求情况,验证是否分隔成功
你可能感兴趣的:(react,react.js,javascript,前端)
Webpack 模块加载、动态引入机制源码解读
AIGC小陈
正则表达式 javascript 开发语言 react.js 前端
今天的文章简单探讨一下Vue和React的不同。本人Vue2和React都用过,但不熟悉Vue3,没用它做过项目。其实我对这两大框架也没有认真钻研过它们的细节,也就是工作上用它们写一些简单业务,或偶尔看看相关的博客文章,但还是有一些浅显的认识的,写下来记录一下。Vue和React都是用于构建UI界面的流行框架。它们的哲学也有很多相似的地方,我们可以认为这些特性是流行前端框架的一个趋势。它们是:组件
探索前端可观察性:如何使用Telemetry提高用户体验
桂月二二
前端 ux
随着前端应用变得日益复杂,可观察性(Observability)在前端开发中的重要性正逐步显现。通过实现Telemetry(遥测)功能,开发者能够收集、分析应用的运行数据,从而更好地了解用户行为、定位问题,并持续优化用户体验。什么是Telemetry?**Telemetry(遥测)**是指通过自动化手段远程采集系统或应用运行时的状态数据和行为信息。这些数据通常包括性能指标、错误日志、用户交互事件等
npm启动前端项目时报错(vue) error:0308010C:digital envelope routines::unsupported
华如锦
开发 本地环境配置 js java 前端 npm vue.js node.js java
vue启动项目时,npmrunserve报下面的错:error:0308010C:digitalenveloperoutines::unsupportedatnewHash(node:internal/crypto/hash:67:19)atObject.createHash(node:crypto:133:10)atFSReqCallback.readFileAfterClose[asoncom
101算法javaScript描述【3】
2401_89317507
算法 javascript java
通常情况下,不能出现超过连续三个相同的罗马数字并且罗马数字中小的数字在大的数字的右边。但也存在特例,例如4不写做IIII,而是IV。数字1在数字5的左边,所表示的数等于大数5减小数1得到的数值4。同样地,数字9表示为IX。这个特殊的规则只适用于以下六种情况:I可以放在V(5)和X(10)的左边,来表示4和9。X可以放在L(50)和C(100)的左边,来表示40和90。C可以放在D(500)和M(1
java xml dom 解析_解析 XML DOM
十二月极光
java xml dom 解析
解析XMLDOM大多数浏览器都内建了供读取和操作XML的XML解析器。解析器把XML转换为JavaScript可存取的对象。实例W3School提供的实例独立于浏览器和平台。这些实例可在所有现代浏览器中运行。解析XML所有现代浏览器都内建了用于读取和操作XML的XML解析器。解析器把XML读入内存,并把它转换为可被JavaScript访问的XMLDOM对象。微软的XML解析器与其他浏览器中的解析器
JavaScript网页设计实战:微信登录 + 获取用户头像和昵称
James吖
javascript 微信 前端 开发语言 目标检测 ecmascript 神经网络
引言在现代Web开发中,社交媒体登录已经成为提升用户体验的一个重要组成部分。微信登录因其广泛的用户基础和便捷的登录方式,深受开发者和用户的喜爱。本文将通过一个实战案例,介绍如何在JavaScript网页应用中集成微信登录功能,并获取用户的头像和昵称。前提条件在开始之前,请确保你已经具备以下条件:已经在微信开放平台上注册并创建了应用,获取到了AppID和AppSecret。了解基本的HTML、CSS
XML DOM 改变节点值
shangaoo
xml java 前端
nodeValue属性用于改变节点值。setAttribute()方法用于改变属性值。尝试一下-实例下面的实例使用XML文件books.xml。函数loadXMLDoc(),位于外部JavaScript中,用于加载XML文件。改变元素的文本节点本例使用nodeValue属性来改变"books.xml"中第一个元素的文本节点。通过使用setAttribute来改变属性值本例使用setAttribut
XMLDOM之浏览器差异
dengguxinghe4335
javascript xhtml ViewUI
DOM解析中的浏览器差异所有现代浏览器都支持w3cDOM规范,不过浏览器之间是有差异的,重要的区别有两点:加载XML的方式;处理空白和换行的方式;1、加载XML的方式:所有现代浏览器都内建了用于读取和操作XML的XML解析器。解析器把XML读入内存,并把它转换为可被JavaScript访问的XMLDOM对象;微软的XML解析器与其他浏览器中的解析器是有差异的。微软的解析器支持对XML文件和XML字
JavaScript-黑马程序员-前端零基础
羊羊的代码笔记本
javascript 前端 html
目录1.初识JavaScript1.1浏览器执行js1.2js的组成1.3js三种书写方式1.4JavaScript输入输出语句2.变量2.1变量是一个装东西的容器2.2变量是使用2.3更新变量2.4声明多个变量1.初识JavaScript是一种高级编程语言1.1浏览器执行js浏览器分成两部分:渲染引擎和js引擎渲染引擎:用来解析HTML和CSS,俗称内核js引擎:也称为js解释器。用来读取网页中
Java 在包管理与模块化中的优势:与其他开发语言的比较
nbsaas-boot
开发语言 java
在开发复杂的、规模庞大的软件系统时,包管理和模块化设计起着至关重要的作用。它们不仅决定了代码的组织和可维护性,还直接影响到团队协作效率、扩展性和性能。在众多编程语言中,Java凭借其成熟的生态系统、强类型系统和标准化的包管理机制,成为了大型企业级应用开发的首选之一。本文将探讨Java在包管理和模块化方面的优势,并与其他流行语言(如Rust、Go、Python、JavaScript和C#)进行对比,
Github 2025-02-01 开源项目月报 Top20
老孙正经胡说
github 开源 Github趋势分析 开源项目 Python Golang
根据GithubTrendings的统计,本月(2025-02-01统计)共有20个项目上榜。根据开发语言中项目的数量,汇总情况如下:开发语言项目数量Python项目8TypeScript项目3JupyterNotebook项目2Rust项目2HTML项目2C++项目1Ruby项目1JavaScript项目1Svelte项目1非开发语言项目1Go项目1Ollama:本地大型语言模型设置与运行创建周
.net core + vue 搭建前后端分离的框架
战族狼魂
C# .netcore
目录步骤一:创建.NETCore后端项目步骤二:创建Vue.js前端项目步骤三:集成后端和前端项目步骤一:创建.NETCore后端项目安装.NETCoreSDK:确保你的开发环境中已安装了最新版本的.NETCoreSDK。你可以从.NET下载页面获取最新版本。创建.NETCoreWebAPI项目:在命令行或者通过VisualStudio等集成开发环境,创建一个新的.NETCoreWebAPI项目。
前后端交互的方式能分为几种
阿贾克斯的黎明
java java
前后端交互主要可以分为以下几类:**一、HTTP请求交互**1.GET请求:用于从服务器获取资源,通常将参数放在URL中进行传递。特点是简单、快速,但不太适合传递大量数据或敏感信息,因为参数会在URL中显示。GET请求:用于获取商品列表、商品详情、用户订单状态等信息。例如,用户浏览商品页面时,前端通过GET请求从服务器获取商品的图片、价格、描述等数据进行展示。2.POST请求:用于向服务器提交数据
Vue3 结合 .NetCore WebApi 前后端分离跨域请求简易实例
醉の虾
.netcore vue.js 前端
1、本地安装Vue3环境参考:VUE3中文文档-快速上手注意:初始安装vue时,需要安装router,否则后续也要安装2、安装axios组件比如:npminstallaxios@latest或pnpminstallaxios@latest3、设置跨域请求代理打开vue3项目根目录的vite.config.js文件,插入跨域请求设置这样实现的效果,假设你的前端域名是http://localhost:
(尚硅谷 Java 学习 B 站大学版)Day 13 面向对象 方法
亢从文_Jackson
java 学习 开发语言
4-5类的成员之二:方法(Method)一、“万事万物皆对象”**:(理解)1、在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构>Scanner,String等>文件:File>网络资源:URL2、涉及到java语言与前端html、后端数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象二、内存解析说明1、引用类型的变量,只可能存储两类值:nu
跨平台的客户端gui到底是选“原生”还是web
扎量丙不要犟
前端 rust javascript tauri electron qt c++
我们讨论的是客户端的“前端”gui部分是选“原生”还是“web”,而不是讨论客户端用“js”还是“原生”。为什么这么说呢?我们应该把客户端也分为“前端”和“后端”,如果客户端没有“后端”,那么不在讨论范围。我们看一下“前端”的定义:(Front-End)通常指与用户直接交互的部分,即用户看到、操作并与之互动的界面。它涵盖了视觉设计、交互逻辑,主要关注用户体验(UserExperience,UX)和
首度揭秘:这款GitHub 3400星的播放器是如何开发出来的
字节跳动技术范儿
java 编程语言 字节跳动 人工智能 软件开发
在字节跳动,有这样一支技术团队:他们开源的项目在GitHub摘得3400多颗星,除了字节跳动,不少其他互联网公司也在用他们的产品;他们经历过不少极限操作,《囧妈》2020年春节期间网络首播的一套技术解决方案,他们在36小时内就完成了;他们拥有交叉领域稀有技能,据说是「前端里最懂多媒体的,多媒体里最懂前端的」。他们,就是字节跳动的web多媒体团队。GitHub3400星的前端开源播放器Web多媒体团
Web 开发入门:从前端到后端的全栈开发探索
HelloZheQ
前端
Web开发是指创建和维护通过网络浏览器访问的应用程序。Web开发涉及到的领域非常广泛,涵盖了前端、后端、数据库等多个技术栈。在这篇文章中,我们将详细介绍Web开发的基本概念、前端和后端的技术、全栈开发的特点以及如何开始从事Web开发。1.Web开发简介Web开发是构建和维护网站或Web应用程序的过程,分为前端开发、后端开发和全栈开发。前端开发负责用户界面和交互体验,后端开发负责服务器端的逻辑处理、
Python网络爬虫调试技巧:解决爬虫中的问题
master_chenchengg
python python Python python开发 IT
Python网络爬虫调试技巧:解决爬虫中的问题引子:当你的小蜘蛛遇到大麻烦知己知彼:了解常见的爬虫错误类型侦探出马:使用开发者工具和日志追踪问题源头化险为夷:调整User-Agent与添加延时策略进阶秘籍:处理JavaScript渲染页面与动态加载内容引子:当你的小蜘蛛遇到大麻烦在一个阳光明媚的下午,我正坐在电脑前,满怀信心地运行着我的Python爬虫脚本。这个脚本是为了从一个大型电子商务网站上抓
探究Fingerprintjs:了解浏览器指纹技术的原理和应用
Bj陈默
javascript
一、什么是FingerprintjsFingerprintjs是一个用于生成浏览器指纹的JavaScript库。浏览器指纹是一种通过收集浏览器的各种信息来识别用户设备的技术。它类似于人的指纹,具有一定的独特性,可以用于区分不同的用户设备。二、浏览器指纹技术的原理(一)基本信息收集用户代理(UserAgent)用户代理字符串包含了浏览器的类型、版本、操作系统等信息。例如,一个典型的用户代理字符串可能
微前端框架qiankun剖析
浮游本尊
前端框架 前端 javascript
一、single-spa简介要了解qiankun的实现机制,那我们不得不从其底层依赖的single-spa说起。随着微前端的发展,我们看到在这个领域之中出现了各式各样的工具包和框架来帮助我们方便快捷的实现自己的微前端应用。在发展早期,single-spa可以说是独树一帜,为我们提供了一种简便的微前端路由工具,大大降低了实现一个微前端应用的成本。我们来看一下一个典型single-spa微前端应用的架
React:封装高阶路由组件,根据有无token控制路由跳转
听海边涛声
react.js 前端 前端框架
有些页面内的内容比较敏感,如果用户没有经过登录获取到token,是不允许跳转的。实现方案:封装一个高阶组件,根据token的有无进行路由跳转控制:
react18高阶组件
我谈山美,我说你媚
前端 javascript reactjs
高阶组件的本质上就是函数,接受一个组件作为参数,然后返回一个组件。解决了组件之间如何横向抽离公共逻辑的问题。类组件之间常使用,函数组件中也可以使用,但更多的时候使用自定义hooks。高阶组件命名一般采用with开头,它可以将普通组件内部的公用功能抽离出来,复用在其被调用的组件上。举个例子,需求:每个组件渲染销毁的时候打印日志importReact,{useEffect}from'react';//
react中关于组件的一些概念(有无状态组件、组件封装与继承 、高阶组件)
不能懒鸭
react基础 面试 react.js javascript 前端
怎么理解“在react中,一切皆为组件”句话react采用组件化的思想,最小的组件单位就是原生HTML元素,采用JSX语法组件声明调用react的虚拟dom,就是一个大的组件树,从父组件层到子组件,在react-routerv4版开始,路由本身也是组件各个库提供的hoc返回也是组件,如withRouter、connectreact中的基础数据stateprops的传递也是以组件为基础1.什么是组件
React 高阶组件(HOC)
web Rookie
React react.js javascript 前端
文章目录一.高阶组件(HOC)的定义二.HOC的作用和优势三.HOC的使用方式四.HOC的注意事项和潜在问题五.应用场景1.权限控制与认证2.数据获取与预加载3.样式和主题管理4.性能优化-缓存数据或组件渲染结果5.日志记录与调试辅助六.总结一.高阶组件(HOC)的定义高阶组件(Higher-OrderComponent,简称HOC)是一种在React中用于复用组件逻辑的高级技术。它本质上是一个函
精讲 JS return 语句的作用
谦虚的w
javascript 前端
函数作用域的限制-在JavaScript中,函数有自己的作用域。在函数内部定义的变量和对象(如localObj)默认情况下在函数外部是无法直接访问的。这是因为函数的作用域是封闭的,它将内部的变量和对象隐藏起来,以防止外部代码随意访问和修改。return语句的作用-当在函数内部使用return语句返回一个对象(如returnlocalObj;)时,实际上是将这个对象的引用传递到函数外部。这样,在函数
HTML基础与进阶技术指南
王奥雷
本文还有配套的精品资源,点击获取简介:HTML是构建网页的基础语言,负责定义网页的内容结构和样式。初学者需要掌握HTML元素、属性、结构等基础知识,并了解HTML5新增的语义化标签,表单元素的使用,布局与定位技术,响应式设计原则,以及如何与JavaScript交互。此外,学习SEO优化和无障碍性设计也是提高网页质量和可访问性的关键。本指南旨在引导初学者从基础到高级技巧,逐步深入理解并实践网页开发。
探索React ARIA:构建无障碍Web应用的利器
高慈鹃Faye
探索ReactARIA:构建无障碍Web应用的利器react-ariaUtilitycomponentstohelpcomposeReactARIAcomponents项目地址:https://gitcode.com/gh_mirrors/re/react-aria随着Web应用程序的日益复杂,提供一个包容性且易于访问的界面变得至关重要。ReactARIA,这个曾经由souporserious维护
前端调试技巧:从 Console 到 Chrome DevTools 的进阶指南
技术出海录
人工智能 前端 React
作为前端开发者,你可能每天都在和Bug打交道。有时候一个简单的问题可能会耗费几个小时,而掌握正确的调试技巧可以大大提高解决问题的效率。今天,我就来分享一些在实际工作中常用的调试技巧。Console的高级用法除了常见的console.log,Console还有很多强大的功能://1.使用console.table展示数组或对象constusers=[{id:1,name:'Alice',role:'
深入剖析 HTML5 新特性:语义化标签和表单控件完全指南
吴师兄大模型
html5 前端 html 语义化标签 表单控件 HTML5 编程
系列文章目录01-从零开始学HTML:构建网页的基本框架与技巧02-HTML常见文本标签解析:从基础到进阶的全面指南03-HTML从入门到精通:链接与图像标签全解析04-HTML列表标签全解析:无序与有序列表的深度应用05-HTML表格标签全面解析:从基础到高级优化技巧06-HTML表单深度解析:GET和POST提交方法07-HTML表单控件类型大全:文本框、密码框、文件上传全掌握08-前端表单验
怎么样才能成为专业的程序员?
cocos2d-x小菜
编程 PHP
如何要想成为一名专业的程序员?仅仅会写代码是不够的。从团队合作去解决问题到版本控制,你还得具备其他关键技能的工具包。当我们询问相关的专业开发人员,那些必备的关键技能都是什么的时候,下面是我们了解到的情况。
关于如何学习代码,各种声音很多,然后很多人就被误导为成为专业开发人员懂得一门编程语言就够了?!呵呵,就像其他工作一样,光会一个技能那是远远不够的。如果你想要成为
java web开发 高并发处理
BreakingBad
java Web 并发 开发 处理 高
java处理高并发高负载类网站中数据库的设计方法(java教程,java处理大量数据,java高负载数据) 一:高并发高负载类网站关注点之数据库 没错,首先是数据库,这是大多数应用所面临的首个SPOF。尤其是Web2.0的应用,数据库的响应是首先要解决的。 一般来说MySQL是最常用的,可能最初是一个mysql主机,当数据增加到100万以上,那么,MySQL的效能急剧下降。常用的优化措施是M-S(
mysql批量更新
ekian
mysql
mysql更新优化:
一版的更新的话都是采用update set的方式,但是如果需要批量更新的话,只能for循环的执行更新。或者采用executeBatch的方式,执行更新。无论哪种方式,性能都不见得多好。
三千多条的更新,需要3分多钟。
查询了批量更新的优化,有说replace into的方式,即:
replace into tableName(id,status) values
微软BI(3)
18289753290
微软BI SSIS
1)
Q:该列违反了完整性约束错误;已获得 OLE DB 记录。源:“Microsoft SQL Server Native Client 11.0” Hresult: 0x80004005 说明:“不能将值 NULL 插入列 'FZCHID',表 'JRB_EnterpriseCredit.dbo.QYFZCH';列不允许有 Null 值。INSERT 失败。”。
A:一般这类问题的存在是
Java中的List
g21121
java
List是一个有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
与 set 不同,列表通常允许重复
读书笔记
永夜-极光
读书笔记
1. K是一家加工厂,需要采购原材料,有A,B,C,D 4家供应商,其中A给出的价格最低,性价比最高,那么假如你是这家企业的采购经理,你会如何决策?
传统决策: A:100%订单 B,C,D:0%
&nbs
centos 安装 Codeblocks
随便小屋
codeblocks
1.安装gcc,需要c和c++两部分,默认安装下,CentOS不安装编译器的,在终端输入以下命令即可yum install gccyum install gcc-c++
2.安装gtk2-devel,因为默认已经安装了正式产品需要的支持库,但是没有安装开发所需要的文档.yum install gtk2*
3. 安装wxGTK
yum search w
23种设计模式的形象比喻
aijuans
设计模式
1、ABSTRACT FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory 工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:
开发管理 CheckLists
aoyouzi
开发管理 CheckLists
开发管理 CheckLists(23) -使项目组度过完整的生命周期
开发管理 CheckLists(22) -组织项目资源
开发管理 CheckLists(21) -控制项目的范围开发管理 CheckLists(20) -项目利益相关者责任开发管理 CheckLists(19) -选择合适的团队成员开发管理 CheckLists(18) -敏捷开发 Scrum Master 工作开发管理 C
js实现切换
百合不是茶
JavaScript 栏目切换
js主要功能之一就是实现页面的特效,窗体的切换可以减少页面的大小,被门户网站大量应用思路:
1,先将要显示的设置为display:bisible 否则设为none
2,设置栏目的id ,js获取栏目的id,如果id为Null就设置为显示
3,判断js获取的id名字;再设置是否显示
代码实现:
html代码:
<di
周鸿祎在360新员工入职培训上的讲话
bijian1013
感悟 项目管理 人生 职场
这篇文章也是最近偶尔看到的,考虑到原博客发布者可能将其删除等原因,也更方便个人查找,特将原文拷贝再发布的。“学东西是为自己的,不要整天以混的姿态来跟公司博弈,就算是混,我觉得你要是能在混的时间里,收获一些别的有利于人生发展的东西,也是不错的,看你怎么把握了”,看了之后,对这句话记忆犹新。 &
前端Web开发的页面效果
Bill_chen
html Web Microsoft
1.IE6下png图片的透明显示:
<img src="图片地址" border="0" style="Filter.Alpha(Opacity)=数值(100),style=数值(3)"/>
或在<head></head>间加一段JS代码让透明png图片正常显示。
2.<li>标
【JVM五】老年代垃圾回收:并发标记清理GC(CMS GC)
bit1129
垃圾回收
CMS概述
并发标记清理垃圾回收(Concurrent Mark and Sweep GC)算法的主要目标是在GC过程中,减少暂停用户线程的次数以及在不得不暂停用户线程的请夸功能,尽可能短的暂停用户线程的时间。这对于交互式应用,比如web应用来说,是非常重要的。
CMS垃圾回收针对新生代和老年代采用不同的策略。相比同吞吐量垃圾回收,它要复杂的多。吞吐量垃圾回收在执
Struts2技术总结
白糖_
struts2
必备jar文件
早在struts2.0.*的时候,struts2的必备jar包需要如下几个:
commons-logging-*.jar Apache旗下commons项目的log日志包
freemarker-*.jar
Jquery easyui layout应用注意事项
bozch
jquery 浏览器 easyui layout
在jquery easyui中提供了easyui-layout布局,他的布局比较局限,类似java中GUI的border布局。下面对其使用注意事项作简要介绍:
如果在现有的工程中前台界面均应用了jquery easyui,那么在布局的时候最好应用jquery eaysui的layout布局,否则在表单页面(编辑、查看、添加等等)在不同的浏览器会出
java-拷贝特殊链表:有一个特殊的链表,其中每个节点不但有指向下一个节点的指针pNext,还有一个指向链表中任意节点的指针pRand,如何拷贝这个特殊链表?
bylijinnan
java
public class CopySpecialLinkedList {
/**
* 题目:有一个特殊的链表,其中每个节点不但有指向下一个节点的指针pNext,还有一个指向链表中任意节点的指针pRand,如何拷贝这个特殊链表?
拷贝pNext指针非常容易,所以题目的难点是如何拷贝pRand指针。
假设原来链表为A1 -> A2 ->... -> An,新拷贝
color
Chen.H
JavaScript html css
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML> <HEAD>&nbs
[信息与战争]移动通讯与网络
comsci
网络
两个坚持:手机的电池必须可以取下来
光纤不能够入户,只能够到楼宇
建议大家找这本书看看:<&
oracle flashback query(闪回查询)
daizj
oracle flashback query flashback table
在Oracle 10g中,Flash back家族分为以下成员:
Flashback Database
Flashback Drop
Flashback Table
Flashback Query(分Flashback Query,Flashback Version Query,Flashback Transaction Query)
下面介绍一下Flashback Drop 和Flas
zeus持久层DAO单元测试
deng520159
单元测试
zeus代码测试正紧张进行中,但由于工作比较忙,但速度比较慢.现在已经完成读写分离单元测试了,现在把几种情况单元测试的例子发出来,希望有人能进出意见,让它走下去.
本文是zeus的dao单元测试:
1.单元测试直接上代码
package com.dengliang.zeus.webdemo.test;
import org.junit.Test;
import o
C语言学习三printf函数和scanf函数学习
dcj3sjt126com
c printf scanf language
printf函数
/*
2013年3月10日20:42:32
地点:北京潘家园
功能:
目的:
测试%x %X %#x %#X的用法
*/
# include <stdio.h>
int main(void)
{
printf("哈哈!\n"); // \n表示换行
int i = 10;
printf
那你为什么小时候不好好读书?
dcj3sjt126com
life
dady, 我今天捡到了十块钱, 不过我还给那个人了
good girl! 那个人有没有和你讲thank you啊
没有啦....他拉我的耳朵我才把钱还给他的, 他哪里会和我讲thank you
爸爸, 如果地上有一张5块一张10块你拿哪一张呢....
当然是拿十块的咯...
爸爸你很笨的, 你不会两张都拿
爸爸为什么上个月那个人来跟你讨钱, 你告诉他没
iptables开放端口
Fanyucai
linux iptables 端口
1,找到配置文件
vi /etc/sysconfig/iptables
2,添加端口开放,增加一行,开放18081端口
-A INPUT -m state --state NEW -m tcp -p tcp --dport 18081 -j ACCEPT
3,保存
ESC
:wq!
4,重启服务
service iptables
Ehcache(05)——缓存的查询
234390216
排序 ehcache 统计 query
缓存的查询
目录
1. 使Cache可查询
1.1 基于Xml配置
1.2 基于代码的配置
2 指定可搜索的属性
2.1 可查询属性类型
2.2 &
通过hashset找到数组中重复的元素
jackyrong
hashset
如何在hashset中快速找到重复的元素呢?方法很多,下面是其中一个办法:
int[] array = {1,1,2,3,4,5,6,7,8,8};
Set<Integer> set = new HashSet<Integer>();
for(int i = 0
使用ajax和window.history.pushState无刷新改变页面内容和地址栏URL
lanrikey
history
后退时关闭当前页面
<script type="text/javascript">
jQuery(document).ready(function ($) {
if (window.history && window.history.pushState) {
应用程序的通信成本
netkiller.github.com
虚拟机 应用服务器 陈景峰 netkiller neo
应用程序的通信成本
什么是通信
一个程序中两个以上功能相互传递信号或数据叫做通信。
什么是成本
这是是指时间成本与空间成本。 时间就是传递数据所花费的时间。空间是指传递过程耗费容量大小。
都有哪些通信方式
全局变量
线程间通信
共享内存
共享文件
管道
Socket
硬件(串口,USB) 等等
全局变量
全局变量是成本最低通信方法,通过设置
一维数组与二维数组的声明与定义
恋洁e生
二维数组 一维数组 定义 声明 初始化
/** * */ package test20111005; /** * @author FlyingFire * @date:2011-11-18 上午04:33:36 * @author :代码整理 * @introduce :一维数组与二维数组的初始化 *summary: */ public c
Spring Mybatis独立事务配置
toknowme
mybatis
在项目中有很多地方会使用到独立事务,下面以获取主键为例
(1)修改配置文件spring-mybatis.xml <!-- 开启事务支持 --> <tx:annotation-driven transaction-manager="transactionManager" /> &n
更新Anadroid SDK Tooks之后,Eclipse提示No update were found
xp9802
eclipse
使用Android SDK Manager 更新了Anadroid SDK Tooks 之后,
打开eclipse提示 This Android SDK requires Android Developer Toolkit version 23.0.0 or above, 点击Check for Updates
检测一会后提示 No update were found