hello word
index.html
<!--
* @Author: Lin zefan
* @Date: 2022-03-21 21:46:14
* @LastEditTime: 2022-03-21 23:06:58
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\example\helloWorld\index.html
*
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="main.js" type="module"></script>
</body>
</html>
main.js
/*
* @Author: Lin zefan
* @Date: 2022-03-21 21:46:14
* @LastEditTime: 2022-03-21 23:07:06
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\example\helloWorld\main.js
*
*/
import { createApp } from "../../lib/mini-vue.esm.js";
import App from "./App.js";
// 这里跟vue一样,通过createApp创建Vue实例,再通过mount挂载到对应节点
const rootContainer = document.querySelector("#root");
createApp(App).mount(rootContainer);
App.js
/*
* @Author: Lin zefan
* @Date: 2022-03-21 21:46:14
* @LastEditTime: 2022-03-21 23:06:51
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\example\helloWorld\App.js
*
*/
import { h } from "../../lib/mini-vue.esm.js";
export default {
name: "App",
setup() {
return {
ctx: "hello word",
};
},
render() {
return h("div", { tId: 1 }, this.ctx);
},
};
createApp
createApp.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-21 21:49:41
* @LastEditTime: 2022-03-22 17:15:09
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\src\runtime-core\createdApp.ts
*
*/
import { render } from "./render";
import { createdVNode } from "./vnode";
// 创建一个Vue实例
export function createApp(rootComponent) {
return {
// 暴露一个mount方法
mount(rootContainer) {
/**
* 1. 将根组件(rootComponent)转换为vnode
* 2. 再通过render函数将vnode渲染到mount接收的容器(rootContainer)中
*/
const vnode = createdVNode(rootComponent);
render(vnode, rootContainer);
},
};
}
render.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-21 22:04:58
* @LastEditTime: 2022-03-22 17:28:41
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\src\runtime-core\render.ts
*
*/
import { isObject } from "../shared/index";
import { processComponent, processElement } from "./component";
export function render(vnode, container) {
patch(vnode, container);
}
export function patch(vnode, container) {
if (isObject(vnode.type)) {
// 是一个Component
processComponent(vnode, container);
} else if (typeof vnode.type === "string") {
// 是一个element
processElement(vnode, container);
}
}
vnode.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-21 21:58:19
* @LastEditTime: 2022-03-21 23:11:23
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\src\runtime-core\vnode.ts
*
*/
/**
* @description: 转换根组件为vnode
* @param {*} type 根组件(App)
* @param {*} props 组件的props
* @param {*} children 组件嵌套的子组件
* @return {vnode}
*/
export function createdVNode(type, props?, children?) {
// 将根组件转换为vnode,再将其暴露
return {
type,
props,
children,
};
}
render
/*
* @Author: Lin zefan
* @Date: 2022-03-21 22:04:58
* @LastEditTime: 2022-03-22 17:28:41
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\src\runtime-core\render.ts
*
*/
import { isObject } from "../shared/index";
import { processComponent, processElement } from "./component";
export function render(vnode, container) {
patch(vnode, container);
}
export function patch(vnode, container) {
if (isObject(vnode.type)) {
// 是一个Component
processComponent(vnode, container);
} else if (typeof vnode.type === "string") {
// 是一个element
processElement(vnode, container);
}
}
处理component
/*
* @Author: Lin zefan
* @Date: 2022-03-21 22:08:11
* @LastEditTime: 2022-03-22 17:34:01
* @LastEditors: Lin zefan
* @Description: 处理组件类型
* @FilePath: \mini-vue3\src\runtime-core\component.ts
*
*/
import { patch } from "./render";
export function processComponent(vnode, container) {
// TODO,这里会比较vnode,然后做创建、更新操作,这里先处理创建
// 创建组件
mountComponent(vnode, container);
// TODO,更新组件
// updateComponent(vnode, container);
}
// -----------------Component创建流程-------------------
function mountComponent(vnode, container) {
// 创建component实例
const instance = createComponentInstance(vnode);
// setup component
setupComponent(instance, container);
// setupRenderEffect
setupRenderEffect(instance, container);
}
// 初始化Component结构
function createComponentInstance(vnode) {
const component = {
vnode,
type: vnode.type,
};
return component;
}
// 初始化setup数据
function setupComponent(instance, container) {
// TODO initProps() - 初始化props
// TODO initSlots() - 初始化slots
// 初始化setup函数返回值
setupStatefulComponent(instance, container);
}
// 初始化setup返回值
function setupStatefulComponent(instance, container) {
/** 获取用户声明的setup函数过程
* 1. 前面通过createApp将根组件转换为vnode
* 2. 之后通过createComponentInstance将vnode进行二次包装
* 3. 最后可以通过instance.type 获取根组件(rootComponent)
*/
const component = instance.type;
const { setup } = component;
if (setup) {
const setupResult = setup();
handleSetupResult(instance, setupResult);
}
}
// 获取 setup() 的返回值并挂载实例
function handleSetupResult(instance, setupResult) {
/** 这里有setup返回值有两种情况
* 1. 是一个函数
* 2. 是一个对象
*/
// 如果是对象,将对象注入上下文
if (typeof setupResult === "object") {
instance.setupState = setupResult;
} else {
// 如果是函数,那么当作render处理TODO
}
// 初始化render函数
finishComponentSetup(instance);
}
// 初始化render函数
function finishComponentSetup(instance) {
const component = instance.type;
// 挂载实例的render函数,取当前组件实例声明得render
if (component.render) {
instance.render = component.render;
}
// 而没有 component.render 咋办捏,其实可以通过编译器来自动生成一个 render 函数
// 这里先不写
}
function setupRenderEffect(instance, container) {
// 通过render函数,获取render返回虚拟节点
const subTree = instance.render();
// 最后通过patch的processElement,将subTree渲染到container(节点)上
patch(subTree, container);
}
// ---------------------Component更新流程----------------------
function updateComponent(vnode, container) {}
处理element
/*
* @Author: Lin zefan
* @Date: 2022-03-22 17:32:00
* @LastEditTime: 2022-03-22 17:32:00
* @LastEditors: Lin zefan
* @Description: 处理dom
* @FilePath: \mini-vue3\src\runtime-core\element.ts
*
*/
import { patch } from "./render";
// ---------------------Element创建流程----------------------
export function processElement(vnode, container) {
const { type, props, children } = vnode;
// 创建根元素
const el = document.createElement(type);
// 设置行内属性
for (const key in props) {
el.setAttribute(key, props[key]);
}
// 设置children
if (typeof children === "string") {
el.textContent = children;
// 数组,可能存在多个子元素
} else if (Array.isArray(children)) {
mountChildren(children, el);
}
container.append(el);
}
function mountChildren(children, container) {
children.forEach((h) => {
patch(h, container);
});
}