文章转自:https://blog.51cto.com/15077561/2594771

默认的目标渲染平台

  • 在vue3中允许用户自定义目标渲染平台,以往的版本中目标渲染被局限于浏览器dom平台,而现在可以把 vue 的开发模型扩展到其他平台。点击进入官网

  • Tips:以往解决把 vue 的开发模型扩展到其他平台(Canvas、iOS、Android等等)的方式之一是借助第三方工具例如WEEX(点击进入官网)

  • 我们先来弄懂vue是如何定义默认的目标渲染平台的,也就是说如何将目标渲染到浏览器dom平台上。可以先参考官方图:

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

  • 我们应该要发出疑惑,写在'./APP.vue'的template是怎么被渲染到浏览器dom平台上又被转换成真实的dom元素呢?如果让我们自己来做得怎么做到呢?我们可以在入口文件main.js中找到关键线索:

import App from './App.vue'
//我们可以打印出App来查看一下
console.log(App)
复制代码
  • 我们先打印出App查看一下,这是一个什么信息?如下图:

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

  • 我们可以借助工具来验证,如下图:

  • 如上图,我们可以明确的看到template经过了vue的特殊加工转换为了render函数,并且这个render函数会依照template的有用信息返回一个虚拟DOM(Vnode),关于虚拟DOM的描述可以参考官网点击进入官网

  • 我们在发现了这个线索之后,我们可以手动调用App对象下的render函数,来查看一下返回的虚拟DOM到底长什么样子,代码和图片如下:

import App from './App.vue'
console.log(App.render());
复制代码

  • 如上图,我们可以从返回的虚拟DOM中得到许多有用的信息,这里我用红色框出来的有用信息来简单实现一下如何渲染到浏览器dom平台上并且让其转换成真实的dom元素,代码如下:

//假设这个是虚拟Dom的信息
//仅仅是为了演示基本思想
const vnode={type:'div',children:'123'
}const element=document.creatElement(vnode.type)
element.innerText=vnode.children//告诉它的出口在哪里 要被渲染到哪里去
//这里的出口先假设为#app这个容器
document.querySelector('#app').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 'vue'
import App from './App.vue'/*
//mount的作用是告诉它的出口在哪里、要被渲染到哪里去
//核心思想就是刚才写的
//document.querySelector('#app').appendChild(element)
*/
createApp(App).mount('#app')
复制代码

自定义的目标渲染平台

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

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

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


  • 在实现之前,我们必须得先学会几个简单的关于canvas的api。为了快速上手,在这里我使用了pixi.js第三方工具(点击进入官网),pixi.js是基于canvas 的游戏渲染引擎库,借助pixi.js可以省去繁琐的操纵canvas的流程,让我们专心于感受vue3的新特性custom renderer的魅力。

  • 下面是使用pixi.js创建canvas并往canvas内添加各种东西的流程图:(最终为了可以直观的看到效果,将canvas呈现在浏览器上(**插入到dom**))

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

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:

  • 我们接下来如何操作来完成这一套自定义逻辑呢??有请我们今天的主角登场:custom renderer(点击进入官网)。

  • 我们先来重写App.vue里的代码,参考如下:

<template><!-- 这里的circle和rect是自定义标签不是组件不是组件不是组件 --><circle x="50" y="50"></circle>
</template><script>
export default {name: 'App',components: {}
}
</script>
复制代码
  • 我们接着重写入口文件main.js中的代码。参考如下:

/* 默认的渲染到浏览器dom平台上的代码import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
*//*自定义渲染到canvas平台上createRenderer就是告诉vue我要自定义渲染平台了
自定义渲染器可以传入特定于平台的类型
*/import { createRenderer } from "vue";
//我们不急着往createRenderer添加相关配置
//我们先打印render查看这个到底是个什么
const render=createRenderer({})
console.log(render,'render');
复制代码
  • 我们从vue中导出了createRenderer函数,在不配置任何选项的情况下打印出render来查看这到底是个什么东西??如下图:

  • 我们在打印出的图中可以发现两条熟悉的线索,一个是该render上有createApp方法另一个是该render上有render方法!!!!

  • 我们还记得import { createApp } from 'vue'这一句代码,这一句代码配合createApp(App).mount('#app')就将写在App.vue中的template给渲染到浏览器Dom平台上了,所以vue暴露出来的createApp是已经帮我们封装好逻辑的了。

  • 我们现在的任务是调用render下的createApp函数来封装实现我们的逻辑,渲染到canvas平台上,我们先来看下面的代码:

import {createRenderer } from 'vue'
import App from './App.vue'//自己要来写逻辑
const render=createRenderer({})/*
自己要来写逻辑  ---->  render下有createApp函数
调用createApp()方法后
返回的对象下依旧是有mount()方法的
*/
render.createApp(这里要填什么).mount(这里又要填什么)
复制代码

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

  • 我们书写createRenderer()函数中的配置项,通过配置项来最终实现我们的逻辑,代码如下:

