Vue3: Vue의 크로스엔드 렌더링 실현

Vue 내부의 구성 요소는 가상 돔 형태로 존재합니다. 다음 코드는 객체 형태로 항목을 설명하는 매우 일반적인 가상 Dom입니다. dom 태그와 비교할 때 이 형식은 브라우저의 제한에서 전체 Vue 프로젝트를 자유롭게 할 수 있으며 Vuejs의 크로스엔드를 실현하는 것이 더 편리합니다.


{
    
    
  tag: 'div',
  props: {
    
    
    id: 'app'
  },
  chidren: [
    {
    
    
      tag: Container,
      props: {
    
    
        className: 'el-container'
      },
      chidren: [
        'Hello Little Gay!!!'
      ]
    }
  ]
}

렌더러는 가상 Dom 주위에 존재합니다. 브라우저에서 가상 Dom을 실제 Dom 개체로 렌더링하고 Vue 소스 코드는 내부적으로 프레임워크의 모든 플랫폼 관련 작업을 독립적인 메서드로 분리합니다. 따라서 하나의 플랫폼에서 Vue 3 렌더링을 구현하려면 다음 메서드만 구현하면 됩니다.

  1. 먼저 createElement를 사용하여 레이블을 만들고 createText를 사용하여 텍스트를 만듭니다. 생성 후 삽입으로 요소를 추가하고, 원격으로 요소를 삭제하고, setText로 텍스트를 업데이트하고, patchProps로 속성을 수정해야 합니다.
  2. 그런 다음 parentNode 및 nextSibling과 같은 메서드를 구현하여 노드의 검색 관계를 실현합니다. 이러한 작업을 완료하면 이론상 플랫폼에서 응용 프로그램을 실현할 수 있습니다.

Vue 3의 runtime-core 모듈은 이러한 인터페이스를 외부 세계에 노출합니다.이 기능을 기반으로 runtime-core는 Vue의 모든 내부 작업을 구현한 다음 위의 모든 메서드를 runtime-dom에 전달합니다.

다음 코드는 Vue 코드가 브라우저 측 작업을 위해 제공하는 기능입니다. 이러한 DOM 프로그래밍 인터페이스는 브라우저 측 추가, 추가 및 삭제 작업을 완료합니다. 이러한 API는 브라우저 측에 고유합니다. 프레임워크가 이러한 기능에 크게 의존하는 경우, 브라우저 측에서만 작동합니다.


export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
    
    
  //插入元素
  insert: (child, parent, anchor) => {
    
    
    parent.insertBefore(child, anchor || null)
  },
  // 删除元素
  remove: child => {
    
    
    const parent = child.parentNode
    if (parent) {
    
    
      parent.removeChild(child)
    }
  },
  // 创建元素
  createElement: (tag, isSVG, is, props): Element => {
    
    
    const el = isSVG
      ? doc.createElementNS(svgNS, tag)
      : doc.createElement(tag, is ? {
    
     is } : undefined)

    if (tag === 'select' && props && props.multiple != null) {
    
    
      ;(el as HTMLSelectElement).setAttribute('multiple', props.multiple)
    }

    return el
  }
  //...其他操作函数
}

프레임워크가 크로스엔드 기능을 구현하려는 경우 렌더러 자체는 플랫폼별 인터페이스에 의존할 수 없습니다.

다음 코드에서는 createRenderer 함수 영역을 통해 렌더러를 생성합니다. 내부 렌더링, 마운트, 패치 및 기타 기능에서 매개 변수 옵션을 통해 추가, 삭제, 수정 및 확인하는 모든 기능을 얻은 후 요소를 렌더링해야 할 때 option.createElement 및 옵션을 통해 구현할 수 있습니다. 끼워 넣다.


export default function createRenderer(options) {
    
    
  const {
    
    
      insert: hostInsert,
      remove: hostRemove,
      patchProp: hostPatchProp,
      createElement: hostCreateElement,
      createText: hostCreateText,
      createComment: hostCreateComment,
      setText: hostSetText,
      setElementText: hostSetElementText,
      parentNode: hostParentNode,
      nextSibling: hostNextSibling,
      setScopeId: hostSetScopeId = NOOP,
      cloneNode: hostCloneNode,
      insertStaticContent: hostInsertStaticContent
   } = options

  function render(vnode, container) {
    
      }

  function mount(vnode, container, isSVG, refNode) {
    
      }

  function mountElement(vnode, container, isSVG, refNode) {
    
      }

  function mountText(vnode, container) {
    
      }

  function patch(prevVNode, nextVNode, container) {
    
      }

  function replaceVNode(prevVNode, nextVNode, container) {
    
      }
  function patchElement(prevVNode, nextVNode, container) {
    
      }
  function patchChildren(
    prevChildFlags,
    nextChildFlags,
    prevChildren,
    nextChildren,
    container
  ) {
    
      }

  function patchText(prevVNode, nextVNode) {
    
      }
  function patchComponent(prevVNode, nextVNode, container) {
    
      }

  return {
    
     render }
}

사용자 지정 렌더러는 Vue를 브라우저의 제한에서 해방시켜 플랫폼 내에서 추가, 삭제, 수정 및 쿼리 기능만 구현하면 Vue 3에 직접 연결할 수 있습니다. 예를 들어 Vue를 애플릿 플랫폼으로 렌더링하여 Vue 3-minipp를 구현할 수 있고, Canvas로 렌더링하여 vue 3-canvas를 구현하고 가상 돔을 Canvas로 렌더링할 수도 있습니다. Vue 3을 threee.js로 렌더링할 수도 있습니다. 3D 세계에서 반응형 개발을 사용합니다.


