React脚手架创建项目
create-react-app
全局安装 cnpm install create-react-app -g
创建项目 create-react-app ‘项目名’
启动项目 npm start
把脚手架隐藏封装的脚本还原 npm run eject
执行eject前先创建本地git仓库并本地提交
因为还原的文件会覆盖一些文件,脚手架需要你先备份代码版本
git init git add . git commit -m ‘first’
1-自定义端口号:/script/start.js 变量PORT中配置
2-设置‘@’路径: /config/webpack.config.js 在resolve选项的alias属性里配置
'@':path.resolve(__dirname,'../src') //两个 点 文件结构不一样
3-favicon制作 搜索在线制作工具,一般尺寸为32*32
4-本地环境配置代理(解决跨域)
安装插件 cnpm install http-proxy-middleware -D
在src目录下新建文件setupProxy.js,在这个文件中进行配置代理
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:8000',
changeOrigin: true,
})
);
};
5-eslint的语法检查规则配置,在package.json/eslintrc.json中的配置
"eslintConfig": {
"extends": "react-app",
"rules": {
"eqeqeq":"off"
}
}
6-热更新原理:webpack与浏览器形成B/S架构,通过websocket通信,把变化的代码推送到前端
需要自己集成的工具
1-sass集成
安装 cnpm install node-sass -D
(sass-loader、style-loader 脚手架已经集成)
2-axios集成
安装 cnpm install axios -S
3-路由集成
安装 cnpm i react-router-dom -S
4-集成动态加载-懒加载-代码分割
安装 cnpm i @loadable/component -S
NavLink 相当于 router-link 外面可以包一个div
Route 视图容器 相当于 router-view层
跟Switch是直接父子关系,中间不能有其他元素包裹 -外面不能包div
exact属性 默认true 精准匹配
false 完全匹配
Switch 包裹的Route后 只匹配第一条
保证匹配关系只有一条成立
使用
// 配置路由
import loadable from '@loadable/component'
const Home = loadable(()=>('./home/Home.js'))
export default [
{
id:1,
path:'/home',
component:Home,
}
]
import {
HashRouter,
BrowserRouter,
NavLink,
Route,
Switch,
Redirect
} from 'react-router-dom'
return(
<HashRouter>
<NavLink to='/list'>列表</NavLink>
<Switch>
<Route path='/list' component={List}></Route>
{/* 重定向 - 放在最后 */}
<Redirect from='/*' to='/home'></Redirect>
</Switch>
</HashRouter>
)
5-antd
安装 cnpm install antd -S
表单
表格
事件选择器
6-redux
1-安装
cnpm install redux -S 用于创建store
cnpm install react-redux -S 把store与react进行关联
核心
action 触发行为 --主要用于触发reducer进行数据改变的行为
给reduce一个改变数据的信号(要改的变量,和改变为什么) 9.35
reducer --作用改变store中的数据,是纯函数
store --共享数据的存储中心
2-在src/store/index.js目录 创建store并抛出
import { createStore } from 'redux'
import reducer from './reducer'
// 创建一个store
// 参数 reducer 第一个参数为必填项
const store = createStore(reducer)
export default store
3-定义reducer(纯函数) /src/store/reduces/reducer
let initState = {
msg:'initState',
list:[]
}
// 参数 当前需要被共享的state
// 用于改变state的action信号
function reducer(state,action){
state = initState
switch (action.type) {
case 1:
// 进行深复制
let newState = JSON.parse(JSON.stringify(state))
newState.msg = action.payload
return newState
default:
return state
}
}
4-定义action /src/store/actions/actions
export function changeMsg(payload){
// 返回一个action
return{
type:1,
payload
}
}
5-App.js中进行上下文关联
npm install react-redux -S 把store与react进行关联
//App.js
// 状态管理 通过上下文把store的数据注入组件
import { Provider } from 'react-redux'
import store from '@/store'
//render(){}
return(
<HashRouter>
<Provider store={store}>
<Provider>
</HashRouter>
)
6-使用store /views/home.js
dispath: 接收一个action然后派发到reducer
import React from 'react'
import { connect } from 'react-redux'
import { changeMsg } from '@/store/actions'
//把state中的数据变映射到当前组件的props中
function mapStateToProps(state){
return{ msg:state.msg}
}
// 把actions中的方法映射到前组件的props中
function mapActionToProps(dispath){
return {
homeChangeMsg:(payload)=>{dispath(changeMsg(payload))}
}
// 使用
click(){
this.props.homeChangeMsg('homeChangeMsg')
}
render() {
return (
<div>
<h1>Home</h1>
{this.props.msg}
<button onClick={this.click.bind(this)}>点击</button>
</div>
)
}
//抛出
export default connect(mapStateToProps,mapActionToProps)(Home)
store三大原则
1-单一数据源 整个应用的state都只存在唯一一个store
2-State是只读的 用深复制创建一个state的副本进行返回
3-使用纯函数reducer来执行修改 让相同的输入得到相同的输出
reducer和action拆分
使用combineReducers API进行reducer组装
// reducer 的组装
import { createStore,combineReducers } from 'redux'
import testReducer from './reducers/test'
import todoReducer from './reducers/todo'
// combinReducers 合并 reducer
// 组件使用时就为 state.todo.msg
const reducer = combineReducers({
test:testReducer,
todo:todoReducer
})
const store = createStore(reducer)
export default store
action不用刻意拆分,在需要的组件中引入即可
import { connect } from 'react-redux'
import {
addTodo,
delTodo,
updateTodo,
clearTodo
} from '@/store/actions/todoAction'
// ------
function mapActionToProps(dispatch) {
return {
addTodo: (payload) => dispatch(addTodo(payload)),
delTodo: (payload) => dispatch(delTodo(payload)),
updateTodo: (payload) => dispatch(updateTodo(payload)),
clearTodo: (payload) => dispatch(clearTodo(payload))
}
}
// ------
数据抽取actionType
action和reducer的数据都来源于此,便于修改也不容易命名冲突
// 把所有的action的type都定义在这里
// 只要这里的不冲突 随意改动不会印象 action和reducertype的统一性
export const TEST_MSG = '1'
export const TODO_ADD = 'TODO_ADD'
export const TODO_DEL = 'TODO_DEL'
小总结:
1-创建store const store = createStore(reducer)
2-定义reducer function reducer(state,action){...}
3-定义action function changeMsg(payload){ return{...}}
4-关联至App <Provider store={store}><Provider>
5-组件中使用 function mapStateToProps(state){}
function mapActionToProps(dispath){}
export default connect(mapStateToProps,mapActionToProps)(Home)
6-拆分reducr和action,抽取出数据到新的模块 actionType
注:把action与reducer关联 通过dispatch函数 接收一个action然后派发到reducer
项目经验
1-登录权限
管理系统, login - 内部页面
用token来获取所有需要初始的东西:角色、菜单列表
NAVLink几乎都是根据后端返回的菜单进行生成
Route是根据views目录生成的
打包
1-考虑环境 - axios中的baseURl修改成测试环境、生成环境
2-图片加速 - 修改为静态加速地址
3-执行npm run build