찾다
웹 프론트엔드JS 튜토리얼작은 React Chpdating vDOM 구축

Build a Tiny React Chpdating vDOM

이 튜토리얼은 이 튜토리얼을 기반으로 하지만 JSX, TypeScript 및 구현하기 더 쉬운 접근 방식을 사용합니다. 내 GitHub 저장소에서 메모와 코드를 확인할 수 있습니다.

이제 반응성에 대해 이야기해 보겠습니다.

오래된 섬유를 저장하세요

새 광케이블과 비교할 수 있도록 기존 광케이블을 보관해야 합니다. 우리는 광섬유에 필드를 추가하여 이를 수행할 수 있습니다. 또한 나중에 유용하게 사용될 전용 필드가 필요합니다.

export interface Fiber {
  type: string
  props: VDomAttributes
  parent: Fiber | null
  child: Fiber | null
  sibling: Fiber | null
  dom: HTMLElement | Text | null
  alternate: Fiber | null
  committed: boolean
}

여기서 커밋된 상태를 설정합니다.

function commit() {
    function commitChildren(fiber: Fiber | null) {
        if(!fiber) {
            return
        }
        if(fiber.dom && fiber.parent?.dom) {
            fiber.parent.dom.appendChild(fiber.dom)
            fiber.committed = true
        }
        if(fiber.dom && fiber.parent && isFragment(fiber.parent.vDom) && !fiber.committed) {
            let parent = fiber.parent
            // find the first parent that is not a fragment
            while(parent && isFragment(parent.vDom)) {
                // the root element is guaranteed to not be a fragment has has a non-fragment parent
                parent = parent.parent!
            }
            parent.dom?.appendChild(fiber.dom!)
            fiber.committed = true
        }
        commitChildren(fiber.child)
        commitChildren(fiber.sibling)
    }
    commitChildren(wip)
    wipParent?.appendChild(wip!.dom!)
    wip!.committed = true
    wip = null
}

오래된 섬유나무도 살려야 합니다.

let oldFiber: Fiber | null = null

function commit() {
    function commitChildren(fiber: Fiber | null) {
        if(!fiber) {
            return
        }
        if(fiber.dom && fiber.parent?.dom) {
            fiber.parent.dom.appendChild(fiber.dom)
            fiber.committed = true
        }
        commitChildren(fiber.child)
        commitChildren(fiber.sibling)
    }
    commitChildren(wip)
    wipParent?.appendChild(wip!.dom!)
    wip!.committed = true
    oldFiber = wip
    wip = null
}

이제 반복 중에 기존 Fiber와 새 Fiber를 비교해야 합니다. 이것을 화해 과정이라고 합니다.

화해

오래된 광섬유와 새 광섬유를 비교해 볼 필요가 있습니다. 초기 작업에서는 먼저 오래된 Fiber를 투입합니다.

export function render(vDom: VDomNode, parent: HTMLElement) {
    wip = {
        parent: null,
        sibling: null,
        child: null,
        vDom: vDom,
        dom: null,
        committed: false,
        alternate: oldFiber,
    }
    wipParent = parent
    nextUnitOfWork = wip
}

그런 다음 새로운 섬유의 생성을 새로운 기능으로 분리합니다.

function reconcile(fiber: Fiber, isFragment: boolean) {
    if (isElement(fiber.vDom)) {
        const elements = fiber.vDom.children ?? []
        let index = 0
        let prevSibling = null

        while (index 



<p>단, 기존 광케이블을 새 광케이블에 장착해야 합니다.<br>
</p>

<pre class="brush:php;toolbar:false">function reconcile(fiber: Fiber, isFragment: boolean) {
    if (isElement(fiber.vDom)) {
        const elements = fiber.vDom.children ?? []
        let index = 0
        let prevSibling = null

        let currentOldFiber = fiber.alternate?.child ?? null
        while (index 



<p>이제 기존 Fiber를 새 Fiber에 장착했습니다. 하지만 재렌더링을 트리거할 항목이 없습니다. 지금은 버튼을 추가하여 수동으로 트리거합니다. 아직 상태가 없기 때문에 vDOM을 변경하기 위해 props를 사용합니다.<br>
</p>

<pre class="brush:php;toolbar:false">import { render } from "./runtime";
import { createElement, fragment, VDomAttributes, VDomNode } from "./v-dom";

type FuncComponent = (props: VDomAttributes, children: VDomNode[]) => JSX.Element

const App: FuncComponent = (props: VDomAttributes, __: VDomNode[]) => {
    return <div>
        
            <h1 id="H">H1</h1>
            <h2 id="props-example-toString">{props["example"]?.toString()}</h2>
            {
                props["show"] ? <p>show</p> : >
            }
            <h1 id="H">H1</h1>
        >
    </div>
}
const app = document.getElementById('app')

const renderButton = document.createElement('button')
renderButton.textContent = 'Render'
let cnt = 0
renderButton.addEventListener('click', () => {
    const vDom: VDomNode = App({
        "example": (new Date()).toString(),
        "show": cnt % 2 === 0
    }, []) as unknown as VDomNode
    cnt++
    render(vDom, app!)
})
document.body.appendChild(renderButton)

이제 renderButton을 클릭하면 렌더링된 결과가 한 번 반복됩니다. 현재의 모든 로직은 단순히 렌더링된 vDOM을 문서에 넣는 것뿐입니다.

Commit 기능에 console.log를 추가하면 Alternative Fiber가 출력되는 것을 볼 수 있습니다.

이제 기존 Fiber와 새 Fiber를 처리하는 방법을 정의하고 정보를 기반으로 DOM을 변경해야 합니다. 규칙은 다음과 같습니다.

새로운 섬유 각각에 대해

  • 오래된 Fiber가 있는 경우 기존 Fiber의 내용을 새 Fiber와 비교하고, 서로 다른 경우 이전 DOM 노드를 새 DOM 노드로 교체하거나 이전 DOM 노드를 새로운 DOM 노드. 두 개의 vDOM이 동일하다는 것은 해당 태그와 모든 속성이 동일하다는 것을 의미합니다. 그들의 아이들은 다를 수 있습니다.
  • 오래된 Fiber가 없으면 새 DOM 노드를 생성하여 상위 노드에 추가합니다.
  • 새 파이버의 경우 하위 또는 형제 자매가 없지만 이전 파이버에는 하위 또는 형제 자매가 있는 경우 기존 하위 또는 형제 자매를 재귀적으로 제거합니다.

좀 헷갈리시나요? 음, 코드를 보여드리겠습니다. 먼저 이전 DOM 생성을 삭제합니다. 그런 다음 위의 규칙을 적용하세요.

첫 번째 규칙, 오래된 섬유가 있으면 오래된 섬유와 새로운 섬유의 함량을 비교합니다. 서로 다른 경우 이전 DOM 노드를 새 DOM 노드로 바꾸거나 이전 DOM 노드를 새 DOM 노드에 복사합니다.

export function vDOMEquals(a: VDomNode, b: VDomNode): boolean {
    if (isString(a) && isString(b)) {
        return a === b
    } else if (isElement(a) && isElement(b)) {
        let ret = a.tag === b.tag && a.key === b.key
        if (!ret) return false
        if (a.props && b.props) {
            const aProps = a.props
            const bProps = b.props
            const aKeys = Object.keys(aProps)
            const bKeys = Object.keys(bProps)
            if (aKeys.length !== bKeys.length) return false
            for (let i = 0; i 



<p>그런 다음 작은 리팩토링을 했습니다.<br>
</p><pre class="brush:php;toolbar:false">export interface Fiber {
  type: string
  props: VDomAttributes
  parent: Fiber | null
  child: Fiber | null
  sibling: Fiber | null
  dom: HTMLElement | Text | null
  alternate: Fiber | null
  committed: boolean
}

이제 커밋에 있어서 기존 Fiber와 새 Fiber를 비교할 수 있는 추가 대안 필드가 생겼습니다.

원래 커밋 기능입니다

function commit() {
    function commitChildren(fiber: Fiber | null) {
        if(!fiber) {
            return
        }
        if(fiber.dom && fiber.parent?.dom) {
            fiber.parent.dom.appendChild(fiber.dom)
            fiber.committed = true
        }
        if(fiber.dom && fiber.parent && isFragment(fiber.parent.vDom) && !fiber.committed) {
            let parent = fiber.parent
            // find the first parent that is not a fragment
            while(parent && isFragment(parent.vDom)) {
                // the root element is guaranteed to not be a fragment has has a non-fragment parent
                parent = parent.parent!
            }
            parent.dom?.appendChild(fiber.dom!)
            fiber.committed = true
        }
        commitChildren(fiber.child)
        commitChildren(fiber.sibling)
    }
    commitChildren(wip)
    wipParent?.appendChild(wip!.dom!)
    wip!.committed = true
    wip = null
}

이름을 조금 바꾸겠습니다. 예전 이름이 틀렸을 뿐이에요(죄송합니다).

let oldFiber: Fiber | null = null

function commit() {
    function commitChildren(fiber: Fiber | null) {
        if(!fiber) {
            return
        }
        if(fiber.dom && fiber.parent?.dom) {
            fiber.parent.dom.appendChild(fiber.dom)
            fiber.committed = true
        }
        commitChildren(fiber.child)
        commitChildren(fiber.sibling)
    }
    commitChildren(wip)
    wipParent?.appendChild(wip!.dom!)
    wip!.committed = true
    oldFiber = wip
    wip = null
}

추가, 복사 및 교체

그럼 우리는 어떻게 해야 할까요? 기존 로직은 추가만 하므로 이를 추출합니다.

export function render(vDom: VDomNode, parent: HTMLElement) {
    wip = {
        parent: null,
        sibling: null,
        child: null,
        vDom: vDom,
        dom: null,
        committed: false,
        alternate: oldFiber,
    }
    wipParent = parent
    nextUnitOfWork = wip
}

더 많은 유연성을 제공하려면 커밋 단계까지 DOM 구성을 연기해야 ​​합니다.

function reconcile(fiber: Fiber, isFragment: boolean) {
    if (isElement(fiber.vDom)) {
        const elements = fiber.vDom.children ?? []
        let index = 0
        let prevSibling = null

        while (index 



<p>첫 번째와 두 번째 규칙에 따라 다음 코드로 리팩토링합니다.<br>
</p>

<pre class="brush:php;toolbar:false">function reconcile(fiber: Fiber, isFragment: boolean) {
    if (isElement(fiber.vDom)) {
        const elements = fiber.vDom.children ?? []
        let index = 0
        let prevSibling = null

        let currentOldFiber = fiber.alternate?.child ?? null
        while (index 



<p>자바스크립트에서는 모든 값이 참조라는 점을 항상 명심하세요. fibre.dom = fibre.alternate.dom이면 fibre.dom과 fibre.alternate.dom은 동일한 개체를 가리킵니다. Fiber.dom을 변경하면 Fiber.alternate.dom도 변경되고 그 반대도 마찬가지입니다. 그렇기 때문에 교체할 ​​때 단순히 Fiber.alternate.dom?.replaceWith(섬유.dom)를 사용했습니다. 그러면 이전 DOM이 새 DOM으로 대체됩니다. 이전 상위 항목을 복사하면 DOM에 대해 Fiber.alternate.dom이 있지만 DOM도 교체됩니다.</p>

<p>단, 아직 삭제 처리는 하지 않았습니다.</p>

<h3>
  
  
  일부 사고
</h3>

<p>알겠습니다. 이전 코드에는 좀 더 복잡한 jsx를 작성하면서 발견한 몇 가지 버그가 포함되어 있으므로 삭제를 구현하기 전에 수정해 보겠습니다.</p>

<p>이전에 버그가 있었습니다. 목록을 props에 전달할 수 없습니다. 이번 기회에 수정해 보겠습니다.<br>
</p>

<pre class="brush:php;toolbar:false">import { render } from "./runtime";
import { createElement, fragment, VDomAttributes, VDomNode } from "./v-dom";

type FuncComponent = (props: VDomAttributes, children: VDomNode[]) => JSX.Element

const App: FuncComponent = (props: VDomAttributes, __: VDomNode[]) => {
    return <div>
        
            <h1 id="H">H1</h1>
            <h2 id="props-example-toString">{props["example"]?.toString()}</h2>
            {
                props["show"] ? <p>show</p> : >
            }
            <h1 id="H">H1</h1>
        >
    </div>
}
const app = document.getElementById('app')

const renderButton = document.createElement('button')
renderButton.textContent = 'Render'
let cnt = 0
renderButton.addEventListener('click', () => {
    const vDom: VDomNode = App({
        "example": (new Date()).toString(),
        "show": cnt % 2 === 0
    }, []) as unknown as VDomNode
    cnt++
    render(vDom, app!)
})
document.body.appendChild(renderButton)

그럼 유형을 수정하세요. 오류가 하나뿐이니까 직접 해 보세요.

그러나 다음과 같은 코드가 있다면

export function vDOMEquals(a: VDomNode, b: VDomNode): boolean {
    if (isString(a) && isString(b)) {
        return a === b
    } else if (isElement(a) && isElement(b)) {
        let ret = a.tag === b.tag && a.key === b.key
        if (!ret) return false
        if (a.props && b.props) {
            const aProps = a.props
            const bProps = b.props
            const aKeys = Object.keys(aProps)
            const bKeys = Object.keys(bProps)
            if (aKeys.length !== bKeys.length) return false
            for (let i = 0; i 



<p>우리 일이 또 망가졌어요... </p>

<p>그렇습니다. 위의 경우 하위 항목이 중첩 배열이 될 수 있기 때문에 이들을 평면화해야 합니다.</p>

<p>하지만 그것만으로는 충분하지 않습니다. 으, 우리 createDom은 정수가 아닌 문자열이나 요소만 인식하므로 숫자를 문자열로 묶어야 합니다.<br>
</p>

<pre class="brush:php;toolbar:false">function buildDom(fiber: Fiber, fiberIsFragment: boolean) {
    if(fiber.dom) return
    if(fiberIsFragment) return
    fiber.dom = createDom(fiber.vDom)
}

function performUnitOfWork(nextUnitOfWork: Fiber | null): Fiber | null {
    if(!nextUnitOfWork) {
        return null
    }
    const fiber = nextUnitOfWork
    const fiberIsFragment = isFragment(fiber.vDom)

    reconcile(fiber)

    buildDom(fiber, fiberIsFragment);

    if (fiber.child) {
        return fiber.child
    }
    let nextFiber: Fiber | null = fiber
    while (nextFiber) {
        if (nextFiber.sibling) {
          return nextFiber.sibling
        }
        nextFiber = nextFiber.parent
    }
    return null
}

좋아, 이제 일이 잘 풀리네요.

렌더링 버튼을 누르면 목록이 업데이트되지만 이전 요소는 여전히 남아 있습니다. 이전 요소를 삭제해야 합니다.

제거하다

여기서 규칙을 다시 설명합니다. 새 파이버에 대해 하위 또는 형제 자매가 없지만 기존 파이버에 하위 또는 형제 자매가 있는 경우 기존 하위 또는 형제 자매를 재귀적으로 제거합니다.

function commit() {
    function commitChildren(fiber: Fiber | null) {
        if(!fiber) {
            return
        }
        if(fiber.dom && fiber.parent?.dom) {
            fiber.parent?.dom?.appendChild(fiber.dom)
            fiber.committed = true
        }

        if(fiber.dom && fiber.parent && isFragment(fiber.parent.vDom) && !fiber.committed) {
            let parent = fiber.parent
            // find the first parent that is not a fragment
            while(parent && isFragment(parent.vDom)) {
                // the root element is guaranteed to not be a fragment has has a non-fragment parent
                parent = parent.parent!
            }
            parent.dom?.appendChild(fiber.dom!)
            fiber.committed = true
        }

        commitChildren(fiber.child)
        commitChildren(fiber.sibling)
    }
    commitChildren(wip)
    wipParent?.appendChild(wip!.dom!)
    wip!.committed = true
    oldFiber = wip
    wip = null
}

재귀 제거를 수행하지 않으면 삭제가 필요한 항목이 여러 개 있을 때 일부 오래된 요소가 매달려 있게 됩니다.
로 변경할 수 있습니다.

function commit() {
    function commitToParent(fiber: Fiber | null) {
        if(!fiber) {
            return
        }
        if(fiber.dom && fiber.parent?.dom) {
            fiber.parent?.dom?.appendChild(fiber.dom)
            fiber.committed = true
        }

        if(fiber.dom && fiber.parent && isFragment(fiber.parent.vDom) && !fiber.committed) {
            let parent = fiber.parent
            // find the first parent that is not a fragment
            while(parent && isFragment(parent.vDom)) {
                // the root element is guaranteed to not be a fragment has has a non-fragment parent
                parent = parent.parent!
            }
            parent.dom?.appendChild(fiber.dom!)
            fiber.committed = true
        }

        commitToParent(fiber.child)
        commitToParent(fiber.sibling)
    }
    commitToParent(wip)
    wipParent?.appendChild(wip!.dom!)
    wip!.committed = true
    oldFiber = wip
    wip = null
}

참고용.

요약

이것은 어려운 장이지만 솔직히 말해서 꽤 전통적인 코딩입니다. 그러나 지금까지 React가 아래에서 위로 어떻게 작동하는지 이해했습니다.

사실 이제 모든 것이 이미 작동할 수 있습니다. 소품을 변경할 때마다 수동으로 다시 렌더링을 실행할 수 있습니다. 하지만 이렇게 답답한 수작업은 우리가 원하는 것이 아닙니다. 우리는 반응이 자동이기를 원합니다. 그래서 다음 장에서 Hook에 대해 이야기해보겠습니다.

위 내용은 작은 React Chpdating vDOM 구축의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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

실제 세계에서 JavaScript의 응용 프로그램에는 서버 측 프로그래밍, 모바일 애플리케이션 개발 및 사물 인터넷 제어가 포함됩니다. 1. 서버 측 프로그래밍은 Node.js를 통해 실현되며 동시 요청 처리에 적합합니다. 2. 모바일 애플리케이션 개발은 재교육을 통해 수행되며 크로스 플랫폼 배포를 지원합니다. 3. Johnny-Five 라이브러리를 통한 IoT 장치 제어에 사용되며 하드웨어 상호 작용에 적합합니다.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축Apr 11, 2025 am 08:23 AM

일상적인 기술 도구를 사용하여 기능적 다중 테넌트 SaaS 응용 프로그램 (Edtech 앱)을 구축했으며 동일한 작업을 수행 할 수 있습니다. 먼저, 다중 테넌트 SaaS 응용 프로그램은 무엇입니까? 멀티 테넌트 SAAS 응용 프로그램은 노래에서 여러 고객에게 서비스를 제공 할 수 있습니다.

Next.js (Frontend Integration)를 사용하여 멀티 테넌트 SaaS 응용 프로그램을 구축하는 방법Next.js (Frontend Integration)를 사용하여 멀티 테넌트 SaaS 응용 프로그램을 구축하는 방법Apr 11, 2025 am 08:22 AM

이 기사에서는 Contrim에 의해 확보 된 백엔드와의 프론트 엔드 통합을 보여 주며 Next.js를 사용하여 기능적인 Edtech SaaS 응용 프로그램을 구축합니다. Frontend는 UI 가시성을 제어하기 위해 사용자 권한을 가져오고 API가 역할 기반을 준수하도록합니다.

JavaScript : 웹 언어의 다양성 탐색JavaScript : 웹 언어의 다양성 탐색Apr 11, 2025 am 12:01 AM

JavaScript는 현대 웹 개발의 핵심 언어이며 다양성과 유연성에 널리 사용됩니다. 1) 프론트 엔드 개발 : DOM 운영 및 최신 프레임 워크 (예 : React, Vue.js, Angular)를 통해 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축합니다. 2) 서버 측 개발 : Node.js는 비 차단 I/O 모델을 사용하여 높은 동시성 및 실시간 응용 프로그램을 처리합니다. 3) 모바일 및 데스크탑 애플리케이션 개발 : 크로스 플랫폼 개발은 개발 효율을 향상시키기 위해 반응 및 전자를 통해 실현됩니다.

JavaScript의 진화 : 현재 동향과 미래 전망JavaScript의 진화 : 현재 동향과 미래 전망Apr 10, 2025 am 09:33 AM

JavaScript의 최신 트렌드에는 Typescript의 Rise, 현대 프레임 워크 및 라이브러리의 인기 및 WebAssembly의 적용이 포함됩니다. 향후 전망은보다 강력한 유형 시스템, 서버 측 JavaScript 개발, 인공 지능 및 기계 학습의 확장, IoT 및 Edge 컴퓨팅의 잠재력을 포함합니다.

Demystifying JavaScript : 그것이하는 일과 중요한 이유Demystifying JavaScript : 그것이하는 일과 중요한 이유Apr 09, 2025 am 12:07 AM

JavaScript는 현대 웹 개발의 초석이며 주요 기능에는 이벤트 중심 프로그래밍, 동적 컨텐츠 생성 및 비동기 프로그래밍이 포함됩니다. 1) 이벤트 중심 프로그래밍을 사용하면 사용자 작업에 따라 웹 페이지가 동적으로 변경 될 수 있습니다. 2) 동적 컨텐츠 생성을 사용하면 조건에 따라 페이지 컨텐츠를 조정할 수 있습니다. 3) 비동기 프로그래밍은 사용자 인터페이스가 차단되지 않도록합니다. JavaScript는 웹 상호 작용, 단일 페이지 응용 프로그램 및 서버 측 개발에 널리 사용되며 사용자 경험 및 크로스 플랫폼 개발의 유연성을 크게 향상시킵니다.

Python 또는 JavaScript가 더 좋습니까?Python 또는 JavaScript가 더 좋습니까?Apr 06, 2025 am 12:14 AM

Python은 데이터 과학 및 기계 학습에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 간결한 구문 및 풍부한 라이브러리 생태계로 유명하며 데이터 분석 및 웹 개발에 적합합니다. 2. JavaScript는 프론트 엔드 개발의 핵심입니다. Node.js는 서버 측 프로그래밍을 지원하며 풀 스택 개발에 적합합니다.

JavaScript를 어떻게 설치합니까?JavaScript를 어떻게 설치합니까?Apr 05, 2025 am 12:16 AM

JavaScript는 이미 최신 브라우저에 내장되어 있기 때문에 설치가 필요하지 않습니다. 시작하려면 텍스트 편집기와 브라우저 만 있으면됩니다. 1) 브라우저 환경에서 태그를 통해 HTML 파일을 포함하여 실행하십시오. 2) Node.js 환경에서 Node.js를 다운로드하고 설치 한 후 명령 줄을 통해 JavaScript 파일을 실행하십시오.

See all articles

핫 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 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

MinGW - Windows용 미니멀리스트 GNU

MinGW - Windows용 미니멀리스트 GNU

이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

메모장++7.3.1

메모장++7.3.1

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

Dreamweaver Mac版

Dreamweaver Mac版

시각적 웹 개발 도구

SublimeText3 Linux 새 버전

SublimeText3 Linux 새 버전

SublimeText3 Linux 최신 버전