《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

栏目: 后端 · 发布时间: 6年前

内容简介:上回还真的有同学提到了这个问题,感谢细心的你。@_noob其实是没任何问题的,只不过看起来违背了常见的结构,像是有问题。其实是上文为了照顾初学者,怕大家因为麻烦而放弃,并没有一次性改的“看起来那么复杂”,我们来填下坑。为了照顾没有实时跟着我连载的同学,每一章的代码单独发布在我的

上回还真的有同学提到了这个问题,感谢细心的你。@_noob

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

其实是没任何问题的,只不过看起来违背了常见的结构,像是有问题。其实是上文为了照顾初学者,怕大家因为麻烦而放弃,并没有一次性改的“看起来那么复杂”,我们来填下坑。

为了照顾没有实时跟着我连载的同学,每一章的代码单独发布在我的 Github 博客,不进行覆盖更新 [除非代码有错误进行修改],这样避免了 2019 年小明同学望着前两章和 Github 最终版本代码发呆 ( release 也不是特别友好)

我们把上文的 renderer/src 改成比较容易理解的 src/renderer ,这其实是一个编程习惯问题。

上文 renderer/src 的问题

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

eslint-config-alloy : 想写出更规范的代码可以参考这个规则

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

下面进入本章正题

axios

使用了 vue 的你,发现 Vue 居然不能发请求,于是你 Google 了下,发现可以用 Vue-Resource。 你去问别人 Vue-Resource 怎么样,他说不要用 Vue-Resource,因为 Vue-Resource 官方已经停止维护了,你应该用 Axios、或者 fetch。但是我们想拥抱 ES6 排除掉了 ES5的fetch(当然也有ES6-fetch),这里我们使用 Axios!

Tips

这里呢也科普一下:什么时候依赖需要放到 dependencies、什么时候依赖需要放到 devDependencies:

devDependencies:顾名思义,仅在开发(dev)模式下如:webpack. .loader、eslint、babel、打包后部署时完全用不到的、仅在开发需要 编译、检测、转换 的放在这里。 dependencies:例如:axios、chart、js-cookie、less、lodash、underscore等运行时的库或 工具 类等相关依赖我们要放在这里

不过基本不用担心,官网都会提供 start 说明,但是我们要大概明白意思,不要机械般的 copy。

引入 Axios

  • 直接玩最新的
    《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

2018-09-28 截图npmjs.com

  • 添加依赖
"dependencies": {    
    "axios": "^0.18.0"
 }
复制代码
《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

基于上一章内容,别忘了重新 npm i 下载一下

还记得我们自动生成的 vue 主页面脚本 main.js吗?

封装axios

我们在 src/renderer/utils 建立一个 request.js 在这个请求脚本中,对 Axios 做一些必要的封装,大概内容是用 拦截器 axios.interceptors 对请求和响应做些拦截,定义一下 API 的前缀,处理一些常见的 HTTP 状态码。

  • interceptors 文档
《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

我尽可能的为大家写了详细的注释。

// src/renderer/utils/request.js
import axios from 'axios'

//这里一般指后端项目API的前缀,例如 /baidu/*/*/1.api  /mi/*/*/2.api
const BASE_API = ""

export function axiosIntercept(Vue, router) {
    const axiosIntercept = axios.create({
        baseURL: BASE_API
    })

    //http request 拦截器 一般用来在请求前塞一些全局的配置、或开启一些 css 加载动画
    axios.interceptors.request.use(
        (config) => {
            // 判断是否存在token,如果存在的话,则每个http header都加上token
            // if (store.getters.accessToken) {
            //     console.log(store.getters.accessToken)
            //     config.headers.Authorization = `token ${store.getters.accessToken}`;
            // }

            //todo:加载动画

            //若有需求可以处理一下 post 亦或改变post传输格式
            if (config.method === 'post') {

            };

            return config;
        }, function (err) {
            return Promise.reject(err);
        });


    //http response 拦截器 一般用来根据一些后端协议特殊返回值做一些处理,例如:权限方面、404... 或关闭一些 css 加载动画
    axiosIntercept.interceptors.response.use(function (response) {
        // todo: 暂停加载动画
        return response;
    }, function (err) {
        //捕获异常
        if (err.response) {
            switch (err.response.status) {
                case 401:
                    // do something 这里我们写完后端做好约束再完善
            }
        }
        return Promise.reject(err);
    });
    return axiosIntercept;
}


复制代码

大家还记得我们用 vue-cli 生成的 vue 主页脚本 main.js 吧,这里我们需要对 Axios 和 Vue 做一个耦合。

// src/renderer/main.js
import axios from 'axios'
import { axiosIntercept } from './utils/request'

// 将Axios扩展到Vue原型链中
Vue.prototype.$http = axiosIntercept(Vue)
复制代码

这样我们在写业务逻辑,直接在 Vue 的上下文中 使用 this.$http 来发送请求。既实现了拦截、又实现了状态的共享。

知其然,知其所以然

  • 这样做的意义在哪?

节省代码量,让代码更加易读

  • 为什么?

扩展到原型链,使 Axios 运行时共享 Vue 原型链的内容,减少了很多指代 Vue 的临时变量

  • 举个栗子

传统情况

import axios from 'axios'


new Vue({
  data: {
    user: ""
  },
  created: function () {
    //此时作用域在 Vue 上,缓存起来,要依赖此变量
    let _this = this;
    axios.get("/user/getUserInfo/" + userName).then(res => {
            if (res.data.code === 200) {
                //此时作用域在axios上,拿不到vue绑定的值,只能借助刚才缓存的_this上下文
                _this.data.user = res.data.user
            }
        });
    }
})

复制代码

代理之后

new Vue({
  data: {
    user: ""
  },
  created: function () {
    // axios 成为了 vue 的原型链一部分,共享vue状态。
    
    this.$http.get("/user/getUserInfo/" + userName).then(res => {
            if (res.data.code === 200) {
                //注意,axios回调,应该尽量使用箭头函数,可以继承父类上下文,否则类似闭包,还是无法共享变量、
                // 更优雅了一些
                this.data.user = res.data.user
            }
        });
    }
})

复制代码

不懂 prototype 可以翻翻我以前写的文章

proxy

先简单弄一下,为前后分离打个小铺垫

webPack

  • webPack 的别名
resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': resolve('src/renderer'),
    }
  },
复制代码

为了使用起来更加优雅,可以为每个常用的目录都建立别名

resolve: {
   extensions: ['.js', '.vue', '.json'],
   alias: {
     'vue$': 'vue/dist/vue.esm.js',
     '@': resolve('src/renderer'),
     'assets': resolve('src/renderer/assets'),
     'components': resolve('src/renderer/components'),
     'container': resolve('src/renderer/container'),
     'utils': resolve('src/renderer/utils')
   }
 },
复制代码

生产和开发的跨域问题

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

dev 是开发时启动的指令 build 是预发布时 webPack 打包的指令

假设笔者只是一个前端,通常呢,在开发调试过程当中,无法避免需要与后端的同学进行 API 的对接,那也就难免会出现跨域问题。当然传统 javaWeb 不需要跨域,(ip 域 端口 任何一个不同皆为跨域) 在 DEV 模式调试中,我们都是尽量选择前端环境规避跨域问题,而不会去额外搭建 nginx 或更改后端代码。

跨域只是针对 JavaScript 的,因为开发者认为浏览器上的脚本是不安全的。

既然我们的 vue 项目是 node 全家桶,依靠 node、webPack 编译 我们直接配置 node 的 proxyTable 作为开发的代理器,这样最简单,依次配置,团队受益。

cnode 掘金 社区 API 举例

cnodejs.org/api 上边cnode 的 API 是可以随意调用的,因为后端做了处理。

看看掘金的: xiaoce-timeline-api-ms.juejin.im/v1/getListB… 请求一下,不出意外浏览器做了跨域报警。

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

哦,我们适配一下 node 代理

官方例子在这: vuejs-templates.github.io/webpack/pro…

扩展一下 proxyTable:

proxyTable: [{
      //拦截所有v1开头的xhr请求
      context: ['/v1'], 
      target: "https://xiaoce-timeline-api-ms.juejin.im",
      cookieDomainRewrite: {
        // 不用cookie
      },
      changeOrigin: true,//重点,此处本地就会虚拟一个服务替我们接受或转发请求
      secure: false
    }],

复制代码

再次发送请求。

  • 愉快的拿到了数据
《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

这样,前后分离的项目可以这样借助 swagger 测试接口,不需要骚扰任何人。实现自己的业务逻辑,简单实现一点。

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

