zoukankan      html  css  js  c++  java
  • 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'; // 引入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 部分,最后返回

    • 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存着的数据
                }}
            ])
        }
    ============
    来源于https://www.jianshu.com/p/1a6e31a7e923


     
  • 相关阅读:
    ASM ClassReader failed to parse class file解决方法
    Android Studio3.1.2升级问题:Configuration 'compile' is obsolete and has been replaced with 'implementation'.
    解决Android Studio出现Failed to open zip file. Gradle's dependency cache may be corrupt的问题
    AS使用lombok注解报错:Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor.
    IDEA调试SpringMvc项目时,出错:java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener,解决办法
    解决Android Studio出现GC overhead limit exceeded
    android ScrollView 控制行数
    [android警告]AndroidManifest.xml警告 Not targeting the latest versions of Android
    Android中的windowSoftInputMode属性详解
    MySQL中的isnull、ifnull和nullif函数用法
  • 原文地址:https://www.cnblogs.com/wangrui38/p/10338051.html
Copyright © 2011-2022 走看看