This article will analyze the Virtual Dom used in Vue 2.5.3 version. updataChildren is the core of the Diff algorithm, so this article conducts a graphic analysis of updataChildren. Let me share with you the Diff algorithm of Vue 2.5 through this article. Friends who need it can refer to it
DOM is "inherently slow", so all major front-end frameworks provide ways to optimize DOM operations. In Angular It is dirty value checking. React first proposed Virtual Dom, and Vue2.0 also added Virtual Dom, which is similar to React.
This article will analyze the Virtual Dom used in Vue 2.5.3 version.
updataChildren is the core of the Diff algorithm, so this article conducts a graphic analysis of updataChildren.
1.VNode object
A VNode instance contains the following attributes. This part of the code is in src/core/vdom/vnode.js Inside
export default class VNode { tag: string | void; data: VNodeData | void; children: ?Array<VNode>; text: string | void; elm: Node | void; ns: string | void; context: Component | void; // rendered in this component's scope key: string | number | void; componentOptions: VNodeComponentOptions | void; componentInstance: Component | void; // component instance parent: VNode | void; // component placeholder node // strictly internal raw: boolean; // contains raw HTML? (server only) isStatic: boolean; // hoisted static node isRootInsert: boolean; // necessary for enter transition check isComment: boolean; // empty comment placeholder? isCloned: boolean; // is a cloned node? isOnce: boolean; // is a v-once node? asyncFactory: Function | void; // async component factory function asyncMeta: Object | void; isAsyncPlaceholder: boolean; ssrContext: Object | void; functionalContext: Component | void; // real context vm for functional nodes functionalOptions: ?ComponentOptions; // for SSR caching functionalScopeId: ?string; // functioanl scope id support
tag: The tag name of the current node
data: The data object of the current node. For specific fields, please refer to vue source code types The definition of VNodeData in /vnode.d.ts
children: array type, containing the child nodes of the current node
text: current The text of the node. Generally, text nodes or comment nodes will have this attribute
elm: the real dom node corresponding to the current virtual node
ns : Node namespace
context: Compilation scope
functionalContext: Functional component scope
-
key: The key attribute of the node is used as the identifier of the node, which is beneficial to the optimization of the patch.
componentOptions: The option information used when creating a component instance
child: the component instance corresponding to the current node
parent: the placeholder node of the component
raw: raw html
isStatic: The identifier of the static node
isRootInsert: Whether to insert as the root node, is
isComment: Whether the current node is a comment node
isCloned: Whether the current node is a clone node
isOnce: Whether the current node has v- once directive
2. Classification of VNode
VNode can be understood as a base class of VueVirtual Dom, through VNode The VNnode instances generated by the constructor can be of the following categories:
EmptyVNode: Annotation node without content
TextVNode: Text node
ElementVNode: Ordinary element node
ComponentVNode: Component node
CloneVNode: Clone node, which can be the above Any type of node, the only difference is that the isCloned attribute is true
3.Create-Element source code analysis
This part of the code is in src/core/vdom/create-element.js. I just paste the code and add my comments
export function createElement ( context: Component, tag: any, data: any, children: any, normalizationType: any, alwaysNormalize: boolean ): VNode { // 兼容不傳data的情況 if (Array.isArray(data) || isPrimitive(data)) { normalizationType = children children = data data = undefined } // 如果alwaysNormalize是true // 那么normalizationType應(yīng)該設(shè)置為常量ALWAYS_NORMALIZE的值 if (isTrue(alwaysNormalize)) { normalizationType = ALWAYS_NORMALIZE } // 調(diào)用_createElement創(chuàng)建虛擬節(jié)點 return _createElement(context, tag, data, children, normalizationType) } export function _createElement ( context: Component, tag?: string | Class<Component> | Function | Object, data?: VNodeData, children?: any, normalizationType?: number ): VNode { /** * 如果存在data.__ob__,說明data是被Observer觀察的數(shù)據(jù) * 不能用作虛擬節(jié)點的data * 需要拋出警告,并返回一個空節(jié)點 * * 被監(jiān)控的data不能被用作vnode渲染的數(shù)據(jù)的原因是: * data在vnode渲染過程中可能會被改變,這樣會觸發(fā)監(jiān)控,導致不符合預(yù)期的操作 */ if (isDef(data) && isDef((data: any).__ob__)) { process.env.NODE_ENV !== 'production' && warn( `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` + 'Always create fresh vnode data objects in each render!', context ) return createEmptyVNode() } // object syntax in v-bind if (isDef(data) && isDef(data.is)) { tag = data.is } if (!tag) { // 當組件的is屬性被設(shè)置為一個falsy的值 // Vue將不會知道要把這個組件渲染成什么 // 所以渲染一個空節(jié)點 // in case of component :is set to falsy value return createEmptyVNode() } // key為非原始值警告 // warn against non-primitive key if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.key) && !isPrimitive(data.key) ) { warn( 'Avoid using non-primitive value as key, ' + 'use string/number value instead.', context ) } // 作用域插槽 // support single function children as default scoped slot if (Array.isArray(children) && typeof children[0] === 'function' ) { data = data || {} data.scopedSlots = { default: children[0] } children.length = 0 } // 根據(jù)normalizationType的值,選擇不同的處理方法 if (normalizationType === ALWAYS_NORMALIZE) { children = normalizeChildren(children) } else if (normalizationType === SIMPLE_NORMALIZE) { children = simpleNormalizeChildren(children) } let vnode, ns // 如果標簽名是字符串類型 if (typeof tag === 'string') { let Ctor // 獲取標簽的命名空間 ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag) // 如果是保留標簽 if (config.isReservedTag(tag)) { // platform built-in elements // 就創(chuàng)建這樣一個vnode vnode = new VNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) // 如果不是保留字標簽,嘗試從vm的components上查找是否有這個標簽的定義 } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) { // component // 如果找到,就創(chuàng)建虛擬組件節(jié)點 vnode = createComponent(Ctor, data, context, children, tag) } else { // unknown or unlisted namespaced elements // check at runtime because it may get assigned a namespace when its // parent normalizes children // 兜底方案,創(chuàng)建一個正常的vnode vnode = new VNode( tag, data, children, undefined, undefined, context ) } } else { // 當tag不是字符串的時候,我們認為tag是組件的構(gòu)造類 // 所以直接創(chuàng)建 // direct component options / constructor vnode = createComponent(tag, data, context, children) } if (isDef(vnode)) { // 應(yīng)用命名空間 if (ns) applyNS(vnode, ns) return vnode } else { // 返回一個空節(jié)點 return createEmptyVNode() } } function applyNS (vnode, ns, force) { vnode.ns = ns if (vnode.tag === 'foreignObject') { // use default namespace inside foreignObject ns = undefined force = true } if (isDef(vnode.children)) { for (let i = 0, l = vnode.children.length; i < l; i++) { const child = vnode.children[i] if (isDef(child.tag) && (isUndef(child.ns) || isTrue(force))) { applyNS(child, ns, force) } } } }
4. Patch principle
The patch function is defined in src/core/vdom/patch.js. The patch logic is relatively simple, so no code is required.
The patch function receives 6 parameters:
oldVnode: old virtual node or old real dom node
vnode: new virtual node
hydrating: Whether to mix it with real dom
removeOnly: special flag, used for
parentElm: parent node
refElm: The new node will be inserted before refElm
The logic of patch is:
if vnode does not exist but oldVnode exists, indicating that the intention is to destroy the old node, then call invokeDestroyHook(oldVnode) to destroy it
If oldVnode does not exist but vnode exists, indicating that the intention is to create a new node, then call createElm to create a new node
else When vnode and oldVnode both exist
If oldVnode and vnode are the same node, call patchVnode to patch
When vnode and oldVnode When they are not the same node, if oldVnode is a real dom node or hydrating is set to true, you need to use the hydrate function to map the virtual dom and the real dom, then set oldVnode to the corresponding virtual dom, find the parent node of oldVnode.elm, according to vnode creates a real dom node and inserts it into the parent node at the position of oldVnode.elm
The logic of patchVnode is:
1. If oldVnode is completely consistent with vnode, Then you don’t need to do anything
2. If oldVnode and vnode are both static nodes and have the same key, and when vnode is a clone node or a node controlled by the v-once command, you only need to change oldVnode. Both elm and oldVnode.child are copied to vnode, and no other operations are required
3. Otherwise, if vnode is not a text node or comment node
If oldVnode and vnode has child nodes, and the child nodes of the two parties are not completely consistent, execute updateChildren
If only oldVnode has child nodes, then delete these nodes
If only vnode has child nodes, then create these child nodes
If neither oldVnode nor vnode has child nodes, but oldVnode is a text node or comment node, Just set the text of vnode.elm to the empty string
4.如果vnode是文本節(jié)點或注釋節(jié)點,但是vnode.text != oldVnode.text時,只需要更新vnode.elm的文本內(nèi)容就可以
代碼如下:
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { // 如果新舊節(jié)點一致,什么都不做 if (oldVnode === vnode) { return } // 讓vnode.el引用到現(xiàn)在的真實dom,當el修改時,vnode.el會同步變化 const elm = vnode.elm = oldVnode.elm // 異步占位符 if (isTrue(oldVnode.isAsyncPlaceholder)) { if (isDef(vnode.asyncFactory.resolved)) { hydrate(oldVnode.elm, vnode, insertedVnodeQueue) } else { vnode.isAsyncPlaceholder = true } return } // reuse element for static trees. // note we only do this if the vnode is cloned - // if the new node is not cloned it means the render functions have been // reset by the hot-reload-api and we need to do a proper re-render. // 如果新舊都是靜態(tài)節(jié)點,并且具有相同的key // 當vnode是克隆節(jié)點或是v-once指令控制的節(jié)點時,只需要把oldVnode.elm和oldVnode.child都復制到vnode上 // 也不用再有其他操作 if (isTrue(vnode.isStatic) && isTrue(oldVnode.isStatic) && vnode.key === oldVnode.key && (isTrue(vnode.isCloned) || isTrue(vnode.isOnce)) ) { vnode.componentInstance = oldVnode.componentInstance return } let i const data = vnode.data if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { i(oldVnode, vnode) } const oldCh = oldVnode.children const ch = vnode.children if (isDef(data) && isPatchable(vnode)) { for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode) if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode) } // 如果vnode不是文本節(jié)點或者注釋節(jié)點 if (isUndef(vnode.text)) { // 并且都有子節(jié)點 if (isDef(oldCh) && isDef(ch)) { // 并且子節(jié)點不完全一致,則調(diào)用updateChildren if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly) // 如果只有新的vnode有子節(jié)點 } else if (isDef(ch)) { if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '') // elm已經(jīng)引用了老的dom節(jié)點,在老的dom節(jié)點上添加子節(jié)點 addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue) // 如果新vnode沒有子節(jié)點,而vnode有子節(jié)點,直接刪除老的oldCh } else if (isDef(oldCh)) { removeVnodes(elm, oldCh, 0, oldCh.length - 1) // 如果老節(jié)點是文本節(jié)點 } else if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, '') } // 如果新vnode和老vnode是文本節(jié)點或注釋節(jié)點 // 但是vnode.text != oldVnode.text時,只需要更新vnode.elm的文本內(nèi)容就可以 } else if (oldVnode.text !== vnode.text) { nodeOps.setTextContent(elm, vnode.text) } if (isDef(data)) { if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode) } }
5.updataChildren原理
updateChildren的邏輯是:
分別獲取oldVnode和vnode的firstChild、lastChild,賦值給oldStartVnode、oldEndVnode、newStartVnode、newEndVnode
如果oldStartVnode和newStartVnode是同一節(jié)點,調(diào)用patchVnode進行patch,然后將oldStartVnode和newStartVnode都設(shè)置為下一個子節(jié)點,
如果oldEndVnode和newEndVnode是同一節(jié)點,調(diào)用patchVnode進行patch,然后將oldEndVnode和newEndVnode都設(shè)置為上一個子節(jié)點,重復上述流程
如果oldStartVnode和newEndVnode是同一節(jié)點,調(diào)用patchVnode進行patch,如果removeOnly是false,那么可以把oldStartVnode.elm移動到oldEndVnode.elm之后,然后把oldStartVnode設(shè)置為下一個節(jié)點,newEndVnode設(shè)置為上一個節(jié)點,重復上述流程
如果newStartVnode和oldEndVnode是同一節(jié)點,調(diào)用patchVnode進行patch,如果removeOnly是false,那么可以把oldEndVnode.elm移動到oldStartVnode.elm之前,然后把newStartVnode設(shè)置為下一個節(jié)點,oldEndVnode設(shè)置為上一個節(jié)點,重復上述流程
如果以上都不匹配,就嘗試在oldChildren中尋找跟newStartVnode具有相同key的節(jié)點,如果找不到相同key的節(jié)點,說明newStartVnode是一個新節(jié)點,就創(chuàng)建一個,然后把newStartVnode設(shè)置為下一個節(jié)點
如果上一步找到了跟newStartVnode相同key的節(jié)點,那么通過其他屬性的比較來判斷這2個節(jié)點是否是同一個節(jié)點,如果是,就調(diào)用patchVnode進行patch,如果removeOnly是false,就把newStartVnode.elm插入到oldStartVnode.elm之前,把newStartVnode設(shè)置為下一個節(jié)點,重復上述流程
如果在oldChildren中沒有尋找到newStartVnode的同一節(jié)點,那就創(chuàng)建一個新節(jié)點,把newStartVnode設(shè)置為下一個節(jié)點,重復上述流程
如果oldStartVnode跟oldEndVnode重合了,并且newStartVnode跟newEndVnode也重合了,這個循環(huán)就結(jié)束了
具體代碼如下:
function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartIdx = 0 // 舊頭索引 let newStartIdx = 0 // 新頭索引 let oldEndIdx = oldCh.length - 1 // 舊尾索引 let newEndIdx = newCh.length - 1 // 新尾索引 let oldStartVnode = oldCh[0] // oldVnode的第一個child let oldEndVnode = oldCh[oldEndIdx] // oldVnode的最后一個child let newStartVnode = newCh[0] // newVnode的第一個child let newEndVnode = newCh[newEndIdx] // newVnode的最后一個child let oldKeyToIdx, idxInOld, vnodeToMove, refElm // removeOnly is a special flag used only by <transition-group> // to ensure removed elements stay in correct relative positions // during leaving transitions const canMove = !removeOnly // 如果oldStartVnode和oldEndVnode重合,并且新的也都重合了,證明diff完了,循環(huán)結(jié)束 while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { // 如果oldVnode的第一個child不存在 if (isUndef(oldStartVnode)) { // oldStart索引右移 oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left // 如果oldVnode的最后一個child不存在 } else if (isUndef(oldEndVnode)) { // oldEnd索引左移 oldEndVnode = oldCh[--oldEndIdx] // oldStartVnode和newStartVnode是同一個節(jié)點 } else if (sameVnode(oldStartVnode, newStartVnode)) { // patch oldStartVnode和newStartVnode, 索引左移,繼續(xù)循環(huán) patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue) oldStartVnode = oldCh[++oldStartIdx] newStartVnode = newCh[++newStartIdx] // oldEndVnode和newEndVnode是同一個節(jié)點 } else if (sameVnode(oldEndVnode, newEndVnode)) { // patch oldEndVnode和newEndVnode,索引右移,繼續(xù)循環(huán) patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue) oldEndVnode = oldCh[--oldEndIdx] newEndVnode = newCh[--newEndIdx] // oldStartVnode和newEndVnode是同一個節(jié)點 } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right // patch oldStartVnode和newEndVnode patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue) // 如果removeOnly是false,則將oldStartVnode.eml移動到oldEndVnode.elm之后 canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)) // oldStart索引右移,newEnd索引左移 oldStartVnode = oldCh[++oldStartIdx] newEndVnode = newCh[--newEndIdx] // 如果oldEndVnode和newStartVnode是同一個節(jié)點 } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left // patch oldEndVnode和newStartVnode patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue) // 如果removeOnly是false,則將oldEndVnode.elm移動到oldStartVnode.elm之前 canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm) // oldEnd索引左移,newStart索引右移 oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] // 如果都不匹配 } else { if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) // 嘗試在oldChildren中尋找和newStartVnode的具有相同的key的Vnode idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx) // 如果未找到,說明newStartVnode是一個新的節(jié)點 if (isUndef(idxInOld)) { // New element // 創(chuàng)建一個新Vnode createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm) // 如果找到了和newStartVnodej具有相同的key的Vnode,叫vnodeToMove } else { vnodeToMove = oldCh[idxInOld] /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && !vnodeToMove) { warn( 'It seems there are duplicate keys that is causing an update error. ' + 'Make sure each v-for item has a unique key.' ) } // 比較兩個具有相同的key的新節(jié)點是否是同一個節(jié)點 //不設(shè)key,newCh和oldCh只會進行頭尾兩端的相互比較,設(shè)key后,除了頭尾兩端的比較外,還會從用key生成的對象oldKeyToIdx中查找匹配的節(jié)點,所以為節(jié)點設(shè)置key可以更高效的利用dom。 if (sameVnode(vnodeToMove, newStartVnode)) { // patch vnodeToMove和newStartVnode patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue) // 清除 oldCh[idxInOld] = undefined // 如果removeOnly是false,則將找到的和newStartVnodej具有相同的key的Vnode,叫vnodeToMove.elm // 移動到oldStartVnode.elm之前 canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm) // 如果key相同,但是節(jié)點不相同,則創(chuàng)建一個新的節(jié)點 } else { // same key but different element. treat as new element createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm) } } // 右移 newStartVnode = newCh[++newStartIdx] } }
6.具體的Diff分析
不設(shè)key,newCh和oldCh只會進行頭尾兩端的相互比較,設(shè)key后,除了頭尾兩端的比較外,還會從用key生成的對象oldKeyToIdx中查找匹配的節(jié)點,所以為節(jié)點設(shè)置key可以更高效的利用dom。
diff的遍歷過程中,只要是對dom進行的操作都調(diào)用api.insertBefore,api.insertBefore只是原生insertBefore的簡單封裝。
比較分為兩種,一種是有vnode.key的,一種是沒有的。但這兩種比較對真實dom的操作是一致的。
對于與sameVnode(oldStartVnode, newStartVnode)和sameVnode(oldEndVnode,newEndVnode)為true的情況,不需要對dom進行移動。
總結(jié)遍歷過程,有3種dom操作:上述圖中都有
1.當oldStartVnode,newEndVnode值得比較,說明oldStartVnode.el跑到oldEndVnode.el的后邊了。
2.當oldEndVnode,newStartVnode值得比較,oldEndVnode.el跑到了oldStartVnode.el的前邊,準確的說應(yīng)該是oldEndVnode.el需要移動到oldStartVnode.el的前邊”。
3.newCh中的節(jié)點oldCh里沒有, 將新節(jié)點插入到oldStartVnode.el的前邊
在結(jié)束時,分為兩種情況:
1.oldStartIdx > oldEndIdx,可以認為oldCh先遍歷完。當然也有可能newCh此時也正好完成了遍歷,統(tǒng)一都歸為此類。此時newStartIdx和newEndIdx之間的vnode是新增的,調(diào)用addVnodes,把他們?nèi)坎暹Mbefore的后邊,before很多時候是為null的。addVnodes調(diào)用的是insertBefore操作dom節(jié)點,我們看看insertBefore的文檔:parentElement.insertBefore(newElement, referenceElement)
如果referenceElement為null則newElement將被插入到子節(jié)點的末尾。如果newElement已經(jīng)在DOM樹中,newElement首先會從DOM樹中移除。所以before為null,newElement將被插入到子節(jié)點的末尾。
2.newStartIdx > newEndIdx, it can be considered that newCh is traversed first. At this time, the vnode between oldStartIdx and oldEndIdx no longer exists in the new child node. Call removeVnodes to delete them from the dom.
The above is what I compiled for everyone. I hope it will be helpful to everyone in the future.
Related articles:
Chrome Firefox comes with debugging tools (detailed tutorial)
About how Vue.js implements infinite scrolling loading
How to implement table filtering using Angular
How to implement a calculator using JavaScript
The above is the detailed content of How to use the Diff algorithm in Vue 2.5. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

You can add a function to the Vue button by binding the button in the HTML template to a method. Define the method and write function logic in the Vue instance.

Netflixusesacustomframeworkcalled"Gibbon"builtonReact,notReactorVuedirectly.1)TeamExperience:Choosebasedonfamiliarity.2)ProjectComplexity:Vueforsimplerprojects,Reactforcomplexones.3)CustomizationNeeds:Reactoffersmoreflexibility.4)Ecosystema

Netflix uses React as its front-end framework. 1) React's componentized development model and strong ecosystem are the main reasons why Netflix chose it. 2) Through componentization, Netflix splits complex interfaces into manageable chunks such as video players, recommendation lists and user comments. 3) React's virtual DOM and component life cycle optimizes rendering efficiency and user interaction management.

There are two ways to jump div elements in Vue: use Vue Router and add router-link component. Add the @click event listener and call this.$router.push() method to jump.

Netflix mainly uses React as the front-end framework, supplemented by Vue for specific functions. 1) React's componentization and virtual DOM improve the performance and development efficiency of Netflix applications. 2) Vue is used in Netflix's internal tools and small projects, and its flexibility and ease of use are key.

The methods to implement the jump of a tag in Vue include: using the a tag in the HTML template to specify the href attribute. Use the router-link component of Vue routing. Use this.$router.push() method in JavaScript. Parameters can be passed through the query parameter and routes are configured in the router options for dynamic jumps.

There are the following methods to implement component jump in Vue: use router-link and <router-view> components to perform hyperlink jump, and specify the :to attribute as the target path. Use the <router-view> component directly to display the currently routed rendered components. Use the router.push() and router.replace() methods for programmatic navigation. The former saves history and the latter replaces the current route without leaving records.

To develop a complete Python Web application, follow these steps: 1. Choose the appropriate framework, such as Django or Flask. 2. Integrate databases and use ORMs such as SQLAlchemy. 3. Design the front-end and use Vue or React. 4. Perform the test, use pytest or unittest. 5. Deploy applications, use Docker and platforms such as Heroku or AWS. Through these steps, powerful and efficient web applications can be built.
