1年Vue項目經驗總結(持續更新中...)

前言

本文講述了畢業實習和正式工做1年以來,使用Vue開發項目的一些我的經歷和想法,僅是我的總結,若有不合理的地方,歡迎吐槽。如下是本文的大概內容。 javascript



1.Vue項目搭建

1.1從VueCli2到VueCli3

一開始實習接觸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: {
   // ...
  }
 }
複製代碼

1.2 Axios二次封裝

axios二次封裝的目的主要是三個方面:java

  • 接口的請求攔截處理(配置處理、攔截重複請求)
  • 接口的響應攔截處理
  • API方法的封裝複用

1.2.1 接口請求攔截處理

1.2.1.1 可配置項

在進行接口請求攔截進行配置處理的時候,針對如下參數,能夠靈活配置。
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 (或者設置爲 nullundefined),promise 將被 resolve; 不然,promise 將被 rejecte validateStatus: function (status) {
   return status >= 200 && status < 300; // 默認的
 },
maxRedirects 定義在 node.js 中 follow 的最大重定向數目 maxRedirects: 5, // 默認的
httpAgent httpAgenthttpsAgent 分別在 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) {
 })

1.2.1.2 攔截重複請求

在網速較慢的狀況下,容易出現用戶屢次點擊而重複請求使得頁面抖動的問題,用戶體驗很差,所以進行攔截重複請求的處理。思路是:
建立請求隊列 ---->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];
};
複製代碼

1.2.2 接口響應攔截處理

接口響應的攔截主要是對接口返回的數據進行提取、封裝使用,以及對請求異常進行統一配置處理。

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);
  }
)
複製代碼

1.2.3 API方法封裝

單獨封裝接口請求方法, 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 => {
//...
//對數據處理
})
複製代碼

1.3跨域處理

簡單來講,爲了防止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既方便又快捷。


2.Vue技巧

2.1 懶加載

懶加載也叫延遲加載,使組件進行異步加載。目的是延遲非必要資源的加載,減小頁面加載的時間,從而優化頁面的性能。

2.1.1 路由懶加載

export default new Router({
  routes:[
    {
     path: '/test',
     name: 'test',
      //懶加載
     component: resolve => require(['../page/test.vue'], resolve),
    },
  ]
})
複製代碼

在路由懶加載下,代碼根據路由被拆分爲不一樣的代碼塊,在切換進入相應的路由時,纔對對應的代碼塊進行加載,加載更加高效了。

image.png

2.1.2 組件懶加載

components: {
  UpdateModal: resolve => { require(['./UpdateNormalTaskModal'], resolve); }
},
複製代碼

在路由懶加載的前提下,進行組件懶加載的對比實驗。
未使用組件懶加載:

image.png

整個頁面爲一個js,大小爲718KB,加載耗時166ms。
在使用組件懶加載的時候:
image.png

整個頁面被拆分爲三個js(53.js),其中懶加載的兩個組件,各自一個js(78.js、91.js),可看出來,53.js的文件大小變小,加載速度變快,將一個js拆分爲多個進行並行加載,能夠提升加載的效率,從而提高性能。

2.2按需引入

按需加載通常用於在使用第三方庫的時候,爲了不第三方庫過大,而形成的對首屏加載帶來的過大的壓力。
以VantUI按需加載爲例

  • 安裝babel-plugin-import
    npm i babel-plugin-import -D
  • 在babel.config.js中配置plugins(插件)
module.exports = {
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ],
  presets: [
    '@vue/app'
  ]
};
複製代碼
  • 在要用到三方組件的vue文件中引入使用
import { Swipe, SwipeItem, ImagePreview } from 'vant';
export default {
  components: {
    vanSwipe: Swipe,
    vanSwipeItem: SwipeItem,
  }
}
複製代碼

2.3 JS中使用本地圖片資源

在vue的v-bind語法中使用到本地資源時,路徑是相對本地文件夾的相對路徑,打包時沒法解析。

2.3.1.靜態資源讀取

將圖片資源放在static靜態資源文件夾下,在使用src時,直接訪問根目錄下的資源
好比圖片放在public目錄下,路徑直接寫爲'/img/case/gkjg/7.jpg'

image.png

2.3.2導入資源

在data中採用require的方式,將圖片資源導入,而後使用imgUrl變量。

data(){
  return {
		imgUrl:require("../assets/test.png")
  }
}
複製代碼

2.4 keep-alive

<keep-alive></keep-alive>包含的組件會被緩存下來,不進行再次渲染DOM,從而節省性能,切換內容時會出發activated和deactivated兩個生命週期鉤子函數,被緩存的組件會保留當前組件的狀態。

2.4.1路由頁面緩存

利用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> 
複製代碼

2.4.2組件緩存

<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>

複製代碼

2.4.3 結合berforeRouteEnter

結合路由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重複渲染,提高了很多性能

相關文章
相關標籤/搜索