跳到主要内容

官方模拟版

2023年06月11日
柏拉文
越努力,越幸运

一、packages/runtime-dom/src/index.ts


import { nodeOps } from './nodeOps';
import { extend } from '@vue/shared';
import { patchProp } from './patchProp';
import { createRenderer } from 'packages/runtime-core/src/renderer';

let renderer;
const rendererOptions = extend({ patchProp }, nodeOps);

function ensureRenderer() {
return renderer || (renderer = createRenderer(rendererOptions));
}

export const render = (...args) => {
ensureRenderer().render(...args);
};

二、packages/runtime-core/src/renderer.ts createRenderer()


export function createRenderer(options) {
return baseCreateRenderer(options);
}

三、packages/runtime-core/src/renderer.ts baseCreateRenderer()


export function baseCreateRenderer(options) {
const {
insert: hostInsert,
remove: hostRemove,
setText: hostSetText,
patchProp: hostPatchProp,
createText: hostCreateText,
createComment: hostCreateComment,
createElement: hostCreateElement,
setElementText: hostSetElementText
} = options;

const patch = (n1, n2, container, anchor = null) => {
};

const processText = (n1, n2, container, anchor) => {
};

const processCommentNode = (n1, n2, container, anchor) => {
};

const processFragment = (n1, n2, container, anchor) => {
};

const processElement = (n1, n2, container, anchor) => {
};

const processComponent = (n1, n2, container, anchor) => {
};

const mountComponent = (initialVNode, container, anchor) => {
};

const updateComponent = (n1, n2) => {};

const setupRenderEffect = (instance, initialValue, container, anchor) => {
};

const mountElement = (vnode, container, anchor) => {
};

const patchElement = (n1, n2) => {
};

const mountChildren = (children, container, anchor) => {
};

const patchChildren = (n1, n2, container, anchor) => {
};

const patchKeyedChildren = (c1, c2, container, parentAnchor) => {
};

const patchProps = (el, vnode, oldProps, newProps) => {
};

const move = (vnode, container, anchor) => {
};

const unmount = vnode => {
};

const render = (vnode, container) => {
};
return {
render
};
}

四、packages/runtime-core/src/renderer.ts render()


const render = (vnode, container) => {
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode);
}
} else {
patch(container._vnode || null, vnode, container);
}
container._vnode = vnode;
};

测试用例


render h element mount

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render } from 'vue';

const div1 = h('div', { class: 'test' }, 'hello render');

render(div1, document.querySelector('#app'));
</script>
</body>
</html>

render h element update

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render } from 'vue';

const div1 = h(
'div',
{
class: 'test-input',
value: '嘻嘻',
type: 'text',
style: { width: '400px', color: 'red' }
},
'hello render-mount'
);
render(div1, document.querySelector('#app'));

setTimeout(() => {
const div1 = h(
'div',
{
class: 'test-input',
value: '哈哈',
type: 'text',
style: { width: '800px' }
},
'hello render-update'
);
render(div1, document.querySelector('#app'));
}, 3000);
</script>
</body>
</html>

render h element event

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render } from 'vue';

const div1 = h(
'div',
{
class: 'test-input',
value: '嘻嘻',
type: 'text',
style: { width: '400px', color: 'red' },
onChange() {
console.log('改变');
}
},
'hello render-mount'
);
render(div1, document.querySelector('#app'));

setTimeout(() => {
const div1 = h(
'div',
{
class: 'test-input',
value: '哈哈',
type: 'text',
style: { width: '800px' },
onBlur() {
console.log('失焦');
}
},
'hello render-update'
);
render(div1, document.querySelector('#app'));
}, 3000);
</script>
</body>
</html>

render h Text

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render, Text } from 'vue';

const div1 = h(Text, 'hello render-mount');
render(div1, document.querySelector('#app'));

setTimeout(() => {
const div1 = h('Text', 'hello render-update');
render(div1, document.querySelector('#app'));
}, 3000);
</script>
</body>
</html>

render h Comment

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render, Comment } from 'vue';

const div1 = h(Comment, 'hello render-mount');
render(div1, document.querySelector('#app'));
</script>
</body>
</html>

render h Fragment

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render, Fragment } from 'vue';

const div1 = h(Fragment, '你好! 世界! 首次');
render(div1, document.querySelector('#app'));

setTimeout(() => {
const div2 = h(Fragment, '你好! 世界! 更新');
render(div2, document.querySelector('#app'));
}, 3000);
</script>
</body>
</html>

render h Component

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render Component</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render } from 'vue';

const component1 = {
render() {
return h('div', 'hello component mount');
}
};

const vnode1 = h(component1);
render(vnode1, document.querySelector('#app'));

setTimeout(() => {
const component2 = {
render() {
return h('div', 'hello component update');
}
};

const vnode2 = h(component2);
render(vnode2, document.querySelector('#app'));
}, 3000);
</script>
</body>
</html>

render h Component data state

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render Component Data State</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render } from 'vue';

const component1 = {
data() {
return {
msg: 'hello component mount'
};
}
};

const vnode1 = h(component1);
render(vnode1, document.querySelector('#app'));
</script>
</body>
</html>

render h Component data lifecycle

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render Component Data State</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render } from 'vue';

const component1 = {
data() {
return {
msg: 'hello component mount'
};
},
render() {
return h('div', this.msg);
},
beforeCreate() {
console.log('beforeCreate');
},
created() {
console.log('created');
},
beforeMount() {
console.log('beforeMount');
},
mounted() {
console.log('mounted');
}
};

const vnode1 = h(component1);
render(vnode1, document.querySelector('#app'));
</script>
</body>
</html>

render h Component data state lifecycle

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render Component Data State</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render } from 'vue';

const component1 = {
data() {
return {
msg: 'hello component mount'
};
},
render() {
return h('div', this.msg);
},
beforeCreate() {
console.log('beforeCreate', this);
},
created() {
console.log('created', this);
},
beforeMount() {
console.log('beforeMount', this);
},
mounted() {
setTimeout(() => {
this.msg = 'hello component update';
}, 3000);
console.log('mounted', this);
}
};

const vnode1 = h(component1);
render(vnode1, document.querySelector('#app'));
</script>
</body>
</html>

render h Component setup

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3-Next-Mini Render Component setup</title>
<script type="importmap">
{
"imports": {
"vue": "../packages/vue/dist/vue.js"
}
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module">
import { h, render, reactive } from 'vue';

const component1 = {
setup() {
const obj = reactive({
msg: 'hello component mount'
});

setTimeout(() => {
obj.msg = 'hello component update';
}, 3000);

return () => h('div', obj.msg);
}
};

const vnode1 = h(component1);
render(vnode1, document.querySelector('#app'));
</script>
</body>
</html>