搜索
首页web前端Vue.js深入浅析vue3中的custom renderer特性

深入浅析vue3中的custom renderer特性

Jul 20, 2021 am 10:27 AM
vue.jsvue3新特性

本篇文章带大家一起来了解一下vue3的新特性custom renderer。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

深入浅析vue3中的custom renderer特性

【相关推荐:《vue.js教程》】

默认的目标渲染平台

  • 在vue3中允许用户自定义目标渲染平台,以往的版本中目标渲染被局限于浏览器dom平台,而现在可以把 vue 的开发模型扩展到其他平台。点击进入官网
  • Tips:以往解决把 vue 的开发模型扩展到其他平台(Canvas、iOS、Android等等)的方式之一是借助第三方工具例如WEEX(点击进入官网)
  • 我们先来弄懂vue是如何定义默认的目标渲染平台的,也就是说如何将目标渲染到浏览器dom平台上。可以先参考官方图:

    深入浅析vue3中的custom renderer特性

  • 我们先构建起一个初始化的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>
  • 写这两个文件后我们一运行命令npm run serve,会发现我们写在'./APP.vue'的template已经被渲染到浏览器dom平台上成为了真实的dom元素了,如下图:

深入浅析vue3中的custom renderer特性

  • 我们应该要发出疑惑,写在'./APP.vue'的template是怎么被渲染到浏览器dom平台上又被转换成真实的dom元素呢?如果让我们自己来做得怎么做到呢?我们可以在入口文件main.js中找到关键线索:
import App from &#39;./App.vue&#39;
//我们可以打印出App来查看一下
console.log(App)
  • 我们先打印出App查看一下,这是一个什么信息?如下图:

深入浅析vue3中的custom renderer特性

  • 在打印出来的App对象里,我们并没有在该对象上找到template属性!我们不禁发出更大的问号,没有了template的信息要怎么做到被转换成真实的dom元素???答案是依靠该对象上的render函数,可以理解为template经过了vue的特殊加工转换为了render函数,并且这个render函数会依照template的有用信息返回一个虚拟DOM(Vnode)
  • 我们可以借助工具来验证,如下图:

深入浅析vue3中的custom renderer特性

  • 如上图,我们可以明确的看到template经过了vue的特殊加工转换为了render函数,并且这个render函数会依照template的有用信息返回一个虚拟DOM(Vnode),关于虚拟DOM的描述可以参考官网点击进入官网
  • 我们在发现了这个线索之后,我们可以手动调用App对象下的render函数,来查看一下返回的虚拟DOM到底长什么样子,代码和图片如下:
import App from &#39;./App.vue&#39;
console.log(App.render());

深入浅析vue3中的custom renderer特性

  • 如上图,我们可以从返回的虚拟DOM中得到许多有用的信息,这里我用红色框出来的有用信息来简单实现一下如何渲染到浏览器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)
  • 我们这一整套的逻辑图如下:

深入浅析vue3中的custom renderer特性

  • 到了这一步我们也做到了如何将写在'./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中的custom renderer特性

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

深入浅析vue3中的custom renderer特性

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

深入浅析vue3中的custom renderer特性


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

深入浅析vue3中的custom renderer特性

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

1深入浅析vue3中的custom renderer特性

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:

1深入浅析vue3中的custom renderer特性

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

1深入浅析vue3中的custom renderer特性

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

1深入浅析vue3中的custom renderer特性

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

1深入浅析vue3中的custom renderer特性

  • 我们书写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中的custom renderer特性

  • 来看一下目录结构:

1深入浅析vue3中的custom renderer特性

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

1深入浅析vue3中的custom renderer特性

  • 我们来看'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中的custom renderer特性的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:掘金--林子酱。如有侵权,请联系admin@php.cn删除
Netflix:探索React(或其他框架)的使用Netflix:探索React(或其他框架)的使用Apr 23, 2025 am 12:02 AM

Netflix选择React来构建其用户界面,因为React的组件化设计和虚拟DOM机制能够高效处理复杂界面和频繁更新。1)组件化设计让Netflix将界面分解成可管理的小组件,提高了开发效率和代码可维护性。2)虚拟DOM机制通过最小化DOM操作,确保了Netflix用户界面的流畅性和高性能。

vue.js和前端:深入研究框架vue.js和前端:深入研究框架Apr 22, 2025 am 12:04 AM

Vue.js被开发者喜爱因为它易于上手且功能强大。1)其响应式数据绑定系统自动更新视图。2)组件系统提高了代码的可重用性和可维护性。3)计算属性和侦听器增强了代码的可读性和性能。4)使用VueDevtools和检查控制台错误是常见的调试技巧。5)性能优化包括使用key属性、计算属性和keep-alive组件。6)最佳实践包括清晰的组件命名、使用单文件组件和合理使用生命周期钩子。

vue.js在前端的力量:关键特征和好处vue.js在前端的力量:关键特征和好处Apr 21, 2025 am 12:07 AM

Vue.js是一个渐进式的JavaScript框架,适用于构建高效、可维护的前端应用。其关键特性包括:1.响应式数据绑定,2.组件化开发,3.虚拟DOM。通过这些特性,Vue.js简化了开发过程,提高了应用性能和可维护性,使其在现代Web开发中备受欢迎。

vue.js比反应好吗?vue.js比反应好吗?Apr 20, 2025 am 12:05 AM

Vue.js和React各有优劣,选择取决于项目需求和团队情况。1)Vue.js适合小型项目和初学者,因其简洁和易上手;2)React适用于大型项目和复杂UI,因其丰富的生态系统和组件化设计。

vue.js的功能:增强前端的用户体验vue.js的功能:增强前端的用户体验Apr 19, 2025 am 12:13 AM

Vue.js通过多种功能提升用户体验:1.响应式系统实现数据即时反馈;2.组件化开发提高代码复用性;3.VueRouter提供平滑导航;4.动态数据绑定和过渡动画增强交互效果;5.错误处理机制确保用户反馈;6.性能优化和最佳实践提升应用性能。

vue.js:定义其在网络开发中的作用vue.js:定义其在网络开发中的作用Apr 18, 2025 am 12:07 AM

Vue.js在Web开发中的角色是作为一个渐进式JavaScript框架,简化开发过程并提高效率。1)它通过响应式数据绑定和组件化开发,使开发者能专注于业务逻辑。2)Vue.js的工作原理依赖于响应式系统和虚拟DOM,优化性能。3)实际项目中,使用Vuex管理全局状态和优化数据响应性是常见实践。

了解vue.js:主要是前端框架了解vue.js:主要是前端框架Apr 17, 2025 am 12:20 AM

Vue.js是由尤雨溪在2014年发布的渐进式JavaScript框架,用于构建用户界面。它的核心优势包括:1.响应式数据绑定,数据变化自动更新视图;2.组件化开发,UI可拆分为独立、可复用的组件。

Netflix的前端:React(或VUE)的示例和应用Netflix的前端:React(或VUE)的示例和应用Apr 16, 2025 am 12:08 AM

Netflix使用React作为其前端框架。1)React的组件化开发模式和强大生态系统是Netflix选择它的主要原因。2)通过组件化,Netflix将复杂界面拆分成可管理的小块,如视频播放器、推荐列表和用户评论。3)React的虚拟DOM和组件生命周期优化了渲染效率和用户交互管理。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用