vue-toy200 行左右代码模拟 vue 实现

联合创作 · 2023-09-19 17:57

vue-toy

npm install --save vue-toy

200行左右代码模拟vue实现。

Vue(options)

interface Options {
    el: HTMLElement | string;
	propsData?: Record<string, any>;
	props?: string[];
	name?: string;
	data?: () => Record<string, any>;
	methods?: Record<string, (e: Event) => void>;
	computed?: Record<string, () => any>;
	watch?: Record<string, (newValue: any, oldValue: any) => any>;
	render: (h: typeof React.createElement) => React.ReactNode;
	renderError?: (h: typeof React.createElement, error: Error) => React.ReactNode;
	mounted?: () => void;
	updated?: () => void;
	destroyed?: () => void;
	errorCaptured?: (e: Error, vm: React.ReactInstance) => void;
}

示例:

import Vue from "vue-toy";

new Vue({
  el: document.getElementById("root"),
  data() {
    return {
      msg: "hello vue toy"
    };
  },
  render(h) {
    return h("h1", null, this.msg);
  }
});

注1vue-toy不包含template的编译实现,因为vue的template最终是编译成类似代码:

render(h) {
    with(this){
        return h("h1", null, msg);
    }
}

本教程中使用了严格模式无法使用with,所以在render里无法省略this

注2vue-toy的视图渲染使用的react,所以render方法的使用同react#render,如:

import Vue from "vue-toy";
import React from "react";

new Vue({
  el: document.getElementById("root"),
  data() {
    return {
      msg: "hello vue toy"
    };
  },
  render() {
    return <h1>{this.msg}</h1>
  }
});

全局 API

Vue.component(ComponentOptions)

interface ComponentOptions {
	props?: string[];
	name?: string;
	data?: () => Record<string, any>;
	methods?: Record<string, (e: Event) => void>;
	computed?: Record<string, () => any>;
	watch?: Record<string, (newValue: any, oldValue: any) => any>;
	render: (h: typeof React.createElement) => React.ReactNode;
	renderError?: (h: typeof React.createElement, error: Error) => React.ReactNode;
	mounted?: () => void;
	updated?: () => void;
	destroyed?: () => void;
	errorCaptured?: (e: Error, vm: React.ReactInstance) => void;
}

示例:

const Hello = Vue.component({
    props: ["msg"],
    render(h){
        return h('div', null, this.msg);
    }
});
export default Hello;

基本原理

// 创建观察对象
// 观察对象主要使用的是Object.defineProperty或Proxy来实现,
// 也可使用类似angular.js的脏检测(不过需要额外的检测调用),
// 如果不在意写法也可以参考knockout或 setXXX getXXX的方式
const data = observable({
    name: 'vue-toy',
});

// 渲染模版
const render = function(){
    return <h1>{data.name}</h1>
}

// 计算render的依赖属性,
// 依赖属性改变时,会重新计算computedFn,并执行监控函数watchFn,
// 属性依赖计算使用栈及可以了。
// watch(computedFn, watchFn);
watch(render, function(newVNode, oldVNode){
    update(newVNode, mountNode);
});

//初始渲染
mount(render(), mountNode);

// 改变观察对象属性,如果render依赖了该属性,则会重新渲染
data.name = 'hello vue toy';

视图渲染部分(既render)使用的是vdom技术,vue2+使用Snabbdom库,vue-toy使用的是react来进行渲染,所以在render函数里你可以直接使用React的JSX语法,不过别忘记import React from 'react',当然也可以使用preact inferno 等 vdom库。

由于vue的template的最终也是解析并生成render函数,模版的解析可用htmleParser库来生成AST,剩下就是解析指令并生产代码,由于工作量大,这里就不具体实现,直接使用jsx。

浏览 4
点赞
评论
收藏
分享

手机扫一扫分享

编辑 分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

编辑 分享
举报