代码:

// blog/index.vue
<template>
  <div class="Blog">
    <h1>{{ msg }}</h1>
     
  <div v-for="(blog,index) in blogList" v-bind:key="index">
    <h3 >
      <a :href="`https://juejin.im/book/`+blog.id" >
        <span>{{blog.title}}</span>
      </a>
      
      </h3>
  </div>
     
  </div>
</template>

<script>
export default {
  name: "Blog",
  data() {
    return {
      msg: "掘金小册一览",
      blogList: []
    };
  },
  created() {
    this.getBlog();
  },
  methods: {
    getBlog() {
      this.$http.get("/v1/getListByLastTime?src=web&pageNum=1").then(res => {
        this.blogList = res.data.d;
      });
    }
  }
};
</script>
复制代码

发布

  • 扫盲

写完代码之后部署到线上,是不会在线上 clone 代码之后 Npm run dev 的:laughing:,那样会有太多太多的垃圾依赖,为用户带来了灾难性的网络请求,通常借助webPack打包之后发布到服务器的web服务当中。

运行

npm run build
复制代码
《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

打包目录是之前配置的webpack

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构
《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

好了,很多人直接双击 index.html 是不行的。

Tip: built files are meant to be served over an HTTP server. Opening index.html over file:// won't work.

需要 http 服务启动,可以扔到本地或服务器的 nginx、apache、tomcat等容器测试,我通常使用 python 启动一个 http 服务来运行(脚本地址)、当然,自己 ide 支持 http 启动也可以。

生产中的跨域

生产当中,因为前端后端必然不同端口,避免跨域,通常使用 nginx 的正向/反向代理作为跨域的手段。(并非负载均衡,两个概念)

server {
        listen       80;
        server_name  localhost;
 
        location ^~ /v1 {
        proxy_pass              https://xiaoce-timeline-api-ms.juejin.im;#代理。
        proxy_set_header        X-Real-IP $remote_addr;#转发客户端真实IP
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        Host $http_host;
        }
 }       
复制代码

简单配置一下就可以了,不多讲,前端同学了解一下就可以了,nginx 能干的事情还有很多。

API 的规范化

你是否为了找某一个业务的接口头痛 你是否还在使用全局搜索找自己的接口 你是否某一个接口不同组件重复写了多次 整理一下自己的接口吧,像上文的 router 一样整齐的划分吧。

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

/renderer 下建立一个 api 的文件夹

《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

webpack.base.conf.js 添加一条 api 的别名,方便我们日后大量调用

'api': resolve('src/renderer/api')
复制代码

我们建立 /renderer/api/juejin.js

import axios from 'axios'

let Api = Function()

Api.prototype = {
    getBlog(page, fn) {
        axios.get(`/v1/getListByLastTime?src=web&pageNum=${page}`).then(res => {
            // if (res.data.code === 200) {
            fn(res.data)
            // }
        }).error()
    }
}
export default new Api()


复制代码

修改一下我们刚才 /blog/index.vue 的 Axios 请求:

  • 先引入 api
import juejin from "@/api/juejin";
复制代码

注掉之前离散的 axios ,使用从 api 中定义过的 XHR 请求数据。

getBlog() {
      // this.$http.get("/v1/getListByLastTime?src=web&pageNum=1").then(res => {
      //   this.blogList = res.data.d;
      // });
      juejin.getBlog("1", response => {
        this.blogList = response.d;
      });
    }
复制代码
《从零构建前后分离的web项目》实战 - 欲善其事必先利其器 继续打磨前端架构

OK,很多同学不理解,本来简单的事情为什么搞复杂了?其实这样不复杂,看起来还很清晰,倘若一个大项目上千个请求,不仅重复导致代码覆盖率低,看起来还很乱,不利于团队协作。本系列文章在带领大家前后分离的基础上,会为大家提供更多有利于团队的开发方式,养成良好习惯。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Paradigms of Artificial Intelligence Programming

Paradigms of Artificial Intelligence Programming

Peter Norvig / Morgan Kaufmann / 1991-10-01 / USD 77.95

Paradigms of AI Programming is the first text to teach advanced Common Lisp techniques in the context of building major AI systems. By reconstructing authentic, complex AI programs using state-of-the-......一起来看看 《Paradigms of Artificial Intelligence Programming》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具