Maison  >  Article  >  interface Web  >  Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

青灯夜游
青灯夜游avant
2021-07-20 10:27:432747parcourir

Cet article vous présentera la nouvelle fonctionnalité de vue3, le moteur de rendu personnalisé. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il sera utile à tout le monde.

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

【Recommandation associée : "tutoriel vue.js"】

Plateforme de rendu cible par défaut

  • Dans vue3, les utilisateurs sont autorisés à personnaliser la plate-forme de rendu cible dans les versions précédentes, le rendu cible était limité. à la plate-forme Browser DOM, et désormais le modèle de développement Vue peut être étendu à d'autres plates-formes. Cliquez pour accéder au site officiel
  • Conseils : Dans le passé, l'un des moyens d'étendre le modèle de développement de vue à d'autres plates-formes (Canvas、iOS、Android等等) consistait à utiliser des outils tiers tels que WEEX (Cliquez pour accéder au site officiel website)
  • Comprenons-le d'abord Comment vue définit-elle la plate-forme de rendu cible par défaut, c'est-à-dire comment restituer la cible sur la plate-forme dom du navigateur. Vous pouvez d'abord vous référer à l'image officielle :

    Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • Nous construisons d'abord un nouveau projet vue3 initialisé pour analyser étape par étape comment vue restitue la cible à la plate-forme dom du navigateur par défaut Ce qui suit est le fichier d'entrée dans. le projet Le code de main.js
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')
  • Enfin, jetons un œil au code de './APP.vue' :
<template>
  <div>我是根组件实例</div>
</template>


<script>
export default {
  name: &#39;App&#39;,
  components: {
  }
}
</script>
  • Après avoir écrit ces deux fichiers, lorsque nous exécuterons la commande npm run serve, nous constaterons que le modèle nous avons écrit dans './APP.vue' a été rendu au navigateur Il est devenu un véritable élément dom sur la plateforme dom du navigateur, comme indiqué ci-dessous :

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • Nous devrions nous demander comment le modèle écrit dans est rendu sur la plateforme dom du navigateur puis converti en un vrai. Qu'en est-il des éléments DOM'./APP.vue' Comment pouvons-nous le faire si nous le faisons nous-mêmes ? Nous pouvons trouver l'indice clé dans le fichier d'entrée  : main.js
  • import App from &#39;./App.vue&#39;
    //我们可以打印出App来查看一下
    console.log(App)
    Imprimons d'abord l'application pour vérifier de quelles informations s'agit-il ? Comme indiqué ci-dessous :

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

    Dans l'objet App imprimé, nous n'avons pas trouvé l'attribut de modèle sur l'objet ! Nous ne pouvons pas nous empêcher de soulever une question plus vaste : comment les informations sans modèle peuvent-elles être converties en véritables éléments DOM ? ? ?
  • La réponse est de s'appuyer sur la fonction de rendu sur l'objet. On peut comprendre que le modèle a été converti en fonction de rendu grâce à un traitement spécial par vue, et cette fonction de rendu renverra un DOM virtuel selon les informations utiles de le modèle(Vnode)
  • Nous pouvons utiliser des outils pour vérifier, comme indiqué ci-dessous :

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

    Comme le montre l'image ci-dessus, nous pouvons clairement voir que le modèle a été converti en fonction de rendu grâce à un traitement spécial par vue, et cette fonction de rendu renverra un DOM virtuel
  • selon les informations utiles du modèle. Concernant le virtuel La description du DOM peut être trouvée sur le site officiel(Vnode)Cliquez pour accéder au site officiel
  • Après avoir découvert cet indice,
  • nous peut appeler manuellement la fonction de rendu sous l'objet App pour voir à quoi ressemble le DOM virtuel renvoyé, le code et l'image sont les suivants :
  • import App from &#39;./App.vue&#39;
    console.log(App.render());

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

    Comme indiqué ci-dessus, nous pouvons
  • obtenir beaucoup d'informations utiles du DOM virtuel renvoyé Ici, j'utilise les informations utiles encadrées en rouge pour simplement implémenter comment le rendre sur la plate-forme dom du navigateur et le convertir en un véritable élément dom , le code est le suivant :
  • //假设这个是虚拟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)
    .
    Notre ensemble complet de schémas logiques est le suivant :

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • 到了这一步我们也做到了如何将写在'./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;)

自定义的目标渲染平台

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

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

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

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

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

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3


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

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

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

1Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

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:

1Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • 我们接下来如何操作来完成这一套自定义逻辑呢??有请我们今天的主角登场: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来查看这到底是个什么东西??如下图:

1Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • 我们在打印出的图中可以发现两条熟悉的线索,一个是该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(这里又要填什么)

1Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • 我们在上面的代码中先不考虑要怎么书写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()函数都有哪些配置项,如图:

1Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • 我们书写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);
}

小案例

  • 先放上案例效果图:

Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • 来看一下目录结构:

1Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

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

1Une analyse approfondie des fonctionnalités du moteur de rendu personnalisé dans vue3

  • 我们来看'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

作者:林子酱

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer