本文講述了畢業實習和正式工做1年以來,使用Vue開發項目的一些我的經歷和想法,僅是我的總結,若有不合理的地方,歡迎吐槽。如下是本文的大概內容。 javascript
一開始實習接觸Vue的腳手架是VueCli2版本,學習的webpack配置也都Cli2的,後來公司使用的是Cli3,因此有一個學習和適應的過程。
VueCli2和VueCli3的差異大概體如今:css
3.0:vue create。
2.0:vue init webpackhtml
3.0啓動npm run serve
2.0啓動npm run dev前端
2.0 config、build文件夾中進行項目的webpack、多環境和打包等配置
3.0 項目結構比2.0要簡潔,缺乏了build和confilg文件,可自行建立與package.json同級的 vue.config.js 文件,進行配置。
主要的經常使用配置整理以下:vue
// vue.config.js 基本配置方法
module.exports = {
// 項目部署的基礎路徑
// 咱們默認假設你的應用將會部署在域名的根部,
// 好比 https://www.my-app.com/
// 若是你的應用時部署在一個子路徑下,那麼你須要在這裏
// 指定子路徑。好比,若是你的應用部署在
// https://www.foobar.com/my-app/
// 那麼將這個值改成 `/my-app/`
// 基本路徑 baseURL已通過時
publicPath: './',
// 打包項目時構建的文件目錄,用法與webpack自己的output.path一致
outputDir: 'dist',
// 靜態資源目錄 (js, css, img, fonts)
assetsDir: 'assets',
// eslint-loader 是否在保存的時候檢查,編譯不規範時,設爲true在命令行中警告,若設爲error則不只警告,而且編譯失敗
lintOnSave: true,
// 調整內部的 webpack 配置。查閱 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/webpack.md
chainWebpack: () => {},
configureWebpack: () => {},
// vue-loader 配置項 https://vue-loader.vuejs.org/en/options.html
vueLoader: {},
// 生產環境是否生成 sourceMap 文件,默認true,若不須要生產環境的sourceMap,能夠設置爲false,加速生產環境的構建
productionSourceMap: true,
// css相關配置
css: {
// 是否使用css分離插件 ExtractTextPlugin,採用獨立樣式文件載入,不採用<style>方式內聯至html文件中
extract: true,
// 是否在構建樣式地圖,false將提升構建速度
sourceMap: false,
// css預設器配置項
loaderOptions: {},
// 啓用 CSS modules for all css / pre-processor files.
// 這個選項不會影響 `*.vue` 文件
modules: false
},
// 在生產環境下爲 Babel 和 TypeScript 使用 `thread-loader`
// 在多核機器下會默認開啓。
parallel: require('os').cpus().length > 1,
// 是否啓用dll See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#dll-mode
dll: false,
// PWA 插件相關配置 see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
pwa: {},
// webpack-dev-server 相關配置
devServer: {
open: process.platform === 'darwin',
host: '0.0.0.0',//若是是真機測試,就使用這個IP
port: 1234,
https: false,
hotOnly: false,
proxy: null, // 設置代理
// proxy: {
// '/api': {
// target: '<url>',
// ws: true,
// changOrigin: true
// }
// },
before: app => {}
},
// 第三方插件配置
pluginOptions: {
// ...
}
}
複製代碼
axios二次封裝的目的主要是三個方面:java
在進行接口請求攔截進行配置處理的時候,針對如下參數,能夠靈活配置。
node
參數 | 意義 | 例子 |
---|---|---|
url | 用於請求的服務器 URL | url: '/user' |
method | 建立請求時使用的方法 | method: 'get' |
baseURL | 自動加在 url 前面,除非 url 是一個絕對 URL,經過設置一個 baseURL 便於爲 axios 實例的方法傳遞相對 URL |
baseURL: 'some-domain.com/api/' |
transformRequest | 容許在向服務器發送前,修改請求數據 // 只能用在 'PUT', 'POST' 和 'PATCH' 這幾個請求方法 // 後面數組中的函數必須返回一個字符串,或 ArrayBuffer,或 Stream |
transformRequest: [function (data) { // 對 data 進行任意轉換處理 |
return data; }], |
||
headers | 即將被髮送的自定義請求頭 | headers: {'X-Requested-With': 'XMLHttpRequest'}, |
params | 即將與請求一塊兒發送的 URL 參數 | params: { ID: 12345 }, |
paramsSerializer | 負責 params 序列化的函數(e.g. www.npmjs.com/package/qs, api.jquery.com/jquery.para…) |
paramsSerializer: function(params) { return Qs.stringify(params, {arrayFormat: 'brackets'}) } |
data | data 是做爲請求主體被髮送的數據只適用於這些請求方法 'PUT', 'POST', 和 'PATCH' 在沒有設置 transformRequest 時,必須是如下類型之一:- string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams - 瀏覽器專屬:FormData, File, Blob - Node 專屬: Stream |
data: { firstName: 'Fred' } |
timeout | 指定請求超時的毫秒數(0 表示無超時時間) | timeout: 1000 |
adapter | 容許自定義處理請求,以使測試更輕鬆,返回一個 promise 並應用一個有效的響應 | adapter: function (config) { /* ... */ }, |
auth | 表示應該使用 HTTP 基礎驗證,並提供憑據,這將設置一個 Authorization 頭,覆寫掉現有的任意使用 headers 設置的自定義 Authorization 頭 |
auth: { username: 'janedoe', password: 's00pers3cret' }, |
responseType | 服務器響應的數據類型,能夠是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' | responseType: 'json', // 默認的 |
xsrfCookieName | 用做 xsrf token 的值的cookie的名稱 | xsrfCookieName: 'XSRF-TOKEN' |
xsrfHeaderName | 承載 xsrf token 的值的 HTTP 頭的名稱 | xsrfHeaderName: 'X-XSRF-TOKEN', // 默認的 |
onUploadProgress | 容許爲上傳處理進度事件 | onUploadProgress: function (progressEvent) { // 對原生進度事件的處理 }, |
onDownloadProgress | 容許爲下載處理進度事件 | onDownloadProgress: function (progressEvent) { // 對原生進度事件的處理 }, |
maxContentLength | 定義容許的響應內容的最大尺寸 | maxContentLength: 2000 |
validateStatus | 定義對於給定的HTTP 響應狀態碼是 resolve 或 reject promise 。若是 validateStatus 返回 true (或者設置爲 null 或 undefined ),promise 將被 resolve; 不然,promise 將被 rejecte |
validateStatus: function (status) { return status >= 200 && status < 300; // 默認的 }, |
maxRedirects | 定義在 node.js 中 follow 的最大重定向數目 | maxRedirects: 5, // 默認的 |
httpAgent | httpAgent 和 httpsAgent 分別在 node.js 中用於定義在執行 http 和 https 時使用的自定義代理。 |
httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), |
proxy | 定義代理服務器的主機名稱和端口 | proxy: { host: '127.0.0.1', port: 9000, auth: : { username: 'mikeymike', password: 'rapunz3l' } }, |
cancelToken | 指定用於取消請求的 cancel token | cancelToken: new CancelToken(function (cancel) { }) |
在網速較慢的狀況下,容易出現用戶屢次點擊而重複請求使得頁面抖動的問題,用戶體驗很差,所以進行攔截重複請求的處理。思路是:
建立請求隊列 ---->jquery
-----攔截處理------
標識即將發送的請求---->
判斷即將發送的請求與隊列中的請求是否相同---->
若相同則執行當前請求的取消方法,並從請求隊列中刪除---->
建立即將請求的取消方法,放入隊列中
攔截處理webpack
request.interceptors.request.use(
config => {
// 攔截重複請求(即當前正在進行的相同請求)
const requestData = getRequestIdentify(config, true); // 標識請求
removePending(requestData, true);// 取消重複請求
config.cancelToken = new CancelToken((c) => { // 建立當前請求的取消方法
pending[requestData] = c;
});
return config;
}, error => {
return Promise.reject(error)
})
複製代碼
標識請求ios
const getRequestIdentify = (config, isReuest = false) => {
let url = config.url;
if (isReuest) {
url = config.baseURL + config.url.substring(1, config.url.length);
}
return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data));
};
複製代碼
取消重複請求
const pending = {};
const CancelToken = axios.CancelToken;
const removePending = (key, isRequest = false) => {
if (pending[key] && isRequest) {
pending[key]('取消重複請求');
}
delete pending[key];
};
複製代碼
接口響應的攔截主要是對接口返回的數據進行提取、封裝使用,以及對請求異常進行統一配置處理。
request.interceptors.response.use(
response => {
const data = response.data || {};
return data;
},
error => {
const code = error.response.status;
if (code) {
let msg = '';
switch (code) {
case 400:
msg = '請求錯誤';
break;
case 401:
msg = '未受權,請登陸';
break;
case 403:
msg = '拒絕訪問';
break;
case 404:
msg = `請求${error.response.config.url}出現404錯誤`;
break;
case 408:
msg = '請求超時';
break;
case 500:
msg = '服務器內部錯誤';
break;
case 501:
msg = '服務未實現';
break;
case 502:
msg = '網關錯誤';
break;
case 503:
msg = '服務不可用';
break;
case 504:
msg = '網關超時';
break;
case 505:
msg = 'HTTP版本不受支持';
break;
}
Message.error(msg);
}
return Promise.reject(error);
}
)
複製代碼
單獨封裝接口請求方法, GET方法的參數爲params,POST方法的參數爲data。
// api.js
import request from '@/utils/request';
export function APIPostMethod(data) { // 自定義接口方法
return request({
url: '/url1',
method: 'post',
data
});
}
export function APIGetMethod(params) { // 自定義接口方法
return request({
url: '/url2',
method: 'get',
params
});
}
複製代碼
在業務中調用API方法
import { APIGetMethod, APIPostMethod } from '@/utils/request';
const params = {}
APIGetMethod(params).then(res => {
//...
//對數據處理
})
複製代碼
簡單來講,爲了防止XSS和CSFR攻擊,瀏覽器的同源策略限制帶來了先後端分離開發時的跨域問題。即當請求與響應不在同一個協議+域名+端口下,就不會被瀏覽器容許。可是同源策略只是瀏覽器的一種策略,不是HTTP協議的一部分,所以服務端調用HTTP接口只是使用HTTP協議,而不會經過瀏覽器,更不會執行JS腳本,因此不會觸發同源策略機制,不存在跨域問題。針對這個特色,能夠從前端配置代理服務器入手。
嘗試過兩種解決跨域的方法:
1.Node.js中間件代理
在vue-cli中,裏用node+webpack+webpack-dev-server代理接口跨域。
//vue.config.js
const config = {
// ...
devServer: {
hot: true,
open: true,
host: '127.0.0.1',
// host: '0.0.0.0',//若是是真機測試,就使用這個IP
port: 8899,
https: false,
hotOnly: false,
proxy: {
'/': {
target: 'http://xxx.xxx.xx.xx:xxxx/',
logLevel: 'debug',
ws: false,
changOrigin: true
}
}
}
}
module.exports = config;
複製代碼
2.Nginx反向代理
經過nginx配置一個代理服務器,域名與本地域名A一致,但端口不一樣,反向代理訪問對方B域名下的接口。
#proxy服務器
server {
listen 8088;
server_name _;
client_max_body_size 50m;
location / {
root html/dist;
try_files $uri $uri/ /index.html;
index /index.html;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ^~/api {
proxy_pass http://xxx.xxx.xx.xx:xxxx/api;
proxy_redirect default;
}
}
複製代碼
前端在啓動項目的時候,須要把項目proxy代理對應的8088端口上(感受這種方式有點多餘,可是同事用這種方式,咱不敢說,因此我的這邊是用的第一種)。
Nginx的方式更適合用於線上部署解決跨域使用,開發環境下,使用vue-cli中的devserve既方便又快捷。
懶加載也叫延遲加載,使組件進行異步加載。目的是延遲非必要資源的加載,減小頁面加載的時間,從而優化頁面的性能。
export default new Router({
routes:[
{
path: '/test',
name: 'test',
//懶加載
component: resolve => require(['../page/test.vue'], resolve),
},
]
})
複製代碼
在路由懶加載下,代碼根據路由被拆分爲不一樣的代碼塊,在切換進入相應的路由時,纔對對應的代碼塊進行加載,加載更加高效了。
components: {
UpdateModal: resolve => { require(['./UpdateNormalTaskModal'], resolve); }
},
複製代碼
在路由懶加載的前提下,進行組件懶加載的對比實驗。
未使用組件懶加載:
按需加載通常用於在使用第三方庫的時候,爲了不第三方庫過大,而形成的對首屏加載帶來的過大的壓力。
以VantUI按需加載爲例
npm i babel-plugin-import -D
module.exports = {
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
],
presets: [
'@vue/app'
]
};
複製代碼
import { Swipe, SwipeItem, ImagePreview } from 'vant';
export default {
components: {
vanSwipe: Swipe,
vanSwipeItem: SwipeItem,
}
}
複製代碼
在vue的v-bind語法中使用到本地資源時,路徑是相對本地文件夾的相對路徑,打包時沒法解析。
將圖片資源放在static靜態資源文件夾下,在使用src時,直接訪問根目錄下的資源
好比圖片放在public目錄下,路徑直接寫爲'/img/case/gkjg/7.jpg'
在data中採用require的方式,將圖片資源導入,而後使用imgUrl變量。
data(){
return {
imgUrl:require("../assets/test.png")
}
}
複製代碼
<keep-alive></keep-alive>
包含的組件會被緩存下來,不進行再次渲染DOM,從而節省性能,切換內容時會出發activated和deactivated兩個生命週期鉤子函數,被緩存的組件會保留當前組件的狀態。
利用router的meta字段
//...router.js
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
meta: {
keepAlive: false // 不須要緩存
}
},
{
path: '/page1',
name: 'Page1',
component: Page1,
meta: {
keepAlive: true // 須要被緩存
}
}
]
})
複製代碼
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
複製代碼
<keep-alive include="test-keep-alive">
<!-- 將緩存name爲test-keep-alive的組件 -->
<component></component>
</keep-alive>
<keep-alive include="a,b">
<!-- 將緩存name爲a或者b的組件,結合動態組件使用 -->
<component :is="view"></component>
</keep-alive>
<!-- 使用正則表達式,需使用v-bind -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<keep-alive exclude="test-keep-alive">
<!-- 將不緩存name爲test-keep-alive的組件 -->
<component></component>
</keep-alive>
複製代碼
結合路由beforeRouteLeave(to, from, next)
的鉤子,設置to.meta.keepAlive
來指定目的頁面是否進行keepAlive。
export default {
data() {
return {};
},
methods: {},
beforeRouteLeave(to, from, next) {
// 設置下一個路由的 meta
to.meta.keepAlive = true; // 當前頁面跳轉到下一個頁面時,讓目的頁面緩存,不刷新
next();
}
};
複製代碼
恰當使用keep-alive,結合activated和deactivated兩個鉤子函數,將不須要更新的內容緩存下來,將須要更新的內容放在兩個鉤子中去處理,這樣能夠減小沒必要要的http請求和DOM重複渲染,提高了很多性能