前端開發及登錄功能實現–Django播客系統(九)
文章目錄
開發環境設置
-
使用react-mobx-starter-master腳手架,解壓更名爲DjangoNode。
-
在src中新增component、service、css目錄
-
注意:沒有特別說明,js開發都在src目錄下
-
目錄結構
DjangoNode/
│-.babelrc
│-.gitignore #git的忽略文件
│-.npmrc #npm的服務器配置,本次使用的是阿里
│-index.html #dev server使用的首頁
│-jsconfig.json
│-LICENSE
│-package-lock.json
│-package.json #項目的根目錄安裝文件,使用npm install可以構建項目
│-README.md
│-webpack.config.dev.js #開發用的配置文件
│-webpack.config.prod.js #生產環境,打包用的配置文件
└─src/
│- componet/ #自己組件文件夾
│- service/ #服務程序
│- css/ #樣式表
│- index.html #模板頁面
│- index.js
-
修改項目信息
package.json
文件{ "name":"blog", "description":"blog project", "author":"xdd" }
-
修改
webpack.config.dev.js
devServer: { compress: true, /* gzip */ //host:'192.168.61.109', /* 設置ip */ port: 3000, publicPath: '/assets/', /* 設置bundled files瀏覽器端訪問地址 */ hot: true, /* 開啓HMR熱模塊替換 */ inline: true, /* 控制瀏覽器控制檯是否顯示信息 */ historyApiFallback: true, stats: { chunks: false }, proxy: { //代理 '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true } } }
-
安裝依賴
npm install
- npm會按照package.json中依賴的包。也可以使用新的包管理工具yarn安裝模塊
- 使用yarn替換npm安裝,速度會更快寫,yarn是並行安裝,npm是串行安裝。
yarn安裝 $ npm install -g yarn 或者,去https://yarn.bootcss.com/docs/install/ 相當於npm install $ yarn * 如果想自己構建腳手架,可以使用如下命令 $npm install #構建腳手架 相當於npm install react-router #添加react-router組件 $ yarn add react-router # 安裝路由,即項目前端web路由 $ yarn add react-router-dom #
-
相關命令
npm命令 Yarn命令 解釋 npm install
yarn install
安裝 npm install [package] --save
yarn add [package]
安裝運行時依賴 npm install [package] --save-dev
yarn add [package] --dev
安裝開發時依賴 npm install [package] --global
yarn global add [package]
全局安裝 npm uninstall [package]
yarn remove [package]
卸載
開發
前端路由
- 前端路由使用react-router組件完成
- 官方文檔https://reacttraining.com/react-router/web/guides/quick-start
- 基本例子https://reacttraining.com/react-router/web/example/basic
- 使用react-route主鍵,更改src/index.js
/* src/index.js文件 */
import React from 'react';
import ReactDom from 'react-dom';
import {Route,Link,BrowserRouter as Router,Switch} from "react-router-dom";
function App() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/users" component={Users} />
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
const Users = () => {
return (
<div>
<h2>Users</h2>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'));
-
注意:
- Link組件相當於a標籤
- Route組件不可見,用來做Router的路由定義。當網頁中的地址欄中地址發生改變,會從Route中匹配對應的路徑加載對應的組件。
-
啓動項目
yarn run start
- start是在配置文件
dockerNode/package.json
中已經定義好了,如下:
{ "scripts": { "test": "jest", "start": "webpack-dev-server --config webpack.config.dev.js --hot --inline", "build": "rimraf dist && webpack -p --config webpack.config.prod.js" }, }
- start是在配置文件
-
在地址欄中輸入
http://127.0.0.1:3000/
或http://127.0.0.1:3000/about
能夠看到頁面的變化。 -
App中,使用了Router路由組件,Router是根,且它只能有一個元素,所以添加了Div
- 訪問
http://127.0.0.1:3000/
- 訪問
http://127.0.0.1:3000/about
- 訪問
-
前端路由:通過一個url加載一個組件,將原來的組件替換。
Route指令
- 它負責靜態路由,只能和Route指定的path匹配,組件就可以顯示。URL變化,將重新匹配路徑
- component屬性設置目標組件
- path是匹配路徑,如果匹配則顯示組件
exact
:布爾值strict
:布爾值
- 沒有path屬性,組件將總是顯示,例如
<Route component={Always} />
- path屬性還支持路徑數組,意思是多個路徑都可以匹配
/* 修改src/index.js文件*/
function App() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
<hr />
<Route exact path={["/","/index"]} component={Home} />
<Route path="/about" component={About} />
<Route path="/users" component={Users} />
<Route component={Always} />
</div>
</Router>
);
}
function Always(){
return(
<div id="footer">
<span>Copyright 2009-2019 xdd.com</span>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'));
-
路由配置
- exact 只能匹配本路徑,不包含子路徑
- strict 路徑尾部有
/
,則必須匹配這個/
,也可以匹配子路徑| - exact strict 一起用,表示嚴格的等於當前指定路徑
路徑 /about
/about/
/about/123
path="/about"
√ √ √ exact path="/about"
√ √ exact path="/about/"
√ √ strict path="/about"
√ √ √ exact strict path="/about"
√ strict path="/about/"
√ √ exact strict path="/about/"
√ -
Switch指令
- 也可以將Route組織到一個Switch中,一旦匹配Switch中的一個Route,就不再匹配其他。但是Route是匹配所有,如果匹配就會顯示組件,無path的Route始終匹配。
- 示例:
/* 修改src/index.js文件*/ function App() { return ( <Router> <div> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/about">About</Link> </li> <li> <Link to="/users">Users</Link> </li> </ul> <hr /> <Switch> <Route path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/users" component={Users} /> <Route component={Always} /> </Switch> </div> </Router> ); }
- 注意這個時候Always組件,其實是404組件了,因爲只有Switch中其上的Route沒有匹配,才輪到它。
登錄組件
- 在component目錄下構建react組件
- 登錄頁面模板https://codepen.io/colorlib/pen/rxddKy?q=login&limit=all&type=type-pens
<div class="login-page">
<div class="form">
<form class="register-form">
<input type="text" placeholder="name"/>
<input type="password" placeholder="password"/>
<input type="text" placeholder="email address"/>
<button>create</button>
<p class="message">Already registered? <a href="#">Sign In</a></p>
</form>
<form class="login-form">
<input type="text" placeholder="username"/>
<input type="password" placeholder="password"/>
<button>login</button>
<p class="message">Not registered? <a href="#">Create an account</a></p>
</form>
</div>
</div>
- 使用這個HTML模板來構建組件
- 特別注意
- 搬到React組件中的時候,要將class屬性改爲className.
- 所有標籤,需要閉合。
-
login.js
- 在component目錄下新建login.js的登錄組件。
- 使用上面的模板的HTML中的登錄部分,挪到render函數中。
- 修改class爲className
- 將
<a>
標籤替換成<Link to="?">
組件 - 注意標籤閉合問題
/* 新建文件src/component/login.js */ import React from "react"; import {Link} from "react-router-dom"; export default class Login extends React.Component { render() { return ( <div className="login-page"> <div className="form"> <form className="login-form"> <input type="text" placeholder="username"/> <input type="password" placeholder="password"/> <button>登錄</button> <p className="message">還未註冊? <Link to="#">請註冊</Link></p> </form> </div> </div> ) } }
-
在
src/index.js
路由中增加登錄組件/* 修改src/index.js文件內容 */ import React from 'react'; import ReactDom from 'react-dom'; import {Route,Link,BrowserRouter as Router,Switch} from "react-router-dom"; import Login from "./component/login"; function App() { return ( <Router> <div> <Route path="/about" component={About} /> <Route path="/login" component={Login} /> <Route exact path="/" component={Home} /> </div> </Router> ); } function Home() { return ( <div> <h2>Home</h2> </div> ); } function About() { return ( <div> <h2>About</h2> </div> ); } ReactDom.render(<App />, document.getElementById('root'));
- 訪問
http://127.0.0.1:3000/login
就可以看到登錄界面了,但是沒有樣式。
- 訪問
-
樣式表
- 在
src/css
中,創建login.css,放入以下內容,然後src/component/login.js
中導入樣式
/* src/component/login.js中導入樣式表 */ import React from "react"; import {Link} from "react-router-dom"; import "../css/login.css";
/* 新建src/css/login.css文件 */ body { background: #456; font-family: SimSun; font-size: 14px; } .login-page { width: 360px; padding: 8% 0 0; margin: auto; } .form { font-family: "Microsoft YaHei", SimSun; position: relative; z-index: 1; background: #FFFFFF; max-width: 360px; margin: 0 auto 100px; padding: 45px; text-align: center; box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24); } .form input { outline: 0; background: #f2f2f2; width: 100%; border: 0; margin: 0 0 15px; padding: 15px; box-sizing: border-box; font-size: 14px; } .form button { text-transform: uppercase; outline: 0; background: #4CAF50; width: 100%; border: 0; padding: 15px; color: #FFFFFF; font-size: 14px; cursor: pointer; } .form button:hover,.form button:active,.form button:focus { background: #43A047; } .form .message { margin: 15px 0 0; color: #b3b3b3; font-size: 12px; } .form .message a { color: #4CAF50; text-decoration: none; }
- 訪問
http://localhost:3000/login
可以看到如下界面
- 在
註冊組件
- 與登錄組件編寫方式差不多,創建
src/component/reg.js
,使用login.css
/*新建src/component/reg.js文件 */
import React from "react";
import {Link} from "react-router-dom";
import "../css/login.css"
export default class Reg extends React.Component {
render(){
return(
<div className="login-page">
<div className="form">
<form className="register-form">
<input type="text" placeholder="name"/>
<input type="text" placeholder="email" />
<input type="password" placeholder="密碼"/>
<input type="password" placeholder="確認密碼"/>
<button>註冊</button>
<p className="message">如果已經註冊 <Link to="#">請登錄</Link></p>
</form>
</div>
</div>
)
}
}
- 在
src/index.js
中增加一條靜態路由
/* src/index.js中修改*/
import Reg from "./component/reg";
function App() {
return (
<Router>
<div>
<Route path="/about" component={About} />
<Route path="/login" component={Login} />
<Route path="/reg" component={Reg} />
<Route exact path="/" component={Home} />
</div>
</Router>
);
}
- 訪問
http://localhost:3000/reg
可以看到如下界面
導航欄鏈接
- 在index.js中增加導航欄鏈接,方便頁面切換
/* 修改src/index.js文件*/
function App() {
return (
<Router>
<div>
<div>
<ul>
<li><Link to="/">主頁</Link></li>
<li><Link to="/login">登錄</Link></li>
<li><Link to="/reg">註冊</Link></li>
<li><Link to="/about">關於</Link></li>
</ul>
</div>
<Route path="/about" component={About} />
<Route path="/login" component={Login} />
<Route path="/reg" component={Reg} />
<Route exact path="/" component={Home} />
</div>
</Router>
);
}
分層
層次 | 作用 | 路徑 |
---|---|---|
視圖層 | 負責數據呈現,負責用戶交互界面 | src/component/xxx.js |
服務層 | 負責業務邏輯處理 | src/service/xxx.js |
Model層 | 數據持久化 |
登錄功能實現
- view層,登錄組件和用戶交互。相當於button點擊觸發onclick,調用事件響應函數handleClick,handleClick中調用服務service層login函數。
- service層,負責業務邏輯處理。調用Model層數據操作函數
- 新建
src/service/user.js
處理調用邏輯
/* 新建src/service/user.js邏輯 */
export default class UserService {
login (email,password) {
//Tood
}
}
- 修改
src/component/login.js
文件
/* 修改`src/component/login.js`文件 */
import 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 (
<div className="login-page">
<div className="form">
<form className="login-form">
<input type="text" placeholder="email"/>
<input type="password" placeholder="password"/>
<button onClick={this.handleClick.bind(this)}>登錄</button>
<p className="message">還未註冊? <Link to="#">請註冊</Link></p>
</form>
</div>
</div>
)
}
}
- 問題:
- 頁面提交
- 這次發現有一些問題,按鈕點擊會提交,導致頁面刷新了。要阻止頁面刷新,其實就是阻止提交。使用event.preventDefault()。
- 如何拿到郵箱和密碼?
event.target.form
返回暗流所在表單,可以看做一個數組。fm[0].value
和fm[1].value
就是文本框的值。
- 如何在Login組件中使用UserService實例呢?
- 使用全局變量,雖然可以,但不好。
- 可以在Login的構造器中通過屬性注入。
- 也可以在外部使用props注入。使用這種方式。
- 頁面提交
- 修改,保證在login組件中使用UserService,使用屬性注入
/* 修改src/component/login.js文件 */
import React from "react";
import {Link} from "react-router-dom";
import "../css/login.css";
import UserService from "../service/user";
const serviec = new UserService();
export default class Login extends React.Component{
render(){
return <_Login service={serviec} />;
}
}
class _Login extends React.Component {
handleClick(event){
event.preventDefault(); /* 阻止from表單提交 */
let fm = event.target.form;
this.props.service.login(fm[0].value,fm[1].value)
}
render() {
return (
<div className="login-page">
<div className="form">
<form className="login-form">
<input type="text" placeholder="email"/>
<input type="password" placeholder="password"/>
<button onClick={this.handleClick.bind(this)}>登錄</button>
<p className="message">還未註冊? <Link to="#">請註冊</Link></p>
</form>
</div>
</div>
)
}
}
UserService的login方法實現
-
代理配置
- 修改webpack.config.dev.jsw文件中proxy部分,保證proxy的target是後臺服務的地址和端口,且要開啓後臺服務。
- 注意:修改這個配置,需要重啓dev server
/* 修改webpack.config.dev.jsw文件 */ devServer: { compress: true, /* gzip */ //host:'192.168.61.109', /* 設置ip */ port: 3000, publicPath: '/assets/', /* 設置bundled files瀏覽器端訪問地址 */ hot: true, /* 開啓HMR熱模塊替換 */ inline: true, /* 控制瀏覽器控制檯是否顯示信息 */ historyApiFallback: true, stats: { chunks: false }, proxy: { //代理 '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true } } }
-
axios異步庫
- axios是一個基於Promise的HTTP異步庫,可以用在瀏覽器或nodejs中。
- 使用axios發起異步調用,完成POST、GET方法的數據提交。可以查照官網的例子。中文說明https://www.kancloud.cn/yunye/axios/234845
-
安裝npm
$ npm install axios
或yarn add axios
- 注意:如果使用yarn安裝,就不要再使用npm安裝包了,以免出現問題。
-
導入
import axios from '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: " + response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); } ).catch(/* 出錯後執行函數 */ function(error){ console.log(error); } ) } }
- 修改
-
問題:
-
404 填入郵箱,密碼,點擊登錄,返回404,查看發現訪問的地址是
http://127.0.0.1:3000/api/users/login
,也就是多了/api
。
-
解決:
- 修改blog server的代碼的路由匹配規則(不建議這麼做,影響比較大)
- rewrite,類似httpd,nginx等的rewrite功能。本次測試使用的是dev server,去官方看看。https://webpack.js.org/configuration/dev-server/#devserver-proxy可以看到pathRewrite可以完成路由重寫。
-
修改
webpack.config.dev.js
文件/* 修改webpack.config.dev.js文件中對應內容*/ devServer: { compress: true, /* gzip */ //host:'192.168.61.109', /* 設置ip */ port: 3000, publicPath: '/assets/', /* 設置bundled files瀏覽器端訪問地址 */ hot: true, /* 開啓HMR熱模塊替換 */ inline: true, /* 控制瀏覽器控制檯是否顯示信息 */ historyApiFallback: true, stats: { chunks: false }, proxy: { //代理 '/api': { target: 'http://127.0.0.1:8000', pathRewrite: {"^/api" : ""}, //將所有代理親戚中已/api開頭的請求中對應字符替換成空 changeOrigin: true } } }
- 重啓dev server.使用正確的郵箱,密碼登錄,返回了json數據,在response.data中可以看到token、user。
- 重啓dev server.使用正確的郵箱,密碼登錄,返回了json數據,在response.data中可以看到token、user。
-
token持久化–LocalStorage
-
使用LocalStorage來存儲token。
-
LocalStorage是HTML5標準增加的技術,是瀏覽器端持久化方案之一。
-
LocalStorage是爲了存儲瀏覽器得到的數據,例如JSON。
-
數據存儲時鍵值對。數據會存儲在不同的域名下面。
-
不同瀏覽器對單個域名下存儲的數據的長度支持不同,有的最多支持2MB。
-
在Charmo瀏覽器中查看,如下
-
SessionStorage和LocalStorage功能差不多,只不過SessionStorage是會話級的,瀏覽器關閉,會話結束,數據清除。而LocalStorage可以持久保存。
-
indexedDB
- 一個域一個datatable
- key-value檢索方式
- 建立在關係型的數據模型之上,具有索引表、遊標、事務等概念
-
store.js
- store.js是一個兼容所有瀏覽器的LocalStorage包裝器,不需要藉助Cookie或者Flash。
- store.js會根據瀏覽器自動選擇使用localStorage、globalStorage或者userData來實現本地存儲功能。
-
安裝
npm i store
或yarn add store
-
測試代碼
-
編寫一個test.js,使用node exec插件按F8執行
let store = require("store"); store.set("user","xdd"); console.log(store.get("user")); store.remove("user"); console.log(store.get("user")); // undefined console.log(store.get("user","a")); //a store.set("user",{name:"xdd",age:30}); console.log(store.get("user").name); store.set("school",{name:"magedu"}); // 遍歷所有鍵值對 store.each(function(value,key){ //注意這裏key,value是反的 console.log(key,"-->",value) }) //清除所有鍵值對 store.clearAll() console.log(store.get("user"));// undefined
-
安裝store的同時,也安裝了expire過期插件,可以在把kv對存儲到LS中的時候順便加入過期時長。
let store = require("store"); //一定要加載插件,否則key不會過期 store.addPlugin(require("store/plugins/expire")); let d = new Date(); store.set("user","xdd",(new Date()).getTime() + (5 * 1000)); //注意時間單位 setInterval(() => console.log(store.get("user","abc")),1000);
-
下面是準備寫在service中的代碼
import store from "store"; import expire from "store/plugins/expire"; store.addPlugin(expire) //存儲token store.set("token",res.data.token,(new Date()).getTime() + (8*3600*1000));
/* 修改對應的src/service/user.js文件 */ import axios from "axios"; import store from "store"; import expire from "store/plugins/expire"; // 過期插件 store.addPlugin(expire) 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.data) console.log("response.status: " + response.status); // 存儲token,注意需要重開一次chrome的調試窗口才能看到 store.set("token",response.data.token,(new Date()).getTime() + (8*3600*1000)); } ).catch(/* 出錯後執行函數 */ function(error){ console.log(error); } ) } }
-
Mobx狀態管理
- Redux和Mobx
- 社區提供的狀態管理庫,有Redux和Mobx。
- Redux代碼優秀,使用嚴格的函數式編程思想,學習曲線陡峭,小項目使用的優劣不明顯。
- Mobx,非常優秀穩定的庫,簡單方便,適合中小項目使用。使用面向對象的方式,容易學習和接受。現在在中小項目中使用也非常廣泛。Mobx和React也是一對強力組合。
- Mobx官網https://mobx.js.org/
- Mobx中文網https://cn.mobx.js.org/
- Mobx是由Mendix、Coinbase、Facebook開源,它實現了觀察者模式。
- 觀察者模式
- 觀察者模式,也稱爲發佈訂閱模式。觀察者觀察某個目標,目標對象(Obserable)狀態發生了變化,會通知自己內部註冊了的觀察者Observer。
- 狀態管理
-
需求:
- 一個組件的onClick觸發事件響應函數,此函數會調用後臺服務。但是後臺服務比較耗時,等處理完,需要引起組件的渲染操作。
- 要組件渲染,就需要改變組件的props或state。
-
同步調用
- 同步調用中,實際上就是等着耗時的函數返回
-
異步調用
-
思路一,使用setTimeout問題
- 無法向內部的等待執行函數傳入參數,比如Root實例。
- 延時執行的函數的返回值無法取到,所以無法通知Root
-
思路二、Promise異步執行
- Promise異步執行,如果成功,將調用回調。
- 不管render中是否顯示state的值,只要state改變,都會觸發render執行
/* 可以在src/index.js中修改測試代碼如下 */ import React from 'react'; import ReactDom from 'react-dom'; class Service{ handle(obj){ //Promise new Promise((resolve,reject) => { //定時器5秒後返回ok setTimeout(() => resolve("ok"), 5000); }).then(value => { //成功後執行 //使用obj obj.setState({ret:(Math.random()*1000)}); } ) } } class Root extends React.Component{ state = {ret:null} handleClick(event){ //異步不能直接使用返回值 this.props.service.handle(this); } render(){ console.log("*****************") return ( <div> <button onClick={this.handleClick.bind(this)}>觸發handleClick函數</button> <span style={{color:"red"}}> {new Date().getTime()} Service中修改state的值是{this.state.ret}</span> </div> ) } } ReactDom.render(<Root service={new Service()} />, document.getElementById('root'));
-
-
Mobx實現
- observable裝飾器:設置被觀察者
- observer裝飾器:設置觀察者,將React組件轉換爲響應式組件
/* 可以在src/index.js中修改測試代碼如下 */ import React from 'react'; import ReactDom from 'react-dom'; import {observable} from 'mobx'; import {observer} from "mobx-react" class Service{ @observable ret = -100; handle(obj){ //Promise new Promise((resolve,reject) => { //定時器5秒後返回ok setTimeout(() => resolve("ok"), 5000); }).then(value => { //成功後執行 this.ret = Math.random()*1000; } ) } } @observer //將react組件轉換爲響應式組件 class Root extends React.Component{ // state = {ret:null} //不使用state了 handleClick(event){ //異步不能直接使用返回值 this.props.service.handle(this); } render(){ console.log("*****************") return ( <div> <button onClick={this.handleClick.bind(this)}>觸發handleClick函數</button> <span style={{color:"red"}}> {new Date().getTime()} Service中修改state的值是{this.props.service.ret /* 如果不使用,當值改變render就不會被調用 */}</span> </div> ) } } ReactDom.render(<Root service={new Service()} />, document.getElementById('root'));
- Service中被觀察者ret變化,導致了觀察者調用render函數。
- 被觀察者變化不引起渲染的情況:
- 將root中的rander中
{this.props.service.ret}
註釋{/* this.props.service.ret */}
。可以看到,如果render中不使用這個被觀察者,render函數就不會調用。
- 將root中的rander中
- 注意:在觀察者render函數中,一定要使用這個被觀察對象。
-
跳轉
- 如果service中ret發生了變化,觀察者Login就會被通知到。一般來說,就會跳轉到用戶界面,需要使用Redirect組件。
// 導入Redirect
import {Link,Redirect} from 'react-router-dom';
//render函數中return
return <Redirect to="/" />; //to表示跳轉到哪裏
Login登錄功能代碼實現
-
src/service/user.js
文件內容/*`src/service/user.js`文件內容*/ import axios from "axios"; import store from "store"; import expire from "store/plugins/expire"; import {observable} from "mobx"; // 過期插件 store.addPlugin(expire) export default class UserService { @observable loggedin = false; //被觀察者 login (email,password) { console.log(email,password); axios.post("/api/users/login",{ email:email, password:password }) /* dev server會代理 */ .then( /* 成功後返回執行函數 */ (function (response){ console.log(response.data) console.log("response.status: " + response.status); // 存儲token,注意需要重開一次chrome的調試窗口才能看到 store.set("token",response.data.token,(new Date()).getTime() + (8*3600*1000)); this.loggedin = true; // 修改被觀察者 }).bind(this) /*注意綁定this,如果不想可以使用箭頭函數*/ ).catch(/* 出錯後執行函數 */ function(error){ console.log(error); } ) } }
-
src/component/login.js
文件內容/* src/component/login.js文件內容 */ import React from "react"; import {Link,Redirect} from "react-router-dom"; import "../css/login.css"; import UserService from "../service/user"; import {observer} from "mobx-react"; const userService = new UserService(); export default class Login extends React.Component{ render(){ return <_Login service={userService} />; } } @observer //將react組件轉換爲響應式組件 class _Login extends React.Component { handleClick(event){ event.preventDefault(); /* 阻止from表單提交 */ let fm = event.target.form; this.props.service.login(fm[0].value,fm[1].value) } render() { console.log(this.props.service.loggedin) if (this.props.service.loggedin) { return <Redirect to="/" />; //已經登錄,直接跳轉 } return ( <div className="login-page"> <div className="form"> <form className="login-form"> <input type="text" placeholder="email"/> <input type="password" placeholder="password"/> <button onClick={this.handleClick.bind(this)}>登錄</button> <p className="message">還未註冊? <Link to="#">請註冊</Link></p> </form> </div> </div> ) } }
- 注意:測試時,開啓Django編寫的後臺服務程序
- 測試成功,成功登錄,寫入Localstorage,也實現了跳轉