Skip to content

Commit 3fc789c

Browse files
author
lishiwen
committed
added emit/props
1 parent cf408da commit 3fc789c

File tree

18 files changed

+507
-30
lines changed

18 files changed

+507
-30
lines changed

.idea/.gitignore

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/mini-vue.iml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/props-simple/App.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { h } from "../../lib/m-vue.esm.js";
2+
import { Foo } from "./Foo.js";
3+
4+
export const App = {
5+
render() {
6+
return h("div", {}, [
7+
h("h1", {}, "Hello World " + this.msg),
8+
h("button", {}, "Click me"),
9+
h(Foo, {
10+
count: this.count,
11+
onAdd(event) {
12+
console.log("emit add ...", event);
13+
},
14+
onUnderlineIn(event) {
15+
console.log("emit underlineIn2hump ...", event);
16+
},
17+
}),
18+
]);
19+
},
20+
21+
setup() {
22+
return {
23+
msg: "props example",
24+
count: 0,
25+
};
26+
},
27+
};

example/props-simple/Foo.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { h } from "../../lib/m-vue.esm.js";
2+
export const Foo = {
3+
render() {
4+
// the value of the props can be obtained through this
5+
return h("div", {}, [
6+
h("div", {}, "props count: " + this.count),
7+
h("button", { onClick: this.emitClickAdd }, "AddBtn"),
8+
h("button", { onClick: this.emitClickRemove }, "RemoveBtn"),
9+
]);
10+
},
11+
setup(props, { emit, ...exn }) {
12+
const emitClickAdd = (event) => {
13+
emit("add", event);
14+
};
15+
const emitClickRemove = (event) => {
16+
emit("underline-in", event);
17+
};
18+
return { emitClickAdd, emitClickRemove };
19+
},
20+
};

example/props-simple/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>props-html</title>
8+
</head>
9+
<body>
10+
<div id="app"></div>
11+
</body>
12+
<script src="./main.js" type="module"></script>
13+
</html>

example/props-simple/main.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createApp } from "../../lib/m-vue.esm.js";
2+
import { App } from "./App.js";
3+
4+
createApp(App).mount(document.getElementById("app"));

lib/m-vue.cjs.js

Lines changed: 162 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
Object.defineProperty(exports, '__esModule', { value: true });
44

5+
function isObject(value) {
6+
return value !== null && typeof value === "object";
7+
}
58
function isStructObject(value) {
69
return Object.prototype.toString.call(value) === "[object Object]";
710
}
@@ -37,43 +40,194 @@ function childrenShapeFlag(vNode) {
3740
}
3841
}
3942