import {
    
     createRenderer } from '@vue/runtime-core'
import * as THREE from 'three'
import {
    
    nextTick} from '@vue/runtime-core'

let renderer

function draw(obj) {
    
    
    const {
    
    camera,cameraPos, scene, geometry,geometryArg,material,mesh,meshY,meshX} = obj
    if([camera,cameraPos, scene, geometry,geometryArg,material,mesh,meshY,meshX].filter(v=>v).length<9){
    
    
        return 
    }
    let cameraObj = new THREE[camera]( 40, window.innerWidth / window.innerHeight, 0.1, 10 )
    Object.assign(cameraObj.position,cameraPos)

    let sceneObj = new THREE[scene]()

    let geometryObj = new THREE[geometry]( ...geometryArg)
    let materialObj = new THREE[material]()

    let meshObj = new THREE[mesh]( geometryObj, materialObj )
    meshObj.rotation.x = meshX
    meshObj.rotation.y = meshY
    sceneObj.add( meshObj )
    renderer.render( sceneObj, cameraObj );

}

const {
    
     createApp: originCa } = createRenderer({
    
    
  insert: (child, parent, anchor) => {
    
    
    if(parent.domElement){
    
    
        draw(child)
    }
  },
  createElement(type, isSVG, isCustom) {
    
    
    return {
    
    
      type
    }
  },
  setElementText(node, text) {
    
    
  },
  patchProp(el, key, prev, next) {
    
    
    el[key] = next
    draw(el)
  },
  parentNode: node => node,
  nextSibling: node => node,
  createText: text => text,
  remove:node=>node

});
function createApp(...args) {
    
    
  const app = originCa(...args)
  return {
    
    
    mount(selector) {
    
    
        renderer = new THREE.WebGLRenderer( {
    
     antialias: true } );
        renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( renderer.domElement );
        app.mount(renderer)
    }
  }
}
export {
    
     createApp }




 import {
    
    Graphics} from "PIXI.js";

export const getNodeOps = (app) => {
    
    
  return {
    
    
    insert: (child, parent, anchor) => {
    
    
      parent.addChild(child);
    },

    remove: (child) => {
    
    
      const parent = child.parentNode;
      if (parent) {
    
    
        parent.removeChild(child);
      }
    },

    createElement: (tag, isSVG, is) => {
    
    
      let element;
      if (tag === "Rectangle") {
    
    
        // 创建一个矩形
        element = new window.PIXI.Graphics();
        element.lineStyle(4, 0xff3300, 1);
        element.beginFill(0x66ccff);
        element.drawRect(0, 0, 64, 64);
        element.endFill();
        element.x = 0;
        element.y = 0;
        // Opt-in to interactivity
        element.interactive = true;

        // Shows hand cursor
        element.buttonMode = true;
      } else if (tag === "Sprite") {
    
    
        element = new window.PIXI.Sprite();
        element.x = 0;
        element.y = 0;
      } else if (tag === "Container") {
    
    
        element = new window.PIXI.Container();
        element.x = 0;
        element.y = 0;
      }

      return element;
    },

    createText: (text) => doc.createTextNode(text),

    createComment: (text) => {
    
    
    //   console.log(text);
    },

    setText: (node, text) => {
    
    
      node.nodeValue = text;
    },

    setElementText: (el, text) => {
    
    
      el.textContent = text;
    },

    parentNode: (node) => node.parentNode,

    nextSibling: (node) => node.nextSibling,

    querySelector: (selector) => doc.querySelector(selector),

    setScopeId(el, id) {
    
    
      el.setAttribute(id, "");
    },

    cloneNode(el) {
    
    
      return el.cloneNode(true);
    },
  };
};

커스텀 렌더러의 원리는 모든 추가, 삭제, 수정 및 쿼리를 노출하는 것입니다.이를 사용할 때 내부 구현 세부 사항을 알 필요는 없으며 플랫폼마다 다른 API를 사용하면 됩니다 .

무협소설에서처럼 명인은 당신에게 내면의 힘을 전달함으로써 당신이 경쟁하도록 조종할 수 있다. 우리가 플레이하는 모든 움직임과 스타일은 우리 뒤에 있는 마스터들로부터 나오지만 우리 스스로 간단한 적응을 했습니다. Vue 렌더러의 설계에서는 문서의 모든 연산을 nodeOps로 분리하고 Vue의 createRenderer 함수를 호출하여 플랫폼 렌더러를 생성합니다.

Canvas 플랫폼의 추가, 삭제, 수정 및 쿼리를 구현하는 한 Vue의 반응형 구문을 사용하여 Canvas 세계에서 그림을 제어하고 게임을 할 수 있습니다.Vue 생태계에서 작은 프로그램 및 기본 앱을 지원하는 원칙도 커스텀 렌더러 기반. .

사용자 지정 렌더러는 어댑터 디자인 패턴 의 사례 도 나타냅니다 . 커스텀 렌더러 API를 배우는 것 외에도 현재 담당하고 있는 프로젝트에서 서로 다른 인터페이스나 플랫폼에 대해 너무 많은 판단 코드를 작성한 부분이 있는지, 서로 다른 것을 캡슐화하는 개별 기능을 확장하기 위해 여러 구성 요소, 플랫폼 및 인터페이스 간의 작동 모드를 핵심 모듈로 통합합니다.

나중에 시간이 있을 때 작성하겠습니다. 노드 환경에서 Vue는 어떻게 렌더링됩니까?

Supongo que te gusta

Origin blog.csdn.net/weixin_44659309/article/details/130469237
Recomendado
Clasificación