ホームページ  >  記事  >  ウェブフロントエンド  >  vue3 のカスタム レンダラー機能の詳細な分析

vue3 のカスタム レンダラー機能の詳細な分析

青灯夜游
青灯夜游転載
2021-07-20 10:27:432866ブラウズ

この記事では、vue3 の新機能 rendercustomer について誰もが学びます。一定の参考値があるので、困っている友達が参考になれば幸いです。

vue3 のカスタム レンダラー機能の詳細な分析

#[関連する推奨事項:「

vue.js チュートリアル 」]

デフォルトのターゲット レンダリング プラットフォーム

    vue3 では、ユーザーはターゲット レンダリング プラットフォームをカスタマイズできます。以前のバージョンでは、ターゲット レンダリングはブラウザー dom プラットフォームに限定されていましたが、vue の開発モデルを他のプラットフォームに拡張できるようになりました。
  • クリックして公式 Web サイトに入ります
  • ヒント: 以前は、vue 開発モデルを他のプラットフォーム (
  • Canvas、iOS、Android) に拡張するという問題を解決する方法の 1 つでした。など) は、WEEX などのサードパーティ ツールを使用することでした (クリックして公式 Web サイトに入ります)
  • まず、vue の仕組みを理解しましょうデフォルトのターゲット レンダリング プラットフォーム、つまりターゲットをレンダリングする方法を定義します。ブラウザー dom プラットフォーム に移動します。まずは公式画像を参照してください:

    vue3 のカスタム レンダラー機能の詳細な分析

  • まず、新しい初期化された vue3 プロジェクトを構築して、vue がデフォルトでターゲットをどのようにレンダリングするかを段階的に分析します。ブラウザ dom プラットフォームに移動します。
  • 次は、プロジェクトのエントリ ファイル main.js のコードです。
  • import { createApp } from 'vue'
    import App from './App.vue'
    
    createApp(App).mount('#app')
    最後に見てみましょう
  • './APP. vue のコード':
  • <template>
      <div>我是根组件实例</div>
    </template>
    
    
    <script>
    export default {
      name: &#39;App&#39;,
      components: {
      }
    }
    </script>
    これら 2 つのファイルを作成した後、コマンド
  • npm runserve を実行すると、 ' で記述したことがわかります。./APP.vue' のテンプレートはブラウザーの dom プラットフォームにレンダリングされ、以下に示すように実際の dom 要素になりました。

vue3 のカスタム レンダラー機能の詳細な分析

'./APP.vue'
    で記述されたテンプレートがどのようにブラウザー dom プラットフォームにレンダリングされ、実際の dom に変換されるのか疑問に思う必要があります。 element
  • ?自分でやる場合はどうすればよいでしょうか?重要な手がかりはエントリ ファイル main.js:
    import App from &#39;./App.vue&#39;
    //我们可以打印出App来查看一下
    console.log(App)
    で見つかります。まずアプリを印刷して確認しましょう。これはどのような情報ですか?以下に示すように:

印刷された App オブジェクトでは、オブジェクトにテンプレート属性が見つかりませんでした。テンプレートのない情報を実際の DOM 要素にどのように変換できるのかという、より大きな疑問を抱かずにはいられません。 ? ? vue3 のカスタム レンダラー機能の詳細な分析答えは、オブジェクトの render 関数に依存することです

. テンプレートは vue による特別な処理を通じて render 関数に変換されており、この render 関数は に基づいて仮想 DOM を返すことがわかります。テンプレートの有用な情報
    (Vnode )
  • 以下に示すように、ツールを使用して検証できます:

上に示したように、テンプレートが vue による特別な処理を通じてレンダリング関数に変換されたことがはっきりとわかります。このレンダリング関数は、次の有益な情報に従って仮想 DOMvue3 のカスタム レンダラー機能の詳細な分析(Vnode)

を返します。仮想 DOM の説明については、公式 Web サイトを参照してください。返された仮想 DOM から多くの有用な情報を取得できます

ここでは、赤いボックス内の有用な情報を使用して、vue3 のカスタム レンダラー機能の詳細な分析 単純にそれをレンダリングする方法を実装します。ブラウザー dom プラットフォームを実行し、それを実際の dom 要素

