前言
最近我在公司用的前端技術(shù)組合是layui+knockout.js的組合。為什么呢?因?yàn)檫@是原來(lái)公司前端留下來(lái)的,以我的前段底子還算摸得清楚,用起來(lái)也不算復(fù)雜,于是就用下去了。但是吧,越用越難用。為什么呢?之所以使用這個(gè)組合,就是希望使用knockout的雙向綁定功能取代dom操作,加上layui這樣一個(gè)相對(duì)比較全的ui庫(kù),組合起來(lái)看似完美。但是吧,有幾個(gè)組件,比如下拉選擇,單選多選等等,就是需要你對(duì)其添加單獨(dú)的綁定事件來(lái)處理數(shù)據(jù),很多時(shí)候就是需要你手動(dòng)刷新ui繪制才能有效果,這樣搞起來(lái)就很煩。尤其是現(xiàn)在,越來(lái)越多的細(xì)致的功能需要去做,而重用的內(nèi)容卻很難搞,就陷入了泥沼之中。
再說(shuō)說(shuō)為啥我沒有強(qiáng)改成vue。其實(shí)吧,我是研究過(guò)vue的,也練過(guò)手寫過(guò)幾篇博客在這個(gè)平臺(tái)的有http://www.itdecent.cn/p/29625af02d79。但是,一直以來(lái)我都沒有搭建成功過(guò)一個(gè)多頁(yè)應(yīng)用,或者說(shuō)可以自動(dòng)掃描多頁(yè)的應(yīng)用?,F(xiàn)在居然讓我碰到了一個(gè)模板,地址是:https://github.com/Plortinus/vue-multiple-pages。然后只需要npm install就可以了。接下來(lái)我們從項(xiàng)目結(jié)構(gòu)的分析開始吧。
基本情況
運(yùn)行
這個(gè)代碼down下來(lái)是可以直接運(yùn)行的,下面是一些基本的操作:
npm run serve # 運(yùn)行server進(jìn)行調(diào)試
npm run build # 構(gòu)建項(xiàng)目,會(huì)生成dist目錄
配置
這里介紹一下主要的配置文件。
/vue.config.js
這個(gè)應(yīng)該是vue項(xiàng)目的主要配置文件,這里可以看到對(duì)多頁(yè)目錄的 讀取,server的基本配置。
/server.js
這個(gè)應(yīng)該是運(yùn)行的服務(wù)的配置,可以看到其讀取的文件目錄,貌似使用的是一個(gè)叫做express的包,具體我也還沒有研究怎么用,后面再說(shuō)吧。
/title.js
這里配置各個(gè)頁(yè)面的標(biāo)題,它在vue.config.js中有被使用到。或許我們可以在頁(yè)面內(nèi)部解決標(biāo)題的問(wèn)題,這個(gè)后面研究了再說(shuō)。
目錄結(jié)構(gòu)
源代碼的頂級(jí)目錄分為public、src,其中public的內(nèi)容很簡(jiǎn)單,看頁(yè)面似乎是在js不運(yùn)行的情況下展示的報(bào)錯(cuò)頁(yè)面。src里面則是我們自己寫的各種內(nèi)容的頁(yè)面。
/src/assets
靜態(tài)資源目錄
/src/components
組件目錄,如果我們希望某個(gè)組件可以在不同的頁(yè)面之間進(jìn)行重用,把它放在這里。
/src/states
狀態(tài)管理邏輯,所有的狀態(tài)管理放在這里會(huì)有助于管理整個(gè)web端的狀態(tài),幫助理清思路。因?yàn)?,狀態(tài),理論上來(lái)說(shuō)是可以跨頁(yè)面、跨組件的,之和業(yè)務(wù)本身的生命周期有關(guān)。
/src/pages
這里就是各個(gè)頁(yè)面的代碼了,要注意的是,頁(yè)面是按照目錄結(jié)構(gòu)組織的,每個(gè)目錄下面的app.js是該頁(yè)面的入口,這個(gè)可以在vue.config.js文件中找到對(duì)應(yīng)的使用。而每個(gè)頁(yè)面是一個(gè)一個(gè)文件夾。訪問(wèn)頁(yè)面的時(shí)候url為:http://域名:端口/目錄結(jié)構(gòu).html,也即最后一個(gè)app.js所在的目錄名字加上.html就是它的url啦。
實(shí)戰(zhàn)demo
實(shí)際寫頁(yè)面的時(shí)候,我發(fā)現(xiàn)個(gè)問(wèn)題,就是它默認(rèn)的element-ui的版本太低了,于是我改成了最新的,2.13.2,這樣就和官網(wǎng)文檔的描述一致了。具體的修改方法是,在package.json文件中,找到element-ui,把后面的版本號(hào)改了。然后,在命令行里執(zhí)行,npm install,就可以了。后面我們開始寫頁(yè)面吧。
修改登陸密碼頁(yè)面
由于我這個(gè)項(xiàng)目是補(bǔ)充之前項(xiàng)目的頁(yè)面,所以不會(huì)從登陸、主頁(yè)這樣的寫,于是我挑了個(gè)最簡(jiǎn)單的頁(yè)面用來(lái)練手。修改登陸密碼頁(yè)面。先粘貼頁(yè)面代碼吧:
<template>
<div>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="原密碼">
<el-input v-model="form.oldPwd" show-password></el-input>
</el-form-item>
<el-form-item label="新密碼">
<el-input v-model="form.newPwd" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">修改登陸密碼</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
form: {
oldPwd: '',
newPwd: ''
}
}
},
methods: {
onSubmit() {
console.log('submit!')
}
}
}
</script>
<style>
</style>
上面的代碼,是只繪制了頁(yè)面的樣子的代碼,可以看到script里面只有空的data及methods,具體的業(yè)務(wù)邏輯都沒有實(shí)現(xiàn)。需要注意的是,在template里面使用到的data也好、methods也好,都需要至少進(jìn)行聲明,否則編譯不過(guò)去,也就無(wú)法調(diào)試了。具體的需要注意的細(xì)節(jié),也就只有el-input最后的show-password了。這是element-ui內(nèi)置的一個(gè)屬性,它幫助我們實(shí)現(xiàn)了一個(gè)密碼框,右側(cè)還有按鈕可以控制密碼框顯示明文還是點(diǎn)號(hào)。需要注意的是,這個(gè)效果就受限制與element-ui的版本,最初的那個(gè)版本就無(wú)法正常使用。
修改登陸密碼請(qǐng)求
這個(gè)頁(yè)面非常簡(jiǎn)單,唯一需要的業(yè)務(wù),也就是把修改密碼的請(qǐng)求提交到后臺(tái)。這里我們就不能用jquery了,我們將會(huì)使用一個(gè)叫做axios的框架。安裝命令如下:
npm install axios
在script中進(jìn)行引入,代碼如下:
import axios from 'axios'
然后就可以開始進(jìn)行ajax請(qǐng)求啦,樣例代碼如下:
axios.post(
'http://localhost:8080/api/sys/users/' + userId + '/modifyPassword',
{
userId: userId,
oldPwd: 'admin123',
newPwd: 'admin1234'
},
{
headers: {
'Content-Type': 'application/json',
SessionToken:
'123456'
}
}
)
.then(data => {
console.log(data)
})
可以看到我們這是進(jìn)行的post請(qǐng)求。它的請(qǐng)求格式其實(shí)是:axios#post(url[, data[, config]]) ,也就是說(shuō),第一個(gè)參數(shù)是要請(qǐng)求的url,這個(gè)是必填的,然后是請(qǐng)求的數(shù)據(jù)。這個(gè)data就是你要請(qǐng)求的json字符串。我不確定axios的請(qǐng)求默認(rèn)是json的,所以,我在后面的config里面加入了json的header,還加入了我用來(lái)做會(huì)話驗(yàn)證的SessionToken的頭。最后在then里面是回調(diào)。
這里需要說(shuō)明的是,這種調(diào)用方式肯定不是最終的調(diào)用方式,最終還需要進(jìn)行生產(chǎn)需要的封裝,以在寫業(yè)務(wù)的時(shí)候可以更少得關(guān)注這些細(xì)節(jié)。但是,有了這個(gè),可以說(shuō)我們就可以完成這個(gè)頁(yè)面了。剩下的只是,也只是如何使用vue及其生態(tài)了。
路徑別名
在代碼中import自己的js如果總是用相對(duì)路徑,其實(shí)韓式蠻煩人的一件事。但是,這里面的根路徑又很不好用,所以就需要引入路徑別名。由于我的項(xiàng)目是使用vue-cli 4.0的,所以它的配置文件是vue.config.js,添加如下代碼即可:
const path = require('path') //引入path模塊
function resolve( dir){
return path.join(__dirname, dir) //path.join(__dirname)設(shè)置絕對(duì)路徑
}
module.exports = {
……
chainWebpack: (config) => {
config.plugins.delete('named-chunks')
config.resolve.alias
//set第一個(gè)參數(shù):設(shè)置的別名,第二個(gè)參數(shù):設(shè)置的路徑
.set('@', resolve('./'))
.set('components', resolve('./src/components'))
.set('assets', resolve('./src/assets'))
.set('pages', resolve('./src/pages'))
.set('tools', resolve('./src/tools'))
},
……
結(jié)合自己的代碼進(jìn)行改造吧。在使用的時(shí)候,我們使用@,就意味著在使用./,依此類推,就有了一些目錄的簡(jiǎn)短的縮寫了,方便使用。
加入統(tǒng)一認(rèn)證中心
認(rèn)證中心原理
最近能夠研究vue+element-ui的技術(shù)組合,也是源于設(shè)計(jì)完成了統(tǒng)一的認(rèn)證中心才可以在一個(gè)web端統(tǒng)一使用兩種不同的web技術(shù)來(lái)進(jìn)行組合。關(guān)鍵點(diǎn)有以下幾點(diǎn):
- 一個(gè)驗(yàn)證會(huì)話的頁(yè)面:注意,這里是頁(yè)面,它被放在認(rèn)證中心,所有需要驗(yàn)證會(huì)話的頁(yè)面都知道它在哪里。
- 需要驗(yàn)證會(huì)話的頁(yè)面中,創(chuàng)建一個(gè)不可見的iframe,里面用來(lái)訪問(wèn)驗(yàn)證回話的頁(yè)面。通信方式使用PostMessage進(jìn)行,這種通信是基于Html5標(biāo)準(zhǔn),同時(shí)可以跨域。通信的目的是為了從驗(yàn)證回話頁(yè)面同步會(huì)話信息到當(dāng)前域下面。
- 基本的流程控制:由于是通過(guò)頁(yè)面進(jìn)行跨域通信的,所以整個(gè)通信都需要在頁(yè)面加載完成之后進(jìn)行。待驗(yàn)證頁(yè)面加載完成之后,才可以操作iframe的dom。iframe頁(yè)面加載完成之后才可以向其發(fā)送驗(yàn)證請(qǐng)求。而驗(yàn)證請(qǐng)求的發(fā)送和接收都是異步的,頁(yè)面并不會(huì)進(jìn)行阻塞等待。如何處理這個(gè)流程也是該功能是否流暢穩(wěn)定的關(guān)鍵。
統(tǒng)一模板組件
如何在每個(gè)頁(yè)面中加入統(tǒng)一認(rèn)證的過(guò)程,又不會(huì)在編寫頁(yè)面中引入過(guò)多的復(fù)雜度,這在vue的組件化體系中,我還是摸索了幾種想法的,現(xiàn)在把我的心路歷程整理下:
- 每個(gè)頁(yè)面加入一個(gè)驗(yàn)證組件:以前我在寫vue的單頁(yè)應(yīng)用的例子的時(shí)候,登錄頁(yè)面和主頁(yè)是在同一個(gè)頁(yè)面中存在的,中間使用了一個(gè)登錄狀態(tài)標(biāo)志位進(jìn)行區(qū)分。整個(gè)效果是挺靈敏的,但是問(wèn)題就是每個(gè)頁(yè)面都會(huì)顯示得直到這個(gè)邏輯的存在,而且需要在html的結(jié)構(gòu)和js代碼中顯示得直到這個(gè)知識(shí),這是很討厭的一件事。但是,因?yàn)檫@個(gè)邏輯肯定可行,所以我后面的思路就變成了怎么把這個(gè)邏輯替換掉。
- 頁(yè)面路由:上述方案肯定能實(shí)現(xiàn)這個(gè)過(guò)程,但是關(guān)于驗(yàn)證過(guò)程的順序要求就變得有些麻煩了。onload事件的順序沒有問(wèn)題,但是PostMessage的異步通信要怎么解決。因?yàn)檫@個(gè)時(shí)候,頁(yè)面本身可能也有需要在onload事件里執(zhí)行的請(qǐng)求,而且需要用到其最終結(jié)果,會(huì)話標(biāo)志。之前我思考到的方案是設(shè)置一個(gè)是否已驗(yàn)證的標(biāo)志位,在未驗(yàn)證的時(shí)候,所有的網(wǎng)絡(luò)請(qǐng)求會(huì)進(jìn)入一個(gè)隊(duì)列而不是直接請(qǐng)求。在收到驗(yàn)證結(jié)果后,依次執(zhí)行這個(gè)隊(duì)列里的請(qǐng)求。也是因?yàn)檫@個(gè)思路,我蠻不想用它的。而如果整個(gè)過(guò)程有一個(gè)頁(yè)面跳轉(zhuǎn)作為分隔,就變得簡(jiǎn)單多了。不過(guò),在我找頁(yè)面路由的實(shí)現(xiàn)方法時(shí),我找到了我現(xiàn)在用的方案。
- 寫一個(gè)通用組件,里面加一個(gè)slot用來(lái)呈現(xiàn)頁(yè)面。在vue中有一種機(jī)制叫做slot,他可以讓我們改變組件里面的內(nèi)容。畢竟組件的使用形式是標(biāo)簽,而如果通過(guò)標(biāo)簽里面的內(nèi)容來(lái)改變組件的呈現(xiàn),這似乎就是我想要的。而因?yàn)榻M件還可以有自己的代碼邏輯,所以驗(yàn)證的過(guò)程就被有效得被隔離開了。于是,一切都變得那么自然,這個(gè)通用組件做我頁(yè)面的頂級(jí)元素就可以了。
組件實(shí)現(xiàn)與應(yīng)用
以下是template部分的代碼:
<template>
<needAuthTemplate>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="原密碼">
<el-input v-model="form.oldPwd" show-password></el-input>
</el-form-item>
<el-form-item label="新密碼">
<el-input v-model="form.newPwd" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">修改登陸密碼</el-button>
</el-form-item>
</el-form>
</needAuthTemplate>
</template>
其中,needAuthTemplate是我定義的組件,它的代碼如下:
<template>
<div>
<slot></slot>
</div>
</template>
<script>
import config from '@/config.js'
console.log(config)
export default {
data() {
return {
// configData: config.authUrl
configData: 'http://localhost/page/sys/sessionCheck.html'
}
},
methods: {
sendCheckCmd(e) {
console.log('sendCheckCmd:' + e)
}
},
mounted: function() {
/**
* 注冊(cè)監(jiān)聽事件
*/
window.addEventListener(
'message',
function(e) {
//如果e中含有type,說(shuō)明是系統(tǒng)的消息,否則則嘗試解析自定義消息
if (e.data.type != undefined) {
return
}
console.log(e.data)
if (e == undefined || e == null) {
return
}
var cmdData = JSON.parse(e.data) //獲取json對(duì)象
//如果cmdType不存在,說(shuō)明入?yún)⒎欠? if (cmdData.cmdType == undefined || cmdData == null) {
return
}
switch (cmdData.cmdType) {
case 'info':
console.log(cmdData.sessionId)
if (cmdData.sessionId == undefined || cmdData.sessionId == null) {
//跳轉(zhuǎn)登錄
window.location = 'http://localhost'
} else {
//刷新會(huì)話標(biāo)志
localStorage.setItem('sessionId', cmdData.sessionId)
//加載后續(xù)頁(yè)面
}
break
}
},
false
)
//創(chuàng)建用來(lái)驗(yàn)證會(huì)話的iframe
let bodyDoc = document.querySelector('body')
let iframe = document.createElement('iframe')
iframe.style.width = '0px'
iframe.style.height = '0px'
iframe.style.display = 'none'
iframe.onload = () => {
var cmdData = {}
cmdData.cmdType = 'check'
iframe.contentWindow.postMessage(JSON.stringify(cmdData), '*')
console.log('加載完成') // 這樣每次都會(huì)觸發(fā)
}
iframe.src = this.configData
//將dom附加到窗口中
bodyDoc.appendChild(iframe)
console.log('mounted')
}
}
</script>
<style>
</style>
這個(gè)代碼相對(duì)較長(zhǎng),有這么而幾個(gè)點(diǎn)需要注意。
- template部分,只是一個(gè)div嵌套了一個(gè)slot,也就是說(shuō)這個(gè)組件展示的內(nèi)容就是slot的內(nèi)容。
- script部分,可以被分為兩個(gè)部分。第一段是給頁(yè)面添加消息監(jiān)聽事件,用來(lái)處理接收會(huì)話消息;第二段則是在當(dāng)前頁(yè)面添加iframe的dom,在這個(gè)dom里我們?cè)L問(wèn)會(huì)話驗(yàn)證頁(yè)面。而且,注意添加dom的順序,一定是一切都配置好了再添加到頁(yè)面上。
但是這個(gè)時(shí)候其實(shí)還是有問(wèn)題的:
- vue的版本:這個(gè)問(wèn)題是我在讀slot的的文檔的時(shí)候看到的,我默認(rèn)的版本是2.5.x的版本,但是2.6.0以后,slot的標(biāo)簽語(yǔ)法發(fā)生了變化,所以,我升級(jí)到了最新的版本。需要注意的是vue和vue-template-compiler的版本要一起升級(jí),否則編譯不過(guò)。
- 要讓template中可以使用這個(gè)標(biāo)簽,是需要在組件文件夾額外寫一個(gè)index.js文件的,文件內(nèi)容為:
import needAuthTemplateComponent from './needAuthTemplate.vue'
const needAuthTemplate = {
install: function(Vue) {
Vue.component('NeedAuthTemplate', needAuthTemplateComponent)
}
}
export default needAuthTemplate
注意,標(biāo)簽的名字是Vue.component的第一個(gè)參數(shù)。而在使用的頁(yè)面的app.js的里面引入這個(gè)組件的代碼也從直接引入vue文件變成了引入組件的目錄,如下:
import NeedAuthTemplate from 'components/needAuthTemplate/'
Vue.use(NeedAuthTemplate)
認(rèn)證中心坑
從實(shí)踐來(lái)看,這次的實(shí)現(xiàn)依然完美實(shí)現(xiàn)多系統(tǒng)的統(tǒng)一認(rèn)證,原因有兩點(diǎn):
- 每次都加載頁(yè)面進(jìn)行驗(yàn)證,其效率并不高,整體性能還是比較慢的。
- 這種設(shè)計(jì)其實(shí)并沒有解決之前我說(shuō)的因?yàn)楫惒叫枰却膯?wèn)題。也就是,如果你切換用戶,原有頁(yè)面你要刷新兩次才能看到新用戶的信息。而如果進(jìn)行阻塞性的等待,則每次加載頁(yè)面用戶都會(huì)有顯示的等待時(shí)間。
基于上述原因,我還是在登錄的地方直接做了多域登錄,現(xiàn)在的方式就只作為會(huì)話同步的手段了。
vue填坑記
上面我們看到了搭建認(rèn)證中心過(guò)程中的坑。搭建完認(rèn)證中心后,由于我們已經(jīng)存在了主框架,所以接下來(lái)的重點(diǎn)是編寫每個(gè)頁(yè)面。而編寫過(guò)程中遇到的重要內(nèi)容,我將會(huì)記錄在此。
methods中的search方法
記住,方法名千萬(wàn)不能用search,這似乎是methods中的內(nèi)置方法名。我在用了這個(gè)方法名之后,一旦在mounted中調(diào)用,就會(huì)導(dǎo)致編譯報(bào)錯(cuò)。該錯(cuò)誤,我調(diào)試了一天,以此謹(jǐn)記。
通過(guò)props與子組件進(jìn)行通信
這次,我的場(chǎng)景是一個(gè)非常常見的場(chǎng)景,添加和修改的彈窗。這兩個(gè)彈窗我做成了一個(gè)組件。組件只是彈窗里面的內(nèi)容,并不包含彈窗,方便以后復(fù)用。我需要將記錄的ID傳入組件內(nèi),以方便根據(jù)是否存在需要加載的ID來(lái)判斷當(dāng)前是當(dāng)前是新增還是編輯。
所以,我需要解決這樣幾個(gè)問(wèn)題:
- 將ID從父組件傳入進(jìn)來(lái)
- 持續(xù)得監(jiān)聽I(yíng)D的變化
將ID從父組件傳入進(jìn)來(lái)
這件事情,說(shuō)來(lái)簡(jiǎn)單,但是由于官方文檔在介紹代碼的時(shí)候并不是以vue文件的方式來(lái)介紹的,而且在說(shuō)的時(shí)候不知道代碼是在父組件還是子組件,所以讓人困惑的老想做些別的事情。現(xiàn)整理如下:
首先是子組件的script部分,因?yàn)楹推渌糠譀]有關(guān)系,所以就不展示了。
export default {
name: 'SaveLampSpecification',
props: ['lampSpecificationId'],
data() {
let self = this
return {
form: {
//燈具規(guī)格ID
lampSpecificationId: self.lampSpecificationId,
……
}
}
}
}
父組件的使用代碼如下:
<template>
……
<el-dialog title="添加燈具規(guī)格" :visible.sync="control.saveFormVisible" width="500px">
<SaveLampSpecification :lampSpecificationId="control.selectedLampSpecificationId"></SaveLampSpecification>
</el-dialog>
……
</tempalte>
<script>
import SaveLampSpecification from 'components/basicData/saveLampSpecification/saveLampSpecification.vue'
export default {
components: {
SaveLampSpecification
},
data() {
……
control: {
//保存燈具規(guī)格的顯隱設(shè)置
saveFormVisible: false,
selectedLampSpecificationId: 1
}
}
},
……
}
</script>
然后我們總體來(lái)說(shuō)一下。首先是在子組件中,需要聲明props以說(shuō)明都有哪些屬性可以使用。官網(wǎng)說(shuō)著這里有個(gè)駝峰轉(zhuǎn)橫杠的自動(dòng)命名轉(zhuǎn)換。親測(cè),沒有。然后,你聲明的這個(gè)屬性的訪問(wèn)方式其實(shí)和data里的是一樣的,在我的代碼中可以看到。但是,為了方便使用,我還是在data里聲明了另一個(gè)屬性,讓它等于這個(gè)屬性。這就是子組件的全部。而父組件,在使用的時(shí)候,將該屬性和data的某個(gè)值進(jìn)行綁定,也就形成了子組件和外部組件屬性的關(guān)聯(lián)。不過(guò)呢,這個(gè)時(shí)候你會(huì)發(fā)現(xiàn)一件事。當(dāng)你改變外面的屬性值后,里面的屬性值是不會(huì)跟著改變的。這里需要在子組件中添加一個(gè)監(jiān)聽代碼來(lái)搞定它:
watch: {
lampSpecificationId() {
let self = this
self.form.lampSpecificationId = self.lampSpecificationId
}
},
watch是vue的標(biāo)準(zhǔn)用法,我就不寫這段代碼的上下文了??傊?,這樣就完成了內(nèi)外屬性的關(guān)聯(lián)綁定了。
重復(fù)打開彈窗遇到的坑
首先,無(wú)意中發(fā)現(xiàn),默認(rèn)情況下彈窗在任意非彈窗位置點(diǎn)擊就關(guān)閉了。不過(guò),找到屬性,設(shè)置掉就可以了。另外我還遇見了另一個(gè)屬性,即每次關(guān)閉都銷毀相關(guān)的元素。調(diào)整后代碼如下:
<el-dialog title="添加燈具規(guī)格"
:visible.sync="control.saveFormVisible" width="500px"
:close-on-click-modal="false"
:destroy-on-close = "false">
<SaveLampSpecification
:lampSpecificationId="control.selectedLampSpecificationId"
v-on:saved="saveLampSpecification">
</SaveLampSpecification>
</el-dialog>
這些呢,只能算是小坑。后面預(yù)見一個(gè)大坑。編輯數(shù)據(jù)的時(shí)候,除了第一次,沒次打開都是上次的數(shù)據(jù)。這讓我很震驚。我是將加載數(shù)據(jù)寫在mounted里面的,難道這里有問(wèn)題?開始得時(shí)候我以為是因?yàn)楫惒郊虞d數(shù)據(jù)導(dǎo)致數(shù)據(jù)沒更新,就查了半天vue更新數(shù)據(jù)不更新界面怎么弄。后來(lái)我發(fā)現(xiàn),每次查詢數(shù)據(jù)的日志是在我關(guān)閉界面的時(shí)候打的,于是我懷疑生命周期不是我預(yù)想的樣子,于是把加載放到了上面寫的watch里面,一切就都好了。
重點(diǎn)說(shuō)明下destroy-on-close
上面的destroy-on-close屬性,默認(rèn)值就是false,只是我發(fā)現(xiàn)了它之后將它設(shè)置成了true。這將導(dǎo)致每次彈窗關(guān)閉后,其組件都會(huì)被銷毀,下次打開就需要重新渲染。
- 你在頁(yè)面中是無(wú)法拿到這個(gè)組件的實(shí)例的,因?yàn)槊看尾伙@示的時(shí)候它都被銷毀了。
- 由于我一個(gè)組件同時(shí)干了創(chuàng)建和編輯的事情,而我又無(wú)法獲取它的實(shí)例,于是上面的邏輯bug就很難處理。比如,我點(diǎn)開創(chuàng)建的時(shí)候,里面是空的。再點(diǎn)擊一個(gè)編輯,加載了數(shù)據(jù)。關(guān)閉后再點(diǎn)開同一個(gè)編輯,就是空的。為什么呢?因?yàn)閣atch沒有被觸發(fā),而組件又被銷毀了,所以它理所當(dāng)然的變成了初始的樣子。而將這個(gè)設(shè)置為false后,一切都o(jì)k了。
綜上所述,該屬性的使用場(chǎng)景需要仔細(xì)考慮。
input的點(diǎn)擊事件
很神奇的是,你會(huì)發(fā)現(xiàn),在element ui的文檔中,input是沒有點(diǎn)擊事件的。但是,其實(shí)這個(gè)事件我們用的并不少。于是找了下怎么加點(diǎn)擊事件,結(jié)果說(shuō),你可以使用原生的click事件就可以了,代碼大概如下:
<el-input v-on:click.native='clickSelectNode' ></el-input>
component 動(dòng)態(tài)切換
在我的一個(gè)功能點(diǎn)上,我需要某個(gè)位置的組件根據(jù)不同的數(shù)據(jù)進(jìn)行不同的展示。由于我數(shù)據(jù)都是按照產(chǎn)品型號(hào)進(jìn)行劃分的,所以不同的型號(hào)我做成了不同的組件。它們?cè)趧?dòng)態(tài)組件component標(biāo)簽上進(jìn)行切換。但是這種切換其實(shí)并不是說(shuō)換一個(gè)屬性就完了這么簡(jiǎn)單。為什么這么說(shuō)呢?首先,假設(shè),我有三個(gè)型號(hào),每個(gè)型號(hào)有三十條數(shù)據(jù)。當(dāng)我點(diǎn)開型號(hào)一的時(shí)候,它會(huì)觸發(fā)mounted事件。但是,當(dāng)我再點(diǎn)開另一條型號(hào)一的數(shù)據(jù)時(shí),這個(gè)事件就不會(huì)再被觸發(fā)了,vue把它存起來(lái)了。而在組件的生命周期中,我并沒有找到準(zhǔn)確得符合這個(gè)時(shí)機(jī)的事件。這就是它惡心的地方。
開始,我嘗試了一種簡(jiǎn)單粗暴的解決方法,設(shè)置component的ref,然后換了組件后直接通過(guò)ref調(diào)用里面一個(gè)我約定了的方法。結(jié)果就是,我得等下次才能夠調(diào)用到上次的ref。我沒有找到確切的原因,猜測(cè)是因?yàn)槲以O(shè)置的是組件的名稱,創(chuàng)建是異步的,所以我拿到的實(shí)際還是上一次的實(shí)例。
后來(lái),我又查到了直接實(shí)例化組件。其實(shí)我成功了,但是,我只是成功調(diào)用到了方法,它里面注入的組件,組件里的數(shù)據(jù)都不存在,也就是它沒有經(jīng)過(guò)完善的生命周期。
最后,我回到了原始的處理思路。設(shè)置一個(gè)props傳參,watch它,結(jié)合mounted事件,覆蓋了所有的變化時(shí)機(jī)。
element ui 表格行單擊和行內(nèi)按鈕重疊
鑒于我寫的是個(gè)管理系統(tǒng),表格就變成了交互的重點(diǎn)之一。為了減少需要使用的按鈕,我將查看記錄詳情的操作變成了單擊某行。但是,行間也有操作按鈕。這個(gè)時(shí)候點(diǎn)擊這些操作的按鈕就變成了它們既執(zhí)行自己的事件,也會(huì)執(zhí)行行單擊事件。這就有些蛋疼了。不過(guò)這件事是由于js的冒泡導(dǎo)致的。知道原因就好說(shuō)了。但是吧,要說(shuō)純js,好說(shuō),這使用vue和element ui封裝過(guò)的,怎么處理就有些討厭了。不過(guò)查了下,還是有簡(jiǎn)單的方式的。在操作按鈕那里套個(gè)div,注明組織點(diǎn)擊事件傳遞即可,代碼如下:
<el-table-column
label="操作">
<template slot-scope="scope">
<div @click.stop>
<el-button v-on:click="modifyLampSpecification(scope.row.sensorId)">編輯</el-button>
<el-button v-on:click="deleteLampSpecification(scope.row.sensorId)">刪除</el-button>
</div>
</template>
</el-table-column>
這里只展示了一個(gè)column的代碼,不過(guò)應(yīng)該足夠看明白了。
element ui timepicker
其實(shí)這個(gè)組件使用起來(lái)相當(dāng)簡(jiǎn)單。讓我非常滿意的是很靈活。之前我用layui的時(shí)候,選擇時(shí)間,是無(wú)法配置只選擇小時(shí)、分鐘的,必須連秒一起選擇。而這里,通過(guò)配置format就可以控制只有小時(shí)和分鐘兩個(gè)選擇,棒棒噠。另外,值得一說(shuō)的就是,設(shè)置它的值默認(rèn)是js的時(shí)間對(duì)象,如果先用字符串,需要你設(shè)置value-format才可以。