import { createRenderer } from "vue";
import { Graphics } from "pixi.js";const renderer = createRenderer({// 创建一个元素 --->  抽象的接口函数// vue执行时会调用这个函数中的逻辑createElement(type) {//参考vnode中的type //因为我们书写了<circle></circle>//所以这里的type会有circleconsole.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容器中具体东西的行为比如改变位置、添加点击事件、等等console.log(el,'可以得到该对象');console.log(key,'可以得到x和y');console.log(nextValue,'可以得到50');*/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函数中返回的elementparent是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 './runtime-canvas/index';
import App from './App.vue';
import {getRootContainer} from './game/index';
createApp(App).mount(getRootContainer());
*/export function createApp(rootComponent) {return renderer.createApp(rootComponent);
}
复制代码

小案例

  • 先放上案例效果图:

  • 来看一下目录结构:

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

  • 我们来看'main.js'文件的代码:

import { createApp } from "./runtime-canvas";
import App from "./App.vue";
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) {case "x":el.x = nextValue;break;case "y":el.y = nextValue;break;default:break;}},insert(el, parent) {console.log(el, parent);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 './game/index';
import {ref,onMounted, onUnmounted} from 'vue';
export default {name: "App",components: {Circle,},setup() {let x=ref('50')let y=ref('50')const game=getGame()onMounted(()=>{// console.log(circle,'circle');// console.log(game,'game');// console.log(circle.value.$el,'xx');game.ticker.add(handleTicker);});const handleTicker = function(){// console.log(circle.value.$el);circle.value.$el.x+=10if(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-=10if(circle.value.$el.x<50){game.ticker.remove(handleTicker2)game.ticker.add(handleTicker);}};// console.log(circle,'circle');let circle=ref(null)onUnmounted(() => {game.ticker.remove(handleTicker)game.ticker.remove(handleTicker2)})return{circle,handleTicker,x,y}}
}
</script><style>
</style>
复制代码

vue3的新特性custom renderer相关推荐

  1. Vue3.0新特性及使用方法

    1.1创建Vue3.0项目 通过脚手架 vite 安装: npm init vite-app hello-vue3 # OR yarn create vite-app hello-vue3 Vite目 ...

  2. vue3.0新特性及用法

    1.性能上提升1.3-2倍 2.按需加载 和 compsition api(组合api) createApp 在 Vue 3 中,改变全局 Vue 行为的 API 现在被移动到了由新的 createA ...

  3. Vue3的新特性变化,上手指南!

    文章目录 一.Vue3相比Vue2,更新了什么变化? 二.Proxy 代理响应式原理 三.组合式 API (Composition API) setup()函数: ref()函数 reactive() ...

  4. input change获取改变之前的值和改变之后的值_使用Vue3.0新特性造轮子 WidgetUI3.0 (Input输入框组件)

    我们先看看组件效果图: 基本用法 type属性(type="password") 占位符(placeholder="请输入") 带清除按钮(clear) 输入限 ...

  5. accept 返回0_使用Vue3.0新特性造轮子 WidgetUI3.0 (Upload上传文件组件)

    我们先看看组件效果: 基本使用 可选参数( icon='icon-service') props属性: title(类型 String) 组件显示的文本,默认"上传'. icon(类型 St ...

  6. 卷不动也得继续学!紧跟vue3的步伐,再来get一波进阶新特性!

    vue3进阶新特性 一.

  7. VUE3对比VUE2的优势及新特性原理

    1.Vue3.0新特性 性能比Vue2.x快1.2~2倍 原因1: diff方法优化: vue2中的虚拟dom是全量的对比(每个节点不论写死的还是动态的都会比较) vue3新增了静态标记(patchf ...

  8. vue.3.0 dom赋值_Vue3.0的几大新特性

    今天写这篇文章主要是为了给自己重温下Vue3.0的新特性,也为了给各位前端朋友做个简单的介绍.目前vue3的开发已经在Alpha阶段,之后应该会有Beta版本,源码地址在:https://github ...

  9. Vue基础+Vue3新特性

    目录 模板语法 文本 原始HTML 属性Attribute 使用 JavaScript 表达式 条件渲染 v-if v-else v-show v-if vs v-show 的区别 列表渲染 事件处理 ...

最新文章

  1. 分布式系统唯一ID生成方案汇总
  2. 车用TVS管 SM8S系列 国产替代
  3. python人工智能——机器学习——数据的降维
  4. 编程能力强化(4)——模拟SQL语句解析
  5. 2018-08-13 Head First OO分析设计一书略读与例子中文化
  6. 46㎡老公房,经建筑设计师神级爆改,中国母子哭了出来
  7. C#中常用的几个委托
  8. python 连接 oracle 循环,4.使用cx_Oracle连接Oracle(高级篇)
  9. nodeJS之TCP模块net
  10. SPSS聚类分析——一个案例演示聚类分析全过程
  11. vulnhub DriftingBlues: 6
  12. 计算机换算单位 gb,mb与gb换算(gb转换mb计算器)
  13. android ppt 转图片显示不全,ppt转pdf图片显示不全怎么办 这两种方法供您参考
  14. 投资理财启蒙之定投出一个千万富翁?
  15. jenkins基础与gitlab代理自动构建
  16. springboot不停机更新(配合Nginx优雅发版)
  17. 明日风尚杂志明日风尚杂志社明日风尚编辑部2022年第8期目录
  18. React.SetStateAction<never[]>
  19. java 搭建个人博客_5分钟 教大家搭建免费个人博客
  20. 人工智能面临的主要威胁是对最终失去实际控制人工智能能力的恐惧

热门文章

  1. postcss-pxtorem px转成rem 项目配置
  2. 撩课-每天刷Web面试题(前10天汇总)-Day12
  3. matlab中表示拉普拉斯分布_分布拟合——正态/拉普拉斯/对数高斯/瑞利 分布
  4. linux find查找深度,linux find 用法
  5. linux find 递归搜索文件名
  6. 解析JDK1.7 HashMap 头插法生成的环形链表死循环问题
  7. 【转载】如何理解数据集中【训练集】、【验证集】和【测试集】
  8. 【C++】STL map 与 multimap 用法和区别
  9. 绕某一点旋转矩阵,分析,
  10. 机器学习sklearn-多项式回归