项目目录搭建
src
components 公共组件
pages 一些页面
assets 资源
utils 工具函数
utils.js
api 异步请求相关
redux
config 配置文件(路由配置)
routes.js
App.js
index.js
config-overrides.js
项目环境搭建
rem适配环境搭建
下载依赖包
yarn add react-app-rewired customize-cra babel-plugin-import postcss-px2rem -D
- react-app-rewired 用于重写脚手架配置 会覆盖react教授架中的配置
- customize-cra 是react-app-rewired依赖的包
- babel-plugin-import 用于ant-mobile按需加载
- postcss-px2rem 将px转成rem
React 手机组件库
yarn add antd-mobile
修改默认配置
- 找到package.json将script修改成下面的样子
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
创建config-overrides
- 在项目根目录创建文件config-overrides.js(用于覆盖脚手架默认配置)
- 和package.json package-lock.json同级
const {
override, // 专门用来覆盖webpack配置项的
fixBabelImports, // antd使用的一个包, 实现antd按需加载
addPostcssPlugins, // 将我们的px-> rem的包
addDecoratorsLegacy
} = require('customize-cra')
module.exports = override(
// antd按需加载
fixBabelImports('import', {
libraryName: 'antd-mobile',
style: 'css'
}),
// rem适配. 将px改为rem
// remUnit: 表示以iphone6手机为例的跟字体
// 值为 37.5时, 表示使用rem方式二的方式进行适配
// 值为 100是, 表示使用rem方式一的方式进行适配
// 注意: 这个插件,仅仅是帮助我们将px改成rem. 设置根字体这个事,还需要程序员自己做
addPostcssPlugins([require('postcss-px2rem')({ remUnit: 100 })]),
// 使用修饰符语法,针对rc-form这个组件使用
addDecoratorsLegacy()
)
在utils目录定义移动端适配代码
// 定义移动端适配的代码
function adapter() {
// 计算字体的大小
// const fontSize = document.documentElement.client / 10;
const fontSize = (document.documentElement.clientWidth * 100) / 375;
// 设置给根标签
document.documentElement.style.fontSize = fontSize + "px";
}
adapter()
window.onresize = adapter
在index.js中引入适配rem的文件
import React from "react"
import ReactDOM from "react-dom"
import App from "./APP"
/*
引入rem适配的代码 fixled.js这个文件,并没有导入任何内容,只是需要执行一下,所以import后面直接写路径即可
*/
import "./utils/fixed"
ReactDOM.render(<App/>,document.getElementById("root"))
测试
- 添加app.css文件,给app默认div设置样式,检查这些插件是否生效
*{
margin:0;
padding: 0;
}
#box{
width: 345px;
height: 150px;
margin: 15px;
background-color: red;
}
路由搭建
react-router-dom
yarn add react-router-dom
routers.js
- 在config下面创建routers.js
import React from "react";
const Home = React.lazy(() => import("../pages/Home"));
const Login = React.lazy(() => import("../pages/Login"));
const RegisterPhone = React.lazy(() =>
import("../pages/Register/RegisterPhone/RegisterPhone")
);
const RegisterCode = React.lazy(() =>
import("../pages/Register/RegisterCode/RegisterCode")
);
const RegisterPassword = React.lazy(() =>
import("../pages/Register/RegisterPassword")
);
const RegisterCountry = React.lazy(() => import("../pages/Country"));
// 定义一个数组. 数组里面有多个个数据, 对应的就会动态的渲染出来多少个Route组件
const routes = [
{
path: "/",
component: Home,
exact: true,
},
{
path: "/home",
component: Home,
},
{
path: "/login",
component: Login,
},
{
path: "/register/phone",
component: RegisterPhone,
},
{
path: "/register/code",
component: RegisterCode,
},
{
path: "/country",
component: RegisterCountry,
},
{
path: "/register/password",
component: RegisterPassword,
},
];
export { routes };
懒加载
传统的import方式,会将代码中写的所有组件,在最开始答应页面的时候,全部加载下来.性能不好
懒加载: 就是什么时候用到这个组件了,这个组件才加载
但是: 如果使用了懒加载,必须配合suspense这个组件使用
为什么? 因为从加载组件到组件加载成功渲染到页面的这段时间.用户看到的是一个空白的屏幕,用户体验不好.最好就是展示一个正在加载的效果. suspense就是帮助我们加载这个效果的
// 传统
import Home from "../pages/Home";
import Login from "../pages/Login";
// 懒加载
const Home = React.lazy(() => import("../pages/Home"));
const Login = React.lazy(() => import("../pages/Login"));
import { lazy } from "react";
// 使用路由懒加载
// lazy不能单独使用,必须配置Suspence组件才能一起使用
const Home = lazy(() =>
import(
"../pages/Home"
)
);
const routes = [
{
path: "/",
component: Home,
},
];
export default routes;
Suspense使用
import {Suspense} from 'react'
// 使用suspense包裹整个应用
<Suspense fallback={正在加载的结构,可以设置加载中图片啊之类}>
...结构
</Suspense>
import React, { Component, Suspense } from "react";
import { Route, BrowserRouter as Router } from "react-router-dom";
import "./app.css";
// 引入要渲染的router
import { routes } from "./config/routes";
export default class App extends Component {
render() {
return (
<Suspense fallback={<div>loading.......</div>}>
<Router>
{routes.map((item) => {
return (
<Route
key={item.path}
path={item.path}
component={item.component}
exact={item.exact}
></Route>
);
})}
</Router>
</Suspense>
);
}
}
项目
搭建页面
使用antd-mobile组件
标题 NavBar
文本输入 InputItem
上下空白 WhiteSpace
左右空白 WingBlank
警告框 Modal
表单校验
使用rc-form库实现
受控组件建议使用rc-form
安装
yarn add rc-form
引入高阶组件函数
import { createForm } from 'rc-form'
调用createForm()()
- 将要进行表单效验的组件,传入createForm的第二次调用
- 使用createForm 得到一个高级组件. 这个高阶组件,会自动传入一个form对象给当前这个组件
export default createForm()(VerifyPhone)
在InputItem使用表单效验
- 通过props获取. form对象身上有一个getFieldProps函数,可以帮助我们做表单校验
const { getFieldProps } = this.props.form
<InputItem
// getFieldProps第一个参数是字段名, 第二个参数可以忽略, 第三个参数是一个对象
{...getFieldProps('phone', {
// rules表示当前表单的校验规则
rules: [
{
// validator表示使用自定义校验规则
validator: this.validator
}
]
})}
clear
placeholder='请输入手机号'
>
</InputItem>
this.getFieldValue(字段名)
- 可以获取到我们通过getFieldProps设置的字段名里的值
const phone = this.props.form.getFieldValue('phone')
定义validator函数
validator = (rule, value, callback) => {
// 第一个参数是一个对象.可以获取到表单量字段名
// 第二个参数是表单项的值
// 第三个参数是一个回调函数,必须手动调用一个,表示验证成功
let isDisabled = true
if (/^1[3456789][\d]{9}$/.test(value)) {
isDisabled = false
}
this.setState({
isDisabled
})
callback()
}
Object.keys()
会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
// simple array
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']
// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']
// getFoo is a property which isn't enumerable
var myObj = Object.create({}, {
getFoo: {
value: function () { return this.foo; }
}
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']
localStorage.setItem( keyName, keyValue)
setItem() 作为 Storage 接口的方法,接受一个键名和值作为参数,将会把键名添加到存储中,如果键名已存在,则更新其对应的值。
- 注意:localStorage只能存储字符串,不能存储对象和数组
- 如果要存储数组/对象,应该将数组/对象转成JSON格式的字符串,然后再存储
下面的函数在本地存储中创建三个数据项。
function populateStorage() {
localStorage.setItem('bgcolor', 'red');
localStorage.setItem('font', 'Helvetica');
localStorage.setItem('image', 'myCat.png');
}
- 存储对象和数组
var arr = [{ name: 'zs' }, { age: 18 }]
// 如果要存数组/对象,应该将数组/对象转成json格式的字符串,然后再存储
const arrStr = JSON.stringify(arr)
localStorage.setItem('test', arrStr)
const str = localStorage.getItem('test')
console.log(str)
const res = JSON.parse(str)
console.log(res)
localStorage.getItem()
getItem() 作为 Storage 接口的方法,接受一个键名(key name)作为参数,并返回对应键名的值(key’s value)。
const phone = this.props.location.state.phone
const phone = localStorage.getItem('phone')
this.props.location.state.xxx
- 可以获取到push和replace方法传递过来的第二个参数
const phone = this.props.location.state.phone
this.props.history.replace('/register/phone', {
phone
})
axios
GET
import axios from 'axios'
export function getCountryData() {
return axios('/common/countryData', {
method: 'GET'
})
}
POST
export function loginPhone(phone, code) {
return axios('/login/phone', {
method: 'POST',
data: {
phone,
code
}
})
}
解决跨域问题
在package.json中添加proxy以后,发送ajax请求直接写地址就好,不需要写服务器地址