12-初始化主流程

127 阅读4分钟

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);
  });
}