Vue問題整理
@mouseover
第三方組件例如el-button
中使用類似api失效,此時需要使用@mouseover.native
蝗锥,其它事件類比。
element文檔里沒有列出的組件
el-scrollbar
章母,默認(rèn)有水平滾動條茫舶,可通過設(shè)置.el-scrollbar__wrap
的overflow-x:hidden
隱藏,這里可能需要使用到深度選擇器/deep/
恤筛,>>>
官还,::v-deep
才能生效
props: {//這個組件接收以下屬性
native: Boolean,//是否不顯示el的滾動條,默認(rèn)false
wrapStyle: {},
wrapClass: {}, //外層盒子class
viewClass: {}, //內(nèi)層盒子class
viewStyle: {},
noresize: Boolean, // 如果 container 尺寸不會發(fā)生變化毒坛,最好設(shè)置它可以優(yōu)化性能
tag: {
type: String,
default: 'div' //以DIV渲染
}
},
data恢復(fù)初始值
Object.assign(this.$data,this.$options.data())
this.$data
是目前DOM中現(xiàn)有的data值望伦,this.$options.data()
是初始的data值林说,這里用到的是Object.assign(target, source)
(MDN),即是將source
源數(shù)據(jù)拷貝到target
目標(biāo)值并返回target
屯伞。和Object.keys
,Object.values
, Object.hasOwnProperty
配合使用起來都比較方便
$refs 和 ref
常用功能類似于選擇器腿箩,使用ref
注冊引用信息string
,使用$refs
選中該對象object
劣摇,相比于id或class選擇器減少了獲取dom節(jié)點的消耗
v-show / v-if / v-else / v-else-if / v-for
v-show
判斷語句是CSS操作珠移,我還在,只不過display:none
了末融,依然存在于dom中
v-if
判斷是動態(tài)渲染钧惧,不合指令就鴻飛冥冥,只存在于JS里勾习,dom里會有一個空注釋行< !-- -- >
表示可能存在的足跡浓瞪,else
和 else-if
的操作一如JS的if else
語句
v-for
這里列出了一種配合使用的方法,在v-for
語句里可以添加條件判斷语卤,list
里的某一項符合既定指令的話會添加dom
節(jié)點追逮,例如對列出的某一項做出解釋或提示。寫個例子吧
<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只出現(xiàn)一次-->
</div>
<!--shows like this-->
-- 1:him --
-- 2:you --
++ 3:me ++
-- 4:her --
axios的全局配置
主要記錄一下axios
在配置時可以自己添加參數(shù)钮孵,新版的axios
似乎獲取不到自定義參數(shù)了,不過寫進headers
里或者config
的其他屬性里也可以判斷眼滤,并不局限巴席。
axios.defaults.baseURL = 'http://url'
//baseURL類似實際url的前綴,發(fā)起請求時候添加的url會添加至baseURl尾部
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
//axios默認(rèn)是application/json的類型诅需,這里全局設(shè)置了POST的請求頭
// 配置攔截器漾唉,可設(shè)置token信息
axios.interceptors.request.use(config => {
//這里調(diào)用了登錄時存入session里的token
let token = window.sessionStorage.getItem('token')
if (token) {
config.headers.Authorization = token
//有token就在請求頭里加入授權(quán)
}
if (config.method == 'post') {
//post請求里會有其他類型的需求,例如上傳文件時用的FormData
if (!config.typeFlag) {//在config里加了一個自定義的Boolean來判斷
config.data = qs.stringify(config.data)
//如果沒有這個typeFlag堰塌,就統(tǒng)一使用qs轉(zhuǎn)換格式赵刑,有的話就不做全局設(shè)置
}
}
return config
}, error => {
return Promise.reject(error)
})
// 配置響應(yīng)攔截器
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
上傳文件的實現(xiàn)
為了排版方便场刑,未使用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文件數(shù)據(jù)
let fd = new FormData();
fd.append("file", this.$refs.uploadFile.$refs.input.files[0]);
//el-input編譯后是兩層铐懊,外層div包裹input,所以用了兩次$refs來獲取files瞎疼,
//用原生的長度也差不多科乎,不糾結(jié)了。
//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 = ""http://名字?jǐn)?shù)據(jù)也清掉
})
.catch(err => {
this.uploading = false;
console.log(err)
});
}
}
nextTick()
最近在使用el-scrollbar
做持續(xù)底部定位的時候發(fā)現(xiàn)有點不準(zhǔn)確捏萍,數(shù)據(jù)的更新和DOM的更新不是很同步,導(dǎo)致滾動條定位在底部的代碼偶爾會失效玉吁,出現(xiàn)這個情況應(yīng)該跟el-scrollbar
的實現(xiàn)方法有關(guān)照弥。最后使用Vue
的nextTick()
方法解決腻异。
nextTick()
的官方解釋是【將回調(diào)延遲到下次 DOM 更新循環(huán)之后執(zhí)行进副。在修改數(shù)據(jù)之后立即使用它,然后等待 DOM 更新】悔常。方法在數(shù)據(jù)更新之后影斑,要等DOM更新完成才會執(zhí)行,可以準(zhǔn)確避免因為數(shù)據(jù)變化過快導(dǎo)致的DOM更新錯誤机打。
scroll() {
var div = this.$refs.chat.$refs.wrap;//拿el-scrollbar的wrap元素
this.$nextTick(() => {
div.scrollTop = div.scrollHeight;
});
}
ajax請求的異步處理
ajax
請求在大部分情況下需要等待響應(yīng)結(jié)果返回后再進行處理矫户,可以使用ES7 async+await
或者ES6 Promise
對象的then()
、catch()
方法
methods:{
async handleClick(){//async標(biāo)記為異步函數(shù)
await this.$ajax.post('url',this.config)//await 等待結(jié)果返回后再執(zhí)行下一句
await ...
}//需要注意await需要搭配async使用残邀,不可單獨存在
//或者使用Promise方法
handleSubmit(){
this.axios.post('url',this.config)
.then((res)=>{
console.log(res)
})
.then(()=>{//后邊也可以繼續(xù)加 then...注意方法里的函數(shù)不能省略
})
.catch((err)=>{//無法獲取結(jié)果時抓錯誤皆辽,像取消操作,服務(wù)器異常
console.log(err)
})
}
}
vue.config.js
Vue-CLi里簡化處理了文件目錄芥挣,可以新建vue.config.js
進行自定義配置驱闷,這里列舉一個簡單的數(shù)據(jù)mock
寫法
module.exports = {
configureWebpack: {
devServer: {//開發(fā)環(huán)境下
before(app) {
//調(diào)用get方法時指向以下設(shè)置
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
獲取接口數(shù)據(jù)
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系統(tǒng)微信分享的坑
IOS系統(tǒng)微信里在A頁面使用vuerouter
跳轉(zhuǎn)到B頁面后,再分享B頁面不會分享當(dāng)前頁面的地址空免,而是分享的A頁面的地址空另,需要在每次路由變化時都重新請求下簽名,發(fā)起簽名請求的url
參數(shù)必須是當(dāng)前頁面的url
蹋砚。
處理方法:建立路由守衛(wèi)扼菠,如果是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
緩存數(shù)據(jù)循榆。所以隨后更改為hash
路由,用#
號對url
進行拆分墨坚,這樣對于同域名下的子頁面秧饮,可以只拿域名去申請簽名,因為hash
后邊的地址其實瀏覽器不認(rèn)的框杜。這樣發(fā)起簽名請求的url
其實就變成hash
之前的一串浦楣,每個頁面都是一樣的。
Vue組件在單個頁面的循環(huán)調(diào)用
最近封裝了一個視頻組件咪辱,用以播放單個監(jiān)控的實時視頻流振劳,在父組件使用for
循環(huán)傳遞不同的值多次調(diào)用,實際渲染時發(fā)現(xiàn)傳遞進去值出現(xiàn)了不同程度的串位油狂,本來給1組件的值傳到了其他組件里历恐,考慮可能是DOM
同時渲染造成的寸癌。這個解釋感覺不太科學(xué),不過實際操作上對for
循環(huán)增加了少量延時之后校正了這個錯位弱贼。Mark
記錄新增:對于頁面渲染的錯位和數(shù)據(jù)混亂大部分情況應(yīng)該使用增加組件key
值的方法解決蒸苇,for
循環(huán)中為每個組件增加唯一key
值可以有效避免混亂。增加延時的方案只適用在特定場合吮旅,并不是很科學(xué)溪烤。
Vue中靜態(tài)文件的頁面引用
以圖片為例,url
可表示為~@/assets/img/logo.png
或相對路徑庇勃。
is 和 v-bind:is
當(dāng)頁面內(nèi)有不同組件大量渲染時檬嘀,template
中單獨列出每一個組件名會顯得麻煩,可以在導(dǎo)入注冊之后责嚷,使用v-bind:is
動態(tài)渲染不同組件鸳兽。例如:在一個需要回顯自定義表單的頁面中,可以根據(jù)表單控件的類型引入不同的組件罕拂,這種寫法明顯會少摳不少字母:
<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>
這樣揍异,當(dāng)item.type === "singleInput"
時,頁面組件會自動渲染成<component is="singleInput"></component>
等同于<singleInput></singleInput>
組件