목차
Prpps의 Diff 알고리즘
Vnode children的Diff算法
reorder算法
DiffChildren算法
웹 프론트엔드 JS 튜토리얼 가상 DOM을 구현하는 방법은 무엇입니까? (코드 예)

가상 DOM을 구현하는 방법은 무엇입니까? (코드 예)

Mar 14, 2019 am 11:44 AM
javascript react.js

이 글의 내용은 가상 DOM을 구현하는 방법에 관한 것입니다. (코드 샘플)에는 특정 참조 값이 있습니다. 도움이 필요한 친구가 참조할 수 있기를 바랍니다.

이 글에서는 virtual-dom의 소스 코드를 읽고 분석하여 Virtual DOM의 구조와 관련 Diff 알고리즘을 설명하므로, 독자가 전체 데이터 구조와 관련 Diff 알고리즘을 어느 정도 이해할 수 있습니다.

Virtual DOM의 Diff 알고리즘 결과가 실제 DOM에 어떻게 매핑되는지는 다음 블로그에서 공개하겠습니다.

이 글의 주요 내용은 다음과 같습니다:

Virtual DOM의 구조와 Virtual DOM의 Diff 알고리즘

참고: 이 Virtual DOM의 구현은 React Virtual DOM의 소스 코드가 아니라 virtual-DOM을 기반으로 합니다. 돔) 도서관. 둘은 원칙적으로 유사하며 이 라이브러리가 더 간단하고 이해하기 쉽습니다. 이 라이브러리와 비교하여 React는 Virtual DOM을 더욱 최적화하고 조정했으며, 이에 대해서는 후속 블로그에서 분석하겠습니다.

Virtual DOM의 구조

VirtualNode

Virtual DOM의 메타데이터 구조로 VirtualNode는 vnode/vnode.js 파일에 위치합니다. 내부 구조를 살펴보기 위해 선언 코드의 일부를 가로채겠습니다.

function VirtualNode(tagName, properties, children, key, namespace) {
    this.tagName = tagName
    this.properties = properties || noProperties //props对象,Object类型
    this.children = children || noChildren //子节点,Array类型
    this.key = key != null ? String(key) : undefined
    this.namespace = (typeof namespace === "string") ? namespace : null
    
    ...

    this.count = count + descendants
    this.hasWidgets = hasWidgets
    this.hasThunks = hasThunks
    this.hooks = hooks
    this.descendantHooks = descendantHooks
}

VirtualNode.prototype.version = version //VirtualNode版本号,isVnode()检测标志
VirtualNode.prototype.type = "VirtualNode" // VirtualNode类型,isVnode()检测标志
로그인 후 복사

위는 특정 태그 이름, 속성, 하위 노드 등을 포함하는 VirtualNode의 전체 구조입니다.

VText

VText는 HTML의 일반 텍스트에 해당하는 일반 텍스트 노드입니다. 따라서 이 속성에는 텍스트라는 하나의 필드만 있습니다.

function VirtualText(text) {
    this.text = String(text)
}

VirtualText.prototype.version = version
VirtualText.prototype.type = "VirtualText"
로그인 후 복사

VPatch

VPatch는 Virtual DOM에서 수행해야 하는 작업 레코드를 나타내는 데이터 구조입니다. 이는 vnode/vpatch.js 파일에 있습니다. 내부의 특정 코드를 살펴보겠습니다.

// 定义了操作的常量,如Props变化,增加节点等
VirtualPatch.NONE = 0
VirtualPatch.VTEXT = 1
VirtualPatch.VNODE = 2
VirtualPatch.WIDGET = 3
VirtualPatch.PROPS = 4
VirtualPatch.ORDER = 5
VirtualPatch.INSERT = 6
VirtualPatch.REMOVE = 7
VirtualPatch.THUNK = 8

module.exports = VirtualPatch

function VirtualPatch(type, vNode, patch) {
    this.type = Number(type) //操作类型
    this.vNode = vNode //需要操作的节点
    this.patch = patch //需要操作的内容
}

