项目简介
vue + axios + vue-router + vuex + ElementUI
架构
vue
vue 数据更新,视图不更新
只有当实例被创建时 data 中存在的属性才是响应式的,Vue 不能检测对象属性的添加或删除;可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性
Vue.set(vm.userProfile, ‘age’, 27)
this.$set(this.transportAddData.serviceOrderList[a].serviceOrderItems[i], ‘deletePoundIds’, [])
vue 数据与方法 vue 对象更改检测注意事项
Vue 不能检测以下变动的数组:
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
vue 数组更新检测
持续触发事件的优化
持续触发某个事件可以用函数的节流和防抖来做性能优化
// 防抖
function(){
…
clearTimeout(timeoutId);
timeoutId = setTimeout(function () {
console.log(‘content’, content);
player(content.msgTypeId, comId)
}, 500);
…
}
// 节流
var canRun = true;
document.getElementById(“throttle”).onscroll = function(){
if(!canRun){
// 判断是否已空闲,如果在执行中,则直接 return
return;
}
canRun = false;
setTimeout(function(){
console.log(“ 函数节流 ”);
canRun = true;
}, 300);
};
javaScript 的 Throttling(节流) 和 Debouncing(防抖)
nextTick
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
get() {
this.$http.get(‘/api/article’).then(function (res) {
this.list = res.data.data.list
// ref list 引用了 ul 元素,我想把第一个 li 颜色变为红色
document.querySelectorAll(‘li’)[0].style.color = ‘red’ // 这里会报错 -,因为还没有 li
this.$nextTick(()=> {
document.querySelectorAll(‘li’)[0].style.color = ‘red’
})
})
},
Vue.nextTick 的原理和用途
音频文件自动播放报错
谷歌浏览器(Chrome 66)音频自动播放报错
DOMException: play() failed because the user didn’t interact with the document first.
解决方案:AudioContext
// Chrome
request.get(‘/baseConfig/messageAudio/play’, {
params: {
“comId”: Cookies.get(‘comId’),
“msgTypeId”: id
},
responseType: ‘arraybuffer’ // 这里需要设置 xhr response 的格式为 arraybuffer
})
.then(req => {
…
let context = new (window.AudioContext || window.webkitAudioContext)();
context.decodeAudioData(req.data, decode => play(context, decode));
function play (context, decodeBuffer) {
sourceadio = context.createBufferSource();
sourceadio.buffer = decodeBuffer;
sourceadio.connect(context.destination);
// 从 0s 开始播放
sourceadio.start(0);
}
…
})
Chrome 66 禁止声音自动播放之后 [AudioContext](https://developer.mozilla.org… [AudioContext.decodeAudioData()](https://developer.mozilla.org…
vuex
使用 vuex 修改 state 的方法和区别
可以直接使用 this.$store.state. 变量 = xxx;
this.$store.dispatch(actionType, payload) 或者 this.$store.commit(commitType, payload)
相同点:能够修改 state 里的变量,并且是响应式的(能触发视图更新)不同点:若将 vue 创建 store 的时候传入 strict: true, 开启严格模式,那么任何修改 state 的操作,只要不经过 mutation 的函数,vue 就会报如下错
throw error : [vuex] Do not mutate vuex store state outside mutation handlers。
使用 commit 提交到 mutation 修改 state 的优点:vuex 能够记录每一次 state 的变化记录,保存状态快照,实现时间漫游/回滚之类的操作。
dispatch 和 commit 的区别在于: 使用 dispatch 是异步操作,commit 是同步操作,所以 一般情况下,推荐直接使用 commit,即 this.$store.commit(commitType, payload),以防异步操作会带来的延迟问题。
vuex strict vuex Mutation vuex actions
vuex 到底是什么
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit(‘increment’)
}
}
})
==vuex 中的 state 本质上是没有 template 的隐藏的 vue 组件。==
vuex 工作原理详解
axios
兼容问题
Edge 41.16299.15.0 post 请求会自动转为 get
microsoft-edge/platform/issues vue 使用 axios 在 EDGE 浏览器上面 post 请求变成了 get 请求
取消请求
场景:每次请求在拦截器中添加 token,后台判断 token 是否过期;当进入一个页面时触发多次请求,且正好 token 已经过期。这个时候需要第一次请求完成之后知道 token 已经过期则弹出提示、页面跳转到登录、停止之后的请求;否则会因为多次请求和 axios 响应拦截而多次弹框提示。
解决方案 axios 自带 cancelToken 取消方法,然后在路由跳转后停止之前的请求
// 请求拦截中 添加 cancelToken
axios.interceptors.request.use(config => {
config.cancelToken = store.source.token
return config
}, err => {
return Promise.reject(err)
})
// 路由跳转中进行判断
router.beforeEach((to, from, next) => {
const CancelToken = axios.CancelToken
store.source.cancel && store.source.cancel()
store.source = CancelToken.source()
next()
})
//sort 文件 /
state: {
source: {
token: null,
cancel: null
}
}
axios api 路由变化时使用 axios 取消所有请求 vue 项目中 axios 请求拦截器与取消 pending 请求功能
存在问题:如果 token 过期提示弹框为二次确认弹框,再次确认之后才会进行页面跳转,那么在点击确认之前,页面中之前的请求还是会继续进行;解决方案:给弹窗添加全局状态,根据状态判断是否需要弹出弹框
预检请求
CORS 跨域 CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,==IE 浏览器不能低于 IE10。== 浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求
请求方法是以下三种方法之一:HEAD GET POST
HTTP 的头信息不超出以下几种字段:Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
简单请求不会触发 CORS 预检请求。
非简单请求
当请求满足下述任一条件时,即为非简单请求:
使用了下面任一 HTTP 方法:PUT DELETE CONNECT OPTIONS TRACE PATCH
人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:AcceptAccept-LanguageContent-LanguageContent-Type (but note the additional requirements below)DPRDownlinkSave-DataViewport-WidthWidth
Content-Type 的值不属于下列之一: application/x-www-form-urlencoded multipart/form-data text/plain
请求中的 XMLHttpRequestUpload 对象注册了任意多个事件监听器。
请求中使用了 ReadableStream 对象。
HTTP 访问控制(CORS)
预检请求
非简单请求,会在正式通信之前,增加一次 HTTP OPTIONS 方法发起的查询请求,称为 ” 预检 ” 请求(preflight)。以获知服务器是否允许该实际请求。” 预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。== 该方法不会对服务器资源产生影响 ==
优化方案
Access-Control-Max-Age: <delta-seconds> // 单位是秒。
表示 preflight request(预检请求)的返回结果(即 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers 提供的信息)可以被缓存多久
Vue Router
push、replace 的区别
push 会向 history 添加新的记录,replace 只是替换掉原来的记录,不会添加新的记录;这就导致了用 replace 方法跳转的页面是无法回到之前的页面的;(类似 window.history)
vue Router 编程式的导航
路由懒加载
为了提升页面加载速度,实现按需加载,也就是当路由访问时才加载对应组件,我们可以结合 vue 的异步组件和 webpack 的代码分割功能来实现路由的懒加载。
{
path: ‘/iov/login’,
name: ‘ 登录 ’,
component: resolve => require([‘@/views/login/login’], resolve),
},
{
path:’/iov/organization’,
name:’ 组织管理 ’,
component:() => import(‘@/views/enterprise/organization’)
}
vue Router 路由懒加载 vue 异步组件 vue + vue-router 懒加载 import / resolve+require
elementUI
表单弹窗中 elementform 清除验证残留提示
给表单添加不同的 ref(REFNAME),如果有相同的 ref 会导致残留验证提示清除失败
this.dialogTranspor = true
// 弹窗打开后 dom 没有生成,所有要用 this.$nextTick
this.$nextTick(function () {
this.$refs.REFNAME.resetFields();
})
页码数无法正常显示
场景:列表页在跳到详情或其他页面后再返回列表页,无法正常显示对应页数(页码数放在 state 中),但请求的数据时正常的;
解决方案:页码数、总页数必须要在同一个对象中,否则当前页码数不能正常显示
data() {
return {
// 完成查询条件
searchComplate: {
“comId”: Cookies.get(‘comId’),
“transportOrderCode”: null,
“orderCode”: null,
“customerId”: null,
“abnormal”: 2,
“startTime”: null,
“endTime”: null,
“pageNum”: 1,
“pageSize”: 20,
total: 0,
currentRow: ”,
dataArea: [”, ”],
activeName: ”,
expands: []
},
}
}
动态多级表单验证
<li v-for=”(item,index) in transportAddData.serviceOrderList”>
<template v-for=”(subItem,subIndex) in item.serviceOrderItems”>
<tr >
<td>
<el-form-item :prop=”‘serviceOrderList.’+index+’.serviceOrderItems.’ + subIndex + ‘.addressName'”
:rules=”{required: true, message: ‘ 卸货地不能为空 ’, trigger: ‘blur’}”>
<el-input v-model=”subItem.addressName” placeholder=” 地址 ”></el-input>
</el-form-item>
</td>
<td>
<el-form-item :prop=”‘serviceOrderList.’+index+’.serviceOrderItems.’ + subIndex + ‘.planTonnage'”
:rules=”[{required: true, message: ‘ 必填项 ’, trigger: ‘blur’},{pattern: /^((([1-9]+(\d+)?)(\.\d+)?)|(0\.\d+))$/, message: ‘ 必须为正数且不为 0 ’}]”>
<el-input v-model=”subItem.planTonnage”
placeholder=” 预卸吨数 ”></el-input>
</el-form-item>
</td>
…
</tr>
</template>
</li>