Implement Setup, render
The primary version
Reactive, effectWatch based on the reactive, effectWatch implementation of mini-vuE-1 based on the reactive, effectWatch implementation of mini-VUE-1 based on the reactive, effectWatch implementation of reactive, render, the code is as follows:
- API simple design and use:
const App = {
render(context) {}
setup(){}}// use
App.render(App.setup())
Copy the code
- Detail implementation:
// ---------- implement setup render ----------------
import { effectWatch, reactive } from './core/reactivity/index.js'
const App = {
render(context) {
effectWatch(() = > {
// reset
document.body.innerHTML = ""
const div = document.createElement('div');
div.innerHTML = context.state.count;
// root
document.body.append(div); })},setup() {
const state = reactive({
count: 0
})
// Use window to verify responsiveness on the console
window.state = state;
return { state }
}
}
App.render(App.setup())
Copy the code
After the browser opens, execute on the console:
state.count ++
Copy the code
We can see that the view in the page has been updated!
Disadvantages of the current code: It destroys all DOM elements every time you update the DOM, which can have a significant impact on performance;
// reset
document.body.innerHTML = ""
Copy the code
Ideally, after the data is updated, the DOM of the corresponding part of the view is updated locally.
Advanced setup, render
Extract and encapsulate the above code to achieve something like the following:
// File path /index.js
import App from './App.js'
import { createApp } from './core/index.js'
createApp(App).mount(document.querySelector("#app"))
Copy the code
- Implement app.js: Basically returns a VUE object
// File path/app.js
import { reactive } from "./core/reactivity/index.js";
export default {
render(context) {
const div = document.createElement('div');
div.innerHTML = context.state.count;
return div;
},
setup() {
const state = reactive({
count: 1,})window.state = state;
return{ state }; }}Copy the code
The code here is the same as the App we initially defined above;
Render returns the data view node with the data binding; [Mount to root] This step is removed;
- implementation
createApp
Currently, mount and update operations are performed.
// File path /core/index.js
import { effectWatch } from './reactivity/index.js'
export function createApp(rootComponent) {
return {
mount(rootContainer) {
const context = rootComponent.setup();
effectWatch(() = > {
rootContainer.innerHTML = "";
constele = rootComponent.render(context); rootContainer.append(ele); })}.}}Copy the code
- Verify: In Chrome Console type:
state.count ++
You can see it on the viewcount
Has changed;
Implement h function
Analyze the virtual DOM/Node structure, mainly as follows:
- tagName
- props
- children
// File path /core/h.js
// Create a vnode
export function h(tag, props, children) {
return {
tag,
props,
children
}
}
Copy the code
Use the h function to create a virtual node
- Modify the
App.js
File, usingh
Functions;
// File path/app.js
import { reactive } from "./core/reactivity/index.js";
import { h } from './core/h.js'
export default {
render(context) {
// const div = document.createElement('div');
// div.innerHTML = context.state.count;
// return div;
// Create a virtual node using the h function
return h('div', {color: 'red'}, context.state.count)
},
setup() {
const state = reactive({
count: 1,})window.state = state;
return{ state }; }}Copy the code
Since the render method does not return a real DOM node, the createApp function does not hang directly on the real DOM node. There are two steps:
- Convert a VNode to a real DOM
- Hang the DOM on the Container
// File path /core/index.js
import { effectWatch } from './reactivity/index.js'
import { mountElement } from './renderer/index.js'
export function createApp(rootComponent) {
return {
mount(rootContainer) {
const context = rootComponent.setup();
effectWatch(() = > {
rootContainer.innerHTML = "";
// const ele = rootComponent.render(context);
// rootContainer.append(ele);
const vnode = rootComponent.render(context);
mountElement(vnode, rootContainer)
})
},
}
}
Copy the code
In this step, we need to implement the mountElement function that converts vNodes into real DOM. This function is mainly used to analyze the properties of vNodes:
- The tag tag name
- Properties of the props tag
- The children tag contains child DOM nodes
Analyze the various possibilities and convert to the real DOM with the following simple code:
export function mountElement(vnode, container) {
const { tag, props, children } = vnode;
// ... if ... else...
}
Copy the code
Detailed implementation:
// File path /core/renderer/index.js
export function mountElement(vnode, container) {
const { tag, props, children } = vnode;
// tag
const el = document.createElement(tag)
// props
for (let key in props) {
const value = props[key]
el.setAttribute(key, value);
}
// children- Determine the string and number types
if (typeof children === 'string' || typeof children === 'number') {
const textNode = document.createTextNode(children)
el.appendChild(textNode);
// If the container is an array, the recursive processing is performed. Note that the container passed in is el, not an external container.
} else if (Array.isArray(children)) {
children.forEach((v) = > {
mountElement(v, el);
})
}
container.appendChild(el);
}
Copy the code
- Test data verification:
// File path/app.js
import { reactive } from "./core/reactivity/index.js";
import { h } from './core/h.js'
export default {
render(context) {
// const div = document.createElement('div');
// div.innerHTML = context.state.count;
// return div;
return h('div',
{ id: 'div-wrapper'},
[
h('h1', { id: 'div-test'.style: 'color: red; font-size: 24px; ' }, context.state.count),
h('span', { class: 'span-str' }, context.state.str),
])
},
setup() {
const state = reactive({
count: 1.str: "Hello, World"
})
window.state = state;
return{ state }; }}Copy the code
At this point, you can see in the browser that the vNode we wrote is rendered on the page;
Manually update state on the console and see the view update as well;
state.str = "Hello, JavaScript"
state.count ++
Copy the code