VirtualPatch.prototype.version = version
VirtualPatch.prototype.type = "VirtualPatch"
로그인 후 복사

상수는 VNode 노드의 작업을 정의합니다. 예를 들어, VTEXT는 VText 노드를 추가하는 것이고 PROPS는 현재 노드의 Props 속성을 변경하는 것입니다.

Virtual DOM의 Diff 알고리즘

이제 Virtual DOM의 세 가지 구조를 이해했으니 이제 Virtual DOM의 Diff 알고리즘을 살펴보겠습니다.

이 Diff 알고리즘은 Virtual DOM의 핵심 알고리즘입니다. 이 알고리즘은 초기 상태 A(VNode)와 최종 상태 B(VNode)를 입력함으로써 A에서 B로의 변경 단계(VPatch)를 얻을 수 있습니다. 획득된 일련의 단계를 기반으로 어떤 노드를 추가해야 하는지 알 수 있습니다. 삭제해야 할 노드와 속성이 변경된 노드. 이 Diff 알고리즘은 세 부분으로 나누어집니다:

VNode의 Diff 알고리즘 Props의 Diff 알고리즘 Vnode 어린이 Diff 알고리즘

이제 이러한 Diff 알고리즘을 하나씩 소개하겠습니다.

VNode의 Diff 알고리즘

이 알고리즘은 단일 VNode에 대한 비교 알고리즘입니다. 두 트리의 단일 노드를 비교하는 시나리오에서 사용됩니다. 구체적인 알고리즘은 다음과 같습니다. 소스 코드를 직접 읽고 싶지 않다면 다음을 참조할 수도 있습니다.

function walk(a, b, patch, index) {
    if (a === b) {
        return
    }

    var apply = patch[index]
    var applyClear = false

    if (isThunk(a) || isThunk(b)) {
        thunks(a, b, patch, index)
    } else if (b == null) {

        // If a is a widget we will add a remove patch for it
        // Otherwise any child widgets/hooks must be destroyed.
        // This prevents adding two remove patches for a widget.
        if (!isWidget(a)) {
            clearState(a, patch, index)
            apply = patch[index]
        }

        apply = appendPatch(apply, new VPatch(VPatch.REMOVE, a, b))
    } else if (isVNode(b)) {
        if (isVNode(a)) {
            if (a.tagName === b.tagName &&
                a.namespace === b.namespace &&
                a.key === b.key) {
                var propsPatch = diffProps(a.properties, b.properties)
                if (propsPatch) {
                    apply = appendPatch(apply,
                        new VPatch(VPatch.PROPS, a, propsPatch))
                }
                apply = diffChildren(a, b, patch, apply, index)
            } else {
                apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))
                applyClear = true
            }
        } else {
            apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))
            applyClear = true
        }
    } else if (isVText(b)) {
        if (!isVText(a)) {
            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))
            applyClear = true
        } else if (a.text !== b.text) {
            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))
        }
    } else if (isWidget(b)) {
        if (!isWidget(a)) {
            applyClear = true
        }

        apply = appendPatch(apply, new VPatch(VPatch.WIDGET, a, b))
    }

    if (apply) {
        patch[index] = apply
    }

    if (applyClear) {
        clearState(a, patch, index)
    }
}
로그인 후 복사

코드의 구체적인 논리는 다음과 같습니다.

두 개의 VNode a와 b가 합동이면 수정하지 않고 직접 반환하는 것으로 간주됩니다.

그 중 하나가 썽크라면 썽크 비교 방법인 썽크를 사용하세요.

a가 위젯이고 b가 비어 있는 경우 a와 해당 하위 노드의 제거 작업이 패치에 재귀적으로 추가됩니다.

b가 VNode인 경우,

a도 VNode인 경우 tagName, 네임스페이스, 키를 비교하고, 동일한 경우 두 VNode의 Prop을 비교하고(아래에 언급된 diffProps 알고리즘 사용) 동시에 두 VNode의 하위 항목(아래 언급된 diffChildren 알고리즘 사용)이 다른 경우 노드 b의 삽입 작업을 패치에 직접 추가하고 마크 위치를 true로 설정합니다.

a가 VNode가 아닌 경우 노드 b의 삽입 작업을 패치에 직접 추가하고 마크 위치를 true로 설정합니다.

b가 VText인 경우 a의 유형이 VText인지 확인하세요. 그렇지 않은 경우 VText 작업을 패치에 추가하고 플래그가 true이고 텍스트 내용이 다른 경우 패치에 VText 작업을 추가하세요. . 가운데.

b가 위젯인 경우 a의 유형이 위젯인지 확인하세요. 그렇다면 플래그를 true로 설정하세요. 유형에 관계없이 패치에 위젯 작업을 추가합니다.

플래그 비트를 확인하고, 플래그가 true이면 a와 해당 하위 노드의 제거 작업을 패치에 재귀적으로 추가하세요.

이것은 단일 VNode 노드의 diff 알고리즘의 전체 프로세스입니다. 이 알고리즘은 전체 diff 알고리즘의 입구이며 두 트리의 비교는 이 알고리즘에서 시작됩니다.

Prpps의 Diff 알고리즘

단일 VNode 노드의 diff 알고리즘을 읽은 후 위에서 언급한 diffProps 알고리즘을 살펴보겠습니다. diffProps算法。

该算法是针对于两个比较的VNode节点的Props比较算法。它是用于两个场景中key值和标签名都相同的情况。具体算法如下,如果不想直接阅读源码的同学也可以翻到下面,会有相关代码流程说明供大家参考:

function diffProps(a, b) {
    var diff

    for (var aKey in a) {
        if (!(aKey in b)) {
            diff = diff || {}
            diff[aKey] = undefined
        }

        var aValue = a[aKey]
        var bValue = b[aKey]

        if (aValue === bValue) {
            continue
        } else if (isObject(aValue) && isObject(bValue)) {
            if (getPrototype(bValue) !== getPrototype(aValue)) {
                diff = diff || {}
                diff[aKey] = bValue
            } else if (isHook(bValue)) {
                 diff = diff || {}
                 diff[aKey] = bValue
            } else {
                var objectDiff = diffProps(aValue, bValue)
                if (objectDiff) {
                    diff = diff || {}
                    diff[aKey] = objectDiff
                }
            }
        } else {
            diff = diff || {}
            diff[aKey] = bValue
        }
    }

    for (var bKey in b) {
        if (!(bKey in a)) {
            diff = diff || {}
            diff[bKey] = b[bKey]
        }
    }

    return diff
}
로그인 후 복사

代码具体逻辑如下:

  1. 遍历a

    이 알고리즘은 비교된 두 VNode 노드에 대한 Props 비교 알고리즘입니다. 두 시나리오 모두에서 키 값과 태그 이름이 동일한 경우에 사용됩니다. 구체적인 알고리즘은 다음과 같습니다. 소스 코드를 직접 읽고 싶지 않다면 하단으로 이동하여 참고할 수 있는 관련 코드 흐름 지침이 있습니다. 🎜
    function reorder(aChildren, bChildren) {
        // O(M) time, O(M) memory
        var bChildIndex = keyIndex(bChildren)
        var bKeys = bChildIndex.keys  // have "key" prop,object
        var bFree = bChildIndex.free  //don't have "key" prop,array
    
        // all children of b don't have "key"
        if (bFree.length === bChildren.length) {
            return {
                children: bChildren,
                moves: null
            }
        }
    
        // O(N) time, O(N) memory
        var aChildIndex = keyIndex(aChildren)
        var aKeys = aChildIndex.keys
        var aFree = aChildIndex.free
    
        // all children of a don't have "key"
        if (aFree.length === aChildren.length) {
            return {
                children: bChildren,
                moves: null
            }
        }
    
        // O(MAX(N, M)) memory
        var newChildren = []
    
        var freeIndex = 0
        var freeCount = bFree.length
        var deletedItems = 0
    
        // Iterate through a and match a node in b
        // O(N) time,
        for (var i = 0 ; i < aChildren.length; i++) {
            var aItem = aChildren[i]
            var itemIndex
    
            if (aItem.key) {
                if (bKeys.hasOwnProperty(aItem.key)) {
                    // Match up the old keys
                    itemIndex = bKeys[aItem.key]
                    newChildren.push(bChildren[itemIndex])
    
                } else {
                    // Remove old keyed items
                    itemIndex = i - deletedItems++
                    newChildren.push(null)
                }
            } else {
                // Match the item in a with the next free item in b
                if (freeIndex < freeCount) {
                    itemIndex = bFree[freeIndex++]
                    newChildren.push(bChildren[itemIndex])
                } else {
                    // There are no free items in b to match with
                    // the free items in a, so the extra free nodes
                    // are deleted.
                    itemIndex = i - deletedItems++
                    newChildren.push(null)
                }
            }
        }
    
        var lastFreeIndex = freeIndex >= bFree.length ?
            bChildren.length :
            bFree[freeIndex]
    
        // Iterate through b and append any new keys
        // O(M) time
        for (var j = 0; j < bChildren.length; j++) {
            var newItem = bChildren[j]
    
            if (newItem.key) {
                if (!aKeys.hasOwnProperty(newItem.key)) {
                    // Add any new keyed items
                    // We are adding new items to the end and then sorting them
                    // in place. In future we should insert new items in place.
                    newChildren.push(newItem)
                }
            } else if (j >= lastFreeIndex) {
                // Add any leftover non-keyed items
                newChildren.push(newItem)
            }
        }
    
        var simulate = newChildren.slice()
        var simulateIndex = 0
        var removes = []
        var inserts = []
        var simulateItem
    
        for (var k = 0; k < bChildren.length;) {
            var wantedItem = bChildren[k]
            simulateItem = simulate[simulateIndex]
    
            // remove items
            while (simulateItem === null && simulate.length) {
                removes.push(remove(simulate, simulateIndex, null))
                simulateItem = simulate[simulateIndex]
            }
    
            if (!simulateItem || simulateItem.key !== wantedItem.key) {
                // if we need a key in this position...
                if (wantedItem.key) {
                    if (simulateItem && simulateItem.key) {
                        // if an insert doesn't put this key in place, it needs to move
                        if (bKeys[simulateItem.key] !== k + 1) {
                            removes.push(remove(simulate, simulateIndex, simulateItem.key))
                            simulateItem = simulate[simulateIndex]
                            // if the remove didn't put the wanted item in place, we need to insert it
                            if (!simulateItem || simulateItem.key !== wantedItem.key) {
                                inserts.push({key: wantedItem.key, to: k})
                            }
                            // items are matching, so skip ahead
                            else {
                                simulateIndex++
                            }
                        }
                        else {
                            inserts.push({key: wantedItem.key, to: k})
                        }
                    }
                    else {
                        inserts.push({key: wantedItem.key, to: k})
                    }
                    k++
                }
                // a key in simulate has no matching wanted key, remove it
                else if (simulateItem && simulateItem.key) {
                    removes.push(remove(simulate, simulateIndex, simulateItem.key))
                }
            }
            else {
                simulateIndex++
                k++
            }
        }
    
        // remove all the remaining nodes from simulate
        while(simulateIndex < simulate.length) {
            simulateItem = simulate[simulateIndex]
            removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key))
        }
    
        // If the only moves we have are deletes then we can just
        // let the delete patch remove these items.
        if (removes.length === deletedItems && !inserts.length) {
            return {
                children: newChildren,
                moves: null
            }
        }
    
        return {
            children: newChildren,
            moves: {
                removes: removes,
                inserts: inserts
            }
        }
    }
    로그인 후 복사
    🎜코드의 구체적인 논리는 다음과 같습니다. 다음: 🎜
    1. 🎜a객체를 트래버스합니다. 🎜
      1. 当key值不存在于b,则将此值存储下来,value赋值为undefined
      2. 当此key对应的两个属性都相同时,继续终止此次循环,进行下次循环。
      3. 当key值对应的value不同且key值对应的两个value都是对象时,判断Prototype值,如果不同则记录key对应的b对象的值;如果b对应的value是hook的话,记录b的值。
      4. 上面条件判断都不同且都是对象时,则继续比较key值对应的两个对象(递归)。
      5. 当有一个不是对象时,直接将b对应的value进行记录。
    2. 遍历b对象,将所有a对象中不存在的key值对应的对象都记录下来。

    整个算法的大致流程如下,因为比较简单,就不画相关流程图了。如果逻辑有些绕的话,可以配合代码食用,效果更佳。

    Vnode children的Diff算法

    下面让我们来看下最后一个算法,就是关于两个VNode节点的children属性的diffChildren算法。这个个diff算法分为两个部分,第一部分是将变化后的结果b的children进行顺序调整的算法,保证能够快速的和a的children进行比较;第二部分就是将a的children与重新排序调整后的b的children进行比较,得到相关的patch。下面,让我们一个一个算法来看。

    reorder算法

    该算法的作用是将b节点的children数组进行调整重新排序,让ab两个children之间的diff算法更加节约时间。具体代码如下:

    function reorder(aChildren, bChildren) {
        // O(M) time, O(M) memory
        var bChildIndex = keyIndex(bChildren)
        var bKeys = bChildIndex.keys  // have "key" prop,object
        var bFree = bChildIndex.free  //don't have "key" prop,array
    
        // all children of b don't have "key"
        if (bFree.length === bChildren.length) {
            return {
                children: bChildren,
                moves: null
            }
        }
    
        // O(N) time, O(N) memory
        var aChildIndex = keyIndex(aChildren)
        var aKeys = aChildIndex.keys
        var aFree = aChildIndex.free
    
        // all children of a don't have "key"
        if (aFree.length === aChildren.length) {
            return {
                children: bChildren,
                moves: null
            }
        }
    
        // O(MAX(N, M)) memory
        var newChildren = []
    
        var freeIndex = 0
        var freeCount = bFree.length
        var deletedItems = 0
    
        // Iterate through a and match a node in b
        // O(N) time,
        for (var i = 0 ; i < aChildren.length; i++) {
            var aItem = aChildren[i]
            var itemIndex
    
            if (aItem.key) {
                if (bKeys.hasOwnProperty(aItem.key)) {
                    // Match up the old keys
                    itemIndex = bKeys[aItem.key]
                    newChildren.push(bChildren[itemIndex])
    
                } else {
                    // Remove old keyed items
                    itemIndex = i - deletedItems++
                    newChildren.push(null)
                }
            } else {
                // Match the item in a with the next free item in b
                if (freeIndex < freeCount) {
                    itemIndex = bFree[freeIndex++]
                    newChildren.push(bChildren[itemIndex])
                } else {
                    // There are no free items in b to match with
                    // the free items in a, so the extra free nodes
                    // are deleted.
                    itemIndex = i - deletedItems++
                    newChildren.push(null)
                }
            }
        }
    
        var lastFreeIndex = freeIndex >= bFree.length ?
            bChildren.length :
            bFree[freeIndex]
    
        // Iterate through b and append any new keys
        // O(M) time
        for (var j = 0; j < bChildren.length; j++) {
            var newItem = bChildren[j]
    
            if (newItem.key) {
                if (!aKeys.hasOwnProperty(newItem.key)) {
                    // Add any new keyed items
                    // We are adding new items to the end and then sorting them
                    // in place. In future we should insert new items in place.
                    newChildren.push(newItem)
                }
            } else if (j >= lastFreeIndex) {
                // Add any leftover non-keyed items
                newChildren.push(newItem)
            }
        }
    
        var simulate = newChildren.slice()
        var simulateIndex = 0
        var removes = []
        var inserts = []
        var simulateItem
    
        for (var k = 0; k < bChildren.length;) {
            var wantedItem = bChildren[k]
            simulateItem = simulate[simulateIndex]
    
            // remove items
            while (simulateItem === null && simulate.length) {
                removes.push(remove(simulate, simulateIndex, null))
                simulateItem = simulate[simulateIndex]
            }
    
            if (!simulateItem || simulateItem.key !== wantedItem.key) {
                // if we need a key in this position...
                if (wantedItem.key) {
                    if (simulateItem && simulateItem.key) {
                        // if an insert doesn&#39;t put this key in place, it needs to move
                        if (bKeys[simulateItem.key] !== k + 1) {
                            removes.push(remove(simulate, simulateIndex, simulateItem.key))
                            simulateItem = simulate[simulateIndex]
                            // if the remove didn&#39;t put the wanted item in place, we need to insert it
                            if (!simulateItem || simulateItem.key !== wantedItem.key) {
                                inserts.push({key: wantedItem.key, to: k})
                            }
                            // items are matching, so skip ahead
                            else {
                                simulateIndex++
                            }
                        }
                        else {
                            inserts.push({key: wantedItem.key, to: k})
                        }
                    }
                    else {
                        inserts.push({key: wantedItem.key, to: k})
                    }
                    k++
                }
                // a key in simulate has no matching wanted key, remove it
                else if (simulateItem && simulateItem.key) {
                    removes.push(remove(simulate, simulateIndex, simulateItem.key))
                }
            }
            else {
                simulateIndex++
                k++
            }
        }
    
        // remove all the remaining nodes from simulate
        while(simulateIndex < simulate.length) {
            simulateItem = simulate[simulateIndex]
            removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key))
        }
    
        // If the only moves we have are deletes then we can just
        // let the delete patch remove these items.
        if (removes.length === deletedItems && !inserts.length) {
            return {
                children: newChildren,
                moves: null
            }
        }
    
        return {
            children: newChildren,
            moves: {
                removes: removes,
                inserts: inserts
            }
        }
    }
    로그인 후 복사

    下面,我们来简单介绍下这个排序算法:

    1. 检查ab中的children是否拥有key字段,如果没有,直接返回b的children数组。
    2. 如果存在,初始化一个数组newChildren,遍历a的children元素。

      1. 如果aChildren存在key值,则去bChildren中找对应key值,如果bChildren存在则放入新数组中,不存在则放入一个null值。
      2. 如果aChildren不存在key值,则从bChildren中不存在key值的第一个元素开始取,放入新数组中。
    3. 遍历bChildren,将所有achildren中没有的key值对应的value或者没有key,并且没有放入新数组的子节点放入新数组中。
    4. 将bChildren和新数组逐个比较,得到从新数组转换到bChildren数组的move操作patch(即remove+insert)。
    5. 返回新数组和move操作列表。

    通过上面这个排序算法,我们可以得到一个新的b的children数组。在使用这个数组来进行比较厚,我们可以将两个children数组之间比较的时间复杂度从o(n^2)转换成o(n)。具体的方法和效果我们可以看下面的DiffChildren算法。

    DiffChildren算法

    function diffChildren(a, b, patch, apply, index) {
        var aChildren = a.children
        var orderedSet = reorder(aChildren, b.children)
        var bChildren = orderedSet.children
    
        var aLen = aChildren.length
        var bLen = bChildren.length
        var len = aLen > bLen ? aLen : bLen
    
        for (var i = 0; i < len; i++) {
            var leftNode = aChildren[i]
            var rightNode = bChildren[i]
            index += 1
    
            if (!leftNode) {
                if (rightNode) {
                    // Excess nodes in b need to be added
                    apply = appendPatch(apply,
                        new VPatch(VPatch.INSERT, null, rightNode))
                }
            } else {
                walk(leftNode, rightNode, patch, index)
            }
    
            if (isVNode(leftNode) && leftNode.count) {
                index += leftNode.count
            }
        }
    
        if (orderedSet.moves) {
            // Reorder nodes last
            apply = appendPatch(apply, new VPatch(
                VPatch.ORDER,
                a,
                orderedSet.moves
            ))
        }
    
        return apply
    }
    로그인 후 복사

    通过上面的重新排序算法整理了以后,两个children比较就只需在相同下标的情况下比较了,即aChildren的第N个元素和bChildren的第N个元素进行比较。然后较长的那个元素做insert操作(bChildren)或者remove操作(aChildren)即可。最后,我们将move操作再增加到patch中,就能够抵消我们在reorder时对整个数组的操作。这样只需要一次便利就得到了最终的patch值。

    总结

    整个Virtual DOM的diff算法设计的非常精巧,通过三个不同的分部算法来实现了VNode、Props和Children的diff算法,将整个Virtual DOM的的diff操作分成了三类。同时三个算法又互相递归调用,对两个Virtual DOM数做了一次(伪)深度优先的递归比较。

