(1) proxy
前端的端口在:localhost:3000
后端的端口在:localhost:1234
所以要在webpack中配置proxy選項(xiàng) (proxy是代理的意思)
在package.json中添加如下配置-------這里用的是create-react-app腳手架eject后的項(xiàng)目
"proxy":"http://localhost:1234" // 把前端的請(qǐng)求都代理到1234端口,和后端一致,即可訪問(wèn)后端接口
(2) axios
配置好proxy后,就可以用axios跨域了
在組件中
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>
登陸頁(yè)面
{
this.props.login && this.props.login.isLogin ? <Redirect to="/user" /> : null
}
<div onClick={this.goLog}>
點(diǎn)擊登陸
</div>
<div onClick={this.goGetData}>
點(diǎn)擊---用axios獲取后端數(shù)據(jù)
</div>
</div>
)
}
}
(axios中文文檔) https://www.kancloud.cn/yunye/axios/234845
(3)axios攔截器
作用:當(dāng)一個(gè)請(qǐng)求發(fā)出的時(shí)候,會(huì)先流過(guò) interceptors 的 request 部分,接著請(qǐng)求會(huì)發(fā)出,當(dāng)接受到響應(yīng)時(shí),會(huì)先流過(guò) interceptors 的 response 部分,最后返回
- interceptor是攔截器的意思
- 作用:在請(qǐng)求或響應(yīng)被 then 或 catch 處理前攔截它們。
// 添加請(qǐng)求攔截器
axios.interceptors.request.use(function (config) {
// 在發(fā)送請(qǐng)求之前做些什么
return config;
}, function (error) {
// 對(duì)請(qǐng)求錯(cuò)誤做些什么
return Promise.reject(error);
});
// 添加響應(yīng)攔截器
axios.interceptors.response.use(function (response) {
// 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
return response;
}, function (error) {
// 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
return Promise.reject(error);
});
如果你想在稍后移除攔截器,可以這樣:
var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
可以為自定義 axios 實(shí)例添加攔截器
var instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
- 實(shí)例:如下
(1) config.js文件
import axios from 'axios';
import {Toast} from 'antd-mobile'; // antd-mobile輕提示組件Toast
// 攔截請(qǐng)求
axios.interceptors.request.use(config => {
Toast.loading('加載中',1) // loading組件,顯示文字加載中,自動(dòng)關(guān)閉延時(shí)1s
console.log('request go');
return config;
}, err => {
console.log('請(qǐng)求失敗')
return Promise.reject(err)
})
//攔截響應(yīng)
axios.interceptors.response.use(config => {
Toast.hide() // 銷毀Toast組件
console.log('response get')
return config;
}, err => {
console.log('響應(yīng)失敗')
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();
antd-mobile https://mobile.ant.design/components/toast-cn/
(4) css Modules模塊化方案
- 支持less和sass的語(yǔ)法
- 解決命名沖突污染等問(wèn)題
- 使用JS和CSS分離的寫法,不會(huì)改變大家的書寫習(xí)慣
- 解決依賴管理不徹底,無(wú)法共享變量,代碼壓縮不徹底
使用webpack項(xiàng)目中,只需要簡(jiǎn)單的配置,如下:
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后面還可以跟具體的命名規(guī)則
// localIdentName 是設(shè)置生成樣式的命名規(guī)則。
}
]
}
]
}
}
------------------------------------------------------------------------------
換一中寫法:(一樣的)
{
test: /\.css$/,
exclude: /node_modules\/antd/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1, // 在css-loader前應(yīng)用的loader的數(shù)目, 默認(rèn)為0
modules:true, // 開啟css-modules模式, 默認(rèn)值為flase
localIdentName:'[name]-[local]-[hash:base64:8]',//css-modules模式下local類名的命名
},
},
]
},
https://segmentfault.com/a/1190000010301977
(詳細(xì))https://www.cnblogs.com/kugeliu/p/7889018.html
(阮一峰)http://www.ruanyifeng.com/blog/2016/06/css_modules.html
(5) Ant Design Mobile (antd-mobile)
- 按需加載
除了安裝 ( antd-mobile ) 之外,還需要安裝 ( babel-plugin-import ) -
這里有個(gè)坑:在package.json中配置babel的時(shí)候(babel-plugin-import插件在安裝后,需要配置babel ),配置完,引入ant組件使用,樣式會(huì)失效?。?!而在未使用css-modules模塊化方案的時(shí)候,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) 因?yàn)槭褂昧薱ss-modules模塊化方案,所以在配置packageg.json中babel的時(shí)候,要修改成如下配置:
{
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標(biāo)簽內(nèi)容插入到html中
- postcss-loader:
如果某些css要考慮到瀏覽器的兼容性(比如css3中的flex),我們要webpack在打包的過(guò)程中自動(dòng)為這些css屬性加上瀏覽器前綴,這時(shí)就用到了postcss-loader和它對(duì)應(yīng)的插件autoprefixer。
http://blog.csdn.net/szu_aker/article/details/72588857
(7) 非路由組件如何使用this.props.history
如果是路由組件,訪問(wèn)history一般都是通過(guò)this.props.history來(lái)操作history
- 而非路由組件通過(guò) {withRouter} 來(lái)使用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 獲取當(dāng)前的url
如何使用history跳轉(zhuǎn)路由(全):https://segmentfault.com/a/1190000011137828
(8) body-parser中間件
可以通過(guò)body-parser對(duì)象創(chuàng)建中間件,當(dāng)接受到客戶端請(qǐng)求時(shí)所有的中間件都會(huì)給req.body添加屬性,請(qǐng)求內(nèi)容為空時(shí),解析為空或者錯(cuò)誤。
- body-parser是非常常用的一個(gè)express中間件,作用是對(duì)post請(qǐng)求的請(qǐng)求體 ( req ) 進(jìn)行解析。
https://www.cnblogs.com/chyingp/p/nodejs-learning-express-body-parser.html
(詳細(xì)) https://www.cnblogs.com/taiyanghua0522/p/7054781.html
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>后端頁(yè)面</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解析的請(qǐng)求體
console.log(req.body, 'req.body')
User.findOne({user,password: MD5PASSWORD(password)}, function(err,doc){
if(!doc){
return res.json({
code:1,
msg: '用戶名或密碼錯(cuò)誤'
})
}
res.cookie('userId', doc._id) // cookie-parser的使用--------寫入cookie
// login接口存入一個(gè)cookie,name是userId,value是res._id
// 存cookie是在res中
// 取cookie是在req中
return res.json({
code:0,
data: doc
})
})
})
----------------------------------------------------
(取出cookie)
Router.get('/info',function(req,res){
// 用戶有沒(méi)有cookie
const {userId} = req.cookies; // 取出cookie,中的userId-------取出cookie,注意是復(fù)數(shù)!!!!
if(!userId) {
return res.json({
code:1
})
}
User.findOne({_id: userId}, function(err,doc){
if(err) {
return res.json({
code:1,
msg: '后端出錯(cuò)'
})
}
return res.json({
code:0,
data: doc
})
})
})
(9) cookie-parser中間件
cookies最常用在‘記住密碼’和‘自動(dòng)登錄’。
cookies 存在于客戶端,安全性較低,一般要存入加密后的信息,并且大部分情況下需要設(shè)置過(guò)期時(shí)間或不使用刪除。
cookie類似于一張身份卡,登陸后,由服務(wù)端返回,帶著cookie即可訪問(wèn)受限資源
-
存cookie
res.cookie('userId', doc._id) // name是userId,value是doc._id
-
取cookie-------------注意:是復(fù)數(shù) req.cookies
const {userId} = req.cookies
// const userId = req.cookies.userId
(10) utility
md5加密庫(kù)
安裝:
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:'后端出錯(cuò)了'
})
}
console.log('用戶名不存在,已添加到數(shù)據(jù)庫(kù)')
return res.json({
code:0
})
})
})
})
----------------------------------------------------
后端比較合格的加密函數(shù):
function MD5PASSWORD(password) {
const salt = 'more_complex_passWord187873871~@!@@@##$$%%DAxiao';
return utility.md5( utility.md5(password + salt) );
}
(11) nodemon
原始node中的express框架,每次修改js代碼后,都要重新手動(dòng)啟動(dòng)才能看到改動(dòng)后的效果,調(diào)試起來(lái)十分不方便。所以我引入了nodemon模塊了彌補(bǔ)這樣缺點(diǎn)。
- 全局安裝
cnpm install nodemon -g
- 修改配置
在package中配置的script中配置的代碼 :
“node”: "node server/server.js"
------
修改為
"node": "nodemon server/server.js"
(12) Route組件中的component可以是一個(gè)函數(shù)
import React from 'react';
import ReactDOM from 'react-dom';
function Boss() {
return (
<div>
boss頁(yè)面
</div>
)
}
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<div>
<AuthRoute></AuthRoute>
<Route path='/boss' component={Boss}></Route>
// 這里的Boss是一個(gè)函數(shù),(不管是function還是class,都是return返回值)
<Route path='/register' component={Register}></Route>
<Route path='/login' component={Login}></Route>
</div>
</BrowserRouter>
</Provider>
, document.getElementById('root'));
registerServiceWorker();
(13) 登陸跳轉(zhuǎn)的邏輯
(1) 注冊(cè)時(shí)拿到注冊(cè)的所有信息,把信息傳到redux中,先做前端判斷(用戶名和密碼是否為空,密碼輸入是否一致 等),然后在redux的action請(qǐng)求后端user/login接口,傳入注冊(cè)信息到請(qǐng)求體中,規(guī)定如果后端返回res.status===200&&res.data.code=0就表示注冊(cè)成功,把注冊(cè)信息寫入redux的reduce中,否則表示注冊(cè)失敗,并且由后端返回錯(cuò)誤信息,并且存入redux,然后取出顯示在前端頁(yè)面 。 ----后端注冊(cè)接口的邏輯:拿到前端傳過(guò)來(lái)的請(qǐng)求數(shù)據(jù),然后查找數(shù)據(jù)庫(kù)時(shí)候存在,如果存在,表示已經(jīng)注冊(cè),返回code===1,和msg=已經(jīng)注冊(cè)過(guò)了。否則表示未注冊(cè),則把前段數(shù)據(jù)存入數(shù)據(jù)庫(kù)。并返回code表示注冊(cè)成功
(2) 注冊(cè)后的跳轉(zhuǎn)邏輯:根據(jù)注冊(cè)時(shí)的身份(genius或者boss) 和 (是否有頭像) 決定跳轉(zhuǎn)到 boss頁(yè)面,genius頁(yè)面, bossinfo頁(yè)面,geniusinfo頁(yè)面
(3) 登陸時(shí),拿到登陸的所有信息,然后做前端判斷(用戶名,密碼是否為空),然后請(qǐng)求后端user/login接口,傳入如果返回 res.status===200 && res.data.code ===0表示登陸成功,然后拿到后端返回的數(shù)據(jù),存入redux的reducer中。否則表示登陸失敗,吧后端返回的錯(cuò)誤信息,存入redux,返回給前端頁(yè)面。--------后端接口:拿到前段給到的請(qǐng)求體,然后查找數(shù)據(jù)庫(kù),如果doc不存在,就返回res.jsoln({code:1}),否則表示用戶信息在數(shù)據(jù)庫(kù)中存在,這是存入cookie,res.cookie('名字',doc._id),然后返回?cái)?shù)據(jù)庫(kù)中存在的用戶信息,返回給前段接口使用
(4) 登陸后的跳轉(zhuǎn)邏輯:和注冊(cè)時(shí)候一樣
(5) 如果是其他頁(yè)面,不在login,和register兩個(gè)頁(yè)面中,則在前段請(qǐng)求另一個(gè)接口,ingo接口,,如果后端返回code是0,res.status是200,表示有用戶信息,則拿到后端返回的數(shù)據(jù)存入redux, 否則表示沒(méi)有用戶信息,路由跳轉(zhuǎn)到登陸頁(yè)面。-------后端接口邏輯:首先在該接口驗(yàn)證cookie是否存在,cost {usrId} = req.cookies,如果不存在返回res.json({code:1}), 然后查找數(shù)據(jù)庫(kù),有數(shù)據(jù)返回?cái)?shù)據(jù),沒(méi)有返回相應(yīng)的狀態(tài)碼
(14) mongoose添加的兩種方式
(1) create方式
缺點(diǎn): 不能存儲(chǔ)id
User.create({
user,
password: MD5PASSWORD(password),
type
}, function(err,doc){
if(err) {
return res.json({
code:1,
msg: '后端出錯(cuò)'
})
}
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: '已經(jīng)注冊(cè)過(guò)了'
})
}
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:'后端出錯(cuò)'
})
}
const {user, type, _id} = doc
res.cookie('userId', _id) // 在注冊(cè)頁(yè)面存cookie
return res.json({
code:0,
data:doc
})
})
})
})
Entity結(jié)構(gòu) http://blog.csdn.net/sinat_25127047/article/details/50560167
(15) mongoose相關(guān)語(yǔ)法
- 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,需要更新的數(shù)據(jù),回掉)
User.findByIdAndUpdate(userId,body,function(err, doc){ ---------------------
const data = Object.assign({},{
user: doc.user,
type: doc.type
},body); // 把body對(duì)象 和user type 合并賦值給data------body是前端請(qǐng)求的實(shí)時(shí)更新的請(qǐng)求體
return res.json({
code:0,
data
})
})
})
(16) Array.find()
數(shù)組實(shí)例的find方法,用于找出第一個(gè)符合條件的數(shù)組成員。它的參數(shù)是一個(gè)回調(diào)函數(shù),所有數(shù)組成員依次執(zhí)行該回調(diào)函數(shù),直到找出第一個(gè)返回值為true的成員,然后返回該成員。如果沒(méi)有符合條件的成員,則返回undefined。
- 找出第一個(gè)符合條件的數(shù)組成員,返回該成員
- 參數(shù)是回調(diào)函數(shù)fallback(value,index,array)
- 沒(méi)有找到符合的成員,返回undefined
- 還可以接受除fallback回調(diào)函數(shù)之外的,第二個(gè)參數(shù),用來(lái)綁定回調(diào)函數(shù)的this對(duì)象。
[1, 4, -5, 10].find(n => n < 0)
// -5
---------------------------------------------------
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
find方法的回調(diào)函數(shù)可以接受三個(gè)參數(shù),依次為當(dāng)前的值、當(dāng)前的位置和原數(shù)組。
---------------------------------------------------
function f(v){
return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person); // 26
(17) Array.findIndex()
數(shù)組實(shí)例的findIndex方法返回第一個(gè)符合條件的數(shù)組成員的位置,如果所有成員都不符合條件,則返回-1。
- 找出第一個(gè)符合條件的數(shù)組成員,返回該成員的位置
- 參數(shù)是回調(diào)函數(shù)fallback(value,index,array)
- 沒(méi)有找到符合數(shù)組成員的位置,返回-1
- 還可以接受除fallback回調(diào)函數(shù)之外的,第二個(gè)參數(shù),用來(lái)綁定回調(diào)函數(shù)的this對(duì)象。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
})
// 2
(18) nodeJS 取參
- req.body ----------是post請(qǐng)求,獲取參數(shù)
- req.query----------是get 請(qǐng)求,獲取參數(shù)
- req.params
- req.param()
(19) browser-cookies
git地址:https://github.com/voltace/browser-cookies
安裝:cnpm isntall browser-cookies -S
- cookies.set 設(shè)置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強(qiáng)制刷新頁(yè)面
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存著的數(shù)據(jù)
}}
])
}