Vue中的 DOM與Diff詳情
DOM Diff
Vue創(chuàng)建視圖分為倆種情況:
- 首次渲染,會用組件
template轉(zhuǎn)換成的真實DOM來替換應(yīng)用中的根元素 - 當(dāng)數(shù)據(jù)更新后,視圖重新渲染,此時并不會重新通過組件
template對應(yīng)的虛擬節(jié)點來創(chuàng)建真實DOM,而是會用老的虛擬節(jié)點和新的虛擬節(jié)點進行比對,根據(jù)比對結(jié)果來更新DOM
第二種情況就是Vue中經(jīng)常談到的DOM Diff,接下來我們將詳細介紹新老節(jié)點的比對過程。
整體思路
老的虛擬節(jié)點和新的虛擬節(jié)點是倆棵樹,會對倆棵樹每層中的虛擬節(jié)點進行比對操作:

在每一層進行對比時,會分別為老節(jié)點和新節(jié)點設(shè)置頭尾指針:

整體的孩子節(jié)點比對思路如下:
- 在老的虛擬節(jié)點和新的虛擬節(jié)點的頭尾指針之間都有元素時進行遍歷
- 對以下情況進行優(yōu)化
- 老節(jié)點的頭指針和新節(jié)點的頭指針相同
- 老節(jié)點的尾指針和新節(jié)點的尾指針相同
- 老節(jié)點的頭指針和新節(jié)點的尾指針相同
- 老節(jié)點的尾指針和新節(jié)點的頭指針相同
- 亂序排列時,要用新節(jié)點的頭節(jié)點到老節(jié)點中查找,如果能找到,對其復(fù)用并移動到相應(yīng)的位置。如果沒有找到,將其插入到真實節(jié)點中
- 遍歷完成后,將新節(jié)點頭指針和尾指針之間的元素插入到真實節(jié)點中,老節(jié)點頭指針和尾指針之間的元素刪除
在我們渲染視圖之前,需要保存當(dāng)前渲染的虛擬節(jié)點。在下一次渲染視圖時,它就是老的虛擬節(jié)點,要和新的虛擬節(jié)點進行對比:
// src/lifecycle.js
Vue.prototype._update = function (vNode) {
const vm = this;
const preVNode = vm._vNode;
vm._vNode = vNode;
if (!preVNode) { // 首次渲染,沒有前一次的虛擬節(jié)點
vm.$el = patch(vm.$el, vNode);
} else { // vm._vNode中存儲了前一次的虛擬節(jié)點,進行dom diff
patch(preVNode, vNode);
}
};下面我們實現(xiàn)patch方法中的邏輯
處理簡單情況
在patch方法中,首先會判斷oldVNode是否為真實DOM。如果不是,會進行DOM diff。
如果新的虛擬節(jié)點和老的虛擬節(jié)點標(biāo)簽不一樣,直接用新的虛擬節(jié)點創(chuàng)建真實節(jié)點,然后替換老的真實節(jié)點即可:
const vm1 = new Vue();
const html1 = `
<div id="app">
111
</div>
`;
// 將模板編譯為render函數(shù)
const render1 = compileToFunctions(html1);
const vNode1 = render1.call(vm1);
// 當(dāng)oldVNode為DOM元素時,會用新節(jié)點直接替換老節(jié)點
patch(document.getElementById('app'), vNode1);
const html2 = `
<span id="app">
333
</span>
`;
// 將新的模本編譯為render函數(shù)
const render2 = compileToFunctions(html2);
// 生成新的虛擬節(jié)點
const vNode2 = render2.call(vm1);
// 老節(jié)點和新節(jié)點進行對比
patch(vNode1, vNode2);上述代碼會直接通過新的虛擬節(jié)點創(chuàng)建的真實節(jié)點來替換老的真實節(jié)點,patch中的代碼如下:
export function patch (oldVNode, vNode) {
if (oldVNode.nodeType) { // 舊的節(jié)點為真實節(jié)點
// some code...
} else { // 新舊節(jié)點都為虛擬節(jié)點,要進行dom diff
if (oldVNode.tag !== vNode.tag) { // 標(biāo)簽不相同,直接用新節(jié)點替換老節(jié)點
const newEle = createElement(vNode);
replaceChild(newEle, oldVNode.el);
return newEle;
}
}
}如果老節(jié)點和新節(jié)點都是文本標(biāo)簽,那么直接用新節(jié)點的文本替換老節(jié)點即可:
// 老的模板
const html1 = `
<div id="app">
111
</div>
`;
// 新的模板
const html2 = `
<div id="app">
333
</div>
`;上例中的新的文本333會替換掉老的文本111,patch中的實現(xiàn)如下:
export function patch (oldVNode, vNode) {
if (oldVNode.nodeType) { // 舊的節(jié)點為真實節(jié)點
// some code ...
} else { // 新舊節(jié)點都為虛擬節(jié)點,要進行dom diff
if (oldVNode.tag !== vNode.tag) { // 不相等直接替換
// some code ...
}
if (!oldVNode.tag) { // 文本節(jié)點,tag相同,都為undefined
oldVNode.el.textContent = vNode.text;
return oldVNode.el;
}
}
}當(dāng)老節(jié)點和新節(jié)點的標(biāo)簽相同時,要更新標(biāo)簽對應(yīng)真實元素的屬性,更新規(guī)則如下:
- 用新節(jié)點中的屬性替換老節(jié)點中的屬性
- 刪除老節(jié)點中多余的屬性
function updateProperties (vNode, oldProps = {}) { // 老節(jié)點和新節(jié)點的屬性
const { el, props } = vNode;
// 用新節(jié)點替換老節(jié)點中的屬性
for (const key in props) { // 為真實DOM設(shè)置新節(jié)點的所有屬性
if (props.hasOwnProperty(key)) {
const value = props[key];
if (key === 'style') {
for (const styleKey in value) {
if (value.hasOwnProperty(styleKey)) {
el.style[styleKey] = value[styleKey];
}
}
} else {
el.setAttribute(key, value);
}
}
}
// 如果老節(jié)點中有,而新節(jié)點中沒有,需要將其刪除
for (const key in oldProps) {
if (oldProps.hasOwnProperty(key) && !props.hasOwnProperty(key)) {
el.removeAttribute(key);
}
}
const style = oldProps.style || {};
const newStyle = props.style || {};
// 刪除老節(jié)點中多余的樣式
for (const key in style) {
if (!newStyle.hasOwnProperty(key) && style.hasOwnProperty(key)) {
el.style[key] = '';
}
}
}在比對完當(dāng)前節(jié)點后,要繼續(xù)比對孩子節(jié)點。孩子節(jié)點可能有以下情況:
- 老節(jié)點孩子為空,新節(jié)點有孩子:將新節(jié)點的每一個孩子節(jié)點創(chuàng)建為真實節(jié)點,插入到老節(jié)點對應(yīng)的真實父節(jié)點中
- 老節(jié)點有孩子,新節(jié)點孩子為空:將老節(jié)點的父節(jié)點的孩子節(jié)點清空
- 老節(jié)點和新節(jié)點都有孩子: 采用雙指針進行對比
patch中對應(yīng)的代碼如下:
export function patch (oldVNode, vNode) {
if (oldVNode.nodeType) { // 舊的節(jié)點為真實節(jié)點
// some code ...
} else { // 新舊節(jié)點都為虛擬節(jié)點,要進行dom diff
// 元素相同,需要比較子元素
const el = vNode.el = oldVNode.el;
// 更新屬性
updateProperties(vNode, oldVNode.props);
const oldChildren = oldVNode.children;
const newChildren = vNode.children;
// 老的有,新的沒有,將老的設(shè)置為空
// 老的沒有,新的有,為老節(jié)點插入多有的新節(jié)點
// 老的和新的都有,遍歷每一個進行比對
if (!oldChildren.length && newChildren.length) {
for (let i = 0; i < newChildren; i++) {
const child = newChildren[i];
el.appendChild(createElement(child));
}
return;
}
if (oldChildren.length && !newChildren.length) {
return el.innerHTML = '';
}
if (oldChildren.length && newChildren.length) {
updateChildren(oldChildren, newChildren, el);
}
return el;
}
}下面我們的邏輯便到了updateChildren中。
比對優(yōu)化
在對孩子節(jié)點的比對中,對一些常見的DOM操作通過雙指針進行了優(yōu)化:
- 列表尾部新增元素
- 列表頭部新增元素
- 列表開始元素移動到末尾
- 列表結(jié)尾元素移動到開頭
我們在代碼中先聲明需要的變量:
function updateChildren (oldChildren, newChildren, parent) {
let oldStartIndex = 0, // 老孩子的開始索引
oldStartVNode = oldChildren[0], // 老孩子的頭虛擬節(jié)點
oldEndIndex = oldChildren.length - 1, // 老孩子的尾索引
oldEndVNode = oldChildren[oldEndIndex]; // 老孩子的尾虛擬節(jié)點
let newStartIndex = 0, // 新孩子的開始索引
newStartVNode = newChildren[0], // 新孩子的頭虛擬節(jié)點
newEndIndex = newChildren.length - 1, // 新孩子的尾索引
newEndVNode = newChildren[newEndIndex]; // 新孩子的尾虛擬節(jié)點
}當(dāng)節(jié)點的tag和key都相同時,我們認為這倆個節(jié)點是同一個節(jié)點,可以進行復(fù)用:
function isSameVNode (oldVNode, newVNode) {
return oldVNode.key === newVNode.key && oldVNode.tag === newVNode.tag;
}下面我們分別來講解對應(yīng)的優(yōu)化邏輯
尾部新增元素
我們在老節(jié)點孩子的末尾新增一個元素作為新節(jié)點,其對應(yīng)的template如下:
const template1 = `
<div id="app">
<ul>
<li key="A" style="color:red">A</li>
<li key="B" style="color:yellow">B</li>
<li key="C" style="color:blue">C</li>
<li key="D" style="color:green">D</li>
</ul>
</div>
`;
const template2 = `
<div id="app">
<ul>
<li key="A" style="color:red">A</li>
<li key="B" style="color:yellow">B</li>
<li key="C" style="color:blue">C</li>
<li key="D" style="color:green">D</li>
<li key="E" style="color:purple">E</li>
</ul>
</div>
`;
此時oldChildren中的頭節(jié)點和newChildren中的頭節(jié)點相同,其比對邏輯如下:
- 繼續(xù)對
oldStartVNode和newStartVNode執(zhí)行patch方法,比對它們的標(biāo)簽、屬性、文本以及孩子節(jié)點 oldStartVNode和newStartVNode同時后移,繼續(xù)進行比對- 遍歷完老節(jié)點后,循環(huán)停止
- 將新節(jié)點中剩余的元素插入到老的虛擬節(jié)點的尾節(jié)點對應(yīng)的真實節(jié)點的下一個兄弟節(jié)點
oldEndVNode.el.nextSibling之前
畫圖演示下詳細的比對邏輯:

代碼如下:
function updateChildren (oldChildren, newChildren, parent) {
while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
if (isSameVNode(oldStartIndex, newStartIndex)) { // 頭和頭相等
// 1. 可能是文本節(jié)點:需要繼續(xù)比對文本節(jié)點
// 2. 可能是元素:先比對元素的屬性,然后再比對子節(jié)點
patch(oldStartVNode, newStartVNode);
oldStartVNode = oldChildren[++oldStartIndex];
newStartVNode = newChildren[++newStartIndex];
}
}
// 新節(jié)點中剩余元素插入到真實節(jié)點中
for (let i = newStartIndex; i <= newEndIndex; i++) {
const child = newChildren[i];
const refEle = oldChildren[oldEndIndex + 1] || null;
parent.insertBefore(createElement(child), refEle);
}
}頭部新增元素
老節(jié)點的孩子的頭部新增元素E,此時新老節(jié)點的template結(jié)構(gòu)如下:
const template1 = `
<div id="app">
<ul>
<li key="A" style="color:red">A</li>
<li key="B" style="color:yellow">B</li>
<li key="C" style="color:blue">C</li>
<li key="D" style="color:green">D</li>
</ul>
</div>
`;
const template2 = `
<div id="app">
<ul>
<li key="E" style="color:purple">E</li>
<li key="A" style="color:red">A</li>
<li key="B" style="color:yellow">B</li>
<li key="C" style="color:blue">C</li>
<li key="D" style="color:green">D</li>
</ul>
</div>
`;其比對邏輯和尾部新增類似,只不過此時是oldEndVNode和newEndVNode相同:
- 繼續(xù)通過
patch比對oldEndVNode和newEndVNode的標(biāo)簽、屬性、文本及孩子節(jié)點 - 此時要將
oldEndVNode和newEndVNode同時前移,繼續(xù)進行比對 - 遍歷完老節(jié)點后,循環(huán)停止
- 將新節(jié)點中剩余的元素插入到老的虛擬節(jié)點的尾節(jié)點對應(yīng)的真實節(jié)點的下一個兄弟節(jié)點
oldEndVNode.el.nextSibling之前
該邏輯的示意圖如下:

patch中新增代碼如下:
function updateChildren (oldChildren, newChildren, parent) {
while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
if (isSameVNode(oldStartIndex, newStartIndex)) { // 頭和頭相等
// some code ...
} else if (isSameVNode(oldEndVNode, newEndVNode)) { // 尾和尾相等
patch(oldEndVNode, newEndVNode);
oldEndVNode = oldChildren[--oldEndIndex];
newEndVNode = newChildren[--newEndIndex];
}
}
// some code ...
}開始元素移動到末尾
在新節(jié)點中,我們將開始元素A移動到末尾,對應(yīng)的template如下:
const template1 = `
<div id="app">
<ul>
<li key="A" style="color:red">A</li>
<li key="B" style="color:yellow">B</li>
<li key="C" style="color:blue">C</li>
<li key="D" style="color:green">D</li>
</ul>
</div>
`;
const template2 = `
<div id="app">
<ul>
<li key="B" style="color:yellow">B</li>
<li key="C" style="color:blue">C</li>
<li key="D" style="color:green">D</li>
<li key="A" style="color:red">A</li>
</ul>
</div>
`;此時oldStartVNode和newEndVNode相同:
- 繼續(xù)通過
patch比對oldStartVNode和newEndVNode的標(biāo)簽、屬性、文本及孩子節(jié)點 - 將
oldStartVNode對應(yīng)的真實節(jié)點插入到oldEndVNode對應(yīng)的真實節(jié)點之后 oldStartVNode后移,newEndVNode前移- 遍歷完新老節(jié)點后,循環(huán)停止,此時元素已經(jīng)移動到了正確的位置
用圖來演示該過程:

在patch方法中編寫代碼:
function updateChildren (oldChildren, newChildren, parent) {
while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
if (isSameVNode(oldStartIndex, newStartIndex)) { // 頭和頭相等
// some code ...
} else if (isSameVNode(oldEndVNode, newEndVNode)) { // 尾和尾相等
// some code ...
} else if (isSameVNode(oldStartVNode, newEndVNode)) { // 將開頭元素移動到了末尾:尾和頭相同
// 老節(jié)點:需要將頭節(jié)點對應(yīng)的元素移動到尾節(jié)點之后
parent.insertBefore(oldStartVNode, oldEndVNode.el.nextSibling);
patch(oldStartVNode, newEndVNode);
oldStartVNode = oldChildren[++oldStartIndex];
newEndVNode = newChildren[--newEndIndex];
}
}
}末尾元素移動到開頭
講解到這里,大家可以先停下閱讀的腳步,參考一下之前的邏輯,想想這里會如何進行比對?
在新節(jié)點中,我們將末尾元素D移動到開頭,對應(yīng)的template如下:
const template1 = `
<div id="app">
<ul>
<li key="A" style="color:red">A</li>
<li key="B" style="color:yellow">B</li>
<li key="C" style="color:blue">C</li>
<li key="D" style="color:green">D</li>
</ul>
</div>
`;
const template2 = `
<div id="app">
<ul>
<li key="D" style="color:green">D</li>
<li key="A" style="color:red">A</li>
<li key="B" style="color:yellow">B</li>
<li key="C" style="color:blue">C</li>
</ul>
</div>
`;此時oldEndVNode和newStartVNode相同:
- 繼續(xù)通過
patch比對oldEndVNode和newStartVNode的標(biāo)簽、屬性、文本及孩子節(jié)點 - 將
oldEndVNode對應(yīng)的真實節(jié)點插入到oldStartVNode對應(yīng)的真實節(jié)點之前 oldEndVNode前移,newStartVNode后移- 遍歷完新老節(jié)點后,循環(huán)停止,此時元素已經(jīng)移動到了正確的位置
畫圖來演示該過程:

在patch方法中添加處理該邏輯的代碼:
function updateChildren (oldChildren, newChildren, parent) {
// 更新子節(jié)點:
// 1. 一層一層進行比較,如果發(fā)現(xiàn)有一層不一樣,直接就會用新節(jié)點的子集來替換父節(jié)點的子集。
// 2. 比較時會采用雙指針,對常見的操作進行優(yōu)化
let oldStartIndex = 0,
oldStartVNode = oldChildren[0],
oldEndIndex = oldChildren.length - 1,
oldEndVNode = oldChildren[oldEndIndex];
let newStartIndex = 0,
newStartVNode = newChildren[0],
newEndIndex = newChildren.length - 1,
newEndVNode = newChildren[newEndIndex];
function makeMap () {
const map = {};
for (let i = 0; i < oldChildren.length; i++) {
const child = oldChildren[i];
child.key && (map[child.key] = i);
}
return map;
}
// 將老節(jié)點的key和索引進行映射,之后可以直接通過key找到索引,然后通過索引找到對應(yīng)的元素
// 這樣提前做好映射關(guān)系,可以將查找的時間復(fù)雜度降到O(1)
const map = makeMap();
while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
if (isSameVNode(oldStartIndex, newStartIndex)) { // 頭和頭相等
// some code ...
} else if (isSameVNode(oldEndVNode, newEndVNode)) { // 尾和尾相等
// some code ...
} else if (isSameVNode(oldStartVNode, newEndVNode)) { // 將開頭元素移動到了末尾:尾和頭相同
// some code ...
} else if (isSameVNode(oldEndVNode, newStartVNode)) { // 將結(jié)尾元素移動到了開頭
// 老節(jié)點: 將尾指針元素插入到頭指針之前
parent.insertBefore(oldEndVNode.el, oldStartVNode.el);
patch(oldEndVNode, newStartVNode);
oldEndVNode = oldChildren[--oldEndIndex];
newStartVNode = newChildren[++newStartIndex];
}
}
}到這里,patch方法中已經(jīng)完成了所有的優(yōu)化操作,下面我們來看下如何對比亂序的孩子節(jié)點
亂序比對
當(dāng)進行比對的元素不滿足優(yōu)化條件時,就要進行亂序?qū)Ρ取O旅媸莻z個亂序的template,看下它們的具體比對過程:
const html1 = `
<div id="app">
<ul>
<li key="D" style="color:red">D</li>
<li key="B" style="color:yellow">B</li>
<li key="Z" style="color:blue">Z</li>
<li key="F" style="color:green">F</li>
</ul>
</div>
`;
const html2 = `
<div id="app">
<ul>
<li key="E" style="color:green">E</li>
<li key="F" style="color:red">F</li>
<li key="D" style="color:yellow">D</li>
<li key="Q" style="color:blue">Q</li>
<li key="B" style="color:#252a34">B</li>
<li key="M" style="color:#fc5185">M</li>
</ul>
</div>
`;亂序比對的邏輯如下:
- 用新節(jié)點中的頭節(jié)點的
key在老節(jié)點中進行查找 - 如果在老節(jié)點中找到
key相同的元素,將對應(yīng)的真實節(jié)點移動到oldStartVNode.el(老虛擬頭節(jié)點對應(yīng)的真實節(jié)點)之前,并且將其對應(yīng)的虛擬節(jié)點設(shè)置為null,之后遇到null跳過即可,不再對其進行比對。 - 繼續(xù)通過
patch方法比對移動的節(jié)點和newStartVNode的標(biāo)簽、屬性、文本以及孩子節(jié)點 - 如果在老節(jié)點中沒有找到
key相同的元素,會為新節(jié)點的頭節(jié)點創(chuàng)建對應(yīng)的真實節(jié)點,將其插入到oldStartVNode.el之前 - 遍歷完成后,將老節(jié)點中頭指針和尾指針之間多余的元素刪除
畫圖演示下template中節(jié)點的比對過程:

在比對開始之前,我們要先遍歷老的孩子節(jié)點,生成key與索引對應(yīng)的map:
function updateChildren (oldChildren, newChildren, parent) {
function makeMap () {
const map = {};
for (let i = 0; i < oldChildren.length; i++) {
const child = oldChildren[i];
child.key && (map[child.key] = i);
}
return map;
}
const map = makeMap();
}有了map之后,便可以很方便的通過key來找到老孩子節(jié)點的索引,然后通過索引直接找到對應(yīng)的孩子節(jié)點,而不用再次進行遍歷操作。
接下來書寫處理亂序節(jié)點的代碼:
function updateChildren (oldChildren, newChildren, parent) {
while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
if (oldStartVNode == null) { // 老節(jié)點null時跳過該次循環(huán)
oldStartVNode = oldChildren[++oldStartIndex];
continue;
} else if (oldEndVNode == null) {
oldEndVNode = oldChildren[--oldEndIndex];
continue;
} else if (isSameVNode(oldStartIndex, newStartIndex)) { // 頭和頭相等
// some code ...
} else if (isSameVNode(oldStartVNode, newEndVNode)) { // 將開頭元素移動到了末尾:尾和頭相同
// some code ...
} else if (isSameVNode(oldEndVNode, newStartVNode)) { // 將結(jié)尾元素移動到了開頭
// some code ...
} else {
// 1. 用key來進行尋找,找到將其移動到頭節(jié)點之前
// 2. 沒有找到,將新頭節(jié)點插入到老頭節(jié)點之前
let moveIndex = map[newStartVNode.key]; // 通過key在map中找到相同元素的索引
if (moveIndex != null) { // 找到了
const moveVNode = oldChildren[moveIndex];
parent.insertBefore(moveVNode.el, oldStartVNode.el);
oldChildren[moveIndex] = null; // 將移動這項標(biāo)記為null,之后跳過,不再進行比對
// 還有對其屬性和子節(jié)點再進行比較
patch(moveVNode, newStartVNode);
} else {
// 為新頭節(jié)創(chuàng)建對應(yīng)的真實節(jié)點并插入到老節(jié)點的頭節(jié)點之前
parent.insertBefore(createElement(newStartVNode), oldStartVNode.el);
}
newStartVNode = newChildren[++newStartIndex];
}
}
// some code ...
// 老節(jié)點中從頭指針到尾指針為多余的元素,需要刪除掉
for (let i = oldStartIndex; i <= oldEndIndex; i++) {
const child = oldChildren[i];
parent.removeChild(child.el);
}
}當(dāng)新節(jié)點在老節(jié)點中存在時,我們會將找到的真實節(jié)點移動到相應(yīng)的位置。此時老節(jié)點中的該節(jié)點不需要再被遍歷,為了防止數(shù)組塌陷,便將該節(jié)點設(shè)置為null。之后再遍歷時,如果發(fā)現(xiàn)節(jié)點的值為null,便跳過本次循環(huán)。
現(xiàn)在我們便完成了Vue在數(shù)組更新時所有的DOM Diff邏輯。
寫在最后
文中主要書寫了patch方法的代碼,其主要功能如下:

希望小伙伴在讀完本文之后,可以對Vue的DOM Diff過程有更深的理解。
到此這篇關(guān)于Vue中的 DOM與Diff詳情的文章就介紹到這了,更多相關(guān)Vue DOM與Diff內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
elementui之el-table-column日期格式顯示方式
文章介紹了如何使用formatter屬性對表格某一列的內(nèi)容進行日期格式化,通過綁定日期格式化的方法實現(xiàn),展示了前端代碼的模板和方法,并給出了前端效果的展示2024-12-12
Vue3+TypeScript實現(xiàn)二維碼生成組件
在?Web?應(yīng)用中,生成二維碼是常見的需求,本文介紹如何用?Vue3?和?TypeScript?開發(fā)一個二維碼生成組件,支持生成圖片或?canvas?形式的二維碼,并提供豐富的配置選項,感興趣的小伙伴跟著小編一起來看看吧2024-04-04
vue中的數(shù)據(jù)格式化filters、formatter方式
這篇文章主要介紹了vue中的數(shù)據(jù)格式化filters、formatter方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
vue 設(shè)置proxyTable參數(shù)進行代理跨域
這篇文章主要介紹了vue 設(shè)置proxyTable參數(shù)進行代理跨域的相關(guān)資料,及代理跨域的概念原理,需要的朋友可以參考下2018-04-04