위 내용은 가상 DOM을 구현하는 방법은 무엇입니까? (코드 예)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 Dec 17, 2023 pm 02:54 PM

WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

WebSocket 및 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 WebSocket 및 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 Dec 17, 2023 pm 05:30 PM

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 Dec 17, 2023 pm 12:09 PM

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 소개: 인터넷의 대중화와 기술의 발전으로 점점 더 많은 레스토랑에서 온라인 주문 서비스를 제공하기 시작했습니다. 실시간 온라인 주문 시스템을 구현하기 위해 JavaScript 및 WebSocket 기술을 사용할 수 있습니다. WebSocket은 TCP 프로토콜을 기반으로 하는 전이중 통신 프로토콜로 클라이언트와 서버 간의 실시간 양방향 통신을 실현할 수 있습니다. 실시간 온라인 주문 시스템에서는 사용자가 요리를 선택하고 주문을 하면

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 Dec 17, 2023 am 09:39 AM

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 오늘날의 디지털 시대에는 점점 더 많은 기업과 서비스에서 온라인 예약 기능을 제공해야 합니다. 효율적인 실시간 온라인 예약 시스템을 구현하는 것이 중요합니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. WebSocket이란 무엇입니까? WebSocket은 단일 TCP 연결의 전이중 방식입니다.

