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

工欲善其事必先利其器 - 继续打磨前端架构

抱歉生病拖更了,1024快乐javascript

本文永久更新地址

填坑

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

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

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

咱们把上文的 renderer/src 改为比较容易理解的 src/renderer ,这实际上是一个编程习惯问题。前端

上文 renderer/src 的问题vue

eslint-config-alloy : 想写出更规范的代码能够参考这个规则java

下面进入本章正题node


axios

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

Tips

这里呢也科普一下:何时依赖须要放到 dependencies、何时依赖须要放到 devDependencies:webpack

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

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

引入 Axios

  • 直接玩最新的

2018-09-28 截图 npmjs.com
  • 添加依赖
"dependencies": {    
    "axios": "^0.18.0"
 }

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

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

封装axios

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

  • interceptors 文档

我尽量的为你们写了详细的注释。

// 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')
   }
 },

生产和开发的跨域问题

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

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

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

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

cnode 掘金 社区 API 举例

https://cnodejs.org/api
上边cnode 的 API 是能够随意调用的,由于后端作了处理。

看看掘金的:
https://xiaoce-timeline-api-m...
请求一下,不出意外浏览器作了跨域报警。

哦,咱们适配一下 node 代理

官方例子在这:
https://vuejs-templates.githu...

扩展一下 proxyTable:

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

再次发送请求。

  • 愉快的拿到了数据

这样,先后分离的项目能够这样借助 swagger 测试接口,不须要骚扰任何人。实现本身的业务逻辑,简单实现一点。

代码:

// 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 的😆,那样会有太多太多的垃圾依赖,为用户带来了灾难性的网络请求,一般借助webPack打包以后发布到服务器的web服务当中。

运行

npm run build

打包目录是以前配置的webpack

好了,不少人直接双击 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 同样整齐的划分吧。

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

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;
      });
    }

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

本章所有代码在这里

关于我

往期文章

《从零构建先后分离 WEB 项目》 序 - 开源的意义

《从零构建先后分离web项目》:开篇 - 纵观WEB历史演变

《从零构建先后分离web项目》探究 - 深刻聊聊先后分离架构

《从零构建先后分离web项目》准备 - 前端了解过关了吗?前端基础架构和技术介绍

《从零构建先后分离web项目》实战 - 5分钟快速构建规范的前端项目骨架

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

相关文章
相关标签/搜索