vue2之響應(yīng)式雙向綁定,在對(duì)象或數(shù)組新增屬性頁(yè)面無(wú)響應(yīng)的情況
vue2響應(yīng)式雙向綁定,在對(duì)象或數(shù)組新增屬性頁(yè)面無(wú)響應(yīng)
問(wèn)題描述
vue2 中可以將數(shù)據(jù)與視圖進(jìn)行綁定,修改 data 對(duì)象的屬性值將引起對(duì)應(yīng)視圖的改變。
Vue2的數(shù)據(jù)視圖綁定是通過(guò)JS特性這一語(yǔ)法實(shí)現(xiàn),其使用中存在數(shù)據(jù)屬性丟失的這 一 bug,主要針對(duì) 對(duì)象或數(shù)組 屬性丟失。
使用 antv 的 a-select (下拉框)組件,使用 v-mode 綁定對(duì)象 的值,但是 對(duì)象之前是沒(méi)有賦值的,是一個(gè) 空對(duì)象 ,這就導(dǎo)致了 頁(yè)面視圖不刷新 ,但是 屬性值有變化 ,這可以說(shuō)是 vue2 的一個(gè)缺陷。
// 空對(duì)象 queryParam:?{ ??????},
解決方法
方法一 (設(shè)置初始值)
給 綁定的對(duì)象 賦初始值 null 或者 ' '
queryParam: { approveStatus : null , },
如果是普通 輸入框 input ,這樣的方法沒(méi)什么問(wèn)題,但是 我的頁(yè)面使用 a-select 下拉框,默認(rèn)有提示消息( placeholder),如果 賦初始值 為空 ,下拉框會(huì)填充空白內(nèi)容 ,覆蓋之前的提示消息 ,這樣的頁(yè)面 會(huì)比較不美觀且不友好
<a-select v-model="queryParam.approveStatus" placeholder="審核狀態(tài)" :allowClear="true"> <a-select-option v-for="status of videoApproveStatus" :key="status.id"> {{ status.text }} </a-select-option> </a-select> <a-select v-model="queryParam.videotypeid" placeholder="視頻類型" :allowClear="true"> <a-select-option v-for="d in videotype" :key="d.myid"> {{ d.name }} </a-select-option> </a-select>
雖然這種方法可以解決綁定對(duì)象屬性丟失問(wèn)題,但是如果給 每個(gè)屬性設(shè)置 初始值 為 null,那么所有的下拉框都是空白 , 可以看到 下拉框 賦初始值 為 null ,頁(yè)面的效果非常不友好 ,沒(méi)有提示信息 ,所有這種方法不推薦
方法二 (創(chuàng)建一個(gè)新的對(duì)象,替換原對(duì)象)
這種方法可以用于需要添加多個(gè)新屬性,再把原對(duì)象與新屬性合并到新對(duì)象中
Object.assign(目標(biāo)對(duì)象,原對(duì)象, 新屬性)
this.queryParam = Object.assign({}, this.queryParam, obj)
我這里是 利用 a-select 下拉框 自帶的 下拉框 改變方法 ,@change ,該方法有兩個(gè)參數(shù) value 和 option ,value 代表你改變的值 ,option (我也不太明白 ,反正里面東西很多),然后 我利用 這個(gè) @change 方法 和 Object.assign(目標(biāo)對(duì)象,原對(duì)象, 新屬性), 解決了狀態(tài)丟失問(wèn)題 ,大家可以 參考下面的代碼 ,根據(jù)自己的實(shí)際情況進(jìn)行調(diào)整 (每個(gè)人的情況都不一樣)
<a-select @change="handleChange" v-model="queryParam.approveStatus" placeholder="審核狀態(tài)" :allowClear="true"> <a-select-option v-for="status of videoApproveStatus" :key="status.id"> {{ status.text }} </a-select-option> </a-select>
handleChange(value,option) { if(option) { // 解決雙向綁定狀態(tài)丟失 this.queryParam = Object.assign({}, this.queryParam, option.context.queryParam) } else { return } },
vue2實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)
JS中的對(duì)象屬性
JS的對(duì)象有兩種屬性進(jìn)行描述 分別是數(shù)據(jù)屬性和訪問(wèn)器屬性。
數(shù)據(jù)屬性有四個(gè)值:
[[Configurable]]
表示是否可以刪除定義,是否可以修改屬性,默認(rèn)為true[[Enumberable]]
是否可以迭代[[Writable]]
是否可以被修改[[Value]]
對(duì)象具體的值
而訪問(wèn)器屬性也有四個(gè)值:
[[Configurable]]
表示是否可以刪除定義,是否可以修改屬性,默認(rèn)為true[[Enumberable]]
是否可以迭代[[Get]]
獲取函數(shù),讀取屬性值時(shí)使用[[Set]]
設(shè)置函數(shù),寫入屬性時(shí)調(diào)用
那么如何實(shí)現(xiàn)數(shù)據(jù)響應(yīng)呢?
利用Object.defineProperty()進(jìn)行數(shù)據(jù)劫持
實(shí)現(xiàn)響應(yīng)式的前提是可以捕捉到到數(shù)據(jù)的更改,獲取數(shù)據(jù)同理,這就需要利用JS對(duì)象的訪問(wèn)器屬性,而更改這些屬性 就要用到JS中的一個(gè)方法 Object.defineProperty() 上述的屬性在更改時(shí),哪怕更改一個(gè)屬性,所有屬性都會(huì)變?yōu)槟J(rèn)值。
具體使用方法如下:
let obj = {name:'Ton', age: 19} _value = obj.name Object.defineProperty(obj, name, { ? ? get(){ ? ? ? ? return _value ? ? }, ? ? set(newValue){ ? ? ? ?_value = newValue ? ? } })
方法的參數(shù)分別是 Object.defineProperty(對(duì)象名, 屬性名, {執(zhí)行器})
而get 函數(shù)在讀取該對(duì)象屬性時(shí)調(diào)用,返回的值為讀取的值; set 函數(shù)會(huì)在設(shè)置新值時(shí)調(diào)用 傳入的newValue為新值。
在Vue中 會(huì)用data一個(gè)對(duì)象包裹所有的值,因此可以用遍歷的方法給每個(gè)屬性加上該方法。
將該邏輯封裝到一個(gè)函數(shù)中:
let data = { ? ? name: 'Ton', ? ? age: 19, ? ? salary: '10k' } Object.keys(data).forEach( key => {? ? ? observe(data, key, data[key]) }) //形成閉包 內(nèi)部的變量不會(huì)消失 function observe(obj, key, value){ ? ? Object.defineProperty(obj, key, { ? ? ? ? get(){ ? ? ? ? ? ? return value ? ? ? ? }, ? ? ? ? set(newValue){ ? ? ? ? ? ? value = newValue ? ? ? ? } ? ? }) }
這樣data中的所有變量都會(huì)被綁定,但如果嵌套對(duì)象或數(shù)組,內(nèi)部的對(duì)象不會(huì)被檢測(cè)到。(可以用vue提供的 $set和 $delate 處理或 使用內(nèi)部重寫的數(shù)組方法)
與標(biāo)簽聯(lián)動(dòng)
與標(biāo)簽聯(lián)動(dòng)的方法有許多,最簡(jiǎn)單的是使用id 來(lái)綁定,而Vue中是使用指令的方式。
過(guò)程主要分兩步:
- 獲取dom
- 將數(shù)據(jù)放上去
... <div id="app"> ? ? <div v-test="name" class="box"></div> ? ? <div v-test="age" class="box"></div> </div> ... function compile(){ ? ? let app = document.getElementById('app') ? ? //獲取所有的子節(jié)點(diǎn) 值為3的是text節(jié)點(diǎn) 1為子標(biāo)簽節(jié)點(diǎn) ? ? app.childNodes.forEach( node => { ? ? ? ? if(node.nodeType === 1){ ? ? ? ? ? ? //遍歷該節(jié)點(diǎn)的屬性 找出 v-text? ? ? ? ? ? ? //node.attributes是個(gè)類數(shù)組對(duì)象, 先轉(zhuǎn)化為數(shù)組 ? ? ? ? ? ? Array.from(node.attributes).forEach( key => { ? ? ? ? ? ? ? ? //解構(gòu)對(duì)象 nodeName 找到屬性 ? ? ? ? ? ? ? ? let { nodeName, nodeValue } = key? ? ? ? ? ? ? ? ? ?//如果存在 則修改值(關(guān)鍵步驟) ? ? ? ? ? ? ? ? if(nodeName === 'v-test'){ ? ? ? ? ? ? ? ? ?? ?node.innerText = data[nodeValue] ? ? ? ? ? ? ? ??? ?} ? ? ? ? ? ? }) ? ? ? ? } ? ? }) } compile() ...
此時(shí)可以獲取節(jié)點(diǎn),并賦值數(shù)據(jù),與數(shù)據(jù)劫持聯(lián)動(dòng),最終結(jié)果如下:
let data = { name: 'Ton', age: 19} //劫持?jǐn)?shù)據(jù) Object.keys(data).forEach(key => { ? ? observe(data, key, data[key]) }) //形成閉包 內(nèi)部的變量不會(huì)消失 function observe(obj, key, value) { ? ? Object.defineProperty(obj, key, { ? ? ? ? get() { ? ? ? ? ? ? return value? ? ? ? ? }, ? ? ? ? set(newValue) { ? ? ? ? ? ? value = newValue ? ? ? ? ? ? compile() ? ? ? ? } ? ? }) } //獲取元素 將數(shù)據(jù)放入 function compile(){ ? ? let app = document.getElementById('app') ? ? //獲取所有的子節(jié)點(diǎn) 包括很多節(jié)點(diǎn) 3 為text 節(jié)點(diǎn) 1為子標(biāo)簽節(jié)點(diǎn) ? ? app.childNodes.forEach( node => { ? ? ? ? if(node.nodeType === 1){ ? ? ? ? ? ? //遍歷該節(jié)點(diǎn)的屬性 找出 v-text? ? ? ? ? ? ? //node.attributes是個(gè)類數(shù)組對(duì)象, 先轉(zhuǎn)化為數(shù)組 ? ? ? ? ? ? let result = Array.from(node.attributes).filter( key => { ? ? ? ? ? ? ? ? //結(jié)構(gòu)屬性的 nodeName 找到屬性 ? ? ? ? ? ? ? ? let { nodeName } = key? ? ? ? ? ? ? ? ? return nodeName === 'v-test' ? ? ? ? ? ? }) ? ? ? ? ? ? if(result){ ? ? ? ? ? ? ? ? node.innerText = data[result[0].nodeValue] ? ? ? ? ? ? } ? ? ? ? } ? ? }) } compile()
每次修改也會(huì)引起DOM元素的修改,實(shí)現(xiàn)響應(yīng)式。
v-model的實(shí)現(xiàn)
同v-test(v-on) 的不同,v-model要實(shí)現(xiàn)雙向綁定,即 input框輸入的值也需要傳回data。實(shí)現(xiàn)的邏輯前面是相同的,都需要獲取元素,但需要新增將input輸入框的內(nèi)容,傳回。
let data = { name: 'Ton', age: 19} //劫持?jǐn)?shù)據(jù) Object.keys(data).forEach(key => { ? ? observe(data, key, data[key]) }) //形成閉包 內(nèi)部的變量不會(huì)消失 function observe(obj, key, value) { ? ? Object.defineProperty(obj, key, { ? ? ? ? get() { ? ? ? ? ? ? return value? ? ? ? ? }, ? ? ? ? set(newValue) { ? ? ? ? ? ? value = newValue ? ? ? ? ? ? compile() ? ? ? ? } ? ? }) } //獲取元素 將數(shù)據(jù)放入 function compile(){ ? ? let app = document.getElementById('app') ? ? //獲取所有的子節(jié)點(diǎn) 包括很多節(jié)點(diǎn) 3 為text 節(jié)點(diǎn) 1為子標(biāo)簽節(jié)點(diǎn) ? ? app.childNodes.forEach( node => { ? ? ? ? if(node.nodeType === 1){ ? ? ? ? ? ? //遍歷該節(jié)點(diǎn)的屬性 找出 v-text? ? ? ? ? ? ? //node.attributes是個(gè)類數(shù)組對(duì)象, 先轉(zhuǎn)化為數(shù)組 ? ? ? ? ? ? let result = Array.from(node.attributes).filter( key => { ? ? ? ? ? ? ? ? //結(jié)構(gòu)屬性的 nodeName 找到屬性 ? ? ? ? ? ? ? ? let { nodeName } = key? ? ? ? ? ? ? ? ? return nodeName === 'v-model' ? ? ? ? ? ? }) ? ? ? ? ? ? if(result){ ? ? ? ? ? ? ? ? node.value = data[result[0].nodeValue] ? ? ? ? ? ? ? ? addEventLisener('input', e => { ?? ??? ??? ??? ??? ??? ?data[result[0].nodevalue] = e.target.value? ?? ??? ??? ??? ??? ?} ? ? ? ? ? ? ? ? ) ? ? ? ? ? ? } ? ? ? ? } ? ? }) } compile()
但目前的方法并不完美,需要添加一個(gè)防抖函數(shù)
let data = { name: 'Ton', age: 19} //劫持?jǐn)?shù)據(jù) Object.keys(data).forEach(key => { ? ? observe(data, key, data[key]) }) //形成閉包 內(nèi)部的變量不會(huì)消失 function observe(obj, key, value) { ? ? Object.defineProperty(obj, key, { ? ? ? ? get() { ? ? ? ? ? ? return value? ? ? ? ? }, ? ? ? ? set(newValue) { ? ? ? ? ? ? value = newValue ? ? ? ? ? ? compile() ? ? ? ? } ? ? }) } //獲取元素 將數(shù)據(jù)放入 function compile(){ ? ? let app = document.getElementById('app') ? ? //獲取所有的子節(jié)點(diǎn) 包括很多節(jié)點(diǎn) 3 為text 節(jié)點(diǎn) 1為子標(biāo)簽節(jié)點(diǎn) ? ? app.childNodes.forEach( node => { ? ? ? ? if(node.nodeType === 1){ ? ? ? ? ? ? //遍歷該節(jié)點(diǎn)的屬性 找出 v-text? ? ? ? ? ? ? //node.attributes是個(gè)類數(shù)組對(duì)象, 先轉(zhuǎn)化為數(shù)組 ? ? ? ? ? ? let result = Array.from(node.attributes).filter( key => { ? ? ? ? ? ? ? ? //結(jié)構(gòu)屬性的 nodeName 找到屬性 ? ? ? ? ? ? ? ? let { nodeName } = key? ? ? ? ? ? ? ? ? return nodeName === 'v-model' ? ? ? ? ? ? }) ? ? ? ? ? ? if(result){ ? ? ? ? ? ? ? ? node.value = data[result[0].nodeValue] ? ? ? ? ? ? ? ? addEventLisener('input', debounce(handel, result[0].nodeValue) ? ? ? ? ? ? } ? ? ? ? } ? ? }) ?? ?function debounce(fn, key, timer = 1000){ ?? ??? ?let t = null ?? ??? ?return function(){ ?? ??? ??? ?if(t) { clearTimeout(t) } ?? ??? ??? ?t= setTimeOut( _ => { ?? ??? ??? ??? ?t = null ?? ??? ??? ??? ?fn.call(this, key, arguments) ?? ??? ??? ?},timer) ?? ??? ?} ?? ?} ?? ?function handel(key, event){ ?? ??? ?data[key] = event.target.value ?? ?} } compile()
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue限制文本輸入框只允許輸入字母、數(shù)字、禁止輸入特殊字符
這篇文章主要介紹了vue限制文本輸入框只允許輸入字母、數(shù)字、不允許輸入特殊字符,通過(guò)監(jiān)聽(tīng)表單輸入的內(nèi)容,使用方法的缺陷,本文通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友參考下吧2023-10-10elementUI實(shí)現(xiàn)級(jí)聯(lián)選擇器
這篇文章主要為大家詳細(xì)介紹了elementUI實(shí)現(xiàn)級(jí)聯(lián)選擇器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11詳解vue2和vue3如何定義響應(yīng)式數(shù)據(jù)
這篇文章主要是來(lái)和大家一起討論一下vue2和vue3是如何定義響應(yīng)式數(shù)據(jù)的,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2023-11-11vue實(shí)現(xiàn)一個(gè)滾動(dòng)條樣式
滾動(dòng)條能夠給用戶帶來(lái)極好的體驗(yàn)效果,今天通過(guò)本文給大家分享vue實(shí)現(xiàn)一個(gè)滾動(dòng)條樣式,代碼簡(jiǎn)單易懂,需要的朋友參考下吧2021-07-07基于vue實(shí)現(xiàn)分頁(yè)組件的示例代碼
分頁(yè)組件是一種用戶界面元素,用于在長(zhǎng)列表或數(shù)據(jù)集中分割內(nèi)容,分頁(yè)組件是每個(gè)開(kāi)發(fā)人員必須掌握的一個(gè)組件,廣泛應(yīng)用在各個(gè)場(chǎng)所,用以用戶方便閱讀等,本文就給大家介紹一下如何基于vue寫出的分頁(yè)組件,需要的朋友可以參考下2023-08-08解決vue中el-date-picker?type=daterange日期不回顯的問(wèn)題
這篇文章主要介紹了解決vue中el-date-picker?type=daterange日期不回顯的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10vue實(shí)現(xiàn)點(diǎn)擊復(fù)制到粘貼板
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)點(diǎn)擊復(fù)制到粘貼板,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08vue3無(wú)config文件夾打包后頁(yè)面空白問(wèn)題及解決
這篇文章主要介紹了vue3無(wú)config文件夾打包后頁(yè)面空白問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05