使用 Vue-cli3 初始化項目1javascript
安裝 Element UI
安裝 Vue-i18n,作相關配置2,3html
原則上須要對 Element 也作 I18n 的處理,可是我以爲 Element 中已經有很完善的多語言翻譯,而 Element 自身尚不支持 Vue-i18n 7+ 版本,要特殊處理,因此就算了,這裏直接使用了 Element 自帶的方法。
. |-- babel.config.js |-- package.json |-- public | `-- index.html |-- src | |-- App.vue | |-- assets | | |-- override-element-ui.less // 覆蓋 Element 默認樣式 | | `-- image // 圖片文件目錄 | |-- components // 全部非 `<router-view />` 中顯示的頁面的 Vue 組件 | | |-- mainMenu.vue // 左側導航 | | |-- pureTitle.vue // 通用的 Title 組件,項目中有多個頁面會用到 | | |-- search.vue // 通用的 Search 頭部組件,一樣會用到 | | `-- userCenter.vue // 字面意思用戶中心,目前只須要顯示用戶名,handle 用戶退出操做。可預見:下拉菜單操做、頭像顯示等... | |-- main.js // Vue-cli 生成的項目基本文件 | |-- router.js // Vue-cli 生成,路由 | |-- store // Vuex相關 | | |-- index.js | | |-- mutationTypes.js | | `-- module // 這裏按照路由劃分了 module,不必定對 | | | |-- someModule.js | | | `-- elseModule.js | |-- utils | | |-- i18n // 國際化支持 | | | |-- index.js | | | `-- lang // 語言字符串文件 | | | |-- en.json | | | `-- zh.json | | |-- request // 處理全部 ajax 請求 | | | |-- index.js // 基於 axios,封裝了須要用到的方法,例如 token 的處理 | | | |-- someComponentName.js // 基於組件分割的請求,在 Vuex action 中調用獨立方法 | | | `-- elseComponentName.js | | |-- timeHelper.js // 時間戳轉換幫助函數 | | `-- userHelper.js // 保存用戶信息幫助函數 | `-- views // `<router-view /> 下內容顯示組件` | |-- Home.vue | |-- Login.vue | |-- MainPage.vue | |-- Manage.vue | `-- Verify.vue |-- .env.development // 環境變量 process.env |-- .env.production // 生產環境下的環境變量 `-- vue.config.js // 更改 Vue-cli 默認配置
先看一張項目初始狀態下,全局引入 Element & 按需引入打包大小的對比:vue
參考:按需引入java
須要注意的地方是:ios
.babelrc
文件,只須要更改 babel.config.js
presets
中爲 ['@vue/app']
,不須要更改成 element 官網中的 es2015
。緣由是 babel 的目前版本已經不推薦按照 JavaScript 版原本區分編譯目標。(// todo:添加相關連接)可用的 babel.config.js
文件以下:git
module.exports = { presets: [ ['@vue/app'], ], plugins: [ [ 'component', { 'libraryName': 'element-ui', "styleLibraryName": "theme-chalk" } ] ] }
正常狀況下,能夠經過直接修改對應 class 的內容修改。但含有 foo__bar 相似格式的樣式不會應用 scoped(由於不是同一個 Vue 實例),所以修改不會生效。github
能夠經過在 *.vue 中不使用 scope
來解決這個問題,Element 在 Issue 中也吐槽過。雖然我不認爲 scope 不是一個好的解決方案。ajax
所以,我選擇了在 main.js
中引入外部樣式表,例如override-element-ui.less
,在外部樣式表中修改 foo__bar 類名的樣式。element-ui
route.js
主要設計以下:json
routes: [ { path: '/login', name: 'login', component: Login }, { path: '/', name: 'home', component: Home, children: [ { path: '/index', component: mainPage }, { path: '/manage', component: manage, children: [ { path: 'user/:uumsid&:userid', component: userDetail }, { path: 'enterprise/:id', component: insDetail }, { path: '/', component: manageList }, ] }, { path: '/verify', component: Verify, }, { path: '/verify/:id', component: VerifyDetail }, { path: '*', redirect: '/index' }, ] }, ]
555...不太想貼代碼,感受太亂、太長。
但不貼又說不清楚。
簡單來講,項目只分爲兩個部分。登錄頁和功能頁。
根路由綁定到了登錄後的首頁。
首頁包含頭部標題、左側菜單還有顯示內容的 <router-view />
。
所以,若是用戶僅輸入 location.host
訪問網站,會被帶到 ${host}/index
頁面。另外,訪問任意未被匹配的 path,都會重定向到 index
頁面。
這個時候會出現兩個分支:
判斷本地是否存在用戶信息是否存在 ? 進行正常操做 :重定向到登陸頁
整個系統只須要分紅須要登陸和不需登錄兩個部分。
當前所開發的系統除登陸頁外,都須要登陸後訪問。
這個部分和路由設計強相關。什麼時候判斷用戶是否登陸在上一部分已經解釋過。
使用 localstorage
來持久化保存用戶信息,處理用戶刷新頁面以及一段時間內關閉瀏覽器後不用從新登陸的需求。(一段時間爲30min)
在 Home.vue
的 created() 鉤子中作以下操做:
created() { let userInfo = getUserFromLocal('userinfo') let verifiedUserInfo = (info) => { let now = new Date().getTime() const PASS_TIME = 1000 * 60 * 30 // 30min if (!info) return false if (now - info.time < PASS_TIME) return true return false } if (!!verifiedUserInfo(userInfo)) { this.onRefresh(userInfo) } else { this.$router.push({path: '/login'}) } }
其中,getUserFromLocal
是在 userHelper.js
中寫的從 localstorage 中獲取數據的方法。經過判斷用戶信息是否存在以及是否過時來決定是跳轉到登陸頁仍是直接使用當前已有的用戶信息。
// 我不以爲這是一個很完美的方案,但根據我搜索的資料來看,確實沒有詳細說過這方面內容的文章。因此,期待能看到更好的方案。
Element
的表單驗證方法
但沒有給出 async-validator
支持的表單驗證觸發方式。
通過查找,支持的觸發方式有: submit, blur, input
我選擇的方案是在規則中使用 submit
時驗證(指表單提交時觸發,所以實際狀況中永遠不會觸發,但能知足需求,即手動觸發驗證。)
同時,在點擊登陸或者發送請求按鈕時,調用 this.$refs.[formEl].validate((boolean) => { // callback})
清除表單驗證結果: <form-item @focus='clearValidate'>...
當表單項不少時,也能夠在<form >...
中調用clearValidate(prop)
, prop 指表單項的 name 值。
產品有一個需求是,在搜索用戶信息時,只能經過郵箱搜索,而且只能輸入字母、數字以及@。所以,咱們須要對用戶輸入數據時即時進行過濾。(別問我爲何不在用戶輸入完成後提示錯誤,這是需求。
我選擇了 watch 輸入框 value 的值:
value(val) { this.$nextTick(() => { this.value = this.reg ? val.replace(this.reg, '') : val }) }
這裏的坑就是須要在 $nextTick() 中更新 value 值,由於 DOM 元素這時才刷新。
這個需求有許多 blog 都給出過不一樣的解決方案,能夠多看看選擇一下。
基於 axios
./utils/request/index.js
:
import axios from 'axios' // BASE_HOST 經過 .env.[development|production] 配置。注意:每一個變量都要用 VUE_APP做爲前綴,不然不能識別 const BASE_HOST = process.env.VUE_APP_API_BASE const TIME_OUT = 1000 * 10 /** * * @param URL {string} * @param params {Object} */ export function POST(URL, params) { if (!URL.includes(BASE_HOST)) URL = BASE_HOST + URL return axios({ method: 'post', url: URL, data: params, timeout: TIME_OUT }) } export function GET(URL, data) { // like POST } /** * @param URL {string} * @param token {string} * @param params {Object} */ export function GET_WITH_TOKEN(URL, token, params) { if (!URL.includes(BASE_HOST)) URL = BASE_HOST + URL return GET(URL, { params, headers: { token: token }, timeout: TIME_OUT }) }
封裝了基本的 GET,POST等方法,沒想到什麼特別的做用,也沒有想到很差的地方,留着爲了以防萬一。(事實上,也用上了。好比超時處理。可是,超時處理在下一層也能作。但這樣的話登錄就須要單獨設置超時了。)
GET_WITH_TOKEN(以及 POST_WITH_TOKEN 等) 用於須要登陸鑑權的接口
在等待api返回數據時能夠用loading告訴用戶頁面正在加載。Element 提供的 Loading組件
通常切換路由後,會在組件的 created()
方法中發送請求。這種狀況下應該在 nextTick()(或者mounted()中) 中調用 loading ,避免頁面切換時找不到 DOM,出現全屏 loading 或頁面閃爍。