Vue 内部的组件是以虚拟 dom 形式存在的。下面的代码就是一个很常见的虚拟 Dom,用对象的方式去描述一个项目。相比 dom 标签相比,这种形式可以让整个 Vue 项目脱离浏览器的限制,更方便地实现 Vuejs 的跨端


{tag: 'div',props: {id: 'app'},chidren: [{tag: Container,props: {className: 'el-container'},chidren: ['Hello Little Gay!!!']}]
}

渲染器是围绕虚拟 Dom 存在的。在浏览器中,我们把虚拟 Dom 渲染成真实的 Dom 对象,Vue 源码内部把一个框架里所有和平台相关的操作,抽离成了独立的方法。所以,我们只需要实现下面这些方法,就可以实现 Vue 3 在一个平台的渲染。

  1. 首先用 createElement 创建标签,还有用 createText 创建文本。创建之后就需要用 insert 新增元素,通过 remote 删除元素,通过 setText 更新文本和 patchProps 修改属性。
  2. 然后再实现 parentNode、nextSibling 等方法实现节点的查找关系。完成这些工作,理论上就可以在一个平台内实现一个应用了。

在 Vue 3 中的 runtime-core 模块,就对外暴露了这些接口,runtime-core 内部基于这些函数实现了整个 Vue 内部的所有操作,然后在 runtime-dom 中传入以上所有方法。

下面的代码就是 Vue 代码提供浏览器端操作的函数,这些 DOM 编程接口完成了浏览器端增加、添加和删除操作,这些 API 都是浏览器端独有的,如果一个框架强依赖于这些函数,那就只能在浏览器端运行。


export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {//插入元素insert: (child, parent, anchor) => {parent.insertBefore(child, anchor || null)},// 删除元素remove: child => {const parent = child.parentNodeif (parent) {parent.removeChild(child)}},// 创建元素createElement: (tag, isSVG, is, props): Element => {const el = isSVG? doc.createElementNS(svgNS, tag): doc.createElement(tag, is ? { is } : undefined)if (tag === 'select' && props && props.multiple != null) {;(el as HTMLSelectElement).setAttribute('multiple', props.multiple)}return el}//...其他操作函数
}

如果一个框架想要实现实现跨端的功能,那么渲染器本身不能依赖任何平台下特有的接口。

在后面的代码中,我们通过 createRenderer 函数区创建了一个渲染器。通过参数 options 获取增删改查所有的函数以后,在内部的 render、mount、patch 等函数中,需要去渲染一个元素的时候,就可以通过 option.createElement 和 option.insert 来实现。


export default function createRenderer(options) {const {insert: hostInsert,remove: hostRemove,patchProp: hostPatchProp,createElement: hostCreateElement,createText: hostCreateText,createComment: hostCreateComment,setText: hostSetText,setElementText: hostSetElementText,parentNode: hostParentNode,nextSibling: hostNextSibling,setScopeId: hostSetScopeId = NOOP,cloneNode: hostCloneNode,insertStaticContent: hostInsertStaticContent} = optionsfunction render(vnode, container) {  }function mount(vnode, container, isSVG, refNode) {  }function mountElement(vnode, container, isSVG, refNode) {  }function mountText(vnode, container) {  }function patch(prevVNode, nextVNode, container) {  }function replaceVNode(prevVNode, nextVNode, container) {  }function patchElement(prevVNode, nextVNode, container) {  }function patchChildren(prevChildFlags,nextChildFlags,prevChildren,nextChildren,container) {  }function patchText(prevVNode, nextVNode) {  }function patchComponent(prevVNode, nextVNode, container) {  }return { render }
}

自定义渲染器让 Vue 脱离了浏览器的限制,我们只需要实现平台内部的增删改查函数后,就可以直接对接 Vue 3。比方说,我们可以把 Vue 渲染到小程序平台,实现 Vue 3-minipp;也可以渲染到 Canvas,实现 vue 3-canvas,把虚拟 dom 渲染成 Canvas;甚至还可以尝试把 Vue 3 渲染到 threee.js 中,在 3D 世界使用响应式开发。


import { createRenderer } from '@vue/runtime-core'
import * as THREE from 'three'
import {nextTick} from '@vue/runtime-core'let rendererfunction draw(obj) {const {camera,cameraPos, scene, geometry,geometryArg,material,mesh,meshY,meshX} = objif([camera,cameraPos, scene, geometry,geometryArg,material,mesh,meshY,meshX].filter(v=>v).length<9){return }let cameraObj = new THREE[camera]( 40, window.innerWidth / window.innerHeight, 0.1, 10 )Object.assign(cameraObj.position,cameraPos)let sceneObj = new THREE[scene]()let geometryObj = new THREE[geometry]( ...geometryArg)let materialObj = new THREE[material]()let meshObj = new THREE[mesh]( geometryObj, materialObj )meshObj.rotation.x = meshXmeshObj.rotation.y = meshYsceneObj.add( meshObj )renderer.render( sceneObj, cameraObj );}const { createApp: originCa } = createRenderer({insert: (child, parent, anchor) => {if(parent.domElement){draw(child)}},createElement(type, isSVG, isCustom) {return {type}},setElementText(node, text) {},patchProp(el, key, prev, next) {el[key] = nextdraw(el)},parentNode: node => node,nextSibling: node => node,createText: text => text,remove:node=>node});
function createApp(...args) {const app = originCa(...args)return {mount(selector) {renderer = new THREE.WebGLRenderer( { antialias: true } );renderer.setSize( window.innerWidth, window.innerHeight );document.body.appendChild( renderer.domElement );app.mount(renderer)}}
}
export { createApp }
import {Graphics} from "PIXI.js";export const getNodeOps = (app) => {return {insert: (child, parent, anchor) => {parent.addChild(child);},remove: (child) => {const parent = child.parentNode;if (parent) {parent.removeChild(child);}},createElement: (tag, isSVG, is) => {let element;if (tag === "Rectangle") {// 创建一个矩形element = new window.PIXI.Graphics();element.lineStyle(4, 0xff3300, 1);element.beginFill(0x66ccff);element.drawRect(0, 0, 64, 64);element.endFill();element.x = 0;element.y = 0;// Opt-in to interactivityelement.interactive = true;// Shows hand cursorelement.buttonMode = true;} else if (tag === "Sprite") {element = new window.PIXI.Sprite();element.x = 0;element.y = 0;} else if (tag === "Container") {element = new window.PIXI.Container();element.x = 0;element.y = 0;}return element;},createText: (text) => doc.createTextNode(text),createComment: (text) => {//   console.log(text);},setText: (node, text) => {node.nodeValue = text;},setElementText: (el, text) => {el.textContent = text;},parentNode: (node) => node.parentNode,nextSibling: (node) => node.nextSibling,querySelector: (selector) => doc.querySelector(selector),setScopeId(el, id) {el.setAttribute(id, "");},cloneNode(el) {return el.cloneNode(true);},};
};

自定义渲染器的原理,就是把所有的增删改查操作暴露出去,使用的时候不需要知道内部的实现细节,我们只需要针对每个平台使用不同的 API 即可

就像武侠小说中高手可以通过给你传输内力的方式控制你进行比武。我们打出去的每招每式都是来源于背后的高手,只不过自己做了简单的适配。在 Vue 渲染器的设计中就把 document 所有的操作都抽离成了 nodeOps,并且通过调用 Vue 的 createRenderer 函数创建平台的渲染器。

只要我们实现了 Canvas 平台的增删改查,就可以在 Canvas 的世界中使用 Vue 的响应式语法控制绘图和做游戏,Vue 生态中对小程序和原生 app 的支持原理也是基于自定义渲染器实现的。

自定义渲染器也代表着适配器设计模式的一个实践。除了自定义渲染器 API 的学习,我们也要反思一下自己现在负责的项目中,有哪些地方为了不同的接口或者平台写了太多的判断代码,是否也可以使用类似自定义渲染器的逻辑和模式,把多个组件、平台、接口之间不同的操作方式封装成一个核心模块,去进行单独函数的扩展。

后面有空再写: Vue 如何在 node 环境中渲染呢?

Vue3 : 实现Vue的跨端渲染相关推荐

  1. 前端vue适配不同的分辨率_浅析 React / Vue 跨端渲染原理与实现

    当下的前端同学对 React 与 Vue 的组件化开发想必不会陌生,RN 与 Weex 的跨界也常为我们所津津乐道.UI 框架在实现这样的跨端渲染时需要做哪些工作,其技术方案能否借鉴乃至应用到我们自己 ...

  2. Vue SSR 服务端渲染原理(简易版本)

    前言 在了解Vue SSR之前,我们要搞明白两个东西先:SSR 和 浏览器的渲染, 涉及到的技术: Vue vue-server-renderer Nodejs Express 1. 什么是SSR S ...

  3. Vuex 数据流管理及Vue.js 服务端渲染(SSR)

    Vuex 数据流管理及Vue.js 服务端渲染(SSR)项目见:https://github.com/smallSix6/fed-e-task-liuhuijun/tree/master/fed-e- ...

  4. Vue.js 服务端渲染

    服务端渲染 SSR 完全指南 在 2.3 发布后我们发布了一份完整的构建 Vue 服务端渲染应用的指南.这份指南非常深入,适合已经熟悉 Vue, webpack 和 Node.js 开发的开发者阅读. ...

  5. Vue开发跨端应用(六)添加onsenui组件库

    安装: npm install onsenui vue-onsenui --save 为什么使用onsenui:https://onsen.io/vue/ 1.拥有大量专门为移动应用设计的ui组件 2 ...

  6. Vue开发跨端应用(一)环境搭建

    前言: 基于vue开发一款跨三端的简单环境,使用electron开发桌面程序,使用cordova开发移动应用 github: https://github.com/317482454/electron ...

  7. Vue开发跨端应用(二)修改electron demo

    项目启动 npm run dev 界面如下: 我们需要删除electron特有的组件,因为里面使用到了一些web没有的属性 删除vue组件: 新建index.vue 接下来修改router 打开运行桌 ...

  8. Vue开发跨端应用(七)添加生成二维码

    二维码组件:QRCode npm i qrcode -s 这里需要注意的是不能放在 created 事件里面,需要放在 mounted事件 var QRCode = require('qrcode') ...

  9. Vue开发跨端应用(五)cordova-ios运行问题

    错误信息如下: Check dependencies Code Signing Error: Signing for "wallet-app" requires a develop ...

最新文章

  1. CAS证书分析(2)
  2. 品牌推广前期要进行哪些针对性的步骤?
  3. pat00-自测2. 素数对猜想 (20)
  4. wpf label字体为斜体_快来收下这份字体设计必备知识点
  5. mfc 如何将cstring转byte_如何将PDF转成JPG?PDF转图片的技巧
  6. 盘点一款Python发包收包利器——scapy
  7. 龙卷风代码html,龙卷风旋涡.html
  8. 利用Q-learning解决Cliff-walking问题
  9. Dom(二十一) 拖拽
  10. 网易2019:矩形重叠
  11. 达梦数据库分区表介绍
  12. 计算机由哪几种显卡,各类显卡大比拼,你会选择哪一款显卡使用?
  13. python方差齐性检验_方差分析中的方差齐性检验_方差齐性检验结果分析
  14. Python 3.7极速入门教程9最佳python中文书籍下载
  15. base64字符串实现下载文件
  16. Mac新手必看教程 Mac系统基本设置 苹果电脑的基本操作
  17. html4.0.1兼容ie7,CSS 完美兼容IE6/IE7/FF的通用hack方法
  18. Activity的生命周期之图记表查
  19. vue3 计算滚动条的滚动距离
  20. 显示器跟服务器的最大距离,关于显示器与人眼之间的距离

热门文章

  1. Linux及操作系统介绍
  2. 海信IP906H-云南电信高安-50T1版-当贝纯净桌面-精简卡刷刷机包
  3. 修改psd图片内文字或图标的颜色
  4. WebSocket 协议详述( java在线聊天室_上篇)
  5. 中软实训-day1 Maven的基本知识
  6. CQUPT Java 随堂测试1
  7. Python绘制饼状图
  8. 判断密码是否包含键盘连续字符【灭霸级】
  9. 阿里移动技术峰会的一些体会 2015-07-04
  10. 菜鸟学数电1-如何看懂TTL名称