溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

vue3中的custom renderer特性有什么用

發(fā)布時間:2021-07-21 13:48:47 來源:億速云 閱讀:357 作者:chen 欄目:編程語言

本篇內(nèi)容主要講解“vue3中的custom renderer特性有什么用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“vue3中的custom renderer特性有什么用”吧!

默認的目標渲染平臺

  • 在vue3中允許用戶自定義目標渲染平臺,以往的版本中目標渲染被局限于瀏覽器dom平臺,而現(xiàn)在可以把 vue 的開發(fā)模型擴展到其他平臺。點擊進入官網(wǎng)

  • Tips:以往解決把 vue 的開發(fā)模型擴展到其他平臺(Canvas、iOS、Android等等)的方式之一是借助第三方工具例如WEEX(點擊進入官網(wǎng))

  • 我們先來弄懂vue是如何定義默認的目標渲染平臺的,也就是說如何將目標渲染到瀏覽器dom平臺上??梢韵葏⒖脊俜綀D:

    vue3中的custom renderer特性有什么用

  • 我們先構(gòu)建起一個初始化的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,會發(fā)現(xiàn)我們寫在'./APP.vue'的template已經(jīng)被渲染到瀏覽器dom平臺上成為了真實的dom元素了,如下圖:

vue3中的custom renderer特性有什么用

  • 我們應(yīng)該要發(fā)出疑惑,寫在'./APP.vue'的template是怎么被渲染到瀏覽器dom平臺上又被轉(zhuǎn)換成真實的dom元素呢?如果讓我們自己來做得怎么做到呢?我們可以在入口文件main.js中找到關(guān)鍵線索:

import App from './App.vue'
//我們可以打印出App來查看一下
console.log(App)
  • 我們先打印出App查看一下,這是一個什么信息?如下圖:

vue3中的custom renderer特性有什么用

  • 在打印出來的App對象里,我們并沒有在該對象上找到template屬性!我們不禁發(fā)出更大的問號,沒有了template的信息要怎么做到被轉(zhuǎn)換成真實的dom元素???答案是依靠該對象上的render函數(shù),可以理解為template經(jīng)過了vue的特殊加工轉(zhuǎn)換為了render函數(shù),并且這個render函數(shù)會依照template的有用信息返回一個虛擬DOM(Vnode)

  • 我們可以借助工具來驗證,如下圖:

vue3中的custom renderer特性有什么用

  • 如上圖,我們可以明確的看到template經(jīng)過了vue的特殊加工轉(zhuǎn)換為了render函數(shù),并且這個render函數(shù)會依照template的有用信息返回一個虛擬DOM(Vnode),關(guān)于虛擬DOM的描述可以參考官網(wǎng)點擊進入官網(wǎng)

  • 我們在發(fā)現(xiàn)了這個線索之后,我們可以手動調(diào)用App對象下的render函數(shù),來查看一下返回的虛擬DOM到底長什么樣子,代碼和圖片如下:

import App from './App.vue'
console.log(App.render());

vue3中的custom renderer特性有什么用

  • 如上圖,我們可以從返回的虛擬DOM中得到許多有用的信息,這里我用紅色框出來的有用信息來簡單實現(xiàn)一下如何渲染到瀏覽器dom平臺上并且讓其轉(zhuǎn)換成真實的dom元素,代碼如下:

//假設(shè)這個是虛擬Dom的信息
//僅僅是為了演示基本思想
const vnode={
  type:'div',
  children:'123'
}


const element=document.creatElement(vnode.type)
element.innerText=vnode.children


//告訴它的出口在哪里 要被渲染到哪里去
//這里的出口先假設(shè)為#app這個容器
document.querySelector('#app').appendChild(element)
  • 我們這一整套的邏輯圖如下:

vue3中的custom renderer特性有什么用

  • 到了這一步我們也做到了如何將寫在'./APP.vue'的template渲染到瀏覽器dom平臺上并且轉(zhuǎn)換成真實的dom元素(雖然寫的代碼很菜),可是這一套邏輯vue已經(jīng)幫我們實現(xiàn)了,我們現(xiàn)在再來看入口文件main.js的代碼