JavaScript와 WebSocket: 효율적인 실시간 일기예보 시스템 구축 JavaScript와 WebSocket: 효율적인 실시간 일기예보 시스템 구축 Dec 17, 2023 pm 05:13 PM

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 Jan 05, 2024 pm 06:08 PM

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

자바스크립트에서 insertBefore를 사용하는 방법 자바스크립트에서 insertBefore를 사용하는 방법 Nov 24, 2023 am 11:56 AM

사용법: JavaScript에서 insertBefore() 메서드는 DOM 트리에 새 노드를 삽입하는 데 사용됩니다. 이 방법에는 삽입할 새 노드와 참조 노드(즉, 새 노드가 삽입될 노드)라는 두 가지 매개 변수가 필요합니다.

JavaScript 및 WebSocket: 효율적인 실시간 이미지 처리 시스템 구축 JavaScript 및 WebSocket: 효율적인 실시간 이미지 처리 시스템 구축 Dec 17, 2023 am 08:41 AM

JavaScript는 웹 개발에 널리 사용되는 프로그래밍 언어인 반면 WebSocket은 실시간 통신에 사용되는 네트워크 프로토콜입니다. 두 가지의 강력한 기능을 결합하면 효율적인 실시간 영상 처리 시스템을 만들 수 있습니다. 이 기사에서는 JavaScript와 WebSocket을 사용하여 이 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 첫째, 실시간 영상처리 시스템의 요구사항과 목표를 명확히 할 필요가 있다. 실시간 이미지 데이터를 수집할 수 있는 카메라 장치가 있다고 가정해 보겠습니다.

See all articles