背景: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能夠玩耍起來了!