使用 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 或页面闪烁。