Vue3 : 实现Vue的跨端渲染
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 在一个平台的渲染。
- 首先用 createElement 创建标签,还有用 createText 创建文本。创建之后就需要用 insert 新增元素,通过 remote 删除元素,通过 setText 更新文本和 patchProps 修改属性。
- 然后再实现 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的跨端渲染相关推荐
- 前端vue适配不同的分辨率_浅析 React / Vue 跨端渲染原理与实现
当下的前端同学对 React 与 Vue 的组件化开发想必不会陌生,RN 与 Weex 的跨界也常为我们所津津乐道.UI 框架在实现这样的跨端渲染时需要做哪些工作,其技术方案能否借鉴乃至应用到我们自己 ...
- Vue SSR 服务端渲染原理(简易版本)
前言 在了解Vue SSR之前,我们要搞明白两个东西先:SSR 和 浏览器的渲染, 涉及到的技术: Vue vue-server-renderer Nodejs Express 1. 什么是SSR S ...
- Vuex 数据流管理及Vue.js 服务端渲染(SSR)
Vuex 数据流管理及Vue.js 服务端渲染(SSR)项目见:https://github.com/smallSix6/fed-e-task-liuhuijun/tree/master/fed-e- ...
- Vue.js 服务端渲染
服务端渲染 SSR 完全指南 在 2.3 发布后我们发布了一份完整的构建 Vue 服务端渲染应用的指南.这份指南非常深入,适合已经熟悉 Vue, webpack 和 Node.js 开发的开发者阅读. ...
- Vue开发跨端应用(六)添加onsenui组件库
安装: npm install onsenui vue-onsenui --save 为什么使用onsenui:https://onsen.io/vue/ 1.拥有大量专门为移动应用设计的ui组件 2 ...
- Vue开发跨端应用(一)环境搭建
前言: 基于vue开发一款跨三端的简单环境,使用electron开发桌面程序,使用cordova开发移动应用 github: https://github.com/317482454/electron ...
- Vue开发跨端应用(二)修改electron demo
项目启动 npm run dev 界面如下: 我们需要删除electron特有的组件,因为里面使用到了一些web没有的属性 删除vue组件: 新建index.vue 接下来修改router 打开运行桌 ...
- Vue开发跨端应用(七)添加生成二维码
二维码组件:QRCode npm i qrcode -s 这里需要注意的是不能放在 created 事件里面,需要放在 mounted事件 var QRCode = require('qrcode') ...
- Vue开发跨端应用(五)cordova-ios运行问题
错误信息如下: Check dependencies Code Signing Error: Signing for "wallet-app" requires a develop ...
最新文章
- CAS证书分析(2)
- 品牌推广前期要进行哪些针对性的步骤?
- pat00-自测2. 素数对猜想 (20)
- wpf label字体为斜体_快来收下这份字体设计必备知识点
- mfc 如何将cstring转byte_如何将PDF转成JPG?PDF转图片的技巧
- 盘点一款Python发包收包利器——scapy
- 龙卷风代码html,龙卷风旋涡.html
- 利用Q-learning解决Cliff-walking问题
- Dom(二十一) 拖拽
- 网易2019:矩形重叠
- 达梦数据库分区表介绍
- 计算机由哪几种显卡,各类显卡大比拼,你会选择哪一款显卡使用?
- python方差齐性检验_方差分析中的方差齐性检验_方差齐性检验结果分析
- Python 3.7极速入门教程9最佳python中文书籍下载
- base64字符串实现下载文件
- Mac新手必看教程 Mac系统基本设置 苹果电脑的基本操作
- html4.0.1兼容ie7,CSS 完美兼容IE6/IE7/FF的通用hack方法
- Activity的生命周期之图记表查
- vue3 计算滚动条的滚动距离
- 显示器跟服务器的最大距离,关于显示器与人眼之间的距离