12. 前后端联调 + ( proxy代理 ) + ( axios拦截器 ) + ( css Modules模块化方案 ) + ( css-loader ) + ( 非路由组件如何使用history ) + ( bodyParser,cookieParser中间件 ) + ( utility MD5加密库 ) + ( nodemon自动重启node ) + +
(1) proxy
前端的端口在:localhost:3000
后端的端口在:localhost:1234
所以要在webpack中配置proxy选项 (proxy是代理的意思)
在package.json中添加如下配置-------这里用的是create-react-app脚手架eject后的项目
"proxy":"http://localhost:1234" // 把前端的请求都代理到1234端口,和后端一致,即可访问后端接口
(2) axios
配置好proxy后,就可以用axios跨域了
import React,{Component} from 'react';import { Redirect } from 'react-router-dom';import axios from 'axios'; // 引入axiosexport 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 部分,最后返回
- 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来操作history
- 而非路由组件通过 {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 存在于客户端,安全性较低,一般要存入加密后的信息,并且大部分情况下需要设置过期时间或不使用删除。
cookie类似于一张身份卡,登陆后,由服务端返回,带着cookie即可访问受限资源
存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加密库
安装:
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模块了弥补这样缺点。
- 全局安装
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页面(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存着的数据
}}
])
}
============
12. 前后端联调 + ( proxy代理 ) + ( axios拦截器 ) + ( css Modules模块化方案 ) + ( css-loader ) + ( 非路由组件如何使用history ) + ( bodyParser,cookieParser中间件 ) + ( utility MD5加密库 ) + ( nodemon自动重启node ) + +的更多相关文章
- [django]django配合前端vue前后端联调,django服务端解决跨域(django-cors-headers)
django内部csrf post提交数据解决 https://www.cnblogs.com/iiiiiher/articles/9164940.html 前端写了个页面,里面$.post发现403 ...
- 研发流程 接口定义&开发&前后端联调 线上日志观察 模型变动
阿里等大厂的研发流程,进去前先了解一下_我们一起进大厂 - SegmentFault 思否 https://segmentfault.com/a/1190000021831640 接口定义 测试用例评 ...
- 【转载】由浅入深分析mybatis通过动态代理实现拦截器(插件)的原理
转自:http://zhangbo-peipei-163-com.iteye.com/blog/2033832?utm_source=tuicool&utm_medium=referral 我 ...
- Vue2学习小记-给Vue2路由导航钩子和axios拦截器做个封装
1.写在前面 最近在学习Vue2,遇到有些页面请求数据需要用户登录权限.服务器响应不符预期的问题,但是总不能每个页面都做单独处理吧,于是想到axios提供了拦截器这个好东西,再于是就出现了本文. 2. ...
- axios拦截器搭配token使用
在了解到cookie.session.token的作用后学习token的使用 cookie是随着url将参数发送到后台,安全性最低,并且大小受限,不超过4kb左右,它的数据保存在客户端 session ...
- vue导航守卫和axios拦截器的区别
在Vue项目中,有两种用户登录状态判断并处理的情况,分别为:导航守卫和axios拦截器. 一.什么是导航守卫? vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航.(在路由跳转时 ...
- vue axios拦截器 + 自编写插件 实现全局 loading 效果;
项目需求:用自定义的 .gif 图标实现全局 loading 效果:为避免在每个页面手动添加,且简单高效的实现,经查阅资料,最终采用了 vue axios拦截器 + 自编写 loading 插件:下面 ...
- Vue基于vuex、axios拦截器实现loading效果及axios的安装配置
准备 利用vue-cli脚手架创建项目 进入项目安装vuex.axios(npm install vuex,npm install axios) axios配置 项目中安装axios模块(npm in ...
- axios拦截器的使用方法
很多时候我们需要在发送请求和响应数据的时候做一些页面处理,比如在请求服务器之前先判断以下用户是登录(通过token判断),或者设置请求头header,或者在请求到数据之前页面显示loading等等,还 ...
随机推荐
- eclipse-JEE配置Tomcat并发布第一个项目
一.配置过程 Window--preferences--Server--Runtime Environment, 然后点击add 我下载的是Tomcat7.0,选择你的版本就行了 选择Tomcat的安 ...
- SpringBoot项目中应用Jedis和一些常见配置
优雅的使用Jedis Redis的Java客户端有很多,Jedis是其中使用比较广泛和性能比较稳定的一个.并且其API和RedisAPI命名风格类似,推荐大家使用 在项目中引入Jedis 可以通过Ma ...
- MySQL5.7 import表结构报错超出表空间界限
事后测试了一下,一下方法就是垃圾,看看可以,别跟着学!!! 数据库重启后,问题依然暴露出来了,参数什么的都是扯,擦 记录一个困扰我好几天的问题.先贴上报错: space name jxtms/Cost ...
- Java实现tif/tiff/bmp图片转换png图片
package org.analysisitem20181016.test; import java.io.File; import java.io.FileOutputStream; import ...
- Codeforces Round #565 (Div. 3)--D. Recover it!--思维+欧拉筛
D. Recover it! Authors guessed an array aa consisting of nn integers; each integer is not less than ...
- C#通用类库整理--字符串处理类
在程序开发中通常需要将字符串转为自己想要的结果,以下三个类库主要实现: 1.GetStrArray(string str, char speater, bool toLower) 把字符串按照分隔符 ...
- 1068 Find More Coins (30分)(dp)
Eva loves to collect coins from all over the universe, including some other planets like Mars. One d ...
- LeetCode | 169. 多数元素
给定一个大小为 n 的数组,找到其中的多数元素.多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是非空的,并且给定的数组总是存在多数元素. 示例 1: 输入: [3,2,3] ...
- 微信小程序实现滑动tab切换和点击tab切换并显示相应的数据(附源代码)
这里主要用到了swiper组件和三目运算,直接上代码, 样式只有三个class,简单粗暴,懒的小伙伴们可以直接拿来用,喜欢的点个支持 <view> <view class=" ...
- IntelliJ IDEA 在方法大括号中{}点击回车多出一个},如何取消
在 File - settings - Editor - General- Smart Keys - Enter 去掉 Insert pair '}' 的对勾就可以了