43+
function hasOwnProperty(originObject, property) {
44+
return Reflect.has(originObject, property);
45+
}
46+
function capitalize(s) {
47+
return s.charAt(0).toUpperCase() + s.slice(1);
48+
}
49+
function underlineIn2hump(string) {
50+
return string.replace(/-(\w)/g, (all, letter) => letter.toUpperCase());
51+
}
52+
53+
const targetMap = new Map;
54+
// 触发依赖
55+
function trigger(target, property) {
56+
const deps = targetMap.get(target).get(property);
57+
if (deps) {
58+
triggerEffects(deps);
59+
}
60+
}
61+
function triggerEffects(deps) {
62+
for (const effect of deps) {
63+
if (effect.scheduler) {
64+
effect.scheduler();
65+
}
66+
else {
67+
effect.run();
68+
}
69+
}
70+
}
71+
72+
const mutableGet = createGetter();
73+
const readonlyGet = createGetter(true);
74+
const mutableSet = createSetter();
75+
const shallowReadonlyGet = createGetter(true, true);
76+
const mutableHandlers = {
77+
get: mutableGet,
78+
set: mutableSet
79+
};
80+
const readonlyHandlers = {
81+
get: readonlyGet,
82+
set(target, property, value) {
83+
console.warn(`Cannot set readonly property "${property}"!`);
84+
return true;
85+
}
86+
};
87+
const shallowReadonlyHandlers = {
88+
get: shallowReadonlyGet,
89+
set(target, property, value) {
90+
console.warn(`Cannot set readonly property "${property}"!`);
91+
return true;
92+
}
93+
};
94+
function createGetter(isReadonly = false, isShallow = false) {
95+
return function get(target, property) {
96+
// TODO: 优化这里的if逻辑
97+
// isReactive
98+
if (property === "_v_isReactive" /* ReactiveFlags.IS_REACTIVE */) {
99+
return !isReadonly;
100+
}
101+
// isReadonly
102+
if (property === "_v_isReadonly" /* ReactiveFlags.IS_READONLY */) {
103+
return isReadonly;
104+
}
105+
// isShallowReadonly
106+
if (property === "_v_isShallowReadonly" /* ReactiveFlags.IS_SHALLOW_READONLY */) {
107+
return isShallow && isReadonly;
108+
}
109+
// isShallowReactive
110+
if (property === "_v_isShallowReactive" /* ReactiveFlags.IS_SHALLOW_REACTIVE */) {
111+
return !isReadonly && isShallow;
112+
}
113+
// 只要出现shallow证明是浅层的处理,可以同时处理shallowReactive 和 shallowReadonly
114+
const value = Reflect.get(target, property);
115+
if (isShallow) {
116+
return value;
117+
}
118+
if (isObject(value)) {
119+
return isReadonly
120+
? readonly(value)
121+
: reactive(value);
122+
}
123+
return value;
124+
};
125+
}
126+
function createSetter() {
127+
return function set(target, property, value) {
128+
const nextValue = Reflect.set(target, property, value);
129+
trigger(target, property);
130+
return nextValue;
131+
};
132+
}
133+
134+
function createReactiveObject(raw, baseHandlers) {
135+
if (!isStructObject(raw)) {
136+
console.warn("the value to be proxies is not an object: " + raw);
137+
return;
138+
}
139+
return new Proxy(raw, baseHandlers);
140+
}
141+
function reactive(raw) {
142+
return createReactiveObject(raw, mutableHandlers);
143+
}
144+
function readonly(raw) {
145+
return createReactiveObject(raw, readonlyHandlers);
146+
}
147+
function shallowReadonly(value) {
148+
return createReactiveObject(value, shallowReadonlyHandlers);
149+
}
150+
151+
function toHandlerKeys(prefix = "on", eventProperty) {
152+
return prefix + eventProperty;
153+
}
154+
function emit(instance, eventProperty, ...args) {
155+
const handlerKey = toHandlerKeys("on", capitalize(underlineIn2hump(eventProperty)));
156+
if (hasOwnProperty(instance.props, handlerKey) && typeof instance.props[handlerKey] === "function") {
157+
// call the event handler
158+
instance.props[handlerKey](...args);
159+
}
160+
}
161+
// TODO: 运行时扩展实例
162+
function extendRuntimeInstance(instance, extendApis = {}) {
163+
console.log("extendApis", extendApis);
164+
// XXX: 临时解决方案
165+
// TODO: 待完善扩展实例
166+
Object.keys(extendApis).forEach(key => {
167+
instance[key] = extendApis[key];
168+
console.log(key, extendApis[key]);
169+
});
170+
}
171+
172+
function initProps(instance, rawProps = {}) {
173+
instance.props = rawProps;
174+
}
175+
40176
const PublicPropertiesMaps = {
41177
"$el": (instance) => instance.vnode.el,
42178
};
43179
const ComponentPublicInstanceHandlers = {
44180
get({ _instance }, key) {
45-
if (key in _instance.setupState) {
181+
// XXX: 是否会因为优先级导致props不可用
182+
if (hasOwnProperty(_instance.setupState, key)) {
46183
return _instance.setupState[key];
47184
}
185+
if (hasOwnProperty(_instance.props, key)) {
186+
return _instance.props[key];
187+
}
48188
if (key in PublicPropertiesMaps) {
49189
return PublicPropertiesMaps[key](_instance);
50190
}
51191
}
52192
};
53193

194+
function promiseEmit() { }
195+
196+
var instanceRuntimeExtendApis = /*#__PURE__*/Object.freeze({
197+
__proto__: null,
198+
promiseEmit: promiseEmit
199+
});
200+
54201
function createComponentInstance(vnode) {
202+
// instance
55203
const component = {
56204
vnode,
57205
type: vnode.type,
58-
setupState: {}
206+
setupState: {},
207+
props: {},
208+
emit: (instance, event) => { },
59209
};
210+
// 官方Emit
211+
component.emit = emit.bind(null, component);
212+
// TODO: runtime instance extend
213+
// NOTE: 这是自己扩展的
214+
extendRuntimeInstance(component, instanceRuntimeExtendApis);
60215
return component;
61216
}
62217
function setupComponent(instance) {
63-
initProps(instance);
218+
initProps(instance, instance.vnode.props);
64219
// initSlots();
65220
setupStatefulComponent(instance);
66221
}
67-
// props
68-
function initProps(instance) {
69-
console.log(instance);
70-
}
71222
function setupStatefulComponent(instance) {
72223
const Component = instance.type;
73224
const context = { _instance: instance, _component: Component };
74225
instance.proxy = new Proxy(context, ComponentPublicInstanceHandlers);
75226
if (Component.setup) {
76-
const setupResult = Component.setup();
227+
// props is readonly because it's a Reflect instance
228+
const readonlyProps = shallowReadonly(instance.props);
229+
// TODO: context
230+
const setupResult = Component.setup(readonlyProps, Object.assign({ emit: instance.emit }, instanceRuntimeExtendApis));
77231
handleSetupResult(instance, setupResult);
78232
}
79233
}

0 commit comments

Comments
 (0)