项目准备
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,前端)
React Native第三方组件库汇总
2401_85124812
作者\/ react native react.js javascript
项目地址:https://github.com/wix/react-native-ui-lib9,ReactNativePaperReactNativePaper是一个跨平台的UI组件库,它遵循MaterialDesign指南,提供了全局主题支持和可选的babel插件,用以减少捆绑包大小。ReactNativePaper项目地址:https://github.com/callstack/react
第九章:归凌-React 国际化引导页
刘行之
react.js javascript 前端
国际化实现一、安装依赖需要同时安装i18next和react-i18next依赖:npminstallreact-i18nexti18next--save二、文件配置在src下新建i18n文件夹,以存放国际化相关配置i18n中分别新建三个文件:config.ts:对i18n进行初始化操作及插件配置en.json:英文语言配置文件zh.json:中文语言配置文件代码如下(示例):importi18n
JavaScript系列(59)--微服务架构设计详解
ᅟᅠ 一进制
JavaScript javascript 微服务 开发语言
JavaScript微服务架构设计详解️今天,让我们深入探讨JavaScript的微服务架构设计。微服务架构是一种将应用拆分为小型、独立服务的架构模式,每个服务都运行在自己的进程中,通过轻量级的通信机制进行交互。微服务基础架构小知识:微服务架构的核心是将单体应用拆分为一组小型服务,每个服务都能独立部署、扩展和升级,从而提高系统的可维护性和可扩展性。//1.基础服务类classMicroServic
Linux如何安装软件
大口吃饭大口吐
服务器 linux
Linux安装软件的4种方式通用二进制格式:直接解压压缩文件,就可以使用。但一定要注意安装平台。软件包管理器:如RPM。软件包管理器的前端工具:如YUM。源代码编译。二进制格式安装二进制软件,分为四个模块二进制程序,位于/bin,/sbin,/usr/bin,/usr/sbin,/usr/local/bin,/usr/local/sbin等目录中。库文件,位于/lib,/usr/lib,/usr/
20250108慧能科技前端面试
uperficialyu
前端面试实战整理 前端
目录ajax怎么取消请求移动端怎么实现px尺寸vite和webpack的区别设计模式讲一下什么是原型链讲一下什么是闭包实现eventbus事件循环项目发布后,如何对项目进行优化,怎么优化vue2的diff算法和vue3的diff算法的区别1.ajax怎么取消请求原生JavaScript(XMLHttpRequest)创建XMLHttpRequest对象发送请求,通过调用其abort()方法来取消请
刷题前必学!字符串实战!用JavaScript学数据结构与算法
JavaScript算法与数据结构-HowieCong务必要熟悉JavaScript使用再来学!一、基础算法技能会根据基础算法技能去衍生问题(1)反转字符串反转字符串可以直接调用相关API即可//定义被反转的字符串conststr='howiecong'//定义反转后的字符串constres=str.split('').reverse().join('')console.log(res)//gn
分布式微服务系统架构第91集:系统性能指标总结
掘金-我是哪吒
分布式 微服务 系统架构 架构 云原生
加群联系作者vx:xiaoda0423仓库地址:https://webvueblog.github.io/JavaPlusDoc/系统性能指标总结系统性能指标包括哪些?业务指标、资源指标、中间件指标、数据库指标、前端指标、稳定性指标、批量处理指标、可扩展性指标、可靠性指标。1)业务指标:主要包括并发用户数、响应时间、处理能力。响应时间ResponseTime:RT对于在线实时交易:互联网企业:50
安装go,并配置开发环境
wjhx
golang 开发语言 后端
本机操作中,使用VSCODE做为前端开发工具,使用go语言。安装vscodewin10系统,下载开发环境,并进行安装,本安装使用版本1.78.2。安装go下载go,本安装版本为1.20.4,安装路径C:\ProgramFiles\Go安装完成后,配置两个系统变量,GOROOT和GOPATH,在安装完成时,发现在用户变量中,有一个GOROOT,但与实际不符,删除后在系统变量中重新配置。GOROOT:
python JSON API duckduckgo search engine 使用duckduckgo API 尝试搜索引擎
weixin_30951743
json python javascript ViewUI
Theduckduckgo.com'ssearchengineisveryneattouse.AcutallyithasmanythingstodowithotherthingssinceitsAPIisalsoveryneat.WecanfindmanyAPIwhicharefreetouseondifferentplatforms.HerewegotoneforJavaScriptongith
四大跨平台开发框架深度解析——uniapp、uniapp-X、React Native与Flutter
chenNorth。
react native uni-app flutter
引言随着移动互联网的飞速发展,跨平台开发框架成为了开发者们关注的焦点。这些框架旨在通过编写一套代码,实现多个平台的应用开发,从而大幅提高开发效率和降低维护成本。本文将深入剖析uniapp、uniapp-X、ReactNative和Flutter这四个主流的跨平台开发框架,探讨它们的优缺点及适用场景,帮助开发者在项目中做出明智的选择。一、uniapp:多平台开发的利器优点跨平台能力:uniapp凭借
移动端混合框架大比拼:Uniapp、Taro、React Native 和 Flutter
YU大宗师
框架 uni-app taro react native
在移动应用开发领域,跨平台框架已成为开发者的得力工具。今天我们将对比四个主流框架:Uniapp、Taro、ReactNative和Flutter,探讨它们在Android和iOS平台上的表现和差异,帮助开发者选择最佳工具进行高效开发。框架介绍及背后公司Uniapp公司:DCloud简介:Uniapp基于Vue.js,支持一套代码编译到多端(包括Android和iOS),特别适合国内市场需求。Tar
深入理解Node.js与WebKit的nw.js桌面应用开发
AR新视野
本文还有配套的精品资源,点击获取简介:NodeWebkit(nw.js)是一个开源项目,允许开发者使用Web技术(HTML、CSS和JavaScript)构建跨平台桌面应用程序。版本0.39.2特别为64位Windows系统设计,将Node.js后端能力和WebKit浏览器前端功能结合起来。本压缩包可能提供了一个教程,包括Node.js的核心概念、WebKit的渲染原理,以及FFmpeg的多媒体处
Node.js设计模式实战:个人精选实践指南
年近半百
本文还有配套的精品资源,点击获取简介:Node.js,基于ChromeV8引擎的JavaScript运行环境,以其异步非阻塞I/O和事件驱动的特性,在后端开发领域有着举足轻重的地位。本集合“Node.js_Design_Patterns”汇聚了作者在深入学习Node.js过程中对设计模式的理解与实践,目的是帮助开发者深入理解并有效应用这些模式,增强代码质量和可维护性。文章详细介绍了工厂模式、单例模
深入掌握JavaScript Node.js开发
黑泡尖子
本文还有配套的精品资源,点击获取简介:JavaScript是前端开发中的主导语言,在Node.js环境下的服务器端编程中也显示出重要性。Node.js是一个基于V8引擎的跨平台JavaScript运行环境,具有事件驱动和非阻塞I/O特性,适用于构建高性能的并发服务器。该压缩包文件可能包含有关Node.js核心概念和模块的资料,比如模块化设计、文件系统操作、HTTP服务器创建、进程和线程管理、网络编
Taro(多端开发框架)快速入门
每天都要进步哦
前端开发 前端框架 前端 前端框架
Taro介绍Taro是一个开放式跨端跨框架解决方案,支持使用React、Vue.js、Nerv等框架来开发小程序、H5、reactNative等应用。特性多端转换支持可以支持转换到H5、ReactNative以及任意小程序平台。目前官方支持转换的平台如下:H5ReactNative微信小程序京东小程序百度小程序支付宝小程序字节跳动小程序QQ小程序钉钉小程序企业微信小程序支付宝IO
前端框架大乱斗!DeepSeek锐评前端三大框架
ZhillerDev
AI 前端框架 前端
关注公众号,获取本文PDF&Markdown文件与其他相关资料感谢您关注,个人将快马加鞭为大家提供更优质的文章与资源!点击前往微信观看本文章文章目录前言锐评开始VueReactAngular总结前言前端框架的世界,就像是一场永无止境的“信仰战争”。Vue、React、Angular,这三个名字一出来,评论区立马就能变成“三国演义”:Vue粉高喊“简单易用,天下第一”,React信徒冷笑“灵活自由,
入门 Taro
My-Way.
taro 前端 javascript
TaroP01:Taro框架简介和介绍Taro是京东凹凸实验室推出的框架,目的就是解决多端混乱的局面。当我们按照一种模式一种代码进行开发,开发完成后,项目就有了在任何终端显示的能力,这让人想想都很爽。1.Taro的优点多端运行:编译多端,通过自身的编译工具,编译成不同环境下的代码,实现多端运行。React:React语法规范,可以让我们使用JSX和React相关的语法(包括Hooks)来开发小程序
AI生成内容——JavaScript中的Promise、async和wait
baby_hua
微信小程序 javascript 开发语言 ecmascript
一、Promise*1.概念:Promise是JavaScript中处理异步操作的一种对象,它表示一个异步操作的最终完成(或失败)及其结果值。一个Promise对象处于以下三种状态之一:Pending(进行中):初始状态,既不是成功,也不是失败状态。Fulfilled(已成功):意味着操作成功完成。Rejected(已失败):意味着操作失败。*2.创建Promise对象:letmyPromise=
深入理解Node.js_架构与最佳实践
随风九天
Vue六脉神剑 前端 node.js 架构
1.引言1.1什么是Node.jsNode.js简介:Node.js是一个基于ChromeV8引擎的JavaScript运行时,用于构建快速、可扩展的网络应用。Node.js的历史背景和发展:Node.js最初由RyanDahl在2009年发布,旨在解决I/O密集型应用的性能问题。随着时间的推移,Node.js社区不断壮大,提供了丰富的库和工具,使其成为构建现代Web应用的重要选择。1.2为什么选
DeepSeek 阐述 2025年前端发展趋势
GISer_Jinger
React Javascript 前端 javascript react.js 前端框架
预测2025年前端的发展趋势。首先,我需要考虑当前的前端技术发展情况,以及近几年的变化趋势。比如,框架方面,React、Vue、Angular这些主流框架的更新方向和社区活跃度。可能用户想知道未来哪些技术会更流行,或者需要学习哪些技能保持竞争力。然后,我应该想到新兴的技术,比如WebAssembly、TypeScript的普及程度,还有构建工具如Vite是否会被更广泛采用。另外,性能优化和用户体验
[JavaScript] 面向对象编程
DevKevin
前端 javascript 开发语言 ecmascript
JavaScript是一种多范式语言,既支持函数式编程,也支持面向对象编程。在ES6引入class语法后,面向对象编程在JavaScript中变得更加易于理解和使用。以下将详细讲解JavaScript中的类(class)、构造函数(constructor)、继承、封装、多态,以及this的相关问题。1.为什么需要类与面向对象编程?面向对象编程(Object-OrientedProgramming,
爆肝两千字!掌握CSS选择器与响应式设计:从基础到高级应用
洛可可白
前端专栏 css 前端
前言欢迎来到我的技术小宇宙!这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。洛可可白:个人主页个人专栏:✅前端技术✅后端技术个人博客:洛可可白博客代码获取:bestwishes0203封面壁纸:洛可可白wallpaper文章目录爆肝两千字!掌握CSS选择器与响应式设计:从基础到高级应用CSS常见选择器1.元素选择
在react中使用redux
土豆切成丝
react JavaScript react.js 前端框架
一、项目搭建1.react项目搭建npxcreate-react-appapp2.安装reduxnpmiredux二、创建store①store/index.jsimport{createStore,combineReducers}from'redux'functionnum(state=0,action){console.log('action',action);switch(action.ty
详细介绍:网站背景更换功能
还是鼠鼠
javascript ajax 前端 vscode html5 css3 前端框架
目录1.HTML部分2.JavaScript部分3.完整流程4.总结5.适用场景本文将介绍如何通过文件上传实现网站背景图片的更换。通过使用JavaScript和Axios,我们可以允许用户上传图片文件并将其作为网站的背景图片。上传的图片URL会保存在浏览器的localStorage中,这样即使刷新页面,背景图片仍然可以保持不变。下面是具体的实现过程以及完整的代码示例。1.HTML部分更换背景图片选
【web前端】单向数据绑定和双向数据绑定有什么区别?
myt2000
arkTS typescript
单向数据绑定和双向数据绑定有什么区别?单向数据绑定和双向数据绑定有什么区别?总结单向数据绑定和双向数据绑定有什么区别?绑定方向:单向数据绑定是只从数据到模板的绑定,即外部数据(如后台数据)的变化会导致页面更新,但页面上的修改不会反馈到数据。而双向数据绑定则是数据模型(Module)和视图(View)之间的双向绑定,即无论用户在视图上的修改还是数据模型中的值发生变化,都会立刻同步到对方的层面。数据更
Vue模板语法与常用指令深度解析
prince_zxill
vue.js javascript 前端 前端框架 架构
Vue模板语法与常用指令深度解析Vue模板语法与常用指令深度解析一、Vue模板语法基础1.1插值语法1.2JavaScript表达式二、核心指令深度解析2.1条件渲染:v-if家族指令2.1.1基础用法与原理2.1.2v-ifvsv-show深度对比2.1.3高级用法模式2.1.4性能优化指南2.2列表渲染:v-for指令全解2.2.1基础语法演进2.2.2Key的重要性与原理2.2.3数组更新检
云南农职《JavaScript交互式网页设计》 综合机试试卷④——蔚蓝网导航栏
2401_89790580
javascript 开发语言 ecmascript
您好,欢迎光临蔚蓝网![登录][免费注册]购物车|我的账户|我的订单|礼品卡|新手入门购物保障购物流程会员介绍常见问题|客户服务css:*{margin:0;padding:0;}a{color:black;text-decoration:none;}/*整个导航栏样式*/#top{width:100%;height:50px;margin:0auto;line-height:50px;backg
Cursor 与多语言开发:全栈开发的利器
drebander
AI 编程 Cursor
引言全栈开发要求开发者跨越前端、后端、数据库甚至数据科学等多个技术领域,而不同技术栈往往需要切换工具和思维方式。Cursor作为一款AI驱动的智能编程助手,凭借其对20+编程语言和主流框架的深度支持,正在成为全栈开发的“瑞士军刀”。本文将解析Cursor的多语言支持能力,并通过前端、后端和数据科学领域的实际案例,展示其如何统一跨技术栈的开发体验。一、Cursor的多语言支持能力1.覆盖全技术栈的语
前端的指导方针---css篇
weixin_33815613
前端 xhtml ViewUI
英语是渣渣,想学英语,又不想花钱报培训班。看不懂的文章,还是翻译一下留着自己看吧。引自:https://github.com/bendc/frontend-guidelinesHTML语义HTML5提供了语义元素的目的是精确描述的内容很多。确保你的词汇丰富的效益。BlogpostPublished:21stFeb,2015…BlogpostPublished:21stFeb,2015…确保你了解你
Spring Boot 优雅实现多租户架构
犬小哈
spring boot 架构 后端 java spring
来源:blog.csdn.net/u010349629/article/details/130737253欢迎加入小哈的星球,你将获得:专属的项目实战/Java学习路线/一对一提问/学习打卡/赠书福利全栈前后端分离博客项目2.0版本完结啦,演示链接:http://116.62.199.48/,新项目正在酝酿中。全程手摸手,后端+前端全栈开发,从0到1讲解每个功能点开发步骤,1v1答疑,直到项目上线
怎么样才能成为专业的程序员?
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