이 기사에서는 vue3의 새로운 기능인 사용자 정의 렌더러를 안내합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.
【관련 추천: "vue.js tutorial"】
Canvas, iOS, Android 등
)으로 확장하는 방법 중 하나가 Third -WEEX와 같은 파티 도구(클릭하여 공식 웹사이트로 들어가세요Canvas、iOS、Android等等
)的方式之一是借助第三方工具例如WEEX(点击进入官网)我们先来弄懂vue是如何定义默认的目标渲染平台的,也就是说如何将目标渲染到浏览器dom平台上。可以先参考官方图:
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)
(Vnode)
(Vnode)
)
다음은 main.js의 항목 파일입니다. 프로젝트 코드
import App from './App.vue' console.log(App.render());
'./APP.vue'
의 코드를 살펴보겠습니다. //假设这个是虚拟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'
에 작성한 템플릿이 표시된 것처럼 브라우저 DOM 플랫폼에 렌더링되어 실제 DOM 요소가 된 것을 확인할 수 있습니다. 아래:
의심을 불러일으키려면 './APP.vue'
에 작성된 템플릿이 어떻게 브라우저 DOM 플랫폼에 렌더링되고 실제 DOM 요소로 변환됩니까
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')🎜🎜먼저 앱을 인쇄하여 이것이 어떤 정보인지 확인해 보겠습니다. 아래와 같이: 🎜🎜🎜🎜🎜🎜인쇄된 App 개체에서 개체의 템플릿 속성을 찾지 못했습니다! 우리는 더 큰 질문을 제기하지 않을 수 없습니다. 템플릿이 없는 정보가 어떻게 실제 DOM 요소로 변환될 수 있습니까? ? ? 🎜답은 객체의 렌더링 함수에 의존하는 것입니다🎜 템플릿이 vue의 특수 처리를 통해 렌더링 함수로 변환되었으며 이 렌더링 함수는 가상 DOM
(Vnode)를 반환한다는 것을 알 수 있습니다. 코드는 템플릿의 유용한 정보에 따라 이루어집니다. >🎜🎜다음과 같은 도구를 사용하여 확인할 수 있습니다. 🎜🎜🎜<img src="https://img.php.cn/upload/image/867/%20612/468/162674681514309vue3%EC%9D%98%20%EC%82%AC%EC%9A%A9%EC%9E%90%20%EC%A0%95%EC%9D%98%20%EB%A0%8C%EB%8D%94%EB%9F%AC%20%EA%B8%B0%EB%8A%A5%EC%97%90%20%EB%8C%80%ED%95%9C%20%EC%8B%AC%EC%B8%B5%20%EB%B6%84%EC%84%9D" title="162674681514309vue3의 사용자 정의 렌더러 기능에 대한 심층 분석" alt="vue3의 사용자 정의 렌더러 기능에 대한 심층 분석">🎜🎜🎜위 그림과 같이 특수한 방법을 통해 템플릿이 렌더링 기능으로 변환된 것을 확실히 알 수 있습니다. vue로 처리하며, 이 렌더링 함수는 템플릿<code>(Vnode)
의 유용한 정보에 따라 가상 DOM을 반환합니다. 가상 DOM에 대한 설명은 공식 웹사이트를 참조하세요🎜클릭하여 입력하세요. 공식 웹사이트🎜🎜🎜이 단서를 발견한 후🎜App 개체🎜에서 렌더링 함수를 수동으로 호출하여 반환을 확인할 수 있습니다. 🎜가상 DOM은 어떻게 생겼나요🎜, 코드와 그림은 다음과 같습니다. 🎜🎜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);🎜 🎜🎜🎜🎜위에 표시된 대로 반환된 가상 DOM에서 🎜유용한 정보를 많이 얻을 수 있습니다🎜. 여기서는 빨간색 상자를 사용하여 유용한 정보를 강조 표시합니다. 자세한 내용은 브라우저 DOM 플랫폼에 렌더링하는 방법을 간단히 구현해 보겠습니다. 코드는 다음과 같습니다. 🎜🎜
<template> <!-- 这里的circle和rect是自定义标签 不是组件不是组件不是组件 --> <circle x="50" y="50"></circle> </template> <script> export default { name: 'App', components: { } } </script>🎜🎜 전체 논리 다이어그램 세트는 다음과 같습니다. 🎜🎜🎜🎜🎜
'./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')
注意分支
)pixi.js
第三方工具(点击进入官网),pixi.js
是基于canvas 的游戏渲染引擎库,借助pixi.js
可以省去繁琐的操纵canvas的流程,让我们专心于感受vue3的新特性custom renderer的魅力。pixi.js
创建canvas并往canvas内添加各种东西的流程图:(最终为了可以直观的看到效果,将canvas呈现在浏览器上(**插入到dom**)
)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平台上了。pixi.js
对canvas进行简单的操纵,并梳理了自定义渲染到canvas平台上的逻辑,让我们在回顾一下逻辑图,再开始着手使用vue3的新特性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');
import { createApp } from 'vue'
这一句代码,这一句代码配合createApp(App).mount('#app')
就将写在App.vue
中的template给渲染到浏览器Dom平台上了,所以vue暴露出来的createApp是已经帮我们封装好逻辑的了。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会有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,'可以得到该对象'); 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函数中返回的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 './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); }
先放上案例效果图:
'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 './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+=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,'circle'); 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!