に変換します。コードは次のとおりです。
  • //假设这个是虚拟Dom的信息
    //仅仅是为了演示基本思想
    const vnode={
      type:&#39;div&#39;,
      children:&#39;123&#39;
    }
    
    
    const element=document.creatElement(vnode.type)
    element.innerText=vnode.children
    
    
    //告诉它的出口在哪里 要被渲染到哪里去
    //这里的出口先假设为#app这个容器
    document.querySelector(&#39;#app&#39;).appendChild(element)
    完全な論理図のセットは次のとおりです。
    • 到了这一步我们也做到了如何将写在'./APP.vue'的template渲染到浏览器dom平台上并且转换成真实的dom元素(虽然写的代码很菜),可是这一套逻辑vue已经帮我们实现了,我们现在再来看入口文件main.js的代码
    /*
    //createApp的作用是将传入的组件转换为真实的Dom元素
    //核心思想就是刚才写的
    //const element=document.creatElement(vnode.type)
    //element.innerText=vnode.children
    */
    import { createApp } from &#39;vue&#39;
    import App from &#39;./App.vue&#39;
    
    
    
    /*
    //mount的作用是告诉它的出口在哪里、要被渲染到哪里去
    //核心思想就是刚才写的
    //document.querySelector(&#39;#app&#39;).appendChild(element)
    */
    createApp(App).mount(&#39;#app&#39;)

    自定义的目标渲染平台

    • 我们在实现自定义的目标渲染平台之前,还得在温习一遍默认的目标渲染平台的流程逻辑图,如下图:

    vue3 のカスタム レンダラー機能の詳細な分析

    • 我们知道canvas也是一个平台,这里就以如何使用vue3渲染到canvas平台上来举例说明。我们先来看成果图:

    vue3 のカスタム レンダラー機能の詳細な分析

    • 我们即将要实现使用vue3的新特性custom renderer来将目标元素渲染到canvas平台上,我们现在实现的逻辑图如下:(注意分支)

    vue3 のカスタム レンダラー機能の詳細な分析


    • 在实现之前,我们必须得先学会几个简单的关于canvas的api。为了快速上手,在这里我使用了pixi.js第三方工具(点击进入官网),pixi.js是基于canvas 的游戏渲染引擎库,借助pixi.js可以省去繁琐的操纵canvas的流程,让我们专心于感受vue3的新特性custom renderer的魅力
    • 下面是使用pixi.js创建canvas并往canvas内添加各种东西的流程图:(最终为了可以直观的看到效果,将canvas呈现在浏览器上(**插入到dom**))

    vue3 のカスタム レンダラー機能の詳細な分析

    • 在vue3的项目使用安装npm i pixi.js后,我们来看一下简单的关于canvas的使用方式,代码和简图如下:

    1vue3 のカスタム レンダラー機能の詳細な分析

    import {
      //初始化
      Application,
      //创建矩形
      Graphics,
      //创建图片
      Sprite,
      //创建文字
      Texture,
      Text,
      TextStyle,
      //创建容器
      Container,
    } from "pixi.js";
    
    /*
    通过 new Application来初始化创建canvas
    options规定创建的canvas的宽和高
    */
    const game = new Application({
      width: 500,
      height: 500,
    });
    
    
    /*
    为了可以直观的看到效果
    将canvas呈现在浏览器上(**插入到dom**)
    
    game.view是canvas视图元素
    */
    document.body.append(game.view);
    
    
    
    /*
    创建一个矩形
    rect.x和rect.y是设置矩形的初始位置偏移量
    
    //单独(独自)添加矩形到canvas容器上使用下一行命令
    game.stage.addChild(rect);
    */
    const rect = new Graphics();
    rect.beginFill(0xffff00);
    rect.drawRect(0, 0, 50, 50);
    rect.endFill();
    rect.x = 50;
    rect.y = 50;
    
    
    
    /*
    创建图片
    
    //单独(独自)添加矩形到canvas容器上使用下一行命令
    game.stage.addChild(img);
    */
    import logo from "./assets/logo.png";
    const img = new Sprite();
    //指定后才允许给图片添加点击事件
    img.interactive = true;
    //指定图片的src路径
    img.texture = Texture.from(logo);
    //添加帧循环 会一直执行handleTicker事件直至删除该帧循环
    game.ticker.add(handleTicker);
    //handleTicker事件 令图片的x偏移量不断增加
    const handleTicker = () => {img.x++};
    
    /*
    pixi的点击事件名 
    必须配合img.interactive = true才能允许被点击
    */
    img.on("pointertap", () => {
      game.ticker.remove(handleTicker);
    });
    
    
    /*
    创建文本
    
    //单独(独自)添加矩形到canvas容器上使用下一行命令
    game.stage.addChild(text);
    */
    const text = new Text("heihei");
    text.style = new TextStyle({
      fill: "red",
    });
    text.x = 380;
    
    
    /*
    创建容器
    
    //容器中可以放图片、文字、矩形等等
    //容器是一个大的整体
    
    //将容器添加到canvas上的话
    //容器中的内容也会一并被添加到canvas上
    //即下一行代码
    game.stage.addChild(box);
    */
    const box = new Container();
    box.addChild(text);
    box.addChild(img);
    //统一的移动它们的位置
    box.x = 2
    
    /*
    如果你想要把你创建的东西渲染到canvas容器内的话
    必须把东西通过game.stage.addChild的方式添加进去才能显示
    */
    
    //单独添加以添加矩形为例
    game.stage.addChild(rect);
    
    
    //添加一个容器
    //(容器中可以包含图片、文字等等也会被一并添加上canvas)
    game.stage.addChild(box);
    • 我们现在借助pixi.js学会了对canvas的简单操纵,接下来我们就要使用vue3的custom renderer来将元素渲染到canvas平台上了。

    自定义渲染到canvas平台上

    • 我们在上一讲已经学会了借助pixi.js对canvas进行简单的操纵,并梳理了自定义渲染到canvas平台上的逻辑,让我们在回顾一下逻辑图,再开始着手使用vue3的新特性custom renderer:

    1vue3 のカスタム レンダラー機能の詳細な分析

    • 我们接下来如何操作来完成这一套自定义逻辑呢??有请我们今天的主角登场:custom renderer(点击进入官网)。
    • 我们先来重写App.vue里的代码,参考如下:
    <template>
        <!-- 这里的circle和rect是自定义标签
        	 不是组件不是组件不是组件 -->
        <circle x="50" y="50"></circle>
    </template>
    
    
    <script>
    export default {
      name: &#39;App&#39;,
      components: {
      }
    }
    </script>
    • 我们接着重写入口文件main.js中的代码。参考如下:
    /* 默认的渲染到浏览器dom平台上的代码
    
    import { createApp } from &#39;vue&#39;
    import App from &#39;./App.vue&#39;
    createApp(App).mount(&#39;#app&#39;)
    */
    
    
    /*自定义渲染到canvas平台上
    
    createRenderer就是告诉vue我要自定义渲染平台了
    自定义渲染器可以传入特定于平台的类型
    */
    
    import { createRenderer } from "vue";
    //我们不急着往createRenderer添加相关配置
    //我们先打印render查看这个到底是个什么
    const render=createRenderer({})
    console.log(render,&#39;render&#39;);
    • 我们从vue中导出了createRenderer函数,在不配置任何选项的情况下打印出render来查看这到底是个什么东西??如下图:

    1vue3 のカスタム レンダラー機能の詳細な分析

    • 我们在打印出的图中可以发现两条熟悉的线索,一个是该render上有createApp方法另一个是该render上有render方法!!!!
    • 我们还记得import { createApp } from 'vue'这一句代码,这一句代码配合createApp(App).mount('#app')就将写在App.vue中的template给渲染到浏览器Dom平台上了,所以vue暴露出来的createApp是已经帮我们封装好逻辑的了。
    • 我们现在的任务是调用render下的createApp函数来封装实现我们的逻辑,渲染到canvas平台上,我们先来看下面的代码:
    import {createRenderer } from &#39;vue&#39;
    import App from &#39;./App.vue&#39;
    
    //自己要来写逻辑
    const render=createRenderer({})
    
    
    /*
    自己要来写逻辑  ---->  render下有createApp函数
    调用createApp()方法后
    返回的对象下依旧是有mount()方法的
    */
    render.createApp(这里要填什么).mount(这里又要填什么)

    1vue3 のカスタム レンダラー機能の詳細な分析

    • 我们在上面的代码中先不考虑要怎么书写createRenderer()函数中的配置项封装逻辑,先来考虑render.createApp(这里要填什么).mount(这里又要填什么)这两个空要怎么填的问题?
    • 我们参考createApp(App).mount('#app')便可以知道第一个空应该要填的是根组件,在这里我们同样填的是import App from './App.vue'导出的App,第二个空应该要填的是根容器,我们需要的是渲染到canvas平台上,所以我们的根容器得是game.stage(这里的game.stage是经过pixi.js初始化后的canvas容器),代码如下:
    import { Application } from "pixi.js";
    
    //通过 new Application来初始化创建canvas
    const game = new Application({
      width: 750,
      height: 750,
    });
    
    // 为了可以直观的看到效果
    // 将canvas呈现在浏览器上(**插入到dom**)
    document.body.append(game.view);
    
    /*
    导出canvas容器供
    render.createApp(这里要填什么).mount(getRootContainer())
    使用
    */
    export function getRootContainer() {
      return game.stage;
    }
    • 紧接着我们就来书写createRenderer()函数中的配置项,通过配置项来最终实现我们的逻辑把在App.vue中重写的template渲染到canvas平台上,来看一下createRenderer()函数都有哪些配置项,如图:

    1vue3 のカスタム レンダラー機能の詳細な分析

    • 我们书写createRenderer()函数中的配置项,通过配置项来最终实现我们的逻辑,代码如下:
    import { createRenderer } from "vue";
    import { Graphics } from "pixi.js";
    
    
    const renderer = createRenderer({
      // 创建一个元素 --->  抽象的接口函数
      // vue执行时会调用这个函数中的逻辑
      createElement(type) {
        //参考vnode中的type 
        //因为我们书写了<circle></circle>
        //所以这里的type会有circle
        console.log(type);
        
        let element;
        //调用canvas api来创建矩形、圆形、图片等等
        //层级关系是后添加的在上面
        switch (type) {
          case "rect":
            element = new Graphics();
            element.beginFill(0xff0000);
            element.drawRect(0, 0, 500, 500);
            element.endFill();
            break;
          case "circle":
            element = new Graphics();
            element.beginFill(0xffff00);
            //第三个参数是圆的半径
            element.drawCircle(0, 0, 50);
            element.endFill();
            break;
        }
        
        //最终一定要返回element否则下方的函数接收不到
        return element;
      },
      patchProp(el, key, prevValue, nextValue) {
        
        /*
        向<circle x="50" y="50"></circle>中
        传递的props能在这里获取到
        利用这点可以去改变canvas容器中具体东西的行为
        比如改变位置、添加点击事件、等等
        如果传递的是响应式数据的话
        当响应式数据变更时canvas上的具体东西也会实时响应更新
        比如实时响应移动改变位置等等
        
        
        console.log(el,&#39;可以得到该对象&#39;);
        console.log(key,&#39;可以得到x和y&#39;);
        console.log(nextValue,&#39;可以得到50&#39;);
        */
        switch (key) {
          case "x":
            el.x = nextValue;
            break;
          case "y":
            el.y = nextValue;
            break;
          default:
            break;
        }
      },
      // 插入到对应的容器内
      insert(el, parent) {
        console.log(el, parent);
        /*
        el是上面的createElement函数中返回的element
    
        parent是render.createApp(App).mount(getRootContainer())中
        getRootContainer()的返回值即canvas容器game.stage;
    
        在该函数中把创建的东西(矩形、图形、圆形等等)添加到canvas容器内
        即game.stage.addChild(element);
        */
        parent.addChild(el);
      },
    });
    
    
    /*
    因为vue中自己暴露了默认可以渲染到dom平台上的createApp方法
    我们模仿这个行为也暴露一个自己封装好的渲染到canvas平台上的createApp方法
    
    只需要通过以下四行代码就可以开始使用了
    import {createApp} from &#39;./runtime-canvas/index&#39;;
    import App from &#39;./App.vue&#39;;
    import {getRootContainer} from &#39;./game/index&#39;;
    createApp(App).mount(getRootContainer());
    */
    
    export function createApp(rootComponent) {
      return renderer.createApp(rootComponent);
    }

    小案例

    • 先放上案例效果图:

    vue3 のカスタム レンダラー機能の詳細な分析

    • 来看一下目录结构:

    1vue3 のカスタム レンダラー機能の詳細な分析

    • 最后温故一下利用custom renderer渲染到canvas平台上的逻辑图:

    1vue3 のカスタム レンダラー機能の詳細な分析

    • 我们来看'main.js'文件的代码:
    //封装自定义渲染到canvas平台上的逻辑
    import { createApp } from "./runtime-canvas";
    import App from "./App.vue";
    //初始化canvas的容器
    import { getRootContainer } from "./game";
    createApp(App).mount(getRootContainer());
    • 我们来看"./game/index.js"文件的代码:
    import { Application } from "pixi.js";
    
    const game = new Application({
      width: 750,
      height: 750,
    });
    
    document.body.append(game.view);
    
    export function getRootContainer() {
      return game.stage;
    }
    
    export function getGame() {
      return game
    }
    • 我们紧接着看"./runtime-canvas/index.js"文件的代码:
    import { createRenderer } from "vue";
    import { Graphics } from "pixi.js";
    const renderer = createRenderer({
      createElement(type) {
        let element;
        switch (type) {
          case "rect":
            element = new Graphics();
            element.beginFill(0xff0000);
            element.drawRect(0, 0, 500, 500);
            element.endFill();
            break;
          case "circle":
          //创建球形
            element = new Graphics();
            element.beginFill(0xffff00);
            element.drawCircle(0, 0, 50);
            element.endFill();
            break;
        }
        return element;
      },
      patchProp(el, key, prevValue, nextValue) {
        switch (key) {
        //根据传递的props初始化‘具体东西元素’的位置
        //如果props是响应式数据那么在该响应式数据改变时
        //会被这里拦截到并实时响应更新视图位置
          case "x":
            el.x = nextValue;
            break;
          case "y":
            el.y = nextValue;
            break;
          default:
            break;
        }
      },
      insert(el, parent) {
        console.log(el, parent);
        //添加到canvas容器内
        parent.addChild(el);
      },
    });
    
    export function createApp(rootComponent) {
      return renderer.createApp(rootComponent);
    }
    • 我们再看'componenets/Circle.vue'文件的代码:
    <template>
      <circle></circle>
    </template>
    
    
    <script>
    export default {
    };
    </script>
    
    <style></style>
    • 我们最后来看App.vue文件的代码:
    <template>
    
        <Circle :x="x" :y="y" ref="circle"></Circle>
        
    </template>
    
    <script>
    import Circle from "./components/Circle";
    import {getGame} from &#39;./game/index&#39;;
    import {ref,onMounted, onUnmounted} from &#39;vue&#39;;
    export default {
      name: "App",
      components: {
        Circle,
      },
    
      setup() {
        let x=ref(&#39;50&#39;)
        let y=ref(&#39;50&#39;)
    
    
        const game=getGame()
        
        onMounted(()=>{
          // console.log(circle,&#39;circle&#39;);
          // console.log(game,&#39;game&#39;);
          // console.log(circle.value.$el,&#39;xx&#39;);
          
          
          game.ticker.add(handleTicker);
    
        });
    
        const handleTicker = function(){
          // console.log(circle.value.$el);
          circle.value.$el.x+=10
          if(circle.value.$el.x>700){
            game.ticker.remove(handleTicker);
            game.ticker.add(handleTicker2);
          }
        }
    
        const handleTicker2 = function(){
          // console.log(circle.value.$el);
          circle.value.$el.x-=10
          if(circle.value.$el.x<50){
            game.ticker.remove(handleTicker2)
            game.ticker.add(handleTicker);
          }
        };
    
    
        // console.log(circle,&#39;circle&#39;);
    
        let circle=ref(null)
    
    
        onUnmounted(() => {
          game.ticker.remove(handleTicker)
          game.ticker.remove(handleTicker2)
        })
    
    
        return{
          circle,
          handleTicker,
          x,
          y
        }
      }
    }
    </script>
    
    <style>
    </style>

    原文地址:https://juejin.cn/post/6910470057961193480

    作者:林子酱

    更多编程相关知识,请访问:编程视频!!

以上がvue3 のカスタム レンダラー機能の詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事は掘金--林子酱で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。