1 form表单验证 复杂逻辑规定时
validator
2 跨域代理问题解决
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DXhV75oB-1611239111870)(06-人资项目总结.assets/image-20201230220408582.png)]![image-20201209144115372](06-人资项目总结.assets/image-20201209144115372.png)
总结 : 当数据不在接口地址 , 而在java服务器时 , 这时就产生了跨域 , 我们应该用api验证跨域 , 再给出java服务器地址 (也就是代理), 获取数据
vue-cli的配置文件即**vue.config.js
**,这里有我们需要的 代理选项
module.exports = {
devServer: {
// 代理配置
proxy: {
// 这里的api 表示如果我们的请求地址有/api的时候,就出触发代理机制
// localhost:8888/api/abc => 代理给另一个服务器
// 本地的前端 =》 本地的后端 =》 代理我们向另一个服务器发请求 (行得通)
// 本地的前端 =》 另外一个服务器发请求 (跨域 行不通)
'/api': {
target: 'www.baidu.com', // 我们要代理的地址
changeOrigin: true, // 是否跨域 需要设置此值为true 才可以让本地服务代理我们发出请求
// 路径重写
pathRewrite: {
// 重新路由 localhost:8888/api/login => www.baidu.com/api/login
'^/api': '' // 假设我们想把 localhost:8888/api/login 变成www.baidu.com/login 就需要这么做
}
},
}
}
}
3 improt 引入方法改名
不过这常量还是之前的
3.1*as abc 导出所有文件 方法
4 action 里数据需return 顺序不同
如果不return 右边vue文件就先执行
return 后异步处理完再叫给vue 文件做后续的处理
5 路由守卫 详细判定
// 创建路由守卫
router.beforeEach((to, from, next) => {
// 判定token
const whiteList = ['/login', '/404'] // 定义白名单 所有不受权限控制的页面
if (store.getters.token) {
debugger
// 有且是登录页
if (to.path === '/login') {
next('/') 这里是强制判定, 实时判断 如果有token还是登录页, 默认进入首页!!!!!!!!
} else { 如果是next( ) 就不会强制判断, 放行下一步操作而已 !!!!!
next()
}
} else {
if (whiteList.indexOf(to.path) > -1) {
// 如果找到了 表示在在名单里面
next()
} else {
// 如果不写白名单,本身就是login无token状态, 再进入login 就产生了死循环
next('/login') // 跳到登录页
}
}
})
router.afterEach((to, path, next) => {
next()
})
是否有token, 有token, 是否为登录页 , 登录就跳首页, 否则放行
没有token, 如果白名单 , 放行 , 不是就登录页
6 进度条插件 nprogress
import router from '@/router'
import store from '@/store'
// 引入一份进度条插件
import NProgress from 'nprogress'
// 引入进度条样式
import 'nprogress/nprogress.css'
// 创建路由守卫
router.beforeEach((to, from, next) => {
// 白名单
NProgress.start() /* 开始进度条 */
const wilData = ['/login', '/404']
// 如果有token
if (store.getters.token) {
// 登录页
if (to.path === 'login') {
router.push('/')
} else {
next()
}
} else {
// 没有token
if (wilData.indexOf(to.path) > -1) {
// 如果在白名单就放行
next()
} else {
// 如果不写白名单,本身就是login无token状态, 再进入login 就产生了死循环
router.push('/login')
}
}
NProgress.done() /* 为了刷新页面或手动跳转 进度条无法停止的情况 */
})
router.afterEach((to, from, next) => {
NProgress.done()
})
设置结束
使用方法 : 引入
// 引入一份进度条插件
import NProgress from 'nprogress'
// 引入进度条样式
import 'nprogress/nprogress.css'
设置开始 NProgress.start() /* 开始进度条 */
设置结束 NProgress.done() 设置两次!
7 请求拦截器 Bearer 加token 加空格!!!
// 请求拦截器
service.interceptors.request.use((data) => {
// 如果本地有token 就发送
if (store.getters.token) { 两种写法但是都要
data.headers.Authorization = 'Bearer ' + store.getters.token 加空格!!!!!!!!!!!
data.headers.Authorization = `Bearer ${store.getters.token}` 加空格!!!!!!!!!!!
}
return data
})
8 自定义指令 解决图片路径惨缺问题
有时,img标签中的src图片加载失败,原来的图片位置会出现一个碎片图标,这样让人很不爽,如何变得美观些呢?
在main全局定义:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GHno4bA-1611239111886)(06-人资项目总结.assets/image-20201211110018515.png)]
默认本地照片:
网络就不能变量形式了 加 ‘’ 字符串类型
export const imageerror = {
// 这个钩子会在用到这个指令的 dom 插入页面时触发
inserted(dom, options) {
// 额外添加逻辑处理 src 为空的状况
dom.src = dom.src || options.value
// 这里可以拿到使用了这个指令的 dom 其实就是图片 img
// 加入img标签出错, 就自动改一个默认地址
dom.onerror = function() {
// 如果出错, 就修改默认地址, 但是如果不想每个地方的默认地址都一样
// 可以在调用指令的时候传值
dom.src = options.value
}
},
// 更新时 , 这个钩子函数会被触发 比如说翻页时
componentUpdated(dom, options) {
dom.src = dom.src || options.value
}
}
定义方法解析
export const imageerror = {
inserted(dom, options) {
dom是原型 此元素
// options是 指令中的变量的解释 其中有一个属性叫做 value 每当自定义v-xxx='接收值' 就等于value
dom.onerror = function() {
dom.src = options.value
}
}
}
如果多个自定义指令, 不想单个引入,和每个注册
就需要遍历
import * as directives from '@/directives'
// 注册自定义指令 引入所有方法 改名叫directives
// 遍历所有的导出的指令对象 完成自定义全局注册
Object.keys(directives).forEach(key => { Object.keys(directives) 得到 0 方法名
每个key 就是每个方法名 1 方法名
// 注册自定义指令
Vue.directive(key, directives[key])
名字 名字对应方法(配置对象)
})
9 查看当前路由地址 this.$route.fullPath
10 token时间失效时 主动 操作
首先我们定义两个方法, 存当前时间到 cookies 取cookies里的时间
登录成功时, 存时间
时间失效定义一个有名函数方法 拿当前时间-存入时间=值 如果属于证明超过了 进行相应处理
如果有token,并且时间失效 就调用清除数据方法, 并到登录页面
被动验证token
error => {
// error 信息 里面 response的对象
if (error.response && error.response.data && error.response.data.code === 10002) {
// 当等于10002的时候 表示 后端告诉我token超时了
store.dispatch('user/logout') // 登出action 删除token
router.push('/login')
} else {
Message.error(error.message) // 提示错误信息
}
return Promise.reject(error)
}
error.response && error.response.data && error.response.data.code
如果取空对象里的值会报错, 所以先判断它有没有error.response 也会减少后台判断
属不属于修改token报错
无论是主动介入还是被动处理,这些操作都是为了更好地处理token,减少错误异常的可能性
11 propos默认给值
export default {
props: {
polist: {
type: Object, 类型
required: true 必选
}
}
}
12 finally 成功与否都会进入 写法不同
13 some 验证每个item 是否为true 进行后续操作
14 监听里 只有有就会执行 immediate
这里遇到的问题:
1 页面刷新, 数据还没回来, created拿不到数据id , 发送不了请求
wilch 监听变化, 从未到有 就可以了
问题二: 跳转页面有id 监听事件触发不了 就用到 immediate
immediate 只要=true, 就会触发handler 里的逻辑 ’
15 . sync 语法糖
子级想开启关闭, 改变某个属性 想要传个事件通知父组件解决 虽可以实现会报错
所以用更严谨的方法写:
父级 :子属性.sync
子级 update:自己属性值, false
16 Dialog 对话框 解决异步自定义校验, 第二次打开校验问题
因为失去焦点的时候, 同时处理两件事, 关闭窗口 校验规则
等第二次点新增时
17 全局组件注册 和全局过滤器
全局过滤器
import * as filters from '@/filters' // 引入工具类
// 注册全局的过滤器
Object.keys(filters).forEach(key => {
// 注册过滤器
Vue.filter(key, filters[key])
})
//另外一种写法
// 过滤器
// 全局过滤器的创建方法是 Vue.filter(名称, 函数体)
import * as filters from '@/filters'
// console.log(filters)
// 拿到的是一个对象, 里面以 key: value 的形式储存了 过滤器名:过滤器函数
// 遍历这个对象
for (var key in filters) {
const filterName = key
const filterFunction = filters[key]
Vue.filter(filterName, filterFunction)
}
18 枚举数据的使用
formatter 是table的方法
引入枚举数据文件
import EmployeeEnum from '@/api/constant/employees'
<!-- 格式化聘用形式 -->
<el-table-column label="聘用形式" sortable :formatter="formatEmployment" />
// 格式化聘用形式
formatEmployment(row, column, cellValue, index) {
// 要去找 1所对应的值
const obj = EmployeeEnum.hireType.find(item => item.id === cellValue)
return obj ? obj.value : '未知'
}
filter 和 some fint 的区别
**filter 是对数组中的每个对象调用回调函数callbackfn方法,复合要求的才会保存 **操作当前元素集,删除不匹配的元素,得到一个新的集合
**find 一般用于验证枚举 字典问题 如果item 符合某一条件 我就取它对象里的另一个属性值 **:在当前选中元素的上下文中寻找符合条件的后代 返回的是子元素 ( 改造子元素 或者用相对应子元素 )
some 是遍历数组 找到符合条件 就会返回true 或者false 一般用在行内式属性值判断
map 是重构数组数据结构
includes 字符串的查找 布尔值类型
findIndex
some 和ever 的区别
相同:every和some都有三个参数,即item-当前项,index-当前项的索引值,array-数组本身;
不同:every相当于逻辑关系中的且,只有所有参数都满足条件时,才返回true,一旦有一个不满足,则逻辑中断,返回false
some相当于逻辑关系中的或,只要有一个参数满足条件,就中断遍历,返回true,若遍历完所有参数,没有符合的项,返回false
every,some遍历数组,every只要有一项不满足条件都返回false some有一项满足都会返回true
find 的使用
employeeenum是引入js文字
19 滚动条隐藏问题
外部套div结构 内部宽度挤出
20 Object.keys 将对象表头数据数组形式 集合
21 腾讯云图片的使用
当前文件
// 引入腾讯云
import COS from 'cos-js-sdk-v5'
// 生成实例
const cos = new COS({
// 密匙
SecretId: 'AKIDa3GQ7B0A4tRnWSTamkY7d0KVUDdnAiJo',
SecretKey: 'QaoldFerYy8G8IBqEQvAAvlLgHnCKBmS'
})
找到模板 https://cloud.tencent.com/document/product/436/35649#.E7.AE.80.E5.8D.95.E4.B8.8A.E4.BC.A0.E5.AF.B9.E8.B1.A1
cos.putObject({
Bucket: 'examplebucket-1250000000', /* 必须 */
Region: 'COS_REGION', /* 存储桶所在地域,必须字段 */
Key: 'exampleobject', /* 必须 */
StorageClass: 'STANDARD',
Body: fileObject, // 上传文件对象
onProgress: function(progressData) {
console.log(JSON.stringify(progressData));
}
}, function(err, data) {
console.log(err || data);
});
照片墙的属性 :http-request=“upload”
22 find 的使用
employeeenum是引入js文字
23 打印二维码
$ npm i qrcode 下载
import QRcode from 'qrcode' 引入
QrCode.toCanvas(dom, info)
dom为一个canvas的dom对象, info为转化二维码的信息
<el-dialog title="二维码" :visible.sync="showCodeDialog" @opened="showQRcode">
<el-row type="flex" justify="center">
<canvas ref="myCanvas" />
</el-row>
</el-dialog>
再弹窗结束完之后再
// 转换并显示二维码
QRcode.toCanvas(this.$refs.myCanvas, this.imageUrl)
如果是组件 Dialog 对话框 用 opened属性
否则js中 nextTick 方法
24 打印网页信息
媒体查询方式 :
1
print() {
点击按钮时打印事件
window.print()
},
2
css样式:
@media print{
其他结构隐藏
.sidebar-container, .has-logo{
display: none;
}
.print{
按钮隐藏
display: none;
}
}
’
插件打印:
$ npm i vue-print-nb 下载
main 里全局注册
import Print from 'vue-print-nb'
Vue.use(Print);
25 reduce的使用
1、格式:
array.reduce(function(total, currentValue, index, arr){
// total: 累积变量,默认为数组的第一个成员
// currentValue: 当前变量,默认为数组的第二个成员
// index: 当前位置(从0开始)
// arr: 原数组
}, 默认值初始值)
数组 [1, 2, 3, 4, 5];
total就是1 3 6 10 不会取最后一个
currentValue 2 3 4 5
index 索引值 从1开始
26 动态路由的使用
next( to . path ) 是为了防止动态路由 刷新后失去权限 页面空白情况 这是vue已知的缺陷 含义 : 再当前路由再跳转一次
路由 : 是 拿到自己id 所拥有的权限 与全部路由的name 进行对比 如果有的话就router.addRoutes( ) 可以访问
404的校验也要放在动态路由后
菜单 : 是静态与动态合成
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
每次退出还要 引入方法 清理上次的动态路由权限
27: 跨模块使用方法
模块内调用 兄弟模块 的方法
28 混入 声明函数互相调用
import 引入
mixins 调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WhozGpNX-1611239111909)(06-人资项目总结.assets/image-20201224164355701.png)]
钩子可以都显示
但函数方法 自己内部有优先权 名字相同 只调用内部方法
全局混入
29 自己定义 日期选择器 日历变更
<template>
<div>
<el-select v-model="currentYear" placeholder="请选择" @change="get">
<el-option
v-for="item in options"
:key="item"
:label="item"
:value="item"
/>
</el-select>
<el-select
v-model="currentMonth"
placeholder="请选择"
@change="get"
>
<el-option
v-for="item in 12"
:key="item"
:label="item"
:value="item"
/>
</el-select>
<el-calendar v-model="currentDate">
<template v-slot:dateCell="{ date, data }" class="content">
<!-- date数据格式, data要渲染的值 -->
<span class="text"> {{ data.day | getDay }}</span>
<span v-if="isRest(date)">休</span>
</template>
</el-calendar>
</div>
</template>
<script>
export default {
filters: {
getDay(val) {
return val.split('-')[2]
}
},
data() {
return {
startDate: new Date(),
currentMonth: null, // 当前月份
currentYear: null, // 当前年份
currentDate: null,
options: []
}
},
created() {
// 同步: 年月都要有值 一个值为null 格式错误
this.currentMonth = this.startDate.getMonth() + 1
this.currentYear = this.startDate.getFullYear()
this.options = Array.from(Array(11), (val, index) => {
const now = new Date()
return index + now.getFullYear() - 5
})
this.get()
},
methods: {
isRest(date) {
// 当前日期在本周的排序号
return date.getDay() === 6 || date.getDay() === 0
},
get() {
const year = this.currentYear
const month = this.currentMonth
this.currentDate = new Date(`${year}-${month}-1`)
}
}
}
</script>
30 i18语言包
语法 $t('hello')
31 全屏按钮功能
目标:实现页面的全屏功能
全屏功能可以借助一个插件来实现
第一步,安装全局插件screenfull
$ npm i screenfull
第二步,封装全屏显示的插件·· src/components/ScreenFull/index.vue
<template>
<!-- 放置一个图标 -->
<div>
<!-- 放置一个svg的图标 -->
<svg-icon icon-class="fullscreen" style="color:#fff; width: 20px; height: 20px" @click="changeScreen" />
</div>
</template>
<script>
import ScreenFull from 'screenfull'
export default {
methods: {
// 改变全屏
changeScreen() {
if (!ScreenFull.isEnabled) {
// 此时全屏不可用
this.$message.warning('此时全屏组件不可用')
return
}
// document.documentElement.requestFullscreen() 原生js调用
// 如果可用 就可以全屏
ScreenFull.toggle()
}
}
}
</script>
<style>
</style>
第三步,全局注册该组件 src/components/index.js
import ScreenFull from './ScreenFull'
Vue.component('ScreenFull', ScreenFull) // 注册全屏组件
第四步,放置于**layout/navbar.vue
**中
<screen-full class="right-menu-item" />
31 打包性能分析
npm run preview -- --report 打印报告
vue.config.js
先排除大包
方法1 手动去cnd 找到链接 加上去
方法2 动态遍历引入
CDN文件配置
但是,没有被打包的几个模块怎么处理?
可以采用CDN的方式,在页面模板中预先引入
vue.config.js
const cdn = {
css: [
// element-ui css
'https://unpkg.com/element-ui/lib/theme-chalk/index.css' // 样式表
],
js: [
// vue must at first!
'https://unpkg.com/vue/dist/vue.js', // vuejs
// element-ui js
'https://unpkg.com/element-ui/lib/index.js', // elementUI
'https://cdn.jsdelivr.net/npm/xlsx@0.16.6/dist/jszip.min.js',
'https://cdn.jsdelivr.net/npm/xlsx@0.16.6/dist/xlsx.full.min.js'
]
}
注入CDN文件到模板
之后通过 html-webpack-plugin
注入到 index.html
之中:
在 chainWebpack(config) {}里放入
config.plugin('html').tap(args => {
args[0].cdn = cdn
return args
})
找到 public/index.html
。通过你配置的CDN Config
依次注入 css 和 js。
<head>
<!-- 引入样式 -->
<% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%=css%>">
<% } %>
</head>
<!-- 引入JS -->
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%=js%>"></script>
<% } %>
最后,进行打包
$ npm run build:prod
32 调用子组件方法
33 单独给vue子组件绑定事件
必须加上native
1,给vue组件绑定事件时候,必须加上native ,否则会认为监听的是来自Item组件自定义的事件
2,等同于在子组件中: 子组件内部处理click事件然后向外发送click事件:$emit("click".fn)
<Item @click.native = "shijian()"></Item>
index.html`之中:
在 chainWebpack(config) {}里放入
config.plugin('html').tap(args => {
args[0].cdn = cdn
return args
})
找到 public/index.html
。通过你配置的CDN Config
依次注入 css 和 js。
<head>
<!-- 引入样式 -->
<% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%=css%>">
<% } %>
</head>
<!-- 引入JS -->
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%=js%>"></script>
<% } %>
最后,进行打包
$ npm run build:prod
32 调用子组件方法
[外链图片转存中…(img-K7TRbsXE-1611239111915)]
33 单独给vue子组件绑定事件
必须加上native
1,给vue组件绑定事件时候,必须加上native ,否则会认为监听的是来自Item组件自定义的事件
2,等同于在子组件中: 子组件内部处理click事件然后向外发送click事件:$emit("click".fn)
<Item @click.native = "shijian()"></Item>