本文共 13029 字,大约阅读时间需要 43 分钟。
使用react-mobx-starter-master脚手架,解压更名为frontend。在src中新增component、service、css目录。
修改项目信息:
{"name": "blog","description": "blog project","author": "sun"}
webpack.confifig.dev.js
devServer: { compress: true, port: 3001, publicPath: '/assets/', hot: true, inline: true, historyApiFallback: true, stats: { chunks: false }, proxy: { '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true, pathRewrite: {"^/api": ""} # 路径重写 } } }
安装依赖:$ npm install
npm会安装package.json中依赖的包。也可以使用新的包管理工具yarn安装模块
yarn安装$ npm install -g yarn或者,去 https://yarn.bootcss.com/docs/install/相当于 npm install$ yarn相当于npm install react-router$ yarn add react-router$ yarn add react-router-dom
前端路由使用react-router组件完成
官网文档 https://reacttraining.com/react-router/web/guides/quick-start
基本例子 https://reacttraining.com/react-router/web/example/basic
使用react-router组件,更改src/index.js
import React from 'react';import ReactDom from 'react-dom';import {Route, Link, BrowserRouter as Router, Switch} from 'react-router-dom';const Home = () => ();const About = () => (Home
);const App = () => (About
);ReactDom.render( , document.getElementById('root'));
Route指令:
路径匹配:
exact 只能匹配本路径,不包含子路径;strict 路径尾部有/,则必须匹配这个/,也可以匹配子路径 ;exact strict 一起用,表示严格的等于当前指定路径
Switch指令:也可以将Route组织到一个Switch中,一旦匹配Switch中的一个Route,就不再匹配其它。但是Route是匹配所 有,如果匹配就会显示组件,无path的Route始终匹配。
登录组件:在component目录下构建react组件。登录页模板 https://codepen.io/colorlib/pen/rxddKy?q=login&limit=all&type=type-pens;
特别注意 :
login.js
在component目录下建立login.js的登录组件。 使用上面的模板的HTML中的登录部分,挪到render函数中。 修改class为className
import React from 'react';import {Link} from 'react-router-dom';export default class Login extends React.Component { render() { return (); }}
index.js:在路由中增加登录组件:
import Login from './component/login';const App = () => ();
注册组件:与登录组件编写方式差不多,创建component/reg.js,使用login.css
import React from 'react';import { Link } from 'react-router-dom';import '../css/login.css'export default class Reg extends React.Component { render() { return (); }}
分层:
在src/service/user.js
export default class UserService { login (email, password) { // TODO }}
src/component/login.js:
// src/component/login.jsimport React from 'react';import {Link} from 'react-router-dom';import '../css/login.css'export default class Login extends React.Component { handleClick(event) { console.log(event.target) } render() { return (); }}
问题:
import React from 'react';import {Link} from 'react-router-dom';import '../css/login.css'import UserService from '../service/user';const service = new UserService();export default class Login extends React.Component { render() { return <_Login service={service} />; }}class _Login extends React.Component { handleClick(event) { event.preventDefault(); let fm = event.target.form; this.props.service.login( fm[0].value, fm[1].value ); } render() { return (); }}
代理设置:修改webpack.confifig.dev.js文件中proxy部分,要保证proxy的target是后台服务的地址和端口,且要开启后台服 务。注意,修改这个配置,需要重启dev server
axios异步库:axios是一个基于Promise的HTTP异步库,可以用在浏览器或nodejs中。 使用axios发起异步调用,完成POST、GET方法的数据提交。可参照官网的例子。中文说明 https://www.kancloud.cn/yunye/axios/234845
安装:$ yarn add axios
service/user.js修改如下:
import axios from 'axios';export default class UserService { login (email, password) { console.log(email, password); axios.post('/api/users/login', { email:email, password:password })/* dev server会代理 */ .then( function (response) { console.log(response); console.log(response.data); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); } ).catch( function (error) { console.log(error); } ) }}
问题:
404:填入邮箱、密码,点击登录按钮,返回404,查看Python服务端,访问地址是 /api/users/login ,也就是多
了/api。如何解决?
1、修改blog server的代码的路由匹配规则,不建议这么做,影响有点大
2、rewrite,类似httpd、nginx等的rewrite功能。本次测试使用的是dev server,去官方看看。https://webpack.js.org/confifiguration/dev-server/#devserver-proxy
可以查到pathRewrite可以完成路由重写。
devServer: { compress: true, port: 3001, publicPath: '/assets/', hot: true, inline: true, historyApiFallback: true, stats: { chunks: false }, proxy: { '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true, pathRewrite: {"^/api": ""} } } }
重启dev server。使用正确的邮箱、密码登录,返回了json数据,在response.data中可以看到token、user。
store.js:store.js 是一个兼容所有浏览器的 LocalStorage 包装器,不需要借助 Cookie 或者 Flash。 store.js 会根据浏览器自动选择使用 localStorage、globalStorage 或者 userData 来实现本地存储功能。
安装:$ yarn add store
let store = require('store')store.set('user', 'sun')console.log(store.get('user'))store.remove('user')console.log(store.get('user'))console.log(store.get('user', 'a'))store.set('user', {name:'sun', age: 29})console.log(store.get('user').name)store.each(function(value, key) { console.log(key, value)})// user { name: 'sun', age: 29 }store.clearAll() // 清除所有console.log(store.get('user')) // undefinedconst store = require('store')store.addPlugin(require('store/plugins/expire'))// 一定要加载插件,否则key永远不会过期let d = new Date()store.set('user', 'sun', (new Date()).getTime() + (5 * 1000)) // 注意时间单位setInterval(() => console.log(store.get('user', 'abc')), 1000)
社区提供的状态管理库,有Redux和Mobx。Redux代码优秀,使用严格的函数式编程思想,学习曲线陡峭,小项目使用的优势不明显。 Mobx,非常优秀稳定的库,简单方便,适合中小项目使用。使用面向对象的方式,容易学习和接受。现在在中小 项目中使用也非常广泛。Mobx和React也是一对强力组合。
Mobx官网 https://mobx.js.org/
中文网 http://cn.mobx.js.org/
MobX 是由 Mendix、Coinbase、Facebook 开源,它实现了观察者模式。
观察者模式,也称为发布订阅模式,观察者观察某个目标,目标对象(Obserable)状态发生了变化,就会通知自己内部注册了的观察者Observer。
状态管理 :
需求 :一个组件的onClick触发事件响应函数,此函数会调用后台服务。但是后台服务比较耗时,等处理完,需要引起组
件的渲染操作。要组件渲染,就需要改变组件的props或state。
异步调用:思路一、使用setTimeout ,使用setTimeout,有2个问题。
思路二、Promise异步执行:Promise异步执行,如果成功执行,将调用回调。
import React from 'react';import ReactDom from 'react-dom';class Service { handle(obj) { // Promise new Promise((resolve, reject) => { setTimeout(() => resolve('OK'), 5000); }).then( value => { // 使用obj obj.setState({ ret: (Math.random() * 100) }); } ) }}class Root extends React.Component { state = { ret: null } handleClick(event) { // 异步不能直接使用返回值 this.props.service.handle(this); } render() { console.log('***********************'); return ({ new Date().getTime() } Service中修改state的值是: { this.state.ret }); }}ReactDom.render( < Root service = {new Service()}/>, document.getElementById('root'));
不管render中是否显示state的值,只要state改变,都会触发render执行。
Mobx实现 :
observable装饰器:设置被观察者
observer装饰器:设置观察者,将React组件转换为响应式组件
import React from 'react';import ReactDom from 'react-dom';import { observable} from 'mobx';import { observer} from 'mobx-react';class Service { @observable ret = -100; handle() { // Promise new Promise((resolve, reject) => { setTimeout(() => resolve('OK'), 2000); }).then( value => { this.ret = (Math.random() * 100); console.log(this.ret); } ) }}@observer // 将react组件转换为响应式组件class Root extends React.Component { //state = {ret:null} // 不使用state了 handleClick(event) { // 异步不能直接使用返回值 this.props.service.handle(this); } render() { console.log('***********************'); return ({ new Date().getTime() } Service中修改state的值是: { this.props.service.ret }); }}ReactDom.render( < Root service = {new Service()}/>, document.getElementById('root'));
Service中被观察者ret变化,导致了观察者调用了render函数。被观察者变化不引起渲染的情况:将上例中的 {this.props.service.ret} 注释 {/*this.props.service.ret*/} 。可以看到,如果在render中不使用这个被观察者,render函数就不会调用。在观察者的render函数中,一定要使用这个被观察对象。
跳转:如果service中ret发生了变化,观察者Login就会被通知到。一般来说,就会跳转到用户界面,需要使用Redirect组
件。
// 导入Redirectimport {Link, Redirect} from 'react-router-dom';//render函数中returnreturn; //to表示跳转到哪里
login登录功能代码实现:
import axios from 'axios'import store from 'store'import expire from 'store/plugins/expire'import { observable } from 'mobx';import '../css/login.css'// const store = require('store')// store.addPlugin(require('store/plugins/expire')) // 这一一定要引一下,否则不会有过期时间store.addPlugin(expire)export default class UserService { // @observable ret = 0 // 这个值的变化会引起login组件的render // @observable ret = '' @observable loggedin = false @observable errMsg = '' @observable reged = false; login(email, password){ // todo console.log(email, password) axios.post('/api/users/login', { email: email, password: password }).then( response => { {/* 此函数与要解决this的问题,具体参考this的坑 */} // console.log(response) console.log(response.data) console.log(response.status) // console.log(response.statusText) let token = response.data.token store.set('token', token, (new Date()).getTime() + (8 * 3600 * 1000)) // 设置token过期时间 // let ret = Math.random() * 1000 + 1000 // console.log(ret) // this.ret = response.data.user.name // console.log(this.ret) this.loggedin = true } ).catch( error => { // this的坑 const {error: err=''} = error.response.data // err是别名 console.log(err) this.errMsg = err } ) } reg(name, email, password) { console.log(name, email, password) axios.post('/api/users/', { email: email, password: password, name: name }).then( response => { {/* 此函数与要解决this的问题,具体参考this的坑 */} // console.log(response) console.log(response.data) console.log(response.status) // console.log(response.statusText) let token = response.data.token store.set('token', token, (new Date()).getTime() + (8 * 3600 * 1000)) // 设置token过期时间 // let ret = Math.random() * 1000 + 1000 // console.log(ret) // this.ret = response.data.user.name // console.log(this.ret) this.reged = true } ).catch( error => { const {error: err=''} = error.response.data // err是别名 console.log(err) this.errMsg = err } ) }}const userService = new UserService()export {userService}
转载地址:http://ztfvi.baihongyu.com/