這篇文章總結了vue項目的所遇到的問題,包括跨域、用戶認證、接口統一管理、路由配置、兼容性處理,性能優化等內容。javascript
項目github地址 :css
根據如下教程安裝,而後設置好環境變量
http://www.runoob.com/nodejs/...html
視頻教程 http://101.110.118.22/github....前端
centos若是裝不上看這裏:https://www.rosehosting.com/b...vue
npm默認使用的源的服務器在國外下載速度慢,因此須要更換源如下兩種方法任選一種java
參考連接: https://npm.taobao.org/
# 安裝 npm install -g cnpm --registry=https://registry.npm.taobao.org #安裝完cnpm,以後再按照依賴就要使用cnpm cnpm install [包名]
參考連接 https://segmentfault.com/a/11...
修改源爲淘寶的源node
npm config set registry http://registry.npm.taobao.org/
咱們在發佈本身包的時候須要將官方的源改回來linux
npm config set registry https://registry.npmjs.org/
切換nodejs版本有兩種方式,分別是nvm
和n
,n更簡單推薦使用
參考連接 https://www.jianshu.com/p/c64...官網 https://github.com/tj/nwebpack
#安裝 npm install -g n #使用n下載所需node版本 n 版本號 #下載最新版本 n latest # 切換版本 輸入 n, 而後選中所需版本 #以指定的版原本執行版本 n use 7.4.0 index.js
linux使用n安裝新版本nodejs以後,若是node -v
仍是原來的版本,那麼就須要改變一下環境變量ios
vim .bash_profile
export NODE_HOME=/usr/local #NODE_HOME改爲新版本nodejs安裝的目錄,若是找不到,find / -name node export PATH=$NODE_HOME/bin:$PATH export NODE_PATH=$NODE_HOME/lib/node_modules:$PATH
修改環境變量參考:https://blog.csdn.net/yi412/a...
參考文檔 http://javascript.ruanyifeng....
vue-cli目前已經更新到3版本,vue-cli3把webpack相關的配置隱藏起來了,全部的配置都在vue.config.js文件夾中,因此使用vue-cli3須要的webpack水平較高,建議使用vue-cli2
參考連接:https://github.com/vuejs/vue-...
安裝:
npm install -g vue-cli
用法:
$ vue init < template-name > < project-name >
例:
$ vue init webpack my-project
目前可用的模塊包括:
vue-cli3x的官方文檔:https://cli.vuejs.org/
Vue-cli3 中vue.config.js文件配置參考文檔:https://cli.vuejs.org/zh/conf...
Vue CLI 的包名稱由 vue-cli
改爲了 @vue/cli
。 若是你已經全局安裝了舊版本的 vue-cli
(1.x 或 2.x),你須要先經過 npm uninstall vue-cli -g
或 yarn global remove vue-cli
卸載它。
安裝
npm install -g @vue/cli
安裝了vue-cli3若是還想使用vue-cli2的init功能,須要安裝一個橋接功能
npm install -g @vue/cli-init
// vue.config.js 配置說明 //官方vue.config.js 參考文檔 https://cli.vuejs.org/zh/config/#css-loaderoptions // 這裏只列一部分,具體配置參考文檔 module.exports = { // 部署生產環境和開發環境下的URL。 // 默認狀況下,Vue CLI 會假設你的應用是被部署在一個域名的根路徑上 //例如 https://www.my-app.com/。若是應用被部署在一個子路徑上,你就須要用這個選項指定這個子路徑。例如,若是你的應用被部署在 https://www.my-app.com/my-app/,則設置 baseUrl 爲 /my-app/。 baseUrl: process.env.NODE_ENV === "production" ? "./" : "/", // outputDir: 在npm run build 或 yarn build 時 ,生成文件的目錄名稱(要和baseUrl的生產環境路徑一致) outputDir: "dist", //用於放置生成的靜態資源 (js、css、img、fonts) 的;(項目打包以後,靜態資源會放在這個文件夾下) assetsDir: "assets", //指定生成的 index.html 的輸出路徑 (打包以後,改變系統默認的index.html的文件名) // indexPath: "myIndex.html", //默認狀況下,生成的靜態資源在它們的文件名中包含了 hash 以便更好的控制緩存。你能夠經過將這個選項設爲 false 來關閉文件名哈希。(false的時候就是讓原來的文件名不改變) filenameHashing: false, // lintOnSave:{ type:Boolean default:true } 問你是否使用eslint `lintOnSave`: true, //若是你想要在生產構建時禁用 eslint-loader,你能夠用以下配置 // lintOnSave: process.env.NODE_ENV !== 'production', //是否使用包含運行時編譯器的 Vue 構建版本。設置爲 true 後你就能夠在 Vue 組件中使用 template 選項了,可是這會讓你的應用額外增長 10kb 左右。(默認false) // runtimeCompiler: false, /** * 若是你不須要生產環境的 source map,能夠將其設置爲 false 以加速生產環境構建。 * 打包以後發現map文件過大,項目文件體積很大,設置爲false就能夠不輸出map文件 * map文件的做用在於:項目打包後,代碼都是通過壓縮加密的,若是運行時報錯,輸出的錯誤信息沒法準確得知是哪裏的代碼報錯。 * 有了map就能夠像未加密的代碼同樣,準確的輸出是哪一行哪一列有錯。 * */ productionSourceMap: false, // 它支持webPack-dev-server的全部選項 devServer: { host: "localhost", port: 1111, // 端口號 https: false, // https:{type:Boolean} open: true, //配置自動啓動瀏覽器 // proxy: 'http://localhost:4000' // 配置跨域處理,只有一個代理 // 配置多個代理 proxy: { "/api": { target: "<url>", ws: true, changeOrigin: true }, "/foo": { target: "<other_url>" } } } };
如下內容依賴環境爲 : vue-cli 版本2.9.x
項目github地址 :
安裝完以上依賴後,就能夠開始一個項目了,咱們先看下後端api的定義
請求
http request header{ //除登陸註冊之外的請求,發起請求時要在請求頭中加入token authorization:jwt } http request body{ }
返回
http response header{ } http response body{ code:業務處理狀態碼 msg:業務處理描述 token:jwt token data:業務數據 }
注:服務器端的host爲118.24.85.97,端口爲22222
1.測試api是否可用
2.註冊
序號 | 參數名 | 是否必填 | 描述 |
---|---|---|---|
1 | name | y | 用戶名 |
2 | pass | y | 密碼 |
3.登陸
序號 | 參數名 | 是否必填 | 描述 |
---|---|---|---|
1 | name | y | 用戶名 |
2 | pass | y | 密碼 |
序號 | 參數名 | 描述 |
---|---|---|
1 | msg | ok |
2 | token | 用於驗證用戶身份的token |
4.獲取當前用戶信息
序號 | 參數名 | 描述 |
---|---|---|
1 | id | 用戶id |
2 | token | 用於驗證用戶身份的token |
在終端中輸入
vue init webpack vue2_template
而後會有一些選項讓你選,按照項目需求選擇,例如我不須要eslint,unit test,就能夠選No,如今選no未來若是須要的話也能夠本身安裝
安裝完成以後,按照提示切換到相應目錄,執行相應指令,而後在瀏覽器打開網址,這樣一個簡單的vue項目就啓動起來了
注意:
首先在src目錄下新建一個文件夾views,用來放咱們的主要頁面,而後在assets文件夾中創建fonts styles imgs,用來存放相應的資源,建完以後,文件夾以下
在這個項目中,咱們使用axios進行數據請求
axios中文文檔: https://www.kancloud.cn/yunye...
# 安裝axios npm/cnpm i axios -S # -S 指安裝到package.json中的dependencies中
安裝完成後,咱們要在main.js中引入,而後測試一下是否成功引入
//main.js文件 import axios from 'axios' axios.get('https://api.github.com/users?since=10') //使用github接口作一下測試 .then(res=>console.log(res)) .catch(err=>console.log(err))
瀏覽器顯示如下信息,說明引入成功
github提供的接口配置了cors,因此咱們可以可以在瀏覽器正常訪問到,但cors兼容性最低到ie10,並且後臺不必定會配置cors,因此在開發時咱們須要配置一下跨域
參考連接:
參考文檔: https://segmentfault.com/a/11...
先找個沒有設置cors的api使用axios訪問一下
axios.get('http://118.24.85.97:22222/api') .then(res=>console.log(res)) .catch(err=>console.log(err))
瀏覽器會由於同源策略報錯
下面進行跨域的配置
配置目錄 config/index.js 13行
proxyTable: { '/apis':{ target:'http://118.24.85.97:22222',//後臺地址 proxyTable 把/apis映射成target 即 /apis=http://118.24.85.97:22222 changeOrigin:true,//是否跨域 pathRewrite:{ '^/apis':'' } } }
再進行訪問數據時就要在接口前面加上/apis(/apis就至關於http://118.24.85.97:22222)
axios.get('/apis/api') .then(res=>console.log(res)) .catch(err=>console.log(err))
而後就發現瀏覽器訪問成功了
proxyTable原理:跨域是瀏覽器禁止的,服務端並不由止跨域 ,因此瀏覽器能夠發給本身的服務端而後,由本身的服務端再轉發給要跨域的服務端,作一層代理。proxyTable使用的是http-proxy-middleware
中間件,內部用的是http-proxy
以上配置的跨域是開發環境下的,在生產環境就自動失效了,並且這樣配置咱們開發時訪問接口時,都要寫成/apis/xxx/xxx
格式,在部署到服務器中時,咱們要把/apis拿掉,才能訪問到正確的url。有兩種方法,一種是在開發環境中設置(經過axios的baseURL),另外一種是在服務器上修改nginx的配置設置。
在這裏詳細說下第一種方式,原理是這樣的:
經過檢測是開發環境和生產環境,設置不一樣的baseURL,使生產環境和開發環境都能正確訪問url
在src目錄下新建一個apis
目錄,而後在apis目錄下新建一個api.config.js
文件
//判斷是不是生產環境 //webpack在開發環境和生產環境分別執行不一樣的js文件,process.env.NODE_ENV設置了不一樣的值,process.env.NODE_ENV在生產環境中值爲'production'(這個值是在build/build.js中第4行設置的) var isPro = process.env.NODE_ENV=== 'production' // 若是是生產環境 咱們就使用服務器的uri,若是是開發環境,咱們就添加/apis前綴 module.exports = { baseUrl: isPro ? 'http://118.24.85.97:22222' : '/apis' }
在main.js中引入這個文件,而後設置axios的baseURL
//引入api.config.js文件,而後設置axios的baseURL import apiConfig from './apis/api.config' axios.defaults.baseURL=apiConfig.baseUrl
再來測試一下不加/apis的接口
axios.get('/api') .then(res=>console.log(res)) .catch(err=>console.log(err))
瀏覽器顯示是ok的。這樣咱們之後使用axios訪問接口就能夠不加/apis了,打包後訪問也不用手動去除/apis
在vue項目開發過程當中,會涉及到不少接口的處理,當項目足夠大時,就須要統一管理接口。具體方法應該挺多的,這裏只介紹一種:使用axios+async/await進行接口的統一管理
通常來講,後臺的接口是分模塊的,例如咱們後臺的測試接口
咱們首先在src目錄下新建一個apis文件夾,後臺提供的全部接口都在這裏定義
第二步,按照後臺提供的模塊新建js文件,咱們新建user.js
auth.js
第三步,引入axios,作相應的配置
在apis目錄下新建一個http.js,在裏面作axios相應的配置
import axios from 'axios' import apiConfig from './api.config' //建立axios的一個實例 var instance = axios.create({ baseURL:apiConfig.baseUrl, timeout: 6000 }) //------------------- 1、請求攔截器 後面介紹 instance.interceptors.request.use(function (config) { return config; }, function (error) { // 對請求錯誤作些什麼 return Promise.reject(error); }); //----------------- 2、響應攔截器 後面介紹 instance.interceptors.response.use(function (response) { return response.data; }, function (error) { // 對響應錯誤作點什麼 return Promise.reject(error); }); /** * 使用es6的export default導出了一個函數,導出的函數代替axios去幫咱們請求數據, * 函數的參數及返回值以下: * @param {String} method 請求的方法:get、post、delete、put * @param {String} url 請求的url: * @param {Object} data 請求的參數 * @returns {Promise} 返回一個promise對象,其實就至關於axios請求數據的返回值 */ export default function (method, url, data = null) { method = method.toLowerCase(); if (method == 'post') { return instance.post(url, data) } else if (method == 'get') { return instance.get(url, { params: data }) } else if (method == 'delete') { return instance.delete(url, { params: data }) }else if(method == 'put'){ return instance.put(url,data) }else{ console.error('未知的method'+method) return false } }
第四步,在apis/xxx.js
文件中引入http.js導出的函數,拿其中一個文件auth.js
說明
//auth.js 用於定義用戶的登陸、註冊、註銷等 import req from './http.js' //定義接口 //在這裏定義了一個登錄的接口,把登錄的接口暴露出去給組件使用 export const LOGIN =params=>req('post','/api/users/login',params) //這裏使用了箭頭函數,轉換一下寫法: // export const LOGIN=function(params){ // return req('post','/api/login',params) // } //定義註冊接口 export const REG =params=>req('post','/api/users/reg',params)
最後一步,在須要用的該api的組件中引入並調用,咱們在App.vue文件中測試下
<template> <div> <h2>登陸</h2> 用戶名<input type="text" v-model="user"> 密碼<input type="password" v-model="pass"> <input type="button" @click="reg" value="註冊"> <input type="button" @click="login" value="登陸"> </div> </template> <script> import {LOGIN,REG} from '../../apis/auth.js' export default { data() { return { user:'', pass:'', err:[] } }, methods: { async reg(){ try { const data = await REG({ name: this.user,pass: this.pass }) console.log(data) alert(JSON.stringify(data)) this.cleanForm() } catch (error) { console.log(error) } }, async login(){ try { const data = await LOGIN({ name: this.user,pass: this.pass }) alert(JSON.stringify(data)) this.cleanForm() } catch (error) { console.log(error) } }, cleanForm(){ this.user='' this.pass='' } }, } </script>
注:若是要打開Login.vue,須要配置對應的路由
上面的代碼引入了auth.js
定義的api,並在對應的方法中使用。代碼中用到了async/await,其實很簡單,能夠假設async是個標識,說明這個函數中有異步請求,await翻譯爲'等',後面接一個異步請求,等後面的異步請求執行完成以後,會把結果賦給=
左邊的值
參考連接 http://www.runoob.com/w3cnote...
總結一下,像上面那樣定義接口雖然麻煩點,但有兩個好處:
Vue Router官方文檔 https://router.vuejs.org/zh/
路由的配置文件在router/index.js文件中先引入文件,再進行配置
首先在views目錄中新建如下頁面
,主頁(Home/Home.vue),登陸頁(Login/Login.vue),測試頁(Test/Test.vue)
而後配置下路由
import Vue from 'vue' import Router from 'vue-router' //@表示 src目錄 webpack的配置在webpack.base.conf.js第29行 alias{'@':resolve('src')} import Home from '@/views/Home/Home.vue' import Login from '@/views/Login/Login.vue' import Test from '@/views/Test/Test.vue' Vue.use(Router) export default new Router({ routes: [//路由規則 { path: '/', name: 'Home', component: Home }, { path:'/login', name:'Login', component:Login }, { path:'/test', name:'Test', component:Test } ] })
路由規則在routes
中進行配置,routes
是一個數組,接受一系列路由規則,每一個路由規則是一個對象,包括路徑、路由名字,和路徑匹配的組件,建議給每一個路由加個名字,在後面可能會用到。
打開瀏覽器,輸入相應的url查看配置的路由是否正確,不正確的話檢查下本身的配置
參考文檔:路由懶加載官方文檔:https://router.vuejs.org/zh/g...
webpack之mainfest解讀:https://github.com/younth/blo...
當打包構建應用時,Javascript 包會變得很是大,影響頁面加載。若是咱們能把不一樣路由對應的組件分割成不一樣的代碼塊,而後當路由被訪問的時候才加載對應組件,這樣就更加高效了。因此,懶加載的含義是當路由被訪問時再去加載對應的js代碼。
首先,不作路由懶加載的狀況下,咱們打包一下(切換到項目目錄,執行npm run build
),而後會發現項目下生產了3個js文件
簡單介紹一下做用:
而後咱們實現一下路由懶加載 @/router/router.js
import Vue from 'vue' import Router from 'vue-router' // import Home from '@/views/Home/Home.vue' // import Login from '@/views/Login/Login.vue' // import Test from '@/views/Test/Test.vue' // 懶加載方式 const Home=()=>import('@/views/Home/Home.vue') const Login=()=>import('@/views/Login/Login.vue') const Test=()=>import('@/views/Test/Test.vue') Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Home', component: Home }, { path:'/login', name:'Login', component:Login }, { path:'/test', name:'Test', component:Test } ] })
懶加載只是改變了一下組件的引用方式,由原來的直接引入變成異步引入,當咱們訪問對應的路由path時,纔會加載相應的路由組件。
配置完成後再執行一次打包,結果以下:
咱們會發現目錄中多出來3個js文件,而且app.js
文件變小了。這說明配置了懶加載以後,app.js中其餘組件的內容被抽離出來,分配到各自的js文件中。配置懶加載以後,剛開始打開頁面只會加載app.js文件,只有在用戶點擊相應路由時,纔會加載對應的js代碼。當咱們的業務代碼很是多時,懶加載是個很好的選擇。
官方文檔: https://router.vuejs.org/zh/g...
配置history模式有兩個緣由,一是由於hash模式看很醜,二是由於預加載要用到History模式,配置很是簡單,只須要配置屬性mode
的值爲'history'
const router = new VueRouter({ mode: 'history', routes: [...] })
不過這種方式須要後臺的支持,當匹配不到url時,返回url/index.html頁面
nginx配置以下
location / { try_files $uri /index.html; }
參考連接:json web token入門教程 http://www.ruanyifeng.com/blo...
jwt官網 https://jwt.io/
咱們經過jwt進行用戶認證,jwt的原理是:服務器認證之後,生成一個json對象,發回給用戶.
{ "id":"001", "姓名":"小明", "角色":"管理員", "到期時間":"2019年3月3日12時30分" }
之後用戶與服務端通訊的時候,都要發回這個json對象。服務器徹底靠這個對象認定用戶身份(通常是經過這個對象的中id去數據庫請求數據)。爲了防止用戶篡改數據,服務器會在生成這個對象的時候,加上簽名。就像這種形式:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
關於JWT保存更新的業務流程以下:
主要邏輯包括:
localStorage
中保存token//1.登陸以後保存token login.vue async login(){ const data = await LOGIN({ name: this.user,pass: this.pass }) //保存token localStorage.setItem('token',data.token) //查看是否保存成功 console.log(localStorage.getItem('token')) }
//每次發送請求以前,講token放到請求頭中 api/http.js //---使用axios的請求攔截器,每次發送請求以前攔截一下 instance.interceptors.request.use(function (config) { // 給頭添加token if (localStorage.getItem('token')){//存在token,加入頭 config.headers.authorization=localStorage.getItem('token') } return config; }, function (error) { // 對請求錯誤作些什麼 return Promise.reject(error); }); //完成以後,記得發送一個請求,看看是否正確添加token //---響應攔截器,服務器響應後先到達這裏 instance.interceptors.response.use(function (response) { if(response.data.code=='2000'){//成功響應,更新token if(response.data.token){ localStorage.setItem('token',response.data.token) } }else{ //錯誤處理 根據不一樣的狀態碼,進行錯誤處理 } return response.data; }, function (error) { // 對響應錯誤作點什麼 return Promise.reject(error); });
除了對token的操做,咱們還要判斷用戶有沒有權限訪問這個頁面(有些頁面是用戶必須登陸才能訪問的),具體配置要使用Vue Router的導航守衛
參考連接: https://router.vuejs.org/zh/g...
在全局前置守衛中進行驗證
//在router/index.js進行配置 //在每次進行路由跳轉以前進行 router.beforeEach((to,from,next)=>{//增長登陸驗證 const isLogin=localStorage.getItem('token')?true:false; if(to.path=='/login'){//若是是登陸頁面,不須要token next(); }else{//若是不是登陸頁面就要判斷是否登陸 isLogin?next():next('/login'); } })
iview官網: https://www.iviewui.com/
爲節省開發時間,咱們每每會使用一些第三方ui庫,好比iview elementui等
咱們在這裏只介紹iview,其餘ui庫大同小異
cnpm i iview --save
官網說,須要下載插件才能按需引入,官網說明,可是不下好像也能夠正常引入
//在main.js文件中引入項目須要的組件 import {Button,Table,Message} from 'iview' //而後註冊組件 Vue.component('Button',Button) Vue.component('Table',Table) Vue.component('Message',Message)
這樣註冊的話太繁瑣,因此須要優化一下
//main.js import {Button,Table,Message} from 'iview' const iviewComs={Button,Table,Message} Object.keys(iviewComs).forEach(key=>{Vue.component(key,component[key])})
代碼都寫在main.js中顯得太擁擠,咱們能夠把代碼拿出去,寫成一個插件
咱們在components文件夾中新建一個文件iview-coms
,用來放iview中引入的組件
//components/iview-coms.js import {Button,Table,Message} from 'iview' const components={Button,Table,Message} const install = function(Vue, opts = {}){ Object.keys(components).forEach(key=>{ Vue.component(key,components[key]) }) } export default install
而後在main.js中引入,use
這個插件
import iviewComs from './components/iview-coms' Vue.use(iviewComs)
ok了,接下來看自定義主題
官網連接:https://www.iviewui.com/docs/...
原理很簡單,就是把ivew的less文件引入,而且覆蓋掉,而後在main.js文件中引入本身的less文件
首先,咱們須要下載解析less文件的loader ,less
和less-loader
,這裏有個坑,下載less的時候要下載3版本如下的,否則會報一堆錯誤
cnpm i less@2.7.2 less-loader -D
下載完就ok了,不須要在webpack中進行配置,由於已經配置好了
而後,在assets/styles/base.less(沒有須要本身新建)中,引入iview的樣式文件,而且覆蓋掉
默認變量列表:https://github.com/iview/ivie...
//assets/styles/base.less //------ 引入iview樣式 @import '~iview/src/styles/index.less'; //------ 覆蓋iview的樣式 @primary-color: #E91E63; @error-color : #FF3300;
最後在main.js引入該less文件
//main.js import './assets/styles/base.less'
此時,引入的組件就能夠在.vue文件中使用了,看一下效果:
ok了。最後還要補充一下,在項目開發過程當中,不可避免的要覆蓋iview默認的樣式,咱們分爲兩種狀況,一種是全局覆蓋,一種是局部覆蓋。
全局覆蓋的話咱們要新建一個less文件,好比叫cover-iview.less
全部覆蓋iview樣式的代碼都放在這裏,而後在base.less中引入這個文件。
局部覆蓋的話要注意不要影響到別的樣式,因此要充分利用less的做用域,例如咱們只須要改home頁面下的iview按鈕樣式,咱們能夠這樣:
.home{ .ivu-btn{ } }
參考文檔:vue插件說明:https://cn.vuejs.org/v2/guide...
項目中每每會使用一些通用的函數,好比獲取當前時間、時間格式轉化,防抖,節流等,咱們能夠把這個公用的部分封裝成插件,在main.js中引入。
首先,在src目錄下新建utils
文件夾,在裏面新建index.js
,utils.js
文件
咱們在utils.js
中編寫本身的工具庫,而後導出
class Utils{ constructor(){ this.d=new Date();//date對象 this.instance=null; } static getInstance(){//單例模式 if(!this.instance){ this.instance = new Utils(); } return this.instance; } pick(obj,arr){//pick({ a: 1, b: '2', 'c': 3 }, ['a', 'c']) =>{a:1,c:3} return arr.reduce((acc,curr)=>{ return (curr in obj && (acc[curr] = obj[curr]), acc) },{}) } dateFormat(datetime,pattern=""){ let vWeek = ["星期天","星期一","星期二","星期三","星期四","星期五","星期六"]; let dt=new Date(datetime); let y=dt.getFullYear(); let m=(dt.getMonth()+1).toString().padStart(2,'0'); let d=dt.getDate().toString().padStart(2,'0'); let hh=dt.getHours().toString().padStart(2,'0'); let mm=dt.getMinutes().toString().padStart(2,'0'); let ss=dt.getSeconds().toString().padStart(2,'0'); let vWeek_s = dt.getDay();//星期 if(pattern.toLowerCase() === 'yyyy-mm-dd'){ return `${y}-${m}-${d}` }else if(pattern.toLowerCase() === 'mm-dd'){ return `${m}-${d}` }else if(pattern.toLowerCase() === 'yyyymmddhhmmss'){ return `${y}${m}${d}${hh}${mm}${ss}` }else { return `${y}-${m}-${d} ${hh}:${mm}:${ss} ${vWeek[vWeek_s]}` } } } const UTIL = Utils.getInstance(); // console.log(UTIL.dateFormat(new Date(),'yyyymmddhhmmss')) //=>20190312110722 // console.log(UTIL.dateFormat(new Date()))//=>2019-03-12 11:07:22 星期二 // console.log(UTIL.pick({ a: 1, b: '2', 'c': 3 }, ['a', 'c']))//=>{a:1,c:3} export default UTIL;
而後在index.js中編寫插件,導出
//utils/index.js import UTIL from './utils.js' const UtilPlugin={} UtilPlugin.install=function(Vue,options){//插件必須有install方法,接受兩個參數,一個是Vue構造器,一個是參數 Vue.prototype.$utils=UTIL//在vue prototype上添加實例方法 } export default UtilPlugin
最後在main.js中引入並use插件
// utils import Util from './utils/index' Vue.use(Util) console.log(Vue.prototype.$util)//打印下是否引入成功
以後就能夠在組件中經過使用this.$utils
調用方法了
咱們的目標是兼容到ie9,對ie8及如下的瀏覽器作相應的跳轉處理(跳轉到瀏覽器下載界面)兼容性對一個程序來講是很是重要的,兼容性測試越早越好
在項目根目錄下中的html中head中加入下面代碼
<!--[if lte IE 8]><script>window.location.href="https://support.dmeng.net/upgrade-your-browser.html?referrer="+encodeURIComponent(window.location.href);</script><![endif]-->
目的是檢測ie瀏覽器的版本,若是低於<=ie8,就跳轉到下面這個頁面
參考連接: https://juejin.im/post/5b2868...
咱們把瀏覽器調到ie9,而後看控制檯報錯信息
報這個錯的緣由是es6的新對象,新表達式,ie9不支持,爲解決這個問題,咱們須要引入babel-polyfill
cnpm i babel-polyfill -D
安裝完成以後,在main.js文件中引入
import 'babel-polyfill'
在項目使用 vue-cli
生成的代碼中,根目錄有一個 .babelrc
文件,這是項目使用 babel 的配置文件。在默認生成的模板內容中,增長 "useBuiltIns": "entry"
的設置內容,這是一個指定哪些內容須要被 polyfill(兼容) 的設置
useBuiltIns 有三個設置選項
false
- 不作任何操做entry
- 根據瀏覽器版本的支持,將 polyfill 需求拆分引入,僅引入有瀏覽器不支持的polyfillusage
- 檢測代碼中 ES6/7/8
等的使用狀況,僅僅加載代碼中用到的 polyfill加入這些代碼後,工程中大部分代碼已能夠兼容到ie9版本,但仍是會有少部分不兼容的特性,例如requestAnimationFrame
、classList
等。對於這些內容,咱們須要本身定義polyfill來解決,在src目錄下新建一個文件夾polyfill,而後在polyfill文件夾下面建一個polyfill.js,咱們在polyfill.js中加入咱們的兼容代碼
而後在main.js中引入這個文件
import './polyfill/polyfill'
解決兼容方式的正確姿式是:拿到ie9瀏覽器下的報錯信息,去goole或者baidu搜索,獲得polyfill,而後加入到本身的polyfill.js文件中
咱們執行一下npm run build
,結果以下:
整個打包過程花了32s左右,如今咱們的項目只是引入了相關的依賴,一些業務邏輯尚未寫,打包速度就那麼慢了,等到咱們寫完整個項目,打包速度還會繼續變長,因此咱們須要優化一下。
優化打包速度,咱們修改的主要是
webpack.prod.conf.js
文件
Webpack 默認提供的 UglifyJS 插件,因爲採用單線程壓縮,速度慢 ;
webpack-parallel-uglify-plugin 插件能夠並行運行 UglifyJS 插件,更加充分而合理的使用 CPU 資源,這能夠大大減小的構建時間;
//安裝 cnpm i webpack-parallel-uglify-plugin -D
//配置 webpack.prod.conf.js //首先刪除項目中的 UglifyJsPlugin插件及配置,第二次打包時提升速度,要把.cache文件加入到gitignore中 // new webpack.optimize.UglifyJsPlugin({ // compress: { // warnings: false, // drop_console: true // }, // sourceMap: true // }), //而後引入並使用咱們剛纔裝的插件
==注意:版本控制工具提交時,要忽略.cache
文件==
配置完後咱們執行npm run build
,發現打包速度降到了23s
再執行一次npm run build
,發現打包速度降到了12s
時間下降那麼可能是由於文件沒有改動,直接利用了緩存中的js文件
通常node.js是單線程執行編譯,而happypack則是啓動node的多線程進行構建,大大提升了構建速度。
首先安裝,
修改webpack.base.conf.js
const HappyPack = require('happypack'); const os = require('os'); const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }); ... ... // 增長plugins plugins: [ new HappyPack({ id: 'happy-babel-js', loaders: ['babel-loader?cacheDirectory=true'], threadPool: happyThreadPool, }) ] ... ... // 修改對應loader { test: /\.js$/, loader: 'happypack/loader?id=happy-babel-js', include: [resolve('src'), resolve('test')], }
配置完成,執行npm run build
what??並無提升速度 不要用這個鬼東西了
https://github.com/mzgoddard/...
#安裝 cnpm install --save-dev hard-source-webpack-plugin
使用,在webpack.prod.conf.js中引入並使用
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); module.exports = { context: // ... entry: // ... output: // ... plugins: [ new HardSourceWebpackPlugin() ] }
結果:
注:要第二次打包才生效
總結下,使用了三個插件,咱們的打包速度從30s下降到4s,awesome!
首先要說明一下,首屏加載速度優化針對的是打包後dist文件。咱們若是要在本地進行測試的話,須要本地有個服務器,咱們在這裏使用nginx。
下載地址: http://nginx.org/en/download....
在官網上找到本身系統適合的nginx版本,下載到本地
#1.解壓文件 tar -xzf nginx-1.14.0.tar.gz #mac可使用解壓縮工具解壓,沒必要用命令行 #2. 配置安裝路徑 --prefix指定安裝路徑 假設我要裝到/usr/local/nginx文件夾中 ./configure --prefix=/Users/best9/local/nginx #編譯 make ##安裝 make install
安裝完成後進入到—prefix
指定的文件夾中,執行ll
,會發現文件夾下有如下目錄
咱們要關心就是我上面標出來的三個目錄
進到sbin目錄中,啓動nginx程序
cd sbin #須要使用root權限,不然會報錯 報錯信息能夠在日誌中查看到,錯誤日誌目錄 /logs/error.log sudo ./nginx
正常的話,nginx會默認在localhost:80端口啓動,在瀏覽器訪問localhost
,就會顯示默認界面
若是電腦的80端口被佔用的話,在conf/nginx.conf
文件中修改端口
nginx使用-s發送信號操做運行中的進程,經常使用命令以下:
注意:使用命令須要在sbin
目錄下
#啓動nginx ./nginx #當即中止服務 -s stop ./nginx -s stop #優雅地中止服務 -s quit ./nginx -s quit #重啓服務 -s reload ./nginx -s reload
咱們在這裏使用nginx配置一個最簡單的靜態文件服務器,更復雜的配置稍後再講
nginx的配置文件地址:conf/nginx.conf
使用vim或者其餘編輯器打開該文件,修改配置文件第43-45行:
vim conf/nginx.conf
location / { alias /Users/best9/github/vue2_template/dist; #訪問/至關於訪問alias配置的目錄 }
配置完成後保存,而後重啓服務
sudo ./sbin/nginx -s reload
要使用root權限重啓
打開瀏覽器訪問localhost
由於沒有登陸,會自動跳轉到登陸界面
到這裏靜態文件服務器就配置好了,但咱們刷新下頁面,會報錯404
這是由於咱們使用了vue router的history模式,咱們須要在nginx中加入如下配置
location / { try_files $uri $uri/ /index.html; }
而後重啓nginx,再刷新頁面就沒問題了
以上步驟就緒後,咱們就能夠來優化加載速度了
打開chrome的devTools面板,切換到Network
,禁用瀏覽器緩存,刷新測試下加載速度,發現整個應用加載大約須要1.97s,以下圖:
把網絡環境切換到Fast 3G
,再測試一次,發現加載用了7.56s,白屏時間6.89s
咱們使用預渲染插件進行優化
使用插件:prerender-spa-plugin
首先,安裝 prerender-spa-plugin
,安裝時件略長,由於其依賴了 phantomjs
cnpm install prerender-spa-plugin --save-dev
咱們只在生產環境中進行預渲染,修改 build/webpack.prod.conf.js
,在配置插件的地方加入以下代碼。
//引入 預渲染插件 const PrerenderSpaP=require('prerender-spa-plugin') //在plugins中配置 new PrerenderSpaP( // 輸出目錄的絕對路徑 path.join(__dirname,'../dist'), //預渲染路由 ['/home','/login'] )
再次執行打包,而後再進行測試:
發現白屏時間爲4.10s,在弱網環境下,使用預渲染,大約能縮減2.5秒的白屏時間
舉個例子:
插件配置以下:
new PrerenderPlugin({ staticDir:path.join(__dirname,'../dist') routes:['/','/about','/login'] })
路由配置以下:
gzip官方文檔 http://nginx.org/en/docs/http...
nginx默認是關閉gzip的,咱們須要本身打開,並進行一些配置:
gzip:on; #打開gzip,關閉爲off gzip_min_length 1; #小於gzip_min_length,不進行壓縮(默認單位爲byte) gzip_comp_level 2; #壓縮級別 gzip_types text/plain text/css application/javascript text/javascript image/jpeg image/gif image/png;#指定類型進行gzip壓縮
配置完成後,咱們再測試一下加載速度:
發現白屏時間爲1.95s,加載文件的體積也變小了
咱們要在本地部署測試,因此後臺的地址是127.0.0.1:22222
項目開發完成後須要部署到服務器,由於是先後端分離,因此前端的應用部署到nginx,後端的應用部署到本身對應的服務器,因此咱們須要配置一下,把後端的服務器變成上游服務,nginx作反向代理服務器
反向代理:服務器根據客戶端的請求,從其關係的一組或多組後端服務器上獲取資源,而後將這些資源返回給客戶端。
因爲上游服務器(後臺服務器)要處理很是複雜的邏輯,因此性能不怎麼樣,咱們使用nginx做爲反向代理服務器後,能夠將請求按照負載均衡算法代理給多臺上游服務器。配置以下:
以上配置是將全部的請求轉發給上游服務器,但若是咱們只想將動態請求轉發給上游服務器,靜態資源由nginx本身處理,就能夠這樣作:
判斷是不是後臺api(根據location的匹配規則),若是是的話,就進行轉發
匹配規則看這裏:https://stackoverflow.com/que...
upstream local{ server 127.0.0.1:22222; #假設在本地部署 } server{ listen:80; server_name localhost; location ~ /api/ { #以`/api/`開頭的uri就行轉發,不然不轉發 ~表明正則表達式匹配 proxy_set_header: Host $host; proxy_set_header: X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://local; } location / { #... alias index等配置 } }
這裏須要注意一個問題:proxy_pass是轉發請求的模塊,當你訪問localhost:80/api/users/login
時,會被轉發到local
的地址,即127.0.0.1:22222/api/users/login
,因此開發環境下訪問後臺接口的URI要寫你部署到nginx的URI,而不是真正的後臺地址(由於被轉發了)
前端配置
//apis/api.config.js //判斷是不是生產環境 var isPro = process.env.NODE_ENV=== 'production' module.exports = { baseUrl: isPro ? 'http://localhost:80' : '/apis'//生產環境下的baseURl是nginx的hoost:port }
項目作完須要發佈到服務器,但每次手動打包,而後ftp傳上去的話就太麻煩了,因此咱們的需求是:git或者svn提交後,自動打包發佈到服務器。使用的工具是jenkins.
參考文檔: https://juejin.im/post/5ad198...
jenkins通常狀況下會裝在服務器,但若是是同一個局域網的話,裝在本機也能夠
linux:
windows下:
java -jar jenkins.war
便可。mac:
在主頁上點擊建立
直接點保存
,而後去安裝插件
首先返回主頁,而後點擊左側菜單 系統管理
->插件管理
須要安裝的插件有:
安裝插件的方式:
安裝完插件以後重啓一下jenkins(安裝完插件後,有個重啓的選項,勾選便可)
當咱們向github/碼雲等遠程倉庫push咱們的代碼時,jenkins能知道咱們提交了代碼,這是自動構建自動部署的前提,鉤子的實現原理是在遠端倉庫上配置一個Jenkins服務器的接口地址,當本地向遠端倉庫發起push時,遠端倉庫會向配置的Jenkins服務器的接口地址發起一個帶參數的請求,jenkins收到後開始工做
打開建立的項目(進入工程->點擊配置
)
構建觸發器
勾選 Generic Webhook Trigger
github倉庫配置鉤子:
進入github項目中該項目頁面,點擊setting
->webhooks
,添加payload URL,
URL格式爲 http://<User ID>:<API Token>@<Jenkins IP地址>:端口/generic-webhook-trigger/invoke
userid和api token在jenkins的系統管理
-管理用戶
-選擇你的用戶點進去
-左側設置
裏
自動化構建:jenkins實現安裝依賴,打包(npm install && npm run build),此外還能夠執行一些測試行爲
點擊構建環境
,勾選nvm
,輸入node版本
點擊構建
,選擇執行shell
,輸入執行命令,多個命令使用&&分開
npm config set registry http://registry.npm.taobao.org/ && npm install && npm run build