12. 先後端聯調 + ( proxy代理 ) + ( axios攔截器 ) + ( css Modules模塊化方案 ) + ( css-loader ) + ( 非路由組件如何使用history

(1) proxy

前端的端口在:localhost:3000
後端的端口在:localhost:1234
因此要在webpack中配置proxy選項 (proxy是代理的意思)javascript

package.json中添加以下配置-------這裏用的是create-react-app腳手架eject後的項目 "proxy":"http://localhost:1234" // 把前端的請求都代理到1234端口,和後端一致,便可訪問後端接口

(2) axios

配置好proxy後,就能夠用axios跨域了php

在組件中
import React,{Component} from 'react';
import { Redirect } from 'react-router-dom';
import axios from 'axios'; // 引入axios
export default class Login extends Component {
     goLog = () => {
    this.props.goLogin();
 }
    goGetData = () => {
         axios.get('/data') // 使用axios
      .then(res => {
        console.log(res,'res')
      } ) }
  render() {
  return (
    <div> 登錄頁面 { this.props.login && this.props.login.isLogin ? <Redirect to="/user" /> : null }
      <div onClick={this.goLog}> 點擊登錄 </div>
      <div onClick={this.goGetData}> 點擊---用axios獲取後端數據 </div>
    </div> )
} }

(3)axios攔截器

做用:當一個請求發出的時候,會先流過 interceptors 的 request 部分,接着請求會發出,當接受到響應時,會先流過 interceptors 的 response 部分,最後返回css

  • interceptor是攔截器的意思
  • 做用:在請求或響應被 then 或 catch 處理前攔截它們。
// 添加請求攔截器 axios.interceptors.request.use(function (config) { // 在發送請求以前作些什麼 return config; }, function (error) { // 對請求錯誤作些什麼 return Promise.reject(error); }); // 添加響應攔截器 axios.interceptors.response.use(function (response) { // 對響應數據作點什麼 return response; }, function (error) { // 對響應錯誤作點什麼 return Promise.reject(error); }); 若是你想在稍後移除攔截器,能夠這樣: var myInterceptor = axios.interceptors.request.use(function () {/*...*/}); axios.interceptors.request.eject(myInterceptor); 能夠爲自定義 axios 實例添加攔截器 var instance = axios.create(); instance.interceptors.request.use(function () {/*...*/}); 

 

  • 實例:以下
    (1) config.js文件 import axios from 'axios'; import {Toast} from 'antd-mobile'; // antd-mobile輕提示組件Toast // 攔截請求 axios.interceptors.request.use(config => { Toast.loading('加載中',1) // loading組件,顯示文字加載中,自動關閉延時1s console.log('request go'); return config; }, err => { console.log('請求失敗') return Promise.reject(err) }) //攔截響應 axios.interceptors.response.use(config => { Toast.hide() // 銷燬Toast組件 console.log('response get') return config; }, err => { console.log('響應失敗') return Promise.reject(err) }) ------------------------------------------------------------- (2) 在入口文件index.js文件中引入上面的cofig.js文件 import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; import {Provider} from 'react-redux'; import {store} from './store/store.js'; // import UserContainer from './component/user/container.js'; import './config/config.js'; import {BrowserRouter} from 'react-router-dom' ReactDOM.render( <Provider store={store}> <BrowserRouter> <App></App> </BrowserRouter> </Provider> , document.getElementById('root')); registerServiceWorker();

(4) css Modules模塊化方案

  • 支持less和sass的語法
  • 解決命名衝突污染等問題
  • 使用JS和CSS分離的寫法,不會改變你們的書寫習慣
  • 解決依賴管理不完全,沒法共享變量,代碼壓縮不完全
    使用webpack項目中,只須要簡單的配置,以下:
webpack.config.dev.js 注意:這裏須要提早安裝css-loader插件!! { module: { rules:[ { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader?modules', // 在css-loader後添加 ?modules便可 // loader: 'css-loader?modules&localIdentName=[name]-[hash:base64:5]' // modules後面還能夠跟具體的命名規則 // localIdentName 是設置生成樣式的命名規則。 } ] } ] } } ------------------------------------------------------------------------------ 換一中寫法:(同樣的) { test: /\.css$/, exclude: /node_modules\/antd/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, // 在css-loader前應用的loader的數目, 默認爲0 modules:true, // 開啓css-modules模式, 默認值爲flase localIdentName:'[name]-[local]-[hash:base64:8]',//css-modules模式下local類名的命名 }, }, ] },

(5) Ant Design Mobile (antd-mobile)

  • 按需加載
    除了安裝 ( antd-mobile ) 以外,還須要安裝 ( babel-plugin-import )
  • 這裏有個坑:在package.json中配置babel的時候(babel-plugin-import插件在安裝後,須要配置babel ),配置完,引入ant組件使用,樣式會失效!!!而在未使用css-modules模塊化方案的時候,ant-mobile能正常使用,( 要使用css-modules的話,要在webpack.config.json中作以下配置:(!)
    (踩坑與填坑) https://segmentfault.com/q/1010000011965218
(!)


 {
        test: /\.css$/, include: /node_modules\/antd/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { modules:false }, }, ] }, { test: /\.css$/, exclude: /node_modules\/antd/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, modules:true, localIdentName:'[name]-[local]-[hash:base64:8]', }, }, ] },
安裝:
cnpm install antd-mobile --save



使用:
import { Button } from 'antd-mobile'; import 'antd-mobile/dist/antd-mobile.css'; 按需加載: (1)安裝babel按需加載插件 babel-plugin-import cnpm install babel-plugin-import --save-dev (2)在create-react-app腳手架eject後,package.json文件中,配置以下: // 本身搭建能夠寫在.babelrc中 "babel": { "presets": [ "react-app" ], "plugins": [ "transform-decorators-legacy", ["import", { "libraryName": "antd-mobile", "style": "css" }] ] } (3) 使用 import { Button } from 'antd-mobile'; ... ... (4) 由於使用了css-modules模塊化方案,因此在配置packageg.json中babel的時候,要修改爲以下配置: { test: /\.css$/, include: /node_modules\/antd/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { modules:false }, }, ] }, { test: /\.css$/, exclude: /node_modules\/antd/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, modules:true, localIdentName:'[name]-[local]-[hash:base64:8]', }, }, ] },

(6) css-loader 和 ( style-loader, postcss-loader )

  • css-loader: 在js中加載css
  • style-loader: 把加載的css做爲style標籤內容插入到html中
  • postcss-loader:
    若是某些css要考慮到瀏覽器的兼容性(好比css3中的flex),咱們要webpack在打包的過程當中自動爲這些css屬性加上瀏覽器前綴,這時就用到了postcss-loader和它對應的插件autoprefixer。
    http://blog.csdn.net/szu_aker/article/details/72588857

(7) 非路由組件如何使用this.props.history

若是是路由組件,訪問history通常都是經過this.props.history來操做historyhtml

  • 而非路由組件經過 {withRouter} 來使用this.props.history
import React from "react"; import {withRouter} from "react-router-dom"; // 引入withRouter class MyComponent extends React.Component { ... myFunction() { this.props.history.push("/some/Path"); } ... } export default withRouter(MyComponent); // 用withRouter包裹該class ---------------------------------------------------------------------------- this.props.location.pathname 獲取當前的url

(8) body-parser中間件

能夠經過body-parser對象建立中間件,當接受到客戶端請求時全部的中間件都會給req.body添加屬性,請求內容爲空時,解析爲空或者錯誤。前端

  • body-parser是很是經常使用的一個express中間件,做用是對post請求的請求體 ( req ) 進行解析。
server.js


const express = require('express'); const bodyParser = require('body-parser'); // 引入body-parser const cookieParser = require('cookie-parser'); // 引入cookie-parser const app = express(); const user = require('./user.js'); app.use(bodyParser.json()) // 使用body-parser app.use(cookieParser()) // 使用cookie-parser app.use('/one', user) app.get('/', function(req, res){ res.send('<p>後端頁面</p>') }) app.listen(3333, function() { console.log('express port 1212 is going') }) ---------------------------------------------------- (存入cookie) user.js Router.post('/login', function(req,res){ const {user, password} = req.body; // req.body是body-parser解析的請求體 console.log(req.body, 'req.body') User.findOne({user,password: MD5PASSWORD(password)}, function(err,doc){ if(!doc){ return res.json({ code:1, msg: '用戶名或密碼錯誤' }) } res.cookie('userId', doc._id) // cookie-parser的使用--------寫入cookie // login接口存入一個cookie,name是userId,value是res._id // 存cookie是在res中 // 取cookie是在req中 return res.json({ code:0, data: doc }) }) }) ---------------------------------------------------- (取出cookie) Router.get('/info',function(req,res){ // 用戶有沒有cookie const {userId} = req.cookies; // 取出cookie,中的userId-------取出cookie,注意是複數!!!! if(!userId) { return res.json({ code:1 }) } User.findOne({_id: userId}, function(err,doc){ if(err) { return res.json({ code:1, msg: '後端出錯' }) } return res.json({ code:0, data: doc }) }) })

(9) cookie-parser中間件

cookies最經常使用在‘記住密碼’和‘自動登陸’。
cookies 存在於客戶端,安全性較低,通常要存入加密後的信息,而且大部分狀況下須要設置過時時間或不使用刪除。java

  • cookie相似於一張身份卡,登錄後,由服務端返回,帶着cookie便可訪問受限資源node

  • 存cookie

res.cookie('userId', doc._id) // name是userId,value是doc._id

 

  • 取cookie-------------注意:是複數 req.cookies 

    const {userId} = req.cookies // const userId = req.cookies.userId

(10) utility

md5加密庫react

安裝:
cnpm install utility --save

引入和使用:
const utility = require('utility'); Router.post('/register', function(req,res){ console.log(req.body,'req.body'); const {user, password, type} = req.body; User.findOne({'user':user},function(err,doc){ if(doc){ return res.json({ code:1, msg: '用戶名存在' }) } User.create({ 'user': user, 'password':utility.md5(password), // 使用-------------- 'type':type },function(err,doc){ if(err){ return res.json({ code:1, msg:'後端出錯了' }) } console.log('用戶名不存在,已添加到數據庫') return res.json({ code:0 }) }) }) }) ---------------------------------------------------- 後端比較合格的加密函數: function MD5PASSWORD(password) { const salt = 'more_complex_passWord187873871~@!@@@##$$%%DAxiao'; return utility.md5( utility.md5(password + salt) ); }

(11) nodemon

原始node中的express框架,每次修改js代碼後,都要從新手動啓動才能看到改動後的效果,調試起來十分不方便。因此我引入了nodemon模塊了彌補這樣缺點。webpack

  • 全局安裝
cnpm install nodemon -g
  • 修改配置
package中配置的script中配置的代碼 : 「node」: "node server/server.js" ------ 修改成 "node": "nodemon server/server.js"

(13) 登錄跳轉的邏輯

(1) 註冊時拿到註冊的全部信息,把信息傳到redux中,先作前端判斷(用戶名和密碼是否爲空,密碼輸入是否一致 等),而後在redux的action請求後端user/login接口,傳入註冊信息到請求體中,規定若是後端返回res.status===200&&res.data.code=0就表示註冊成功,把註冊信息寫入redux的reduce中,不然表示註冊失敗,而且由後端返回錯誤信息,而且存入redux,而後取出顯示在前端頁面 。 ----後端註冊接口的邏輯:拿到前端傳過來的請求數據,而後查找數據庫時候存在,若是存在,表示已經註冊,返回code===1,和msg=已經註冊過了。不然表示未註冊,則把前段數據存入數據庫。並返回code表示註冊成功
(2) 註冊後的跳轉邏輯:根據註冊時的身份(genius或者boss) 和 (是否有頭像) 決定跳轉到 boss頁面,genius頁面, bossinfo頁面,geniusinfo頁面ios

(3) 登錄時,拿到登錄的全部信息,而後作前端判斷(用戶名,密碼是否爲空),而後請求後端user/login接口,傳入若是返回 res.status===200 && res.data.code ===0表示登錄成功,而後拿到後端返回的數據,存入redux的reducer中。不然表示登錄失敗,吧後端返回的錯誤信息,存入redux,返回給前端頁面。--------後端接口:拿到前段給到的請求體,而後查找數據庫,若是doc不存在,就返回res.jsoln({code:1}),不然表示用戶信息在數據庫中存在,這是存入cookie,res.cookie('名字',doc._id),而後返回數據庫中存在的用戶信息,返回給前段接口使用
(4) 登錄後的跳轉邏輯:和註冊時候同樣
(5) 若是是其餘頁面,不在login,和register兩個頁面中,則在前段請求另外一個接口,ingo接口,,若是後端返回code是0,res.status是200,表示有用戶信息,則拿到後端返回的數據存入redux, 不然表示沒有用戶信息,路由跳轉到登錄頁面。-------後端接口邏輯:首先在該接口驗證cookie是否存在,cost {usrId} = req.cookies,若是不存在返回res.json({code:1}), 而後查找數據庫,有數據返回數據,沒有返回相應的狀態碼

(14) mongoose添加的兩種方式

(1) create方式 缺點: 不能存儲id User.create({ user, password: MD5PASSWORD(password), type }, function(err,doc){ if(err) { return res.json({ code:1, msg: '後端出錯' }) } return res.json({ code:0 }) }) -------------------------------------------- (2) new mongoose.model().save方式: Router.post('/register', function(req,res) { console.log(req.body) const { user, password, type } = req.body; User.findOne({user}, function(err,doc){ if(doc) { return res.json({ code: 1, msg: '已經註冊過了' }) } const userModel = new User({ // new mongoose.model('user') user, password: MD5PASSWORD(password), type }) userModel.save(function(err,doc){ // .save if(err){ return res.json({ code:1, msg:'後端出錯' }) } const {user, type, _id} = doc res.cookie('userId', _id) // 在註冊頁面存cookie return res.json({ code:0, data:doc }) }) }) }) 

Entity結構 http://blog.csdn.net/sinat_25127047/article/details/50560167

(15) mongoose相關語法

  • findByIdAndUpdate
Router.post('/update', function(req,res) { const {userId} = req.cookies; console.log(req.cookies,'req.cookies'); if(!userId) { return json.dumps({ code:1 }) } const body = req.body // findByIdAndUpdate(須要查找的id,須要更新的數據,回掉) User.findByIdAndUpdate(userId,body,function(err, doc){ --------------------- const data = Object.assign({},{ user: doc.user, type: doc.type },body); // 把body對象 和user type 合併賦值給data------body是前端請求的實時更新的請求體 return res.json({ code:0, data }) }) }) 

(16) Array.find()

數組實例的find方法,用於找出第一個符合條件的數組成員。它的參數是一個回調函數,全部數組成員依次執行該回調函數,直到找出第一個返回值爲true的成員,而後返回該成員。若是沒有符合條件的成員,則返回undefined。

  • 找出第一個符合條件的數組成員,返回該成員
  • 參數是回調函數fallback(value,index,array)
  • 沒有找到符合的成員,返回undefined
  • 還能夠接受除fallback回調函數以外的,第二個參數,用來綁定回調函數的this對象。
[1, 4, -5, 10].find(n => n < 0) // -5 --------------------------------------------------- [1, 5, 10, 15].find(function(value, index, arr) { return value > 9; }) // 10 find方法的回調函數能夠接受三個參數,依次爲當前的值、當前的位置和原數組。 --------------------------------------------------- function f(v){ return v > this.age; } let person = {name: 'John', age: 20}; [10, 12, 26, 15].find(f, person); // 26 

(17) Array.findIndex()

數組實例的findIndex方法返回第一個符合條件的數組成員的位置,若是全部成員都不符合條件,則返回-1。

  • 找出第一個符合條件的數組成員,返回該成員的位置
  • 參數是回調函數fallback(value,index,array)
  • 沒有找到符合數組成員的位置,返回-1
  • 還能夠接受除fallback回調函數以外的,第二個參數,用來綁定回調函數的this對象。
[1, 5, 10, 15].findIndex(function(value, index, arr) { return value > 9; }) // 2

(18) nodeJS 取參

  • req.body ----------是post請求,獲取參數
  • req.query----------是get 請求,獲取參數
  • req.params
  • req.param()

(19) browser-cookies

git地址:https://github.com/voltace/browser-cookies
安裝:cnpm isntall browser-cookies -S

  • cookies.set 設置cookie
  • cookies.get 獲取cookie
  • cookies.erase 清除cookie ------------- erase是清除,抹去的意思
var cookies = require('browser-cookies'); cookies.set('firstName', 'Lisa'); cookies.set('firstName', 'Lisa', {expires: 365}); // Expires after 1 year cookies.set('firstName', 'Lisa', {secure: true, domain: 'www.example.org'}); cookies.get('firstName'); // Returns cookie value (or null) cookies.erase('firstName'); // Removes cookie 

(20) window.location.href = window.location.href強制刷新頁面

http://blog.csdn.net/github_37483541/article/details/59481084

註銷登錄:

    logout =() => { // erase是清除,抹掉的意思 // browserCookies.erase('userId'); // window.location.href = window.location.href const alert = Modal.alert alert('註銷','肯定要註銷登錄嗎?',[ {text:'取消', onPress: () => console.log('canscel')}, {text:'肯定', onPress: () => { browserCookies.erase('userId'); // window.location.href = window.location.href this.props.logoutAll(); // 該方法清除redux中register存着的數據 }} ]) }
============
來源於https://www.jianshu.com/p/1a6e31a7e923
相關文章
相關標籤/搜索