/*
//createApp的作用是將傳入的組件轉(zhuǎn)換為真實的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')

自定義的目標渲染平臺

  • 我們在實現(xiàn)自定義的目標渲染平臺之前,還得在溫習(xí)一遍默認的目標渲染平臺的流程邏輯圖,如下圖:

vue3中的custom renderer特性有什么用

  • 我們知道canvas也是一個平臺,這里就以如何使用vue3渲染到canvas平臺上來舉例說明。我們先來看成果圖:

vue3中的custom renderer特性有什么用

  • 我們即將要實現(xiàn)使用vue3的新特性custom renderer來將目標元素渲染到canvas平臺上,我們現(xiàn)在實現(xiàn)的邏輯圖如下:(注意分支)

vue3中的custom renderer特性有什么用


  • 在實現(xiàn)之前,我們必須得先學(xué)會幾個簡單的關(guān)于canvas的api。為了快速上手,在這里我使用了pixi.js第三方工具(點擊進入官網(wǎng)),pixi.js是基于canvas 的游戲渲染引擎庫,借助pixi.js可以省去繁瑣的操縱canvas的流程,讓我們專心于感受vue3的新特性custom renderer的魅力。

  • 下面是使用pixi.js創(chuàng)建canvas并往canvas內(nèi)添加各種東西的流程圖:(最終為了可以直觀的看到效果,將canvas呈現(xiàn)在瀏覽器上(**插入到dom**))

vue3中的custom renderer特性有什么用

  • 在vue3的項目使用安裝npm i pixi.js后,我們來看一下簡單的關(guān)于canvas的使用方式,代碼和簡圖如下:

vue3中的custom renderer特性有什么用

import {
  //初始化
  Application,
  //創(chuàng)建矩形
  Graphics,
  //創(chuàng)建圖片
  Sprite,
  //創(chuàng)建文字
  Texture,
  Text,
  TextStyle,
  //創(chuàng)建容器
  Container,
} from "pixi.js";

/*
通過 new Application來初始化創(chuàng)建canvas
options規(guī)定創(chuàng)建的canvas的寬和高
*/
const game = new Application({
  width: 500,
  height: 500,
});


/*
為了可以直觀的看到效果
將canvas呈現(xiàn)在瀏覽器上(**插入到dom**)

game.view是canvas視圖元素
*/
document.body.append(game.view);



/*
創(chuàng)建一個矩形
rect.x和rect.y是設(shè)置矩形的初始位置偏移量

//單獨(獨自)添加矩形到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;



/*
創(chuàng)建圖片

//單獨(獨自)添加矩形到canvas容器上使用下一行命令
game.stage.addChild(img);
*/
import logo from "./assets/logo.png";
const img = new Sprite();
//指定后才允許給圖片添加點擊事件
img.interactive = true;
//指定圖片的src路徑
img.texture = Texture.from(logo);
//添加幀循環(huán) 會一直執(zhí)行handleTicker事件直至刪除該幀循環(huán)
game.ticker.add(handleTicker);
//handleTicker事件 令圖片的x偏移量不斷增加
const handleTicker = () => {img.x++};

/*
pixi的點擊事件名 
必須配合img.interactive = true才能允許被點擊
*/
img.on("pointertap", () => {
  game.ticker.remove(handleTicker);
});


/*
創(chuàng)建文本

//單獨(獨自)添加矩形到canvas容器上使用下一行命令
game.stage.addChild(text);
*/
const text = new Text("heihei");
text.style = new TextStyle({
  fill: "red",
});
text.x = 380;


/*
創(chuàng)建容器

//容器中可以放圖片、文字、矩形等等
//容器是一個大的整體

//將容器添加到canvas上的話
//容器中的內(nèi)容也會一并被添加到canvas上
//即下一行代碼
game.stage.addChild(box);
*/
const box = new Container();
box.addChild(text);
box.addChild(img);
//統(tǒng)一的移動它們的位置
box.x = 2

/*
如果你想要把你創(chuàng)建的東西渲染到canvas容器內(nèi)的話
必須把東西通過game.stage.addChild的方式添加進去才能顯示
*/

//單獨添加以添加矩形為例
game.stage.addChild(rect);


//添加一個容器
//(容器中可以包含圖片、文字等等也會被一并添加上canvas)
game.stage.addChild(box);
  • 我們現(xiàn)在借助pixi.js學(xué)會了對canvas的簡單操縱,接下來我們就要使用vue3的custom renderer來將元素渲染到canvas平臺上了。

自定義渲染到canvas平臺上

  • 我們在上一講已經(jīng)學(xué)會了借助pixi.js對canvas進行簡單的操縱,并梳理了自定義渲染到canvas平臺上的邏輯,讓我們在回顧一下邏輯圖,再開始著手使用vue3的新特性custom renderer:

vue3中的custom renderer特性有什么用

  • 我們接下來如何操作來完成這一套自定義邏輯呢??有請我們今天的主角登場:custom renderer(點擊進入官網(wǎng))。

  • 我們先來重寫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添加相關(guān)配置
//我們先打印render查看這個到底是個什么
const render=createRenderer({})
console.log(render,'render');
  • 我們從vue中導(dǎo)出了createRenderer函數(shù),在不配置任何選項的情況下打印出render來查看這到底是個什么東西??如下圖:

vue3中的custom renderer特性有什么用

  • 我們在打印出的圖中可以發(fā)現(xiàn)兩條熟悉的線索,一個是該render上有createApp方法另一個是該render上有render方法!!!!

  • 我們還記得import { createApp } from 'vue'這一句代碼,這一句代碼配合createApp(App).mount('#app')就將寫在App.vue中的template給渲染到瀏覽器Dom平臺上了,所以vue暴露出來的createApp是已經(jīng)幫我們封裝好邏輯的了。

  • 我們現(xiàn)在的任務(wù)是調(diào)用render下的createApp函數(shù)來封裝實現(xiàn)我們的邏輯,渲染到canvas平臺上,我們先來看下面的代碼:

import {createRenderer } from 'vue'
import App from './App.vue'

//自己要來寫邏輯
const render=createRenderer({})


/*
自己要來寫邏輯  ---->  render下有createApp函數(shù)
調(diào)用createApp()方法后
返回的對象下依舊是有mount()方法的
*/
render.createApp(這里要填什么).mount(這里又要填什么)

vue3中的custom renderer特性有什么用

  • 我們在上面的代碼中先不考慮要怎么書寫createRenderer()函數(shù)中的配置項封裝邏輯,先來考慮render.createApp(這里要填什么).mount(這里又要填什么)這兩個空要怎么填的問題?

  • 我們參考createApp(App).mount('#app')便可以知道第一個空應(yīng)該要填的是根組件,在這里我們同樣填的是import App from './App.vue'導(dǎo)出的App,第二個空應(yīng)該要填的是根容器,我們需要的是渲染到canvas平臺上,所以我們的根容器得是game.stage(這里的game.stage是經(jīng)過pixi.js初始化后的canvas容器),代碼如下:

import { Application } from "pixi.js";

//通過 new Application來初始化創(chuàng)建canvas
const game = new Application({
  width: 750,
  height: 750,
});

// 為了可以直觀的看到效果
// 將canvas呈現(xiàn)在瀏覽器上(**插入到dom**)
document.body.append(game.view);

/*
導(dǎo)出canvas容器供
render.createApp(這里要填什么).mount(getRootContainer())
使用
*/
export function getRootContainer() {
  return game.stage;
}
  • 緊接著我們就來書寫createRenderer()函數(shù)中的配置項,通過配置項來最終實現(xiàn)我們的邏輯把在App.vue中重寫的template渲染到canvas平臺上,來看一下createRenderer()函數(shù)都有哪些配置項,如圖:

vue3中的custom renderer特性有什么用

  • 我們書寫createRenderer()函數(shù)中的配置項,通過配置項來最終實現(xiàn)我們的邏輯,代碼如下:

import { createRenderer } from "vue";
import { Graphics } from "pixi.js";


const renderer = createRenderer({
  // 創(chuàng)建一個元素 --->  抽象的接口函數(shù)
  // vue執(zhí)行時會調(diào)用這個函數(shù)中的邏輯
  createElement(type) {
    //參考vnode中的type 
    //因為我們書寫了<circle></circle>
    //所以這里的type會有circle
    console.log(type);
    
    let element;
    //調(diào)用canvas api來創(chuàng)建矩形、圓形、圖片等等
    //層級關(guān)系是后添加的在上面
    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);
        //第三個參數(shù)是圓的半徑
        element.drawCircle(0, 0, 50);
        element.endFill();
        break;
    }
    
    //最終一定要返回element否則下方的函數(shù)接收不到
    return element;
  },
  patchProp(el, key, prevValue, nextValue) {
    
    /*
    向<circle x="50" y="50"></circle>中
    傳遞的props能在這里獲取到
    利用這點可以去改變canvas容器中具體東西的行為
    比如改變位置、添加點擊事件、等等
    如果傳遞的是響應(yīng)式數(shù)據(jù)的話
    當響應(yīng)式數(shù)據(jù)變更時canvas上的具體東西也會實時響應(yīng)更新
    比如實時響應(yīng)移動改變位置等等
    
    
    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;
    }
  },
  // 插入到對應(yīng)的容器內(nèi)
  insert(el, parent) {
    console.log(el, parent);
    /*
    el是上面的createElement函數(shù)中返回的element

    parent是render.createApp(App).mount(getRootContainer())中
    getRootContainer()的返回值即canvas容器game.stage;

    在該函數(shù)中把創(chuàng)建的東西(矩形、圖形、圓形等等)添加到canvas容器內(nèi)
    即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);
}

小案例

  • 先放上案例效果圖:

vue3中的custom renderer特性有什么用

  • 來看一下目錄結(jié)構(gòu):

vue3中的custom renderer特性有什么用

  • 最后溫故一下利用custom renderer渲染到canvas平臺上的邏輯圖:

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":
      //創(chuàng)建球形
        element = new Graphics();
        element.beginFill(0xffff00);
        element.drawCircle(0, 0, 50);
        element.endFill();
        break;
    }
    return element;
  },
  patchProp(el, key, prevValue, nextValue) {
    switch (key) {
    //根據(jù)傳遞的props初始化‘具體東西元素’的位置
    //如果props是響應(yīng)式數(shù)據(jù)那么在該響應(yīng)式數(shù)據(jù)改變時
    //會被這里攔截到并實時響應(yīng)更新視圖位置
      case "x":
        el.x = nextValue;
        break;
      case "y":
        el.y = nextValue;
        break;
      default:
        break;
    }
  },
  insert(el, parent) {
    console.log(el, parent);
    //添加到canvas容器內(nèi)
    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>

到此,相信大家對“vue3中的custom renderer特性有什么用”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI