Vue问题整理
@mouseover
第三方组件例如el-button中使用类似api失效,此时需要使用@mouseover.native,其它事件类比。
element文档里没有列出的组件
el-scrollbar,默认有水平滚动条,可通过设置.el-scrollbar__wrap的overflow-x:hidden隐藏,这里可能需要使用到深度选择器/deep/,>>>,::v-deep才能生效
props: {//这个组件接收以下属性
native: Boolean,//是否不显示el的滚动条,默认false
wrapStyle: {},
wrapClass: {}, //外层盒子class
viewClass: {}, //内层盒子class
viewStyle: {},
noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能
tag: {
type: String,
default: 'div' //以DIV渲染
}
},
data恢复初始值
Object.assign(this.$data,this.$options.data())
this.$data是目前DOM中现有的data值,this.$options.data()是初始的data值,这里用到的是Object.assign(target, source)(MDN),即是将source源数据拷贝到target目标值并返回target。和Object.keys,Object.values, Object.hasOwnProperty配合使用起来都比较方便
$refs 和 ref
常用功能类似于选择器,使用ref注册引用信息string,使用$refs选中该对象object,相比于id或class选择器减少了获取dom节点的消耗
v-show / v-if / v-else / v-else-if / v-for
v-show判断语句是CSS操作,我还在,只不过display:none了,依然存在于dom中
v-if判断是动态渲染,不合指令就鸿飞冥冥,只存在于JS里,dom里会有一个空注释行< !-- -- >表示可能存在的足迹,else 和 else-if的操作一如JS的if else语句
v-for这里列出了一种配合使用的方法,在v-for语句里可以添加条件判断,list里的某一项符合既定指令的话会添加dom节点,例如对列出的某一项做出解释或提示。写个例子吧
<div v-for="user in userList" :key="user.id">
<span v-if="user.name == 'me'">++{{ user.id }}:{{ user.name }}++</span>
<span v-else>--{{ user.id }}:{{ user.name }}--</span>
<!--这里用了v-else,3:me只出现一次-->
</div>
<!--shows like this-->
-- 1:him --
-- 2:you --
++ 3:me ++
-- 4:her --
axios的全局配置
主要记录一下axios在配置时可以自己添加参数,新版的axios似乎获取不到自定义参数了,不过写进headers里或者config的其他属性里也可以判断,并不局限。
axios.defaults.baseURL = 'http://url'
//baseURL类似实际url的前缀,发起请求时候添加的url会添加至baseURl尾部
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
//axios默认是application/json的类型,这里全局设置了POST的请求头
// 配置拦截器,可设置token信息
axios.interceptors.request.use(config => {
//这里调用了登录时存入session里的token
let token = window.sessionStorage.getItem('token')
if (token) {
config.headers.Authorization = token
//有token就在请求头里加入授权
}
if (config.method == 'post') {
//post请求里会有其他类型的需求,例如上传文件时用的FormData
if (!config.typeFlag) {//在config里加了一个自定义的Boolean来判断
config.data = qs.stringify(config.data)
//如果没有这个typeFlag,就统一使用qs转换格式,有的话就不做全局设置
}
}
return config
}, error => {
return Promise.reject(error)
})
// 配置响应拦截器
axios.interceptors.response.use(res => {
if (res.data.code == '401') {
window.sessionStorage.removeItem('token')
router.push('/')
return {data: {
code: 401,
msg: '登录超时'
}}
//这里重新回了一个401的data,这样请求实际收到的是这个return的值
}
return Promise.resolve(res)
}, error => {
return Promise.reject(error)
})
export default axios
上传文件的实现
为了排版方便,未使用el-upload
<!--使用form包裹一个input,用于上传后的清除操作-->
<form id="uploadForm" style="display:none">
<el-input id="uploadFile" ref="uploadFile" v-model="file" type="file" accept="application/msword"></el-input>
</form>
<!--组合一个带前置按钮的输入框,禁用输入,用于选择文件和显示名称-->
<el-input v-model="file" size="medium" disabled>
<template slot="prepend">
<el-button type="primary" size="medium" @click="chooseFile">选择文件</el-button>
</template>
</el-input>
<el-button type="primary" size="medium" @click="upload" :loading="uploading">上传</el-button>

methods:{
chooseFile() {
//模拟点击
document.getElementById("uploadFile").click();
},
upload(){
this.uploading = true;
//新建FormData并append文件数据
let fd = new FormData();
fd.append("file", this.$refs.uploadFile.$refs.input.files[0]);
//el-input编译后是两层,外层div包裹input,所以用了两次$refs来获取files,
//用原生的长度也差不多,不纠结了。
//fd.append("file", document.getElementById("uploadFile").files[0]);
this.$http.request({
url: this.baseUrl,
method: "post",
data: fd,
typeFlag: true,//seeing this flag ?
headers: { "Content-Type": "multipart/form-data" }
})
.then(res => {
this.uploading = false;
if (res.data.code == 0) {
this.$message.success(res.data.msg);
} else {
this.$message.warning(res.data.msg);
}
//使用form的原生JS方法reset()在上传成功后清除input的files,
//el-form的resetFields()好像并不好用。
document.getElementById("uploadForm").reset();
this.file = ""//名字数据也清掉
})
.catch(err => {
this.uploading = false;
console.log(err)
});
}
}
nextTick()
最近在使用el-scrollbar做持续底部定位的时候发现有点不准确,数据的更新和DOM的更新不是很同步,导致滚动条定位在底部的代码偶尔会失效,出现这个情况应该跟el-scrollbar的实现方法有关。最后使用Vue的nextTick()方法解决。
nextTick()的官方解释是【将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新】。方法在数据更新之后,要等DOM更新完成才会执行,可以准确避免因为数据变化过快导致的DOM更新错误。
scroll() {
var div = this.$refs.chat.$refs.wrap;//拿el-scrollbar的wrap元素
this.$nextTick(() => {
div.scrollTop = div.scrollHeight;
});
}
ajax请求的异步处理
ajax请求在大部分情况下需要等待响应结果返回后再进行处理,可以使用ES7 async+await或者ES6 Promise对象的then()、catch()方法
methods:{
async handleClick(){//async标记为异步函数
await this.$ajax.post('url',this.config)//await 等待结果返回后再执行下一句
await ...
}//需要注意await需要搭配async使用,不可单独存在
//或者使用Promise方法
handleSubmit(){
this.axios.post('url',this.config)
.then((res)=>{
console.log(res)
})
.then(()=>{//后边也可以继续加 then...注意方法里的函数不能省略
})
.catch((err)=>{//无法获取结果时抓错误,像取消操作,服务器异常
console.log(err)
})
}
}
vue.config.js
Vue-CLi里简化处理了文件目录,可以新建vue.config.js进行自定义配置,这里列举一个简单的数据mock写法
module.exports = {
configureWebpack: {
devServer: {//开发环境下
before(app) {
//调用get方法时指向以下设置
app.get('/api/goods', function (req, res) {
res.json({
list: [
{ name: 'good1', price: 100 },
{ name: 'good2', price: 80 },
{ name: 'good3', price: 60 }
]
});
});
}
}
}
}
然后在页面组件里使用axios或async+await获取接口数据
created(){
axios.get('/api/goods')
.then(res=>{
this.goods = res.data.list
})
.catch(()=>{})
}
//----------
async created(){
const res = await axios.get('/api/goods')
this.goods = res.data.list
}
webpack-bundle-analyzer
使用交互式可缩放树形图可视化webpack输出文件的大小
vue.config.js配置:
module.exports = {
chainWebpack: config => {
config.plugin('webpack-bundle-analyzer')
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
}
}
运行命令:npm run serve
IOS系统微信分享的坑
IOS系统微信里在A页面使用vuerouter跳转到B页面后,再分享B页面不会分享当前页面的地址,而是分享的A页面的地址,需要在每次路由变化时都重新请求下签名,发起签名请求的url参数必须是当前页面的url。
处理方法:建立路由守卫,如果是IOS就重新请求签名。
router.beforeEach ((to, from, next) => {
let u = navigator.userAgent;
let isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isiOS && to.path !== location.pathname) {
location.assign(to.fullPath)
} else {
next()
}
});
这样实际体验很差,而且不利于vuex缓存数据。所以随后更改为hash路由,用#号对url进行拆分,这样对于同域名下的子页面,可以只拿域名去申请签名,因为hash后边的地址其实浏览器不认的。这样发起签名请求的url其实就变成hash之前的一串,每个页面都是一样的。
Vue组件在单个页面的循环调用
最近封装了一个视频组件,用以播放单个监控的实时视频流,在父组件使用for循环传递不同的值多次调用,实际渲染时发现传递进去值出现了不同程度的串位,本来给1组件的值传到了其他组件里,考虑可能是DOM同时渲染造成的。这个解释感觉不太科学,不过实际操作上对for循环增加了少量延时之后校正了这个错位。Mark记录新增:对于页面渲染的错位和数据混乱大部分情况应该使用增加组件key值的方法解决,for循环中为每个组件增加唯一key值可以有效避免混乱。增加延时的方案只适用在特定场合,并不是很科学。
Vue中静态文件的页面引用
以图片为例,url可表示为~@/assets/img/logo.png或相对路径。
is 和 v-bind:is
当页面内有不同组件大量渲染时,template中单独列出每一个组件名会显得麻烦,可以在导入注册之后,使用v-bind:is动态渲染不同组件。例如:在一个需要回显自定义表单的页面中,可以根据表单控件的类型引入不同的组件,这种写法明显会少抠不少字母:
<template v-for="(item) in items">
<form-item :key="item.id" :label="item.title" :rules="item.rules" :prop="item.prop">
<component v-bind:is="item.type"></component>
<form-item>
</template>
这样,当item.type === "singleInput"时,页面组件会自动渲染成<component is="singleInput"></component>等同于<singleInput></singleInput>组件