【源码】vue3 为什么说跨平台能力更强
【公司内部分享】探索Vue.js框架内部原理和实现机制,旨在理解其核心概念和技术细节,以便更好地应用和定制Vue.js。
相关文章
- vue3 为什么说渲染、更新更快、内存占用更少
- vue3 为什么说打包体积更小
- vue3 为什么说跨平台能力更强
- vue2 项目结构与架构设计介绍
- vue2 变化侦测与异步更新源码刨析
- vue2 模板编译源码刨析
- vue2 patch源码刨析
- vuex4 源码浅析
vue3 为什么说跨平台能力更强
一、渲染器 API
Vue 3 引入了渲染器 API,使得可以自定义渲染目标。这意味着 Vue 3 可以将组件渲染到各种不同的目标上,从而支持更多的应用场景和平台。
二、createRenderer
用于创建一个自定义渲染器。它接受一个包含渲染器选项的对象作为参数,并返回一个渲染器实例。渲染器选项对象包含用于控制渲染过程的方法,比如 createElement、patchProp、insert 和 remove 等。
createElement(type: string, props?: Object, children?: string | VNode | VNode[]) => VNode:创建一个虚拟节点。
patchProp(el: Element, key: string, prevValue: any, nextValue: any, isSVG?: boolean, prevChildren?: VNode[]):更新 DOM 元素的属性。这个方法用于在更新期间对 DOM 元素的属性进行处理。
insert(child: Node, parent: Node, anchor?: Node | null):将节点插入到父节点中。用于处理新节点的插入。
remove(child: Node):从父节点中移除节点。用于处理节点的移除。
二、应用场景
服务器端渲染(SSR)
渲染器 API 可以用于在服务器端渲染 Vue 组件,生成 HTML,并将其发送给客户端。这对于提高首屏加载性能和 SEO 非常有用。
1 |
|
静态网站生成器(SSG)
渲染器 API 可以用于在构建时(而不是在运行时)将 Vue 组件渲染为静态 HTML 文件,从而实现静态网站生成器的功能。
1 |
|
桌面应用程序
渲染器 API 可以用于将 Vue 组件渲染到桌面应用程序的窗口中,从而实现基于 Web 技术的桌面应用程序。
1 |
|
移动应用程序
渲染器 API 可以用于将 Vue 组件渲染到移动应用程序的 WebView 中,从而实现基于 Web 技术的移动应用程序。
1 |
|
小程序
渲染器 API 可以用于将 Vue 组件渲染到小程序的自定义组件中,从而实现在小程序中使用 Vue 的功能。
Vue 组件 MyComponent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20const { createApp, h } = require('vue');
// 定义一个简单的 Vue 组件
const MyComponent = {
data() {
return {
message: 'Hello, Mini Program!'
};
},
// 渲染函数,返回一个 VNode
render() {
return h('view', {}, this.message);
}
};
// 创建 Vue 应用实例
const app = createApp(MyComponent);
// 将 Vue 组件挂载到 DOM 中
const vnode = app.mount();微信小程序自定义组件 vue-component 的 vue-component.wxml 文件
微信小程序页面中引入 vue-component 组件
微信小程序页面中的逻辑部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Page({
data: {
vueSrc: 'data:text/html;charset=utf-8,' + encodeURIComponent('<div id="app"></div>')
},
onReady: function () {
// 使用微信小程序自定义组件的接口获取 web-view 组件实例
const webView = this.selectComponent('#web-view');
// 在 web-view 组件中渲染 Vue 组件
webView.postMessage({
type: 'render',
vnode: vnode
});
}
});
其他自定义渲染目标
渲染器 API 还可以用于将 Vue 组件渲染到其他自定义渲染目标上,例如 Canvas、PDF、SVG 等。
Canvas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60const { createRenderer } = require('vue');
// 创建自定义渲染器
const renderer = createRenderer({
// 创建虚拟节点
createElement(type, props, children) {
return {
type,
props,
children
};
},
// 更新 DOM 元素的属性
patchProp(el, key, prevValue, nextValue) {
// 不需要更新 Canvas 上的属性
},
// 将节点插入到父节点中
insert(child, parent, anchor) {
// 不需要将节点插入到 Canvas 中
},
// 从父节点中移除节点
remove(child) {
// 不需要从 Canvas 中移除节点
}
});
// 定义一个简单的 Vue 组件
const MyComponent = {
data() {
return {
message: 'Hello, Canvas!'
};
},
// 渲染函数,返回一个 VNode
render() {
return {
type: 'text',
props: {
x: 50,
y: 50,
text: this.message,
fillStyle: 'black'
}
};
}
};
// 创建 Vue 应用实例
const { createApp, h } = require('vue');
const app = createApp(MyComponent);
// 使用自定义渲染器渲染应用程序
const vnode = app.mount();
// 获取 Canvas 元素
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 渲染组件到 Canvas 上
renderer.render(vnode, ctx);pdf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66import { createApp } from 'vue';
import jsPDF from 'jspdf';
// 创建自定义渲染器
const renderer = {
// 创建虚拟节点
createElement(type, props, children) {
return {
type,
props,
children
};
},
// 更新 DOM 元素的属性
patchProp(el, key, prevValue, nextValue) {
// 不需要更新 PDF 上的属性
},
// 将节点插入到父节点中
insert(child, parent, anchor) {
// 不需要将节点插入到 PDF 中
},
// 从父节点中移除节点
remove(child) {
// 不需要从 PDF 中移除节点
}
};
// 定义一个简单的 Vue 组件
const MyComponent = {
data() {
return {
message: 'Hello, PDF!'
};
},
// 渲染函数,返回一个 VNode
render() {
return {
type: 'text',
props: {
x: 10,
y: 10,
text: this.message,
fontSize: 12
}
};
}
};
// 创建 Vue 应用实例
const app = createApp(MyComponent);
// 使用自定义渲染器渲染应用程序
const vnode = app.mount();
// 创建 PDF 实例
const pdf = new jsPDF();
// 渲染组件到 PDF 上
renderer.render(vnode, {
drawText(text, x, y, options) {
pdf.text(text, x, y, options);
}
});
// 保存 PDF 文件
pdf.save('example.pdf');svg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70const { createApp } = require('@vue/runtime-core');
// 创建自定义渲染器
const renderer = {
// 创建虚拟节点
createElement(type, props, children) {
return {
type,
props,
children
};
},
// 更新 DOM 元素的属性
patchProp(el, key, prevValue, nextValue) {
el.setAttribute(key, nextValue);
},
// 将节点插入到父节点中
insert(child, parent, anchor) {
parent.appendChild(child);
},
// 从父节点中移除节点
remove(child) {
child.parentNode.removeChild(child);
}
};
// 定义一个简单的 Vue 组件
const MyComponent = {
data() {
return {
message: 'Hello, SVG!'
};
},
// 渲染函数,返回一个 VNode
render() {
return {
type: 'svg',
props: {
width: '200',
height: '200'
},
children: [
{
type: 'text',
props: {
x: '10',
y: '50',
fill: 'black'
},
children: this.message
}
]
};
}
};
// 创建 Vue 应用实例
const app = createApp(MyComponent);
// 使用自定义渲染器渲染应用程序
const vnode = app.mount();
// 创建 SVG 元素
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
// 渲染组件到 SVG 上
renderer.insert(vnode, svg, null);
// 将 SVG 添加到 DOM 中
document.body.appendChild(svg);