├── .gitignore ├── README.md ├── babel.config.js ├── example ├── apiProviderInject │ ├── App.js │ ├── index.html │ └── main.js ├── componentEmit │ ├── App.js │ ├── index.html │ └── main.js ├── componentSlot │ ├── App.js │ ├── index.html │ └── main.js ├── currentInstance │ ├── App.js │ ├── index.html │ └── main.js ├── helloworld │ ├── App.js │ ├── index.html │ └── main.js ├── patchChildren │ ├── App.js │ ├── index.html │ └── main.js └── update │ ├── App.js │ ├── index.html │ └── main.js ├── index.html ├── lib ├── guide-mini-vue3.cjs.js └── guide-mini-vue3.esm.js ├── package.json ├── packages ├── index.ts ├── reactivity │ ├── __test__ │ │ ├── computed.spec.ts │ │ ├── effect.spec.ts │ │ ├── reactive.spec.ts │ │ ├── readonly.spec.ts │ │ └── ref.spec.ts │ └── src │ │ ├── baseHandlers.ts │ │ ├── computed.ts │ │ ├── effect.ts │ │ ├── index.ts │ │ ├── reactive.ts │ │ └── ref.ts ├── runtime-core │ └── src │ │ ├── apiInject.ts │ │ ├── component.ts │ │ ├── componentEmit.ts │ │ ├── componentProps.ts │ │ ├── componentPublicInstance.ts │ │ ├── componentSlots.ts │ │ ├── createApp.ts │ │ ├── h.ts │ │ ├── helpers │ │ └── renderSlots.ts │ │ ├── index.ts │ │ ├── renderer.ts │ │ └── vnode.ts ├── runtime-dom │ └── index.ts └── shared │ ├── index.ts │ └── shapeFlags.ts ├── rollup.config.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /yarn.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mini-vue3 2 | 3 | > 实现一个简易的 Vue3 用来学习源码 4 | 5 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ["@babel/preset-env", { targets: { node: "current" } }], 4 | "@babel/preset-typescript", 5 | ], 6 | } 7 | -------------------------------------------------------------------------------- /example/apiProviderInject/App.js: -------------------------------------------------------------------------------- 1 | import { h, provide, inject } from "../../lib/guide-mini-vue3.esm.js" 2 | 3 | const Child = { 4 | name: "Child", 5 | setup() { 6 | const foo = inject("foo") 7 | return { foo } 8 | }, 9 | render() { 10 | return h("div", {}, "value =>" + this.foo) 11 | }, 12 | } 13 | 14 | const App = { 15 | name: "App", 16 | render() { 17 | return h("div", { id: "aaaaa" }, [h(Child)]) 18 | }, 19 | setup() { 20 | provide("foo", "foo-value") 21 | 22 | return { 23 | msg: "hello mini-vue3!!!", 24 | } 25 | }, 26 | } 27 | 28 | export default App 29 | -------------------------------------------------------------------------------- /example/apiProviderInject/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/apiProviderInject/main.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js" 2 | import { createApp } from "../../lib/guide-mini-vue3.esm.js" 3 | const rootContainer = document.querySelector("#app") 4 | createApp(App).mount(rootContainer) 5 | -------------------------------------------------------------------------------- /example/componentEmit/App.js: -------------------------------------------------------------------------------- 1 | import { h, renderSlots } from "../../lib/guide-mini-vue3.esm.js" 2 | 3 | const Foo = { 4 | setup(props, { emit }) { 5 | return { 6 | emitAdd() { 7 | emit("add") 8 | }, 9 | } 10 | }, 11 | render() { 12 | const btn = h("button", { onClick: this.emitAdd }, "emitAdd") 13 | const foo = h("div", {}, "foo") 14 | return h("div", {}, [foo, btn]) 15 | }, 16 | } 17 | 18 | const app = h("div", {}, [ 19 | h(Foo, { 20 | onAdd() { 21 | console.log("onAdd") 22 | }, 23 | }), 24 | ]) 25 | 26 | const App = { 27 | render() { 28 | return app 29 | }, 30 | setup() { 31 | return {} 32 | }, 33 | } 34 | 35 | export default App 36 | -------------------------------------------------------------------------------- /example/componentEmit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/componentEmit/main.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js" 2 | import { createApp } from "../../lib/guide-mini-vue3.esm.js" 3 | const rootContainer = document.querySelector("#app") 4 | createApp(App).mount(rootContainer) 5 | -------------------------------------------------------------------------------- /example/componentSlot/App.js: -------------------------------------------------------------------------------- 1 | import { 2 | h, 3 | renderSlots, 4 | createTextVnode, 5 | } from "../../lib/guide-mini-vue3.esm.js" 6 | 7 | const Foo = { 8 | setup(props) {}, 9 | render() { 10 | const foo = h("p", {}, "foo") 11 | return h("div", {}, [ 12 | renderSlots(this.$slots, "header", { a: 1 }), 13 | foo, 14 | renderSlots(this.$slots, "footer"), 15 | ]) 16 | }, 17 | } 18 | 19 | const foo = h( 20 | Foo, 21 | {}, 22 | { 23 | header: ({ a }) => h("p", {}, "header slot" + a), 24 | footer: () => [ 25 | h("p", {}, "footer slot"), 26 | h("p", {}, "footer slot"), 27 | createTextVnode("hello"), 28 | ], 29 | } 30 | ) 31 | 32 | const app = h("div", { id: "app" }, ["App", foo]) 33 | 34 | const App = { 35 | render() { 36 | return app 37 | }, 38 | setup() { 39 | return {} 40 | }, 41 | } 42 | 43 | export default App 44 | -------------------------------------------------------------------------------- /example/componentSlot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/componentSlot/main.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js" 2 | import { createApp } from "../../lib/guide-mini-vue3.esm.js" 3 | const rootContainer = document.querySelector("#app") 4 | createApp(App).mount(rootContainer) 5 | -------------------------------------------------------------------------------- /example/currentInstance/App.js: -------------------------------------------------------------------------------- 1 | import { h, getCurrentInstance } from "../../lib/guide-mini-vue3.esm.js" 2 | 3 | const Foo = { 4 | name: "Foo", 5 | setup() { 6 | const instance = getCurrentInstance() 7 | console.log(instance) 8 | }, 9 | render() { 10 | return h("p", {}, "foo") 11 | }, 12 | } 13 | 14 | const App = { 15 | name: "App", 16 | render() { 17 | return h("div", {}, [h(Foo), "currentInstance Demo"]) 18 | }, 19 | setup() { 20 | const instance = getCurrentInstance() 21 | 22 | console.log(instance) 23 | }, 24 | } 25 | 26 | export default App 27 | -------------------------------------------------------------------------------- /example/currentInstance/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/currentInstance/main.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js" 2 | import { createApp } from "../../lib/guide-mini-vue3.esm.js" 3 | const rootContainer = document.querySelector("#app") 4 | createApp(App).mount(rootContainer) 5 | -------------------------------------------------------------------------------- /example/helloworld/App.js: -------------------------------------------------------------------------------- 1 | import { h } from "../../lib/guide-mini-vue3.esm.js" 2 | 3 | const Title = { 4 | setup(props) { 5 | props.count++ 6 | }, 7 | render() { 8 | return h("div", {}, this.count) 9 | }, 10 | } 11 | 12 | const App = { 13 | render() { 14 | return h("div", { id: "aaaaa" }, [ 15 | h( 16 | "h1", 17 | { 18 | id: "red", 19 | onClick() { 20 | console.log("click") 21 | }, 22 | onMouseDown() { 23 | console.log("mousedown") 24 | }, 25 | }, 26 | "Hello Vue3!!!" 27 | ), 28 | h("h2", {}, this.msg), 29 | h(Title, { count: 1 }), 30 | ]) 31 | }, 32 | setup() { 33 | return { 34 | msg: "hello mini-vue3!!!", 35 | } 36 | }, 37 | } 38 | 39 | export default App 40 | -------------------------------------------------------------------------------- /example/helloworld/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/helloworld/main.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js" 2 | import { createApp } from "../../lib/guide-mini-vue3.esm.js" 3 | const rootContainer = document.querySelector("#app") 4 | createApp(App).mount(rootContainer) 5 | -------------------------------------------------------------------------------- /example/patchChildren/App.js: -------------------------------------------------------------------------------- 1 | import { createTextVnode, h, ref } from "../../lib/guide-mini-vue3.esm.js" 2 | 3 | const App = { 4 | render() { 5 | const textToArray = h( 6 | "div", 7 | {}, 8 | !this.isChange ? "text children" : [h("div", {}, "A"), h("div", {}, "B")] 9 | ) 10 | 11 | const textToText = h( 12 | "div", 13 | {}, 14 | !this.isChange ? "text children" : "new text children" 15 | ) 16 | 17 | const ArrayToText = h( 18 | "div", 19 | {}, 20 | !this.isChange ? [h("div", {}, "A"), h("div", {}, "B")] : "text children" 21 | ) 22 | return ArrayToText 23 | }, 24 | setup() { 25 | const isChange = ref(false) 26 | 27 | window.isChange = isChange 28 | return { 29 | isChange, 30 | } 31 | }, 32 | } 33 | 34 | export default App 35 | -------------------------------------------------------------------------------- /example/patchChildren/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/patchChildren/main.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js" 2 | import { createApp } from "../../lib/guide-mini-vue3.esm.js" 3 | const rootContainer = document.querySelector("#app") 4 | createApp(App).mount(rootContainer) 5 | -------------------------------------------------------------------------------- /example/update/App.js: -------------------------------------------------------------------------------- 1 | import { createTextVnode, h, ref } from "../../lib/guide-mini-vue3.esm.js" 2 | 3 | const App = { 4 | render() { 5 | return h("div", { ...this.props }, [ 6 | h("h1", {}, createTextVnode(this.count)), 7 | h("button", { onClick: this.changePropsDemo }, "add Count"), 8 | h("button", { onClick: this.changePropsDemo1 }, "修改props"), 9 | h( 10 | "button", 11 | { onClick: this.changePropsDemo2 }, 12 | "修改props为undefind,删除prop" 13 | ), 14 | h( 15 | "button", 16 | { onClick: this.changePropsDemo3 }, 17 | "新的props中不存在某个属性,则删除该属性" 18 | ), 19 | ]) 20 | }, 21 | setup() { 22 | const props = ref({ 23 | foo: "foo", 24 | bar: "bar", 25 | }) 26 | 27 | const count = ref(0) 28 | 29 | const changePropsDemo = () => { 30 | count.value++ 31 | } 32 | 33 | // 修改props 34 | const changePropsDemo1 = () => { 35 | props.value.foo = "new-foo" 36 | } 37 | 38 | // 设置 props 为undefined 删除prop 39 | const changePropsDemo2 = () => { 40 | props.value.foo = undefined 41 | } 42 | 43 | // 新的props中不存在某个属性,则删除该属性 44 | const changePropsDemo3 = () => { 45 | props.value = { 46 | foo: "foo", 47 | } 48 | } 49 | 50 | return { 51 | count, 52 | props, 53 | changePropsDemo, 54 | changePropsDemo1, 55 | changePropsDemo2, 56 | changePropsDemo3, 57 | } 58 | }, 59 | } 60 | 61 | export default App 62 | -------------------------------------------------------------------------------- /example/update/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/update/main.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js" 2 | import { createApp } from "../../lib/guide-mini-vue3.esm.js" 3 | const rootContainer = document.querySelector("#app") 4 | createApp(App).mount(rootContainer) 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 154 | 155 | -------------------------------------------------------------------------------- /lib/guide-mini-vue3.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | var extend = Object.assign; 6 | var EMPTY_OBJ = {}; 7 | var isObject = function (target) { return typeof target === "object"; }; 8 | var hasChanged = function (value, newValue) { return !Object.is(value, newValue); }; 9 | var hasOwn = function (value, key) { 10 | return Object.prototype.hasOwnProperty.call(value, key); 11 | }; 12 | var camelize = function (str) { 13 | return str.replace(/-(\w)/g, function (_, c) { return (c ? c.toUpperCase() : ""); }); 14 | }; 15 | var capitalize = function (str) { return str.charAt(0).toUpperCase() + str.slice(1); }; 16 | var toHandlerKey = function (str) { return (str ? "on" + capitalize(str) : ""); }; 17 | 18 | exports.activeEffect = void 0; 19 | var targetMap = new WeakMap(); 20 | function track(target, key) { 21 | if (!exports.activeEffect) { 22 | return; 23 | } 24 | var depsMap = targetMap.get(target); 25 | if (!depsMap) { 26 | depsMap = new Map(); 27 | targetMap.set(target, depsMap); 28 | } 29 | var dep = depsMap.get(key); 30 | if (!dep) { 31 | dep = new Set(); 32 | depsMap.set(key, dep); 33 | } 34 | trackEffects(dep); 35 | } 36 | function trackEffects(dep) { 37 | dep.add(exports.activeEffect); 38 | exports.activeEffect.deps.push(dep); 39 | } 40 | function trigger(target, key) { 41 | var depsMap = targetMap.get(target); 42 | if (!depsMap) { 43 | return; 44 | } 45 | var dep = depsMap.get(key); 46 | if (!dep) { 47 | return; 48 | } 49 | triggerEffects(dep); 50 | } 51 | function triggerEffects(dep) { 52 | dep.forEach(function (effect) { 53 | if (effect.scheduler) { 54 | effect.scheduler(); 55 | } 56 | else { 57 | effect.run(); 58 | } 59 | }); 60 | } 61 | var ReactiveEffect = /** @class */ (function () { 62 | function ReactiveEffect(_fn, scheduler) { 63 | this._fn = _fn; 64 | this.scheduler = scheduler; 65 | this.deps = []; 66 | this.active = true; 67 | exports.activeEffect = this; 68 | } 69 | ReactiveEffect.prototype.run = function () { 70 | return this._fn(); 71 | }; 72 | ReactiveEffect.prototype.stop = function () { 73 | if (this.active) { 74 | cleanupEffect(this); 75 | if (this.onStop) { 76 | this.onStop(); 77 | } 78 | this.active = false; 79 | } 80 | }; 81 | return ReactiveEffect; 82 | }()); 83 | function cleanupEffect(effect) { 84 | effect.deps.forEach(function (dep) { 85 | dep.delete(effect); 86 | }); 87 | effect.deps.length = 0; 88 | } 89 | function effect(fn, options) { 90 | var _effect = new ReactiveEffect(fn); 91 | if (options) { 92 | extend(_effect, options); 93 | } 94 | _effect.run(); 95 | exports.activeEffect = null; 96 | var runner = _effect.run.bind(_effect); 97 | runner.effect = _effect; 98 | return runner; 99 | } 100 | function stop(runner) { 101 | runner.effect.stop(); 102 | } 103 | 104 | var get = createGetter(); 105 | var set = createSetter(); 106 | var readonlyGet = createGetter(true); 107 | var shallowReactiveGet = createGetter(false, true); 108 | var shallowReadonlyGet = createGetter(true, true); 109 | function createGetter(isReadonly, isShallow) { 110 | if (isReadonly === void 0) { isReadonly = false; } 111 | if (isShallow === void 0) { isShallow = false; } 112 | return function (target, key) { 113 | if (key === "__v_isReactive" /* ReactiveFlags.IS_REACTIVE */) { 114 | return !isReadonly; 115 | } 116 | if (key === "__v_isReadonly" /* ReactiveFlags.IS_READONLY */) { 117 | return isReadonly; 118 | } 119 | var value = Reflect.get(target, key); 120 | if (isObject(value) && !isShallow) { 121 | return isReadonly ? readonly(value) : reactive(value); 122 | } 123 | if (!isReadonly) { 124 | track(target, key); 125 | } 126 | return value; 127 | }; 128 | } 129 | function createSetter() { 130 | return function set(target, key, value) { 131 | var oldValue = Reflect.get(target, key); 132 | if (hasChanged(oldValue, value)) { 133 | var result = Reflect.set(target, key, value); 134 | trigger(target, key); 135 | return result; 136 | } 137 | return oldValue; 138 | }; 139 | } 140 | var mutableHandlers = { 141 | get: get, 142 | set: set, 143 | }; 144 | var shallowMutableHandlers = extend({}, shallowReactiveGet, { 145 | get: shallowReadonlyGet, 146 | }); 147 | var readonlyHandlers = { 148 | get: readonlyGet, 149 | set: function (target, key, value) { 150 | console.warn("key: ".concat(key, " set \u5931\u8D25 \u56E0\u4E3A target \u662F readonly"), key); 151 | return true; 152 | }, 153 | }; 154 | var shallowReadonlyHandlers = extend({}, readonlyHandlers, { 155 | get: shallowReadonlyGet, 156 | }); 157 | 158 | function reactive(raw) { 159 | return createActiveObject(raw, mutableHandlers); 160 | } 161 | function readonly(raw) { 162 | return createActiveObject(raw, readonlyHandlers); 163 | } 164 | function shallowReactive(raw) { 165 | return createActiveObject(raw, shallowMutableHandlers); 166 | } 167 | function shallowReadonly(raw) { 168 | return createActiveObject(raw, shallowReadonlyHandlers); 169 | } 170 | function createActiveObject(raw, baseHandlers) { 171 | if (!isObject(raw)) { 172 | console.log("target ".concat(raw, " \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61")); 173 | return raw; 174 | } 175 | return new Proxy(raw, baseHandlers); 176 | } 177 | function isReadonly(value) { 178 | return !!value["__v_isReadonly" /* ReactiveFlags.IS_READONLY */]; 179 | } 180 | function isReactive(value) { 181 | return !!value["__v_isReactive" /* ReactiveFlags.IS_REACTIVE */]; 182 | } 183 | function isProxy(value) { 184 | return isReactive(value) || isReadonly(value); 185 | } 186 | 187 | var ComputedRefImpl = /** @class */ (function () { 188 | function ComputedRefImpl(getter) { 189 | var _this = this; 190 | this._dirty = true; 191 | this._getter = getter; 192 | this._effect = new ReactiveEffect(getter, function () { 193 | if (!_this._dirty) { 194 | _this._dirty = true; 195 | } 196 | }); 197 | } 198 | Object.defineProperty(ComputedRefImpl.prototype, "value", { 199 | get: function () { 200 | if (this._dirty) { 201 | this._dirty = false; 202 | this._value = this._effect.run(); 203 | } 204 | return this._value; 205 | }, 206 | enumerable: false, 207 | configurable: true 208 | }); 209 | return ComputedRefImpl; 210 | }()); 211 | function computed(getter) { 212 | return new ComputedRefImpl(getter); 213 | } 214 | 215 | var refImpl = /** @class */ (function () { 216 | function refImpl(value) { 217 | this.__v_ref = true; 218 | this._rawValue = value; 219 | this._value = convert(value); 220 | this.dep = new Set(); 221 | } 222 | Object.defineProperty(refImpl.prototype, "value", { 223 | get: function () { 224 | if (exports.activeEffect) { 225 | trackEffects(this.dep); 226 | } 227 | return this._value; 228 | }, 229 | set: function (value) { 230 | if (hasChanged(value, this._rawValue)) { 231 | this._rawValue = value; 232 | this._value = convert(value); 233 | triggerEffects(this.dep); 234 | } 235 | }, 236 | enumerable: false, 237 | configurable: true 238 | }); 239 | return refImpl; 240 | }()); 241 | function convert(value) { 242 | return isObject(value) ? reactive(value) : value; 243 | } 244 | function ref(value) { 245 | return new refImpl(value); 246 | } 247 | function isRef(ref) { 248 | return !!ref.__v_ref; 249 | } 250 | function unRef(ref) { 251 | return isRef(ref) ? ref.value : ref; 252 | } 253 | function proxyRefs(objectwithRefs) { 254 | if (objectwithRefs === void 0) { objectwithRefs = {}; } 255 | return new Proxy(objectwithRefs, { 256 | get: function (target, key) { 257 | return unRef(Reflect.get(target, key)); 258 | }, 259 | set: function (target, key, value) { 260 | if (isRef(target[key]) && !isRef(value)) { 261 | return (target[key].value = value); 262 | } 263 | else { 264 | return Reflect.set(target, key, value); 265 | } 266 | }, 267 | }); 268 | } 269 | 270 | function emit(instance, event) { 271 | var props = instance.props; 272 | var handlerName = toHandlerKey(camelize(event)); 273 | var handler = props[handlerName]; 274 | handler && handler(); 275 | } 276 | 277 | function initProps(instance, rawProps) { 278 | instance.props = rawProps || {}; 279 | } 280 | 281 | var publicPropertiesMap = { 282 | $el: function (i) { return i.vnode.el; }, 283 | $slots: function (i) { return i.slots; }, 284 | }; 285 | var publicInstanceProxyHandlers = { 286 | get: function (_a, key) { 287 | var instance = _a._; 288 | var setupState = instance.setupState, props = instance.props; 289 | if (hasOwn(setupState, key)) { 290 | return setupState[key]; 291 | } 292 | if (hasOwn(props, key)) { 293 | return props[key]; 294 | } 295 | var publicGetter = publicPropertiesMap[key]; 296 | if (publicGetter) { 297 | return publicGetter(instance); 298 | } 299 | }, 300 | }; 301 | 302 | function initSlots(instance, children) { 303 | instance.slots = children; 304 | } 305 | 306 | var currentInstance = null; 307 | function createComponentInstance(vnode, parentComponent) { 308 | var component = { 309 | vnode: vnode, 310 | type: vnode.type, 311 | setupState: {}, 312 | props: {}, 313 | slots: {}, 314 | provides: {}, 315 | isMounted: false, 316 | subTree: {}, 317 | parent: parentComponent, 318 | emit: function (instance, event) { }, 319 | }; 320 | component.emit = emit.bind(null, component); 321 | return component; 322 | } 323 | function setupComponent(instance) { 324 | initProps(instance, instance.vnode.props); 325 | initSlots(instance, instance.vnode.children); 326 | setupStatefulComponent(instance); 327 | } 328 | function setupStatefulComponent(instance) { 329 | var Component = instance.type; 330 | instance.proxy = new Proxy({ _: instance }, publicInstanceProxyHandlers); 331 | var setup = Component.setup; 332 | if (setup) { 333 | setCurrentInstance(instance); 334 | var setupResult = setup(shallowReadonly(instance.props), { 335 | emit: instance.emit, 336 | }); 337 | setCurrentInstance(null); 338 | handleSetupResult(instance, proxyRefs(setupResult)); 339 | } 340 | } 341 | function handleSetupResult(instance, setupResult) { 342 | if (typeof setupResult === "object") { 343 | // 如果是object那么返回的就是context 344 | instance.setupState = setupResult; 345 | } 346 | finishCompoentSetup(instance); 347 | } 348 | function finishCompoentSetup(instance) { 349 | var Component = instance.type; 350 | instance.render = Component.render; 351 | } 352 | function getCurrentInstance() { 353 | return currentInstance; 354 | } 355 | function setCurrentInstance(instance) { 356 | currentInstance = instance; 357 | } 358 | 359 | var Fragment = Symbol("Fragment"); 360 | var Text = Symbol("Text"); 361 | function createVNode(type, props, children) { 362 | var vnode = { 363 | type: type, 364 | props: props, 365 | children: children, 366 | shapeFlag: getShapeFlag(type, children), 367 | el: null, 368 | }; 369 | return vnode; 370 | } 371 | function createTextVnode(text) { 372 | return createVNode(Text, {}, String(text)); 373 | } 374 | function getShapeFlag(type, children) { 375 | var flag = 0; 376 | // 处理 element 和 component 的flag 377 | if (typeof type === "string") { 378 | flag |= 1 /* ShapeFlags.ELEMENT */; 379 | } 380 | else if (type === Text) { 381 | flag |= 4 /* ShapeFlags.TEXT_CHILDREN */; 382 | } 383 | else { 384 | flag |= 2 /* ShapeFlags.STATEFUL_COMPONENT */; 385 | } 386 | // 处理 children 的flag 387 | if (Array.isArray(children)) { 388 | flag |= 8 /* ShapeFlags.ARRAY_CHILDREN */; 389 | } 390 | else if (typeof children === "string") { 391 | flag |= 4 /* ShapeFlags.TEXT_CHILDREN */; 392 | } 393 | return flag; 394 | } 395 | 396 | function createAppApi(render) { 397 | return function createApp(rootComponent) { 398 | return { 399 | mount: function (rootContainer) { 400 | var vnode = createVNode(rootComponent); 401 | render(vnode, rootContainer); 402 | }, 403 | }; 404 | }; 405 | } 406 | 407 | function createRender(options) { 408 | var createTextNode = options.createTextNode, insert = options.insert, createElement = options.createElement, hostPatchProps = options.hostPatchProps, setElementInnerContext = options.setElementInnerContext, removeAllChildren = options.removeAllChildren; 409 | function render(vnode, rootContainer) { 410 | patch(null, vnode, rootContainer, null); 411 | } 412 | function patch(n1, n2, container, parentComponent) { 413 | var type = n2.type, shapeFlag = n2.shapeFlag; 414 | switch (type) { 415 | case Fragment: 416 | processFragment(n1, n2, container); 417 | break; 418 | case Text: 419 | processText(n1, n2, container); 420 | break; 421 | default: 422 | if (shapeFlag & 2 /* ShapeFlags.STATEFUL_COMPONENT */) { 423 | // vnode 是一个component 424 | // h(App,{},[]) 425 | processComponent(n1, n2, container, parentComponent); 426 | } 427 | else if (shapeFlag & 1 /* ShapeFlags.ELEMENT */) { 428 | // vnode 是一个element 429 | // h('div',{class:'red'},null) 430 | processElement(n1, n2, container, parentComponent); 431 | } 432 | else { 433 | // vnode 不是component 和 element 那就可能是一个文本节点 434 | // h('div',{},[h('p',{},null),'222',this.msg]) 435 | insert(createTextNode(n2), container); 436 | } 437 | } 438 | } 439 | function processText(n1, n2, container) { 440 | var children = n2.children; 441 | var textNode = (n2.el = createTextNode(children)); 442 | insert(textNode, container); 443 | } 444 | function processFragment(n1, n2, container) { 445 | mountChildren(n2.children, container, null); 446 | } 447 | function processElement(n1, n2, container, parentComponent) { 448 | if (!n1) { 449 | mountElement(n2, container, parentComponent); 450 | } 451 | else { 452 | updateElement(n1, n2); 453 | } 454 | } 455 | function updateElement(n1, n2, container) { 456 | var oldProps = n1.props || EMPTY_OBJ; 457 | var newProps = n2.props || EMPTY_OBJ; 458 | var el = (n2.el = n1.el); 459 | // 对比props 460 | patchProps(el, oldProps, newProps); 461 | // 对比children 462 | patchChildren(n1, n2, el); 463 | } 464 | function patchChildren(n1, n2, container) { 465 | // n1 children 是文本节点 466 | if (!!(n1.shapeFlag & 4 /* ShapeFlags.TEXT_CHILDREN */)) { 467 | if (!!(n2.shapeFlag & 8 /* ShapeFlags.ARRAY_CHILDREN */)) { 468 | // text children => array children 469 | removeAllChildren(container); 470 | mountChildren(n2.children, container, null); 471 | } 472 | else { 473 | // text children => text children 474 | if (n1.children !== n2.children) { 475 | setElementInnerContext(container, n2.children); 476 | } 477 | } 478 | } 479 | else { 480 | // n1 children 是一个数组 481 | if (!!(n2.shapeFlag & 8 /* ShapeFlags.ARRAY_CHILDREN */)) ; 482 | else { 483 | // array children => text children 484 | removeAllChildren(container); 485 | setElementInnerContext(container, n2.children); 486 | } 487 | } 488 | } 489 | function patchProps(el, oldProps, newProps) { 490 | if (oldProps !== newProps) { 491 | for (var key in newProps) { 492 | var prevProp = oldProps[key]; 493 | var nextProp = newProps[key]; 494 | if (prevProp !== nextProp) { 495 | hostPatchProps(el, key, prevProp, nextProp); 496 | } 497 | } 498 | if (oldProps !== EMPTY_OBJ) { 499 | for (var key in oldProps) { 500 | if (!(key in newProps)) { 501 | hostPatchProps(el, key, oldProps[key], null); 502 | } 503 | } 504 | } 505 | } 506 | } 507 | function mountElement(vnode, container, parentComponent) { 508 | var type = vnode.type, children = vnode.children, props = vnode.props, shapeFlag = vnode.shapeFlag; 509 | var el = (vnode.el = createElement(type)); 510 | //children 511 | if (shapeFlag & 8 /* ShapeFlags.ARRAY_CHILDREN */) { 512 | mountChildren(children, el, parentComponent); 513 | } 514 | else if (!!(children.shapeFlag & 4 /* ShapeFlags.TEXT_CHILDREN */)) { 515 | var vlaue = children.children; 516 | insert(createTextNode(vlaue), el); 517 | } 518 | else { 519 | setElementInnerContext(el, children); 520 | } 521 | // props 522 | for (var key in props) { 523 | var val = props[key]; 524 | hostPatchProps(el, key, null, val); 525 | } 526 | insert(el, container); 527 | } 528 | function mountChildren(children, container, parentComponent) { 529 | children.forEach(function (child) { 530 | patch(null, child, container, parentComponent); 531 | }); 532 | } 533 | function processComponent(n1, n2, container, parentComponent) { 534 | mountComponent(n2, container, parentComponent); 535 | } 536 | function mountComponent(initinalVNode, container, parentComponent) { 537 | var instance = createComponentInstance(initinalVNode, parentComponent); 538 | setupComponent(instance); 539 | setupRenderEffect(instance, container); 540 | } 541 | function setupRenderEffect(instance, container) { 542 | effect(function () { 543 | var proxy = instance.proxy; 544 | if (!instance.isMounted) { 545 | console.log("init"); 546 | var subTree = (instance.subTree = instance.render.call(proxy)); 547 | patch(null, subTree, container, instance); 548 | instance.vnode.el = subTree.el; 549 | instance.isMounted = true; 550 | } 551 | else { 552 | console.log("update"); 553 | var subTree = instance.render.call(proxy); 554 | var preSubTree = instance.subTree; 555 | instance.vnode.el = subTree.el; 556 | patch(preSubTree, subTree, container, instance); 557 | } 558 | }); 559 | } 560 | return { 561 | createApp: createAppApi(render), 562 | }; 563 | } 564 | 565 | function h(type, props, children) { 566 | return createVNode(type, props, children); 567 | } 568 | 569 | function renderSlots(slots, name, props) { 570 | var slot = slots[name]; 571 | if (slot) { 572 | if (typeof slot === "function") { 573 | return createVNode(Fragment, {}, handleSlotChilren(slot(props))); 574 | } 575 | } 576 | } 577 | function handleSlotChilren(slot) { 578 | return Array.isArray(slot) ? slot : [slot]; 579 | } 580 | 581 | function provide(key, value) { 582 | var instance = getCurrentInstance(); 583 | if (instance) { 584 | var provides = instance.provides; 585 | provides[key] = value; 586 | } 587 | } 588 | function inject(key) { 589 | var instance = getCurrentInstance(); 590 | if (instance) { 591 | var value = null; 592 | while (value === null) { 593 | var parent_1 = instance.parent; 594 | if (parent_1) { 595 | if (parent_1.provides[key]) { 596 | return parent_1.provides[key]; 597 | } 598 | } 599 | } 600 | } 601 | } 602 | 603 | function createTextNode(text) { 604 | return document.createTextNode(text); 605 | } 606 | function insert(child, container) { 607 | container.appendChild(child); 608 | } 609 | function createElement(type) { 610 | return document.createElement(type); 611 | } 612 | function patchProps(el, key, prevVal, nextVal) { 613 | var isOn = function (key) { return /^on[A-Z]*/.test(key); }; 614 | // event 615 | if (isOn(key)) { 616 | var event_1 = key.slice(2).toLocaleLowerCase(); 617 | el.addEventListener(event_1, nextVal); 618 | } 619 | else { 620 | // attribute 621 | if (nextVal === undefined || nextVal === null) { 622 | el.removeAttribute(key); 623 | } 624 | else { 625 | el.setAttribute(key, nextVal); 626 | } 627 | } 628 | } 629 | function setElementInnerContext(el, context) { 630 | el.textContent = context; 631 | } 632 | function removeAllChildren(el) { 633 | el.innerHTML = ""; 634 | } 635 | var renderer = createRender({ 636 | createTextNode: createTextNode, 637 | insert: insert, 638 | createElement: createElement, 639 | hostPatchProps: patchProps, 640 | setElementInnerContext: setElementInnerContext, 641 | removeAllChildren: removeAllChildren, 642 | }); 643 | function createApp(rootComponent) { 644 | return renderer.createApp(rootComponent); 645 | } 646 | 647 | exports.ReactiveEffect = ReactiveEffect; 648 | exports.cleanupEffect = cleanupEffect; 649 | exports.computed = computed; 650 | exports.createApp = createApp; 651 | exports.createTextVnode = createTextVnode; 652 | exports.effect = effect; 653 | exports.getCurrentInstance = getCurrentInstance; 654 | exports.h = h; 655 | exports.inject = inject; 656 | exports.isProxy = isProxy; 657 | exports.isReactive = isReactive; 658 | exports.isReadonly = isReadonly; 659 | exports.isRef = isRef; 660 | exports.provide = provide; 661 | exports.proxyRefs = proxyRefs; 662 | exports.reactive = reactive; 663 | exports.readonly = readonly; 664 | exports.ref = ref; 665 | exports.renderSlots = renderSlots; 666 | exports.shallowReactive = shallowReactive; 667 | exports.shallowReadonly = shallowReadonly; 668 | exports.stop = stop; 669 | exports.track = track; 670 | exports.trackEffects = trackEffects; 671 | exports.trigger = trigger; 672 | exports.triggerEffects = triggerEffects; 673 | exports.unRef = unRef; 674 | -------------------------------------------------------------------------------- /lib/guide-mini-vue3.esm.js: -------------------------------------------------------------------------------- 1 | var extend = Object.assign; 2 | var EMPTY_OBJ = {}; 3 | var isObject = function (target) { return typeof target === "object"; }; 4 | var hasChanged = function (value, newValue) { return !Object.is(value, newValue); }; 5 | var hasOwn = function (value, key) { 6 | return Object.prototype.hasOwnProperty.call(value, key); 7 | }; 8 | var camelize = function (str) { 9 | return str.replace(/-(\w)/g, function (_, c) { return (c ? c.toUpperCase() : ""); }); 10 | }; 11 | var capitalize = function (str) { return str.charAt(0).toUpperCase() + str.slice(1); }; 12 | var toHandlerKey = function (str) { return (str ? "on" + capitalize(str) : ""); }; 13 | 14 | var activeEffect; 15 | var targetMap = new WeakMap(); 16 | function track(target, key) { 17 | if (!activeEffect) { 18 | return; 19 | } 20 | var depsMap = targetMap.get(target); 21 | if (!depsMap) { 22 | depsMap = new Map(); 23 | targetMap.set(target, depsMap); 24 | } 25 | var dep = depsMap.get(key); 26 | if (!dep) { 27 | dep = new Set(); 28 | depsMap.set(key, dep); 29 | } 30 | trackEffects(dep); 31 | } 32 | function trackEffects(dep) { 33 | dep.add(activeEffect); 34 | activeEffect.deps.push(dep); 35 | } 36 | function trigger(target, key) { 37 | var depsMap = targetMap.get(target); 38 | if (!depsMap) { 39 | return; 40 | } 41 | var dep = depsMap.get(key); 42 | if (!dep) { 43 | return; 44 | } 45 | triggerEffects(dep); 46 | } 47 | function triggerEffects(dep) { 48 | dep.forEach(function (effect) { 49 | if (effect.scheduler) { 50 | effect.scheduler(); 51 | } 52 | else { 53 | effect.run(); 54 | } 55 | }); 56 | } 57 | var ReactiveEffect = /** @class */ (function () { 58 | function ReactiveEffect(_fn, scheduler) { 59 | this._fn = _fn; 60 | this.scheduler = scheduler; 61 | this.deps = []; 62 | this.active = true; 63 | activeEffect = this; 64 | } 65 | ReactiveEffect.prototype.run = function () { 66 | return this._fn(); 67 | }; 68 | ReactiveEffect.prototype.stop = function () { 69 | if (this.active) { 70 | cleanupEffect(this); 71 | if (this.onStop) { 72 | this.onStop(); 73 | } 74 | this.active = false; 75 | } 76 | }; 77 | return ReactiveEffect; 78 | }()); 79 | function cleanupEffect(effect) { 80 | effect.deps.forEach(function (dep) { 81 | dep.delete(effect); 82 | }); 83 | effect.deps.length = 0; 84 | } 85 | function effect(fn, options) { 86 | var _effect = new ReactiveEffect(fn); 87 | if (options) { 88 | extend(_effect, options); 89 | } 90 | _effect.run(); 91 | activeEffect = null; 92 | var runner = _effect.run.bind(_effect); 93 | runner.effect = _effect; 94 | return runner; 95 | } 96 | function stop(runner) { 97 | runner.effect.stop(); 98 | } 99 | 100 | var get = createGetter(); 101 | var set = createSetter(); 102 | var readonlyGet = createGetter(true); 103 | var shallowReactiveGet = createGetter(false, true); 104 | var shallowReadonlyGet = createGetter(true, true); 105 | function createGetter(isReadonly, isShallow) { 106 | if (isReadonly === void 0) { isReadonly = false; } 107 | if (isShallow === void 0) { isShallow = false; } 108 | return function (target, key) { 109 | if (key === "__v_isReactive" /* ReactiveFlags.IS_REACTIVE */) { 110 | return !isReadonly; 111 | } 112 | if (key === "__v_isReadonly" /* ReactiveFlags.IS_READONLY */) { 113 | return isReadonly; 114 | } 115 | var value = Reflect.get(target, key); 116 | if (isObject(value) && !isShallow) { 117 | return isReadonly ? readonly(value) : reactive(value); 118 | } 119 | if (!isReadonly) { 120 | track(target, key); 121 | } 122 | return value; 123 | }; 124 | } 125 | function createSetter() { 126 | return function set(target, key, value) { 127 | var oldValue = Reflect.get(target, key); 128 | if (hasChanged(oldValue, value)) { 129 | var result = Reflect.set(target, key, value); 130 | trigger(target, key); 131 | return result; 132 | } 133 | return oldValue; 134 | }; 135 | } 136 | var mutableHandlers = { 137 | get: get, 138 | set: set, 139 | }; 140 | var shallowMutableHandlers = extend({}, shallowReactiveGet, { 141 | get: shallowReadonlyGet, 142 | }); 143 | var readonlyHandlers = { 144 | get: readonlyGet, 145 | set: function (target, key, value) { 146 | console.warn("key: ".concat(key, " set \u5931\u8D25 \u56E0\u4E3A target \u662F readonly"), key); 147 | return true; 148 | }, 149 | }; 150 | var shallowReadonlyHandlers = extend({}, readonlyHandlers, { 151 | get: shallowReadonlyGet, 152 | }); 153 | 154 | function reactive(raw) { 155 | return createActiveObject(raw, mutableHandlers); 156 | } 157 | function readonly(raw) { 158 | return createActiveObject(raw, readonlyHandlers); 159 | } 160 | function shallowReactive(raw) { 161 | return createActiveObject(raw, shallowMutableHandlers); 162 | } 163 | function shallowReadonly(raw) { 164 | return createActiveObject(raw, shallowReadonlyHandlers); 165 | } 166 | function createActiveObject(raw, baseHandlers) { 167 | if (!isObject(raw)) { 168 | console.log("target ".concat(raw, " \u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61")); 169 | return raw; 170 | } 171 | return new Proxy(raw, baseHandlers); 172 | } 173 | function isReadonly(value) { 174 | return !!value["__v_isReadonly" /* ReactiveFlags.IS_READONLY */]; 175 | } 176 | function isReactive(value) { 177 | return !!value["__v_isReactive" /* ReactiveFlags.IS_REACTIVE */]; 178 | } 179 | function isProxy(value) { 180 | return isReactive(value) || isReadonly(value); 181 | } 182 | 183 | var ComputedRefImpl = /** @class */ (function () { 184 | function ComputedRefImpl(getter) { 185 | var _this = this; 186 | this._dirty = true; 187 | this._getter = getter; 188 | this._effect = new ReactiveEffect(getter, function () { 189 | if (!_this._dirty) { 190 | _this._dirty = true; 191 | } 192 | }); 193 | } 194 | Object.defineProperty(ComputedRefImpl.prototype, "value", { 195 | get: function () { 196 | if (this._dirty) { 197 | this._dirty = false; 198 | this._value = this._effect.run(); 199 | } 200 | return this._value; 201 | }, 202 | enumerable: false, 203 | configurable: true 204 | }); 205 | return ComputedRefImpl; 206 | }()); 207 | function computed(getter) { 208 | return new ComputedRefImpl(getter); 209 | } 210 | 211 | var refImpl = /** @class */ (function () { 212 | function refImpl(value) { 213 | this.__v_ref = true; 214 | this._rawValue = value; 215 | this._value = convert(value); 216 | this.dep = new Set(); 217 | } 218 | Object.defineProperty(refImpl.prototype, "value", { 219 | get: function () { 220 | if (activeEffect) { 221 | trackEffects(this.dep); 222 | } 223 | return this._value; 224 | }, 225 | set: function (value) { 226 | if (hasChanged(value, this._rawValue)) { 227 | this._rawValue = value; 228 | this._value = convert(value); 229 | triggerEffects(this.dep); 230 | } 231 | }, 232 | enumerable: false, 233 | configurable: true 234 | }); 235 | return refImpl; 236 | }()); 237 | function convert(value) { 238 | return isObject(value) ? reactive(value) : value; 239 | } 240 | function ref(value) { 241 | return new refImpl(value); 242 | } 243 | function isRef(ref) { 244 | return !!ref.__v_ref; 245 | } 246 | function unRef(ref) { 247 | return isRef(ref) ? ref.value : ref; 248 | } 249 | function proxyRefs(objectwithRefs) { 250 | if (objectwithRefs === void 0) { objectwithRefs = {}; } 251 | return new Proxy(objectwithRefs, { 252 | get: function (target, key) { 253 | return unRef(Reflect.get(target, key)); 254 | }, 255 | set: function (target, key, value) { 256 | if (isRef(target[key]) && !isRef(value)) { 257 | return (target[key].value = value); 258 | } 259 | else { 260 | return Reflect.set(target, key, value); 261 | } 262 | }, 263 | }); 264 | } 265 | 266 | function emit(instance, event) { 267 | var props = instance.props; 268 | var handlerName = toHandlerKey(camelize(event)); 269 | var handler = props[handlerName]; 270 | handler && handler(); 271 | } 272 | 273 | function initProps(instance, rawProps) { 274 | instance.props = rawProps || {}; 275 | } 276 | 277 | var publicPropertiesMap = { 278 | $el: function (i) { return i.vnode.el; }, 279 | $slots: function (i) { return i.slots; }, 280 | }; 281 | var publicInstanceProxyHandlers = { 282 | get: function (_a, key) { 283 | var instance = _a._; 284 | var setupState = instance.setupState, props = instance.props; 285 | if (hasOwn(setupState, key)) { 286 | return setupState[key]; 287 | } 288 | if (hasOwn(props, key)) { 289 | return props[key]; 290 | } 291 | var publicGetter = publicPropertiesMap[key]; 292 | if (publicGetter) { 293 | return publicGetter(instance); 294 | } 295 | }, 296 | }; 297 | 298 | function initSlots(instance, children) { 299 | instance.slots = children; 300 | } 301 | 302 | var currentInstance = null; 303 | function createComponentInstance(vnode, parentComponent) { 304 | var component = { 305 | vnode: vnode, 306 | type: vnode.type, 307 | setupState: {}, 308 | props: {}, 309 | slots: {}, 310 | provides: {}, 311 | isMounted: false, 312 | subTree: {}, 313 | parent: parentComponent, 314 | emit: function (instance, event) { }, 315 | }; 316 | component.emit = emit.bind(null, component); 317 | return component; 318 | } 319 | function setupComponent(instance) { 320 | initProps(instance, instance.vnode.props); 321 | initSlots(instance, instance.vnode.children); 322 | setupStatefulComponent(instance); 323 | } 324 | function setupStatefulComponent(instance) { 325 | var Component = instance.type; 326 | instance.proxy = new Proxy({ _: instance }, publicInstanceProxyHandlers); 327 | var setup = Component.setup; 328 | if (setup) { 329 | setCurrentInstance(instance); 330 | var setupResult = setup(shallowReadonly(instance.props), { 331 | emit: instance.emit, 332 | }); 333 | setCurrentInstance(null); 334 | handleSetupResult(instance, proxyRefs(setupResult)); 335 | } 336 | } 337 | function handleSetupResult(instance, setupResult) { 338 | if (typeof setupResult === "object") { 339 | // 如果是object那么返回的就是context 340 | instance.setupState = setupResult; 341 | } 342 | finishCompoentSetup(instance); 343 | } 344 | function finishCompoentSetup(instance) { 345 | var Component = instance.type; 346 | instance.render = Component.render; 347 | } 348 | function getCurrentInstance() { 349 | return currentInstance; 350 | } 351 | function setCurrentInstance(instance) { 352 | currentInstance = instance; 353 | } 354 | 355 | var Fragment = Symbol("Fragment"); 356 | var Text = Symbol("Text"); 357 | function createVNode(type, props, children) { 358 | var vnode = { 359 | type: type, 360 | props: props, 361 | children: children, 362 | shapeFlag: getShapeFlag(type, children), 363 | el: null, 364 | }; 365 | return vnode; 366 | } 367 | function createTextVnode(text) { 368 | return createVNode(Text, {}, String(text)); 369 | } 370 | function getShapeFlag(type, children) { 371 | var flag = 0; 372 | // 处理 element 和 component 的flag 373 | if (typeof type === "string") { 374 | flag |= 1 /* ShapeFlags.ELEMENT */; 375 | } 376 | else if (type === Text) { 377 | flag |= 4 /* ShapeFlags.TEXT_CHILDREN */; 378 | } 379 | else { 380 | flag |= 2 /* ShapeFlags.STATEFUL_COMPONENT */; 381 | } 382 | // 处理 children 的flag 383 | if (Array.isArray(children)) { 384 | flag |= 8 /* ShapeFlags.ARRAY_CHILDREN */; 385 | } 386 | else if (typeof children === "string") { 387 | flag |= 4 /* ShapeFlags.TEXT_CHILDREN */; 388 | } 389 | return flag; 390 | } 391 | 392 | function createAppApi(render) { 393 | return function createApp(rootComponent) { 394 | return { 395 | mount: function (rootContainer) { 396 | var vnode = createVNode(rootComponent); 397 | render(vnode, rootContainer); 398 | }, 399 | }; 400 | }; 401 | } 402 | 403 | function createRender(options) { 404 | var createTextNode = options.createTextNode, insert = options.insert, createElement = options.createElement, hostPatchProps = options.hostPatchProps, setElementInnerContext = options.setElementInnerContext, removeAllChildren = options.removeAllChildren; 405 | function render(vnode, rootContainer) { 406 | patch(null, vnode, rootContainer, null); 407 | } 408 | function patch(n1, n2, container, parentComponent) { 409 | var type = n2.type, shapeFlag = n2.shapeFlag; 410 | switch (type) { 411 | case Fragment: 412 | processFragment(n1, n2, container); 413 | break; 414 | case Text: 415 | processText(n1, n2, container); 416 | break; 417 | default: 418 | if (shapeFlag & 2 /* ShapeFlags.STATEFUL_COMPONENT */) { 419 | // vnode 是一个component 420 | // h(App,{},[]) 421 | processComponent(n1, n2, container, parentComponent); 422 | } 423 | else if (shapeFlag & 1 /* ShapeFlags.ELEMENT */) { 424 | // vnode 是一个element 425 | // h('div',{class:'red'},null) 426 | processElement(n1, n2, container, parentComponent); 427 | } 428 | else { 429 | // vnode 不是component 和 element 那就可能是一个文本节点 430 | // h('div',{},[h('p',{},null),'222',this.msg]) 431 | insert(createTextNode(n2), container); 432 | } 433 | } 434 | } 435 | function processText(n1, n2, container) { 436 | var children = n2.children; 437 | var textNode = (n2.el = createTextNode(children)); 438 | insert(textNode, container); 439 | } 440 | function processFragment(n1, n2, container) { 441 | mountChildren(n2.children, container, null); 442 | } 443 | function processElement(n1, n2, container, parentComponent) { 444 | if (!n1) { 445 | mountElement(n2, container, parentComponent); 446 | } 447 | else { 448 | updateElement(n1, n2); 449 | } 450 | } 451 | function updateElement(n1, n2, container) { 452 | var oldProps = n1.props || EMPTY_OBJ; 453 | var newProps = n2.props || EMPTY_OBJ; 454 | var el = (n2.el = n1.el); 455 | // 对比props 456 | patchProps(el, oldProps, newProps); 457 | // 对比children 458 | patchChildren(n1, n2, el); 459 | } 460 | function patchChildren(n1, n2, container) { 461 | // n1 children 是文本节点 462 | if (!!(n1.shapeFlag & 4 /* ShapeFlags.TEXT_CHILDREN */)) { 463 | if (!!(n2.shapeFlag & 8 /* ShapeFlags.ARRAY_CHILDREN */)) { 464 | // text children => array children 465 | removeAllChildren(container); 466 | mountChildren(n2.children, container, null); 467 | } 468 | else { 469 | // text children => text children 470 | if (n1.children !== n2.children) { 471 | setElementInnerContext(container, n2.children); 472 | } 473 | } 474 | } 475 | else { 476 | // n1 children 是一个数组 477 | if (!!(n2.shapeFlag & 8 /* ShapeFlags.ARRAY_CHILDREN */)) ; 478 | else { 479 | // array children => text children 480 | removeAllChildren(container); 481 | setElementInnerContext(container, n2.children); 482 | } 483 | } 484 | } 485 | function patchProps(el, oldProps, newProps) { 486 | if (oldProps !== newProps) { 487 | for (var key in newProps) { 488 | var prevProp = oldProps[key]; 489 | var nextProp = newProps[key]; 490 | if (prevProp !== nextProp) { 491 | hostPatchProps(el, key, prevProp, nextProp); 492 | } 493 | } 494 | if (oldProps !== EMPTY_OBJ) { 495 | for (var key in oldProps) { 496 | if (!(key in newProps)) { 497 | hostPatchProps(el, key, oldProps[key], null); 498 | } 499 | } 500 | } 501 | } 502 | } 503 | function mountElement(vnode, container, parentComponent) { 504 | var type = vnode.type, children = vnode.children, props = vnode.props, shapeFlag = vnode.shapeFlag; 505 | var el = (vnode.el = createElement(type)); 506 | //children 507 | if (shapeFlag & 8 /* ShapeFlags.ARRAY_CHILDREN */) { 508 | mountChildren(children, el, parentComponent); 509 | } 510 | else if (!!(children.shapeFlag & 4 /* ShapeFlags.TEXT_CHILDREN */)) { 511 | var vlaue = children.children; 512 | insert(createTextNode(vlaue), el); 513 | } 514 | else { 515 | setElementInnerContext(el, children); 516 | } 517 | // props 518 | for (var key in props) { 519 | var val = props[key]; 520 | hostPatchProps(el, key, null, val); 521 | } 522 | insert(el, container); 523 | } 524 | function mountChildren(children, container, parentComponent) { 525 | children.forEach(function (child) { 526 | patch(null, child, container, parentComponent); 527 | }); 528 | } 529 | function processComponent(n1, n2, container, parentComponent) { 530 | mountComponent(n2, container, parentComponent); 531 | } 532 | function mountComponent(initinalVNode, container, parentComponent) { 533 | var instance = createComponentInstance(initinalVNode, parentComponent); 534 | setupComponent(instance); 535 | setupRenderEffect(instance, container); 536 | } 537 | function setupRenderEffect(instance, container) { 538 | effect(function () { 539 | var proxy = instance.proxy; 540 | if (!instance.isMounted) { 541 | console.log("init"); 542 | var subTree = (instance.subTree = instance.render.call(proxy)); 543 | patch(null, subTree, container, instance); 544 | instance.vnode.el = subTree.el; 545 | instance.isMounted = true; 546 | } 547 | else { 548 | console.log("update"); 549 | var subTree = instance.render.call(proxy); 550 | var preSubTree = instance.subTree; 551 | instance.vnode.el = subTree.el; 552 | patch(preSubTree, subTree, container, instance); 553 | } 554 | }); 555 | } 556 | return { 557 | createApp: createAppApi(render), 558 | }; 559 | } 560 | 561 | function h(type, props, children) { 562 | return createVNode(type, props, children); 563 | } 564 | 565 | function renderSlots(slots, name, props) { 566 | var slot = slots[name]; 567 | if (slot) { 568 | if (typeof slot === "function") { 569 | return createVNode(Fragment, {}, handleSlotChilren(slot(props))); 570 | } 571 | } 572 | } 573 | function handleSlotChilren(slot) { 574 | return Array.isArray(slot) ? slot : [slot]; 575 | } 576 | 577 | function provide(key, value) { 578 | var instance = getCurrentInstance(); 579 | if (instance) { 580 | var provides = instance.provides; 581 | provides[key] = value; 582 | } 583 | } 584 | function inject(key) { 585 | var instance = getCurrentInstance(); 586 | if (instance) { 587 | var value = null; 588 | while (value === null) { 589 | var parent_1 = instance.parent; 590 | if (parent_1) { 591 | if (parent_1.provides[key]) { 592 | return parent_1.provides[key]; 593 | } 594 | } 595 | } 596 | } 597 | } 598 | 599 | function createTextNode(text) { 600 | return document.createTextNode(text); 601 | } 602 | function insert(child, container) { 603 | container.appendChild(child); 604 | } 605 | function createElement(type) { 606 | return document.createElement(type); 607 | } 608 | function patchProps(el, key, prevVal, nextVal) { 609 | var isOn = function (key) { return /^on[A-Z]*/.test(key); }; 610 | // event 611 | if (isOn(key)) { 612 | var event_1 = key.slice(2).toLocaleLowerCase(); 613 | el.addEventListener(event_1, nextVal); 614 | } 615 | else { 616 | // attribute 617 | if (nextVal === undefined || nextVal === null) { 618 | el.removeAttribute(key); 619 | } 620 | else { 621 | el.setAttribute(key, nextVal); 622 | } 623 | } 624 | } 625 | function setElementInnerContext(el, context) { 626 | el.textContent = context; 627 | } 628 | function removeAllChildren(el) { 629 | el.innerHTML = ""; 630 | } 631 | var renderer = createRender({ 632 | createTextNode: createTextNode, 633 | insert: insert, 634 | createElement: createElement, 635 | hostPatchProps: patchProps, 636 | setElementInnerContext: setElementInnerContext, 637 | removeAllChildren: removeAllChildren, 638 | }); 639 | function createApp(rootComponent) { 640 | return renderer.createApp(rootComponent); 641 | } 642 | 643 | export { ReactiveEffect, activeEffect, cleanupEffect, computed, createApp, createTextVnode, effect, getCurrentInstance, h, inject, isProxy, isReactive, isReadonly, isRef, provide, proxyRefs, reactive, readonly, ref, renderSlots, shallowReactive, shallowReadonly, stop, track, trackEffects, trigger, triggerEffects, unRef }; 644 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-vue3", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "lib/guide-mini-vue3.cjs.js", 6 | "module": "lib/guide-mini-vue3.esm.js", 7 | "scripts": { 8 | "test": "jest", 9 | "build": "rollup -c rollup.config.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@rollup/plugin-node-resolve": "^13.1.3", 16 | "@rollup/plugin-typescript": "^8.3.0" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "^7.17.5", 20 | "@babel/preset-env": "^7.16.11", 21 | "@babel/preset-typescript": "^7.16.7", 22 | "@types/jest": "^27.4.0", 23 | "babel-jest": "^27.5.1", 24 | "jest": "^27.5.1", 25 | "rollup": "^2.68.0", 26 | "tslib": "^2.3.1", 27 | "typescript": "^4.5.5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./reactivity/src" 2 | export * from "./runtime-dom" 3 | -------------------------------------------------------------------------------- /packages/reactivity/__test__/computed.spec.ts: -------------------------------------------------------------------------------- 1 | import { computed } from "../src/computed" 2 | import { reactive } from "../src/reactive" 3 | describe("reactivity/computed", () => { 4 | it("should when get", () => { 5 | const state: any = reactive({ count: 1 }) 6 | const double = computed(() => state.count * 2) 7 | expect(double.value).toBe(2) 8 | }) 9 | 10 | it("should compute lazily", () => { 11 | const value: any = reactive({ 12 | foo: 1, 13 | }) 14 | const getter = jest.fn(() => value.foo) 15 | const cValue = computed(getter) 16 | 17 | // lazy 18 | expect(getter).not.toHaveBeenCalled() 19 | 20 | expect(cValue.value).toBe(1) 21 | expect(getter).toHaveBeenCalledTimes(1) 22 | 23 | // should not compute again 24 | cValue.value 25 | expect(getter).toHaveBeenCalledTimes(1) 26 | 27 | // // should not compute until needed 28 | value.foo = 2 29 | expect(getter).toHaveBeenCalledTimes(1) 30 | 31 | // now it should compute 32 | expect(cValue.value).toBe(2) 33 | expect(getter).toHaveBeenCalledTimes(2) 34 | 35 | // should not compute again 36 | cValue.value 37 | expect(getter).toHaveBeenCalledTimes(2) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /packages/reactivity/__test__/effect.spec.ts: -------------------------------------------------------------------------------- 1 | import { reactive } from "../src/reactive" 2 | import { effect, stop } from "../src/effect" 3 | 4 | describe("reactivity/effect", () => { 5 | it("should observe basic properties", () => { 6 | const counter: any = reactive({ num: 1 }) 7 | let dummy 8 | 9 | effect(function () { 10 | dummy = counter.num 11 | }) 12 | 13 | expect(dummy).toBe(1) 14 | 15 | // update 16 | counter.num++ 17 | expect(dummy).toBe(2) 18 | }) 19 | 20 | it("should return runner when call effect", () => { 21 | let foo = 10 22 | const runner = effect(() => { 23 | foo++ 24 | return "foo" 25 | }) 26 | expect(foo).toBe(11) 27 | const r = runner() 28 | expect(foo).toBe(12) 29 | expect(r).toBe("foo") 30 | }) 31 | 32 | it("scheduler", () => { 33 | let dummy 34 | let run: any 35 | const scheduler = jest.fn(() => { 36 | run = runner 37 | }) 38 | const obj: any = reactive({ foo: 1 }) 39 | const runner = effect( 40 | () => { 41 | dummy = obj.foo 42 | }, 43 | { scheduler } 44 | ) 45 | expect(scheduler).not.toHaveBeenCalled() 46 | expect(dummy).toBe(1) 47 | // should be called on first trigger 48 | obj.foo++ 49 | expect(scheduler).toHaveBeenCalledTimes(1) 50 | // should not run yet 51 | expect(dummy).toBe(1) 52 | // manually run 53 | run() 54 | // should have run 55 | expect(dummy).toBe(2) 56 | }) 57 | 58 | it("stop", () => { 59 | let dummy 60 | let a 61 | const obj: any = reactive({ prop: 1, a: 1 }) 62 | const runner = effect(() => { 63 | dummy = obj.prop 64 | }) 65 | obj.prop = 2 66 | expect(dummy).toBe(2) 67 | stop(runner) 68 | obj.prop++ 69 | expect(dummy).toBe(2) 70 | 71 | // stopped effect should still be manually callable 72 | runner() 73 | expect(dummy).toBe(3) 74 | }) 75 | 76 | it("events: onStop", () => { 77 | const onStop = jest.fn() 78 | const runner = effect(() => {}, { 79 | onStop, 80 | }) 81 | 82 | stop(runner) 83 | expect(onStop).toBeCalledTimes(1) 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /packages/reactivity/__test__/reactive.spec.ts: -------------------------------------------------------------------------------- 1 | import { isReactive, shallowReactive, isProxy, reactive } from "../src" 2 | 3 | describe("reactivity/reactive", () => { 4 | test("Object", () => { 5 | const original = { foo: 1 } 6 | const observed: any = reactive(original) 7 | expect(observed).not.toBe(original) 8 | 9 | // get 10 | expect(observed.foo).toBe(1) 11 | 12 | // set 13 | observed.foo = 2 14 | expect(original.foo).toBe(2) 15 | }) 16 | 17 | it("should make nested values reactive", () => { 18 | const obj = { 19 | foo: { 20 | bar: 1, 21 | }, 22 | } 23 | const wrapped: any = reactive(obj) 24 | expect(isReactive(obj)).toBe(false) 25 | expect(isReactive(wrapped)).toBe(true) 26 | expect(isReactive(obj.foo)).toBe(false) 27 | expect(isReactive(wrapped.foo)).toBe(true) 28 | expect(isProxy(obj)).toBe(false) 29 | expect(isProxy(obj.foo)).toBe(false) 30 | expect(isProxy(wrapped)).toBe(true) 31 | expect(isProxy(wrapped.foo)).toBe(true) 32 | }) 33 | 34 | it("should make shallow values reactive", () => { 35 | const obj = { 36 | foo: { 37 | bar: 1, 38 | }, 39 | } 40 | const wrapped: any = shallowReactive(obj) 41 | expect(isReactive(wrapped.foo)).toBe(false) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /packages/reactivity/__test__/readonly.spec.ts: -------------------------------------------------------------------------------- 1 | import { readonly, isReadonly, shallowReadonly, isProxy } from "../src" 2 | describe("reactivity/readonly", () => { 3 | it("should when get", () => { 4 | const original = { foo: 1, bar: { baz: 2 } } 5 | const wrapped: any = readonly(original) 6 | expect(wrapped).not.toBe(original) 7 | expect(wrapped.foo).toBe(1) 8 | }) 9 | it("should not set when set", () => { 10 | console.warn = jest.fn() 11 | 12 | const user: any = readonly({ 13 | age: 10, 14 | }) 15 | 16 | user.age = 11 17 | 18 | expect(console.warn).toBeCalled() 19 | }) 20 | 21 | it("should make nested values readonly", () => { 22 | const obj = { 23 | foo: { 24 | bar: 1, 25 | }, 26 | } 27 | const wrapped: any = readonly(obj) 28 | expect(isReadonly(obj)).toBe(false) 29 | expect(isReadonly(wrapped)).toBe(true) 30 | expect(isReadonly(obj.foo)).toBe(false) 31 | expect(isReadonly(wrapped.foo)).toBe(true) 32 | expect(isProxy(obj)).toBe(false) 33 | expect(isProxy(obj.foo)).toBe(false) 34 | expect(isProxy(wrapped)).toBe(true) 35 | expect(isProxy(wrapped.foo)).toBe(true) 36 | }) 37 | 38 | it("should make shallow values readonly", () => { 39 | const obj = { 40 | foo: { 41 | bar: 1, 42 | }, 43 | } 44 | const wrapped: any = shallowReadonly(obj) 45 | expect(isReadonly(wrapped.foo)).toBe(false) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /packages/reactivity/__test__/ref.spec.ts: -------------------------------------------------------------------------------- 1 | import { ref, effect, isRef, unRef, proxyRefs } from "../src" 2 | describe("ractivity/ref", () => { 3 | it("should hold a value", () => { 4 | const a = ref(1) 5 | expect(a.value).toBe(1) 6 | a.value = 2 7 | expect(a.value).toBe(2) 8 | }) 9 | 10 | it("should be reactive", () => { 11 | const a = ref(1) 12 | let dummy 13 | let calls = 0 14 | effect(() => { 15 | calls++ 16 | dummy = a.value 17 | }) 18 | expect(calls).toBe(1) 19 | expect(dummy).toBe(1) 20 | a.value = 2 21 | expect(calls).toBe(2) 22 | expect(dummy).toBe(2) 23 | // same value should not trigger 24 | a.value = 2 25 | expect(calls).toBe(2) 26 | }) 27 | 28 | it("should make nested properties reactive", () => { 29 | const a = ref({ 30 | count: 1, 31 | }) 32 | let dummy 33 | effect(() => { 34 | dummy = a.value.count 35 | }) 36 | expect(dummy).toBe(1) 37 | a.value.count = 2 38 | expect(dummy).toBe(2) 39 | a.value = { 40 | count: 10, 41 | } 42 | expect(dummy).toBe(10) 43 | }) 44 | 45 | it("isRef", () => { 46 | const a = ref(0) 47 | expect(isRef(a)).toBe(true) 48 | expect(isRef(1)).toBe(false) 49 | }) 50 | 51 | it("unRef", () => { 52 | const a = ref(1) 53 | expect(unRef(a)).toBe(1) 54 | expect(unRef(1)).toBe(1) 55 | }) 56 | 57 | it("proxyRefs", () => { 58 | const user = { 59 | age: ref(18), 60 | name: "lisi", 61 | } 62 | const proxyUser = proxyRefs(user) 63 | expect(user.age.value).toBe(18) 64 | expect(proxyUser.age).toBe(18) 65 | expect(proxyUser.name).toBe("lisi") 66 | proxyUser.age = 20 67 | expect(proxyUser.age).toBe(20) 68 | expect(user.age.value).toBe(20) 69 | proxyUser.age = ref(10) 70 | expect(proxyUser.age).toBe(10) 71 | expect(user.age.value).toBe(10) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /packages/reactivity/src/baseHandlers.ts: -------------------------------------------------------------------------------- 1 | import { track, trigger } from "./effect" 2 | import { ReactiveFlags, readonly, reactive } from "./reactive" 3 | import { isObject, extend, hasChanged } from "../../shared" 4 | 5 | const get = createGetter() 6 | const set = createSetter() 7 | const readonlyGet = createGetter(true) 8 | const shallowReactiveGet = createGetter(false, true) 9 | const shallowReadonlyGet = createGetter(true, true) 10 | 11 | export function createGetter(isReadonly = false, isShallow = false) { 12 | return function (target: object, key: string) { 13 | if (key === ReactiveFlags.IS_REACTIVE) { 14 | return !isReadonly 15 | } 16 | if (key === ReactiveFlags.IS_READONLY) { 17 | return isReadonly 18 | } 19 | 20 | const value = Reflect.get(target, key) 21 | 22 | if (isObject(value) && !isShallow) { 23 | return isReadonly ? readonly(value) : reactive(value) 24 | } 25 | 26 | if (!isReadonly) { 27 | track(target, key) 28 | } 29 | return value 30 | } 31 | } 32 | 33 | export function createSetter() { 34 | return function set(target: object, key: string, value: any) { 35 | const oldValue = Reflect.get(target, key) 36 | if (hasChanged(oldValue, value)) { 37 | const result = Reflect.set(target, key, value) 38 | trigger(target, key) 39 | return result 40 | } 41 | return oldValue 42 | } 43 | } 44 | 45 | export const mutableHandlers = { 46 | get, 47 | set, 48 | } 49 | 50 | export const shallowMutableHandlers = extend({}, shallowReactiveGet, { 51 | get: shallowReadonlyGet, 52 | }) 53 | 54 | export const readonlyHandlers = { 55 | get: readonlyGet, 56 | set(target, key, value) { 57 | console.warn(`key: ${key} set 失败 因为 target 是 readonly`, key) 58 | return true 59 | }, 60 | } 61 | 62 | export const shallowReadonlyHandlers = extend({}, readonlyHandlers, { 63 | get: shallowReadonlyGet, 64 | }) 65 | -------------------------------------------------------------------------------- /packages/reactivity/src/computed.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveEffect } from "./effect" 2 | 3 | class ComputedRefImpl { 4 | _getter: any 5 | _dirty: boolean = true 6 | private _value: any 7 | private _effect: ReactiveEffect 8 | constructor(getter) { 9 | this._getter = getter 10 | this._effect = new ReactiveEffect(getter, () => { 11 | if (!this._dirty) { 12 | this._dirty = true 13 | } 14 | }) 15 | } 16 | get value() { 17 | if (this._dirty) { 18 | this._dirty = false 19 | this._value = this._effect.run() 20 | } 21 | return this._value 22 | } 23 | } 24 | 25 | export function computed(getter) { 26 | return new ComputedRefImpl(getter) 27 | } 28 | -------------------------------------------------------------------------------- /packages/reactivity/src/effect.ts: -------------------------------------------------------------------------------- 1 | import { extend } from "../../shared" 2 | 3 | export let activeEffect: any 4 | const targetMap = new WeakMap() 5 | 6 | export function track(target: Object, key: string) { 7 | if (!activeEffect) { 8 | return 9 | } 10 | 11 | let depsMap = targetMap.get(target) 12 | if (!depsMap) { 13 | depsMap = new Map() 14 | targetMap.set(target, depsMap) 15 | } 16 | 17 | let dep = depsMap.get(key) 18 | if (!dep) { 19 | dep = new Set() 20 | depsMap.set(key, dep) 21 | } 22 | 23 | trackEffects(dep) 24 | } 25 | 26 | export function trackEffects(dep: any) { 27 | dep.add(activeEffect) 28 | activeEffect.deps.push(dep) 29 | } 30 | 31 | export function trigger(target: Object, key: string) { 32 | let depsMap = targetMap.get(target) 33 | if (!depsMap) { 34 | return 35 | } 36 | 37 | let dep = depsMap.get(key) 38 | if (!dep) { 39 | return 40 | } 41 | triggerEffects(dep) 42 | } 43 | 44 | export function triggerEffects(dep: any) { 45 | dep.forEach((effect) => { 46 | if (effect.scheduler) { 47 | effect.scheduler() 48 | } else { 49 | effect.run() 50 | } 51 | }) 52 | } 53 | 54 | export class ReactiveEffect { 55 | deps: any = [] 56 | active = true 57 | onStop: (() => void) | undefined 58 | constructor(public _fn: Function, public scheduler?: () => any) { 59 | activeEffect = this 60 | } 61 | run() { 62 | return this._fn() 63 | } 64 | stop() { 65 | if (this.active) { 66 | cleanupEffect(this) 67 | if (this.onStop) { 68 | this.onStop() 69 | } 70 | this.active = false 71 | } 72 | } 73 | } 74 | 75 | export function cleanupEffect(effect) { 76 | effect.deps.forEach((dep: any) => { 77 | dep.delete(effect) 78 | }) 79 | effect.deps.length = 0 80 | } 81 | 82 | export function effect(fn: Function, options?) { 83 | const _effect = new ReactiveEffect(fn) 84 | 85 | if (options) { 86 | extend(_effect, options) 87 | } 88 | 89 | _effect.run() 90 | activeEffect = null 91 | const runner: any = _effect.run.bind(_effect) 92 | runner.effect = _effect 93 | return runner 94 | } 95 | 96 | export function stop(runner) { 97 | runner.effect.stop() 98 | } 99 | -------------------------------------------------------------------------------- /packages/reactivity/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./reactive" 2 | export * from "./computed" 3 | export * from "./effect" 4 | export * from "./ref" 5 | -------------------------------------------------------------------------------- /packages/reactivity/src/reactive.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from "../../shared" 2 | import { 3 | mutableHandlers, 4 | readonlyHandlers, 5 | shallowMutableHandlers, 6 | shallowReadonlyHandlers, 7 | } from "./baseHandlers" 8 | 9 | export const enum ReactiveFlags { 10 | IS_REACTIVE = "__v_isReactive", 11 | IS_READONLY = "__v_isReadonly", 12 | } 13 | 14 | export function reactive(raw: object) { 15 | return createActiveObject(raw, mutableHandlers) 16 | } 17 | 18 | export function readonly(raw: object) { 19 | return createActiveObject(raw, readonlyHandlers) 20 | } 21 | 22 | export function shallowReactive(raw) { 23 | return createActiveObject(raw, shallowMutableHandlers) 24 | } 25 | export function shallowReadonly(raw) { 26 | return createActiveObject(raw, shallowReadonlyHandlers) 27 | } 28 | 29 | function createActiveObject(raw: object, baseHandlers: object) { 30 | if (!isObject(raw)) { 31 | console.log(`target ${raw} 必须是一个对象`) 32 | return raw 33 | } 34 | return new Proxy(raw, baseHandlers) 35 | } 36 | 37 | export function isReadonly(value: any): boolean { 38 | return !!value[ReactiveFlags.IS_READONLY] 39 | } 40 | 41 | export function isReactive(value: any): boolean { 42 | return !!value[ReactiveFlags.IS_REACTIVE] 43 | } 44 | 45 | export function isProxy(value: any): boolean { 46 | return isReactive(value) || isReadonly(value) 47 | } 48 | -------------------------------------------------------------------------------- /packages/reactivity/src/ref.ts: -------------------------------------------------------------------------------- 1 | import { trackEffects, triggerEffects, activeEffect } from "./effect" 2 | import { hasChanged, isObject } from "../../shared" 3 | import { reactive } from "./reactive" 4 | 5 | class refImpl { 6 | private _value: any 7 | public _rawValue: any 8 | public __v_ref: boolean = true 9 | public dep 10 | constructor(value) { 11 | this._rawValue = value 12 | this._value = convert(value) 13 | this.dep = new Set() 14 | } 15 | 16 | get value() { 17 | if (activeEffect) { 18 | trackEffects(this.dep) 19 | } 20 | return this._value 21 | } 22 | 23 | set value(value) { 24 | if (hasChanged(value, this._rawValue)) { 25 | this._rawValue = value 26 | this._value = convert(value) 27 | triggerEffects(this.dep) 28 | } 29 | } 30 | } 31 | 32 | function convert(value) { 33 | return isObject(value) ? reactive(value) : value 34 | } 35 | 36 | export function ref(value) { 37 | return new refImpl(value) 38 | } 39 | 40 | export function isRef(ref) { 41 | return !!ref.__v_ref 42 | } 43 | 44 | export function unRef(ref) { 45 | return isRef(ref) ? ref.value : ref 46 | } 47 | 48 | export function proxyRefs(objectwithRefs = {}) { 49 | return new Proxy(objectwithRefs, { 50 | get(target, key) { 51 | return unRef(Reflect.get(target, key)) 52 | }, 53 | set(target, key, value) { 54 | if (isRef(target[key]) && !isRef(value)) { 55 | return (target[key].value = value) 56 | } else { 57 | return Reflect.set(target, key, value) 58 | } 59 | }, 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /packages/runtime-core/src/apiInject.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance } from "./component" 2 | 3 | export function provide(key, value) { 4 | const instance = getCurrentInstance() 5 | 6 | if (instance) { 7 | const { provides } = instance 8 | provides[key] = value 9 | } 10 | } 11 | 12 | export function inject(key) { 13 | const instance = getCurrentInstance() 14 | 15 | if (instance) { 16 | let value = null 17 | while (value === null) { 18 | const { parent } = instance 19 | if (parent) { 20 | if (parent.provides[key]) { 21 | return parent.provides[key] 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/runtime-core/src/component.ts: -------------------------------------------------------------------------------- 1 | import { proxyRefs } from "../../reactivity/src" 2 | import { shallowReadonly } from "../../reactivity/src/reactive" 3 | import { emit } from "./componentEmit" 4 | import { initProps } from "./componentProps" 5 | import { publicInstanceProxyHandlers } from "./componentPublicInstance" 6 | import { initSlots } from "./componentSlots" 7 | 8 | let currentInstance: any = null 9 | 10 | export function createComponentInstance(vnode, parentComponent) { 11 | const component = { 12 | vnode, 13 | type: vnode.type, 14 | setupState: {}, 15 | props: {}, 16 | slots: {}, 17 | provides: {}, 18 | isMounted: false, 19 | subTree: {}, 20 | parent: parentComponent, 21 | emit: (instance, event) => {}, 22 | } 23 | 24 | component.emit = emit.bind(null, component) 25 | 26 | return component 27 | } 28 | 29 | export function setupComponent(instance) { 30 | initProps(instance, instance.vnode.props) 31 | initSlots(instance, instance.vnode.children) 32 | setupStatefulComponent(instance) 33 | } 34 | 35 | function setupStatefulComponent(instance) { 36 | const Component = instance.type 37 | 38 | instance.proxy = new Proxy({ _: instance }, publicInstanceProxyHandlers) 39 | 40 | const { setup } = Component 41 | if (setup) { 42 | setCurrentInstance(instance) 43 | const setupResult = setup(shallowReadonly(instance.props), { 44 | emit: instance.emit, 45 | }) 46 | setCurrentInstance(null) 47 | handleSetupResult(instance, proxyRefs(setupResult)) 48 | } 49 | } 50 | 51 | function handleSetupResult(instance, setupResult) { 52 | if (typeof setupResult === "object") { 53 | // 如果是object那么返回的就是context 54 | instance.setupState = setupResult 55 | } else if (typeof setupResult === "function") { 56 | // 如果是function那么返回的是render函数 57 | } 58 | finishCompoentSetup(instance) 59 | } 60 | 61 | function finishCompoentSetup(instance) { 62 | const Component = instance.type 63 | instance.render = Component.render 64 | } 65 | 66 | export function getCurrentInstance() { 67 | return currentInstance 68 | } 69 | 70 | function setCurrentInstance(instance) { 71 | currentInstance = instance 72 | } 73 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentEmit.ts: -------------------------------------------------------------------------------- 1 | import { camelize, toHandlerKey } from "../../shared" 2 | 3 | export function emit(instance, event) { 4 | const { props } = instance 5 | 6 | const handlerName = toHandlerKey(camelize(event)) 7 | const handler = props[handlerName] 8 | handler && handler() 9 | } 10 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentProps.ts: -------------------------------------------------------------------------------- 1 | export function initProps(instance, rawProps) { 2 | instance.props = rawProps || {} 3 | } 4 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentPublicInstance.ts: -------------------------------------------------------------------------------- 1 | import { hasOwn } from "../../shared" 2 | 3 | const publicPropertiesMap = { 4 | $el: (i) => i.vnode.el, 5 | $slots: (i) => i.slots, 6 | } 7 | 8 | export const publicInstanceProxyHandlers = { 9 | get({ _: instance }, key) { 10 | const { setupState, props } = instance 11 | if (hasOwn(setupState, key)) { 12 | return setupState[key] 13 | } 14 | 15 | if (hasOwn(props, key)) { 16 | return props[key] 17 | } 18 | 19 | const publicGetter = publicPropertiesMap[key] 20 | if (publicGetter) { 21 | return publicGetter(instance) 22 | } 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /packages/runtime-core/src/componentSlots.ts: -------------------------------------------------------------------------------- 1 | export function initSlots(instance, children) { 2 | instance.slots = children 3 | } 4 | -------------------------------------------------------------------------------- /packages/runtime-core/src/createApp.ts: -------------------------------------------------------------------------------- 1 | import { createVNode } from "./vnode" 2 | 3 | export function createAppApi(render) { 4 | return function createApp(rootComponent: any) { 5 | return { 6 | mount(rootContainer) { 7 | const vnode = createVNode(rootComponent) 8 | 9 | render(vnode, rootContainer) 10 | }, 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/runtime-core/src/h.ts: -------------------------------------------------------------------------------- 1 | import { createVNode } from "./vnode" 2 | 3 | export function h(type, props?, children?) { 4 | return createVNode(type, props, children) 5 | } 6 | -------------------------------------------------------------------------------- /packages/runtime-core/src/helpers/renderSlots.ts: -------------------------------------------------------------------------------- 1 | import { createVNode, Fragment } from "../vnode" 2 | 3 | export function renderSlots(slots, name, props) { 4 | const slot = slots[name] 5 | if (slot) { 6 | if (typeof slot === "function") { 7 | return createVNode(Fragment, {}, handleSlotChilren(slot(props))) 8 | } 9 | } 10 | } 11 | 12 | function handleSlotChilren(slot) { 13 | return Array.isArray(slot) ? slot : [slot] 14 | } 15 | -------------------------------------------------------------------------------- /packages/runtime-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { h } from "./h" 2 | export { createTextVnode } from "./vnode" 3 | export { getCurrentInstance } from "./component" 4 | export { renderSlots } from "./helpers/renderSlots" 5 | export { provide, inject } from "./apiInject" 6 | -------------------------------------------------------------------------------- /packages/runtime-core/src/renderer.ts: -------------------------------------------------------------------------------- 1 | import { effect } from "../../reactivity/src" 2 | import { EMPTY_OBJ } from "../../shared" 3 | import { ShapeFlags } from "../../shared/shapeFlags" 4 | import { createComponentInstance, setupComponent } from "./component" 5 | import { createAppApi } from "./createApp" 6 | import { Fragment, Text } from "./vnode" 7 | 8 | export function createRender(options) { 9 | const { 10 | createTextNode, 11 | insert, 12 | createElement, 13 | hostPatchProps, 14 | setElementInnerContext, 15 | removeAllChildren, 16 | } = options 17 | function render(vnode, rootContainer) { 18 | patch(null, vnode, rootContainer, null) 19 | } 20 | 21 | function patch(n1, n2, container, parentComponent) { 22 | const { type, shapeFlag } = n2 23 | 24 | switch (type) { 25 | case Fragment: 26 | processFragment(n1, n2, container) 27 | break 28 | case Text: 29 | processText(n1, n2, container) 30 | break 31 | default: 32 | if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) { 33 | // vnode 是一个component 34 | // h(App,{},[]) 35 | processComponent(n1, n2, container, parentComponent) 36 | } else if (shapeFlag & ShapeFlags.ELEMENT) { 37 | // vnode 是一个element 38 | // h('div',{class:'red'},null) 39 | processElement(n1, n2, container, parentComponent) 40 | } else { 41 | // vnode 不是component 和 element 那就可能是一个文本节点 42 | // h('div',{},[h('p',{},null),'222',this.msg]) 43 | insert(createTextNode(n2), container) 44 | } 45 | } 46 | } 47 | 48 | function processText(n1, n2, container) { 49 | const { children } = n2 50 | const textNode = (n2.el = createTextNode(children)) 51 | insert(textNode, container) 52 | } 53 | 54 | function processFragment(n1, n2, container) { 55 | mountChildren(n2.children, container, null) 56 | } 57 | 58 | function processElement(n1, n2, container, parentComponent) { 59 | if (!n1) { 60 | mountElement(n2, container, parentComponent) 61 | } else { 62 | updateElement(n1, n2, container) 63 | } 64 | } 65 | 66 | function updateElement(n1, n2, container) { 67 | const oldProps = n1.props || EMPTY_OBJ 68 | const newProps = n2.props || EMPTY_OBJ 69 | 70 | const el = (n2.el = n1.el) 71 | 72 | // 对比props 73 | patchProps(el, oldProps, newProps) 74 | 75 | // 对比children 76 | patchChildren(n1, n2, el) 77 | } 78 | 79 | function patchChildren(n1, n2, container) { 80 | // n1 children 是文本节点 81 | if (!!(n1.shapeFlag & ShapeFlags.TEXT_CHILDREN)) { 82 | if (!!(n2.shapeFlag & ShapeFlags.ARRAY_CHILDREN)) { 83 | // text children => array children 84 | removeAllChildren(container) 85 | mountChildren(n2.children, container, null) 86 | } else { 87 | // text children => text children 88 | if (n1.children !== n2.children) { 89 | setElementInnerContext(container, n2.children) 90 | } 91 | } 92 | } else { 93 | // n1 children 是一个数组 94 | if (!!(n2.shapeFlag & ShapeFlags.ARRAY_CHILDREN)) { 95 | // array children => array children 96 | } else { 97 | // array children => text children 98 | removeAllChildren(container) 99 | setElementInnerContext(container, n2.children) 100 | } 101 | } 102 | } 103 | 104 | function patchProps(el, oldProps, newProps) { 105 | if (oldProps !== newProps) { 106 | for (const key in newProps) { 107 | const prevProp = oldProps[key] 108 | const nextProp = newProps[key] 109 | 110 | if (prevProp !== nextProp) { 111 | hostPatchProps(el, key, prevProp, nextProp) 112 | } 113 | } 114 | 115 | if (oldProps !== EMPTY_OBJ) { 116 | for (const key in oldProps) { 117 | if (!(key in newProps)) { 118 | hostPatchProps(el, key, oldProps[key], null) 119 | } 120 | } 121 | } 122 | } 123 | } 124 | 125 | function mountElement(vnode, container, parentComponent) { 126 | const { type, children, props, shapeFlag } = vnode 127 | const el = (vnode.el = createElement(type)) 128 | 129 | //children 130 | if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { 131 | mountChildren(children, el, parentComponent) 132 | } else if (!!(children.shapeFlag & ShapeFlags.TEXT_CHILDREN)) { 133 | const vlaue = children.children 134 | insert(createTextNode(vlaue), el) 135 | } else { 136 | setElementInnerContext(el, children) 137 | } 138 | 139 | // props 140 | for (const key in props) { 141 | const val = props[key] 142 | hostPatchProps(el, key, null, val) 143 | } 144 | 145 | insert(el, container) 146 | } 147 | 148 | function mountChildren(children, container, parentComponent) { 149 | children.forEach((child) => { 150 | patch(null, child, container, parentComponent) 151 | }) 152 | } 153 | 154 | function processComponent(n1, n2, container, parentComponent) { 155 | mountComponent(n2, container, parentComponent) 156 | } 157 | 158 | function mountComponent(initinalVNode, container, parentComponent) { 159 | const instance = createComponentInstance(initinalVNode, parentComponent) 160 | setupComponent(instance) 161 | setupRenderEffect(instance, container) 162 | } 163 | 164 | function setupRenderEffect(instance, container) { 165 | effect(() => { 166 | const { proxy } = instance 167 | if (!instance.isMounted) { 168 | console.log("init") 169 | const subTree = (instance.subTree = instance.render.call(proxy)) 170 | patch(null, subTree, container, instance) 171 | instance.vnode.el = subTree.el 172 | instance.isMounted = true 173 | } else { 174 | console.log("update") 175 | const subTree = instance.render.call(proxy) 176 | const preSubTree = instance.subTree 177 | instance.vnode.el = subTree.el 178 | patch(preSubTree, subTree, container, instance) 179 | } 180 | }) 181 | } 182 | 183 | return { 184 | createApp: createAppApi(render), 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /packages/runtime-core/src/vnode.ts: -------------------------------------------------------------------------------- 1 | import { ShapeFlags } from "../../shared/shapeFlags" 2 | 3 | export const Fragment = Symbol("Fragment") 4 | export const Text = Symbol("Text") 5 | 6 | export function createVNode(type, props?, children?) { 7 | const vnode = { 8 | type, 9 | props, 10 | children, 11 | shapeFlag: getShapeFlag(type, children), 12 | el: null, 13 | } 14 | return vnode 15 | } 16 | 17 | export function createTextVnode(text) { 18 | return createVNode(Text, {}, String(text)) 19 | } 20 | 21 | function getShapeFlag(type, children) { 22 | let flag = 0 23 | // 处理 element 和 component 的flag 24 | if (typeof type === "string") { 25 | flag |= ShapeFlags.ELEMENT 26 | } else if (type === Text) { 27 | flag |= ShapeFlags.TEXT_CHILDREN 28 | } else { 29 | flag |= ShapeFlags.STATEFUL_COMPONENT 30 | } 31 | 32 | // 处理 children 的flag 33 | if (Array.isArray(children)) { 34 | flag |= ShapeFlags.ARRAY_CHILDREN 35 | } else if (typeof children === "string") { 36 | flag |= ShapeFlags.TEXT_CHILDREN 37 | } 38 | 39 | return flag 40 | } 41 | -------------------------------------------------------------------------------- /packages/runtime-dom/index.ts: -------------------------------------------------------------------------------- 1 | import { createAppApi } from "../runtime-core/src/createApp" 2 | import { createRender } from "../runtime-core/src/renderer" 3 | 4 | function createTextNode(text) { 5 | return document.createTextNode(text) 6 | } 7 | function insert(child, container) { 8 | container.appendChild(child) 9 | } 10 | function createElement(type) { 11 | return document.createElement(type) 12 | } 13 | function patchProps(el, key, prevVal, nextVal) { 14 | const isOn = (key: string) => /^on[A-Z]*/.test(key) 15 | // event 16 | if (isOn(key)) { 17 | const event = key.slice(2).toLocaleLowerCase() 18 | el.addEventListener(event, nextVal) 19 | } else { 20 | // attribute 21 | if (nextVal === undefined || nextVal === null) { 22 | el.removeAttribute(key) 23 | } else { 24 | el.setAttribute(key, nextVal) 25 | } 26 | } 27 | } 28 | 29 | function setElementInnerContext(el, context) { 30 | el.textContent = context 31 | } 32 | 33 | function removeAllChildren(el) { 34 | el.innerHTML = "" 35 | } 36 | 37 | const renderer = createRender({ 38 | createTextNode, 39 | insert, 40 | createElement, 41 | hostPatchProps: patchProps, 42 | setElementInnerContext, 43 | removeAllChildren, 44 | }) 45 | 46 | export function createApp(rootComponent) { 47 | return renderer.createApp(rootComponent) 48 | } 49 | 50 | export * from "../runtime-core/src" 51 | -------------------------------------------------------------------------------- /packages/shared/index.ts: -------------------------------------------------------------------------------- 1 | export const extend = Object.assign 2 | 3 | export const EMPTY_OBJ = {} 4 | 5 | export const isObject = (target) => typeof target === "object" 6 | 7 | export const hasChanged = (value, newValue) => !Object.is(value, newValue) 8 | 9 | export const hasOwn = (value, key) => 10 | Object.prototype.hasOwnProperty.call(value, key) 11 | 12 | export const camelize = (str) => 13 | str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : "")) 14 | 15 | const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1) 16 | 17 | export const toHandlerKey = (str) => (str ? "on" + capitalize(str) : "") 18 | -------------------------------------------------------------------------------- /packages/shared/shapeFlags.ts: -------------------------------------------------------------------------------- 1 | export const enum ShapeFlags { 2 | ELEMENT = 1, 3 | STATEFUL_COMPONENT = 1 << 1, 4 | TEXT_CHILDREN = 1 << 2, 5 | ARRAY_CHILDREN = 1 << 3, 6 | } 7 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import pkg from "./package.json" 2 | import typescript from "@rollup/plugin-typescript" 3 | import resolve from "@rollup/plugin-node-resolve" 4 | 5 | export default { 6 | input: "./packages/index.ts", 7 | output: [ 8 | { 9 | format: "cjs", 10 | file: pkg.main, 11 | }, 12 | , 13 | { 14 | format: "es", 15 | file: pkg.module, 16 | }, 17 | ], 18 | plugins: [typescript(), resolve], 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | /* Projects */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 7 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 11 | /* Language and Environment */ 12 | "target": "ES5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 13 | "lib": [ 14 | "DOM", 15 | "ES6" 16 | ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 17 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 18 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 19 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 20 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 21 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 22 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 23 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 24 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 25 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 26 | /* Modules */ 27 | "module": "esnext", /* Specify what module code is generated. */ 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | "types": [ 35 | "jest" 36 | ], /* Specify type package names to be included without being referenced in a source file. */ 37 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 38 | // "resolveJsonModule": true, /* Enable importing .json files */ 39 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 40 | /* JavaScript Support */ 41 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 42 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 43 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 44 | /* Emit */ 45 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | /* Interop Constraints */ 69 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 70 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 71 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 72 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 73 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 74 | /* Type Checking */ 75 | "strict": true, /* Enable all strict type-checking options. */ 76 | "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 77 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 78 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 79 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 80 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 81 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 82 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 83 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 84 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 85 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 86 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 87 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 88 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 89 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 90 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 91 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 92 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 93 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 94 | /* Completeness */ 95 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 96 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 97 | } 98 | } --------------------------------------------------------------------------------