背景:css
部门有个须要前端展现的页面,0前端开发经验,初次接触Vue,实现从后端到前端,从入门,开发、打包到部署,完整的历程。html
ES6基础知识了解,看了阮一峰的ES6入门前端
而后粗略撸了一遍Vue官方文档,动手用webpack搭建了一个简单的demo。vue
看了Echarts的官方demo,了解了几种数据图表的数据结构。由于我要作的项目就是要将后端接口的数据拿到,而后图形化的形式展现出来。node
下面是整个项目过程当中的关键点以及一些坑、react
后端采用Golang web框架beego实现RESTFul接口,参考了官方文档,而后本身写了一个小demo。比较容易上手,一周就完成后端接口。jquery
在构建应用时须要访问一个 API 并展现其数据,调研Vue的多种方式后选择了官方推荐的axiox。webpack
前端是个发展迅速的领域,前端请求天然也发展迅速,从原生的XHR到jquery ajax,再到如今的axios和fetch。ios
$.ajax({
type: 'POST',
url: url,
data: data,
dataType: dataType,
success: function() {},
error: function() {}
})
复制代码
它是对原生XHR的封装,还支持JSONP,很是方便;真的是用过的都说好。可是随着react,vue等前端框架的兴起,jquery早已不复当年之勇。不少状况下咱们只须要使用ajax,可是却须要引入整个jquery,这很是的不合理,因而便有了fetch的解决方案。nginx
fetch号称是ajax的替代品,它的API是基于Promise设计的,旧版本的浏览器不支持Promise,须要使用polyfill es6-promise
举个例子:
// 原生XHR
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText) // 从服务器获取数据
}
}
xhr.send()
// fetch
fetch(url)
.then(response => {
if (response.ok) {
response.json()
}
})
.then(data => console.log(data))
.catch(err => console.log(err))
复制代码
看起来好像是方便点,then链就像以前熟悉的callback。
在MDN上,讲到它跟jquery ajax的区别,这也是fetch很奇怪的地方:
当接收到一个表明错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即便该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (可是会将 resolve 的返回值的 ok 属性设置为 false ), 仅当网络故障时或请求被阻止时,才会标记为 reject。 默认状况下, fetch 不会从服务端发送或接收任何 cookies, 若是站点依赖于用户 session,则会致使未经认证的请求(要发送 cookies,必须设置 credentials 选项).
忽然感受这还不如jquery ajax好用呢?别急,再搭配上async/await将会让咱们的异步代码更加优雅:
async function test() {
let response = await fetch(url);
let data = await response.json();
console.log(data)
}
复制代码
看起来是否是像同步代码同样?简直完美!好吧,其实并不完美,async/await是ES7的API,目前还在试验阶段,还须要咱们使用babel进行转译成ES5代码。
还要提一下的是,fetch是比较底层的API,不少状况下都须要咱们再次封装。 好比:
// jquery ajax
$.post(url, {name: 'test'})
// fetch
fetch(url, {
method: 'POST',
body: Object.keys({name: 'test'}).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&')
})
复制代码
因为fetch是比较底层的API,因此须要咱们手动将参数拼接成'name=test'的格式,而jquery ajax已经封装好了。因此fetch并非开箱即用的。
另外,fetch还不支持超时控制。
哎呀,感受fetch好垃圾啊,,还须要继续成长。。
axios是尤雨溪大神推荐使用的,它也是对原生XHR的封装。它有如下几大特性:
简单使用
axios({
method: 'GET',
url: url,
})
.then(res => {console.log(res)})
.catch(err => {console.log(err)})
复制代码
并发请求,官方的并发例子:
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
复制代码
axios体积比较小,也没有上面fetch的各类问题,我认为是当前最好的请求方式
详情参考官方文档
为了方便开发,将axios的方法结合后端接口,进行封装,使用起来会比较方便。
首先建立一个request.js,内容以下:
import axios from 'axios';
import Qs from 'qs';
function checkStatus(err) {
let msg = "", level = "error";
switch (err.response.status) {
case 401:
msg = "您尚未登录";
break;
case 403:
msg = "您没有该项权限";
break;
case 404:
msg = "资源不存在";
break;
case 500:
msg = "服务器发生了点意外";
break;
}
try {
msg = res.data.msg;
} catch (err) {
} finally {
if (msg !== "" && msg !== undefined && msg !== null) {
store.dispatch('showSnackBar', {text: msg, level: level});
}
}
return err.response;
}
function checkCode(res) {
if ((res.status >= 200 && res.status < 400) && (res.data.status >= 200 && res.data.status < 400)) {
let msg = "", level = "success";
switch (res.data.status) {
case 201:
msg = "建立成功";
break;
case 204:
msg = "删除成功";
break;
}
try {
msg = res.data.success;
} catch (err) {
} finally {
}
return res;
}
return res;
}
//这里封装axios的get,post,put,delete等方法
export default {
get(url, params) {
return axios.get(
url,
params,
).then(checkCode).catch((error)=>{console.log(error)});
},
post(url, data) {
return axios.post(
url,
Qs.stringify(data),
).then(checkCode).catch(checkStatus);
},
put(url, data) {
return axios.put(
url,
Qs.stringify(data),
).then(checkCode).catch(checkStatus);
},
delete(url, data) {
return axios.delete(
url,
{data: Qs.stringify(data)},
).then(checkCode).catch(checkStatus);
},
patch(url, data) {
return axios.patch(
url,
data,
).then(checkCode).catch(checkStatus);
},
};
复制代码
建立一个api.js,存放后端的接口:
//导入上面的request模块
import request from './request';
//声明后端接口
export const urlUserPrefix = '/v1/users';
export const urlProductPrefix = '/v1/products';
//使用前面封装好的方法,调用后端接口
export const getUserslInfoLast = data => request.get(`${urlUserPrefix}`, data);
export const getProductsInfo = data => request.get(`${urlProductPrefix}`, data);
复制代码
在.vue文件中使用定义的方法,获取后端接口的数据:
export default {
components: {
chart: ECharts,
},
store,
name: 'ResourceTypeLine',
data: () =>({
seconds: -1,
//define dataset
apiResponse:{},
initOptions: {
renderer: options.renderer || 'canvas'
},
mounted:function() {
this.fTimeArray = this.getFormatTime()
//调用method里面的方法
this.getUserInfo()
},
methods: {
//异步方式调用后端接口
async getUserInfo() {
const resThis = await urlUserPrefix({
params: {
//get的参数在这里添加
beginTime: this.fTimeArray[0],
endTime: this.fTimeArray[1],
}
});
this.apiResponseThisMonth = resThis.data
try {
} catch (err) {
console.log(err);
}
},
复制代码
为了更方便地与后台联调,须要在用vue脚手架建立地项目中,在config目录的index.js文件设置proxytable来实现跨域请求,具体代码以下:
module.exports = {
build: {
env: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '.',
productionSourceMap: false,
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
},
dev: {
env: require('./dev.env'),
port: 8080,
// hosts:"0.0.0.0",
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
//配置跨域请求,注意配置完以后须要重启编译该项目
proxyTable: {
//请求名字变量能够本身定义
'/api': {
target: 'http://test.com', // 请求的接口域名或IP地址,开头是http或https
// secure: false, // 若是是https接口,须要配置这个参数
changeOrigin: true,// 是否跨域,若是接口跨域,须要进行这个参数配置
pathRewrite: {
'^/api':""//表示须要rewrite重写路径
}
}
},
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
}
}
复制代码
因为开发环境中配置的跨域在将项目打包为静态文件时是没有用的 ,就想到了用 nginx 经过反向代理的方式解决这个问题,可是其中有一个巨大的坑,后面会讲到。
liunx 下 nginx 安装配置(将不作多的阐述,请自行百度)
# 新增的服务
# 新增的服务
server {
listen 8086; # 监听的端口
location / {
root /var/www; # vue 打包后静态文件存放的地址
index index.html; # 默认主页地址
}
location /v1 {
proxy_pass http://47.106.184.89:9010/v1; # 代理接口地址
}
location /testApi {
proxy_pass http://40.106.197.89:9086/testApi; # 代理接口地址
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
复制代码
/var/www是我当前将vue 文件打包后存放在 liunx下的路径 ,
当咱们启动 nginx 后 就能够经过http://ip地址:8086/访问到vue 打包的静态文件。
2.location /v1 指拦截以
v1开头的请求,http请求格式为
http://ip地址:8086/v1/***,这里有一个坑!必定要按照上面的配置文件**:proxy_pass http://47.106.184.89:9010/v1;若是你像我一开始写的:proxy_pass http://47.106.184.89:9010/;,你永远也匹配不到对应的接口!意思是你的接口地址以
v1开头,location的匹配也要以
v1`开头。
proxy_pass http://47.106.197.89:9093/v1;` 当拦截到须要处理的请求时,将拦截请求代理到接口地址。
下面是config/index.js配置文件
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')
module.exports = {
build: {
env: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '.',
productionSourceMap: false,
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
},
dev: {
env: require('./dev.env'),
port: 8080,
// hosts:"0.0.0.0",
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
//配置跨域请求,注意配置完以后须要重启编译该项目
proxyTable: {
//请求名字变量能够本身定义
'/api': {
target: 'http://billing.hybrid.cloud.ctripcorp.com', // 请求的接口域名或IP地址,开头是http或https
// secure: false, // 若是是https接口,须要配置这个参数
changeOrigin: true,// 是否跨域,若是接口跨域,须要进行这个参数配置
pathRewrite: {
'^/api':""//表示须要rewrite重写路径
}
}
},
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
}
}
复制代码
打包Docker镜像
FROM Nginx:base
MAINTAINER hantmac <hantmac@outlook.com>
WORKDIR /opt/workDir
RUN mkdir /var/log/workDir
COPY dist /var/www
ADD nginx/default.conf /etc/nginx/conf.d/default.conf
ENTRYPOINT nginx -g "daemon off;"
复制代码
这是首次接触前端的第一个项目,期间经历了从后端接口开发,前端框架选型(一度想要用react,后来仍是放弃),熟悉Vue,到组件开发,webpack构建,Nginx部署,Docker发布的完整过程。虽然页面比较简单,可是期间的采坑无数。Vue确实是对小白比较友好,广大后端er能够玩耍起来了!