Skip to content
This repository has been archived by the owner on Aug 16, 2023. It is now read-only.

Commit

Permalink
fix: add Annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
zeromake committed Jul 25, 2017
1 parent ae206d3 commit 1ca7126
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 95 deletions.
8 changes: 7 additions & 1 deletion src/clone-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ import { h } from "./h";
import { VNode } from "./vnode";
import { extend } from "./util";

/**
* 通过VNode对象新建一个自定义的props,children的VNode对象
* @param vnode 旧vnode
* @param props 新的props
* @param children 新的子组件
*/
export function cloneElement(vnode: VNode, props: any, ...children: any[]) {
const child: any = children.length > 2 ? children : vnode.children;
const child: any = children.length > 0 ? children : vnode.children;
return h(
vnode.nodeName,
extend({}, vnode.attributes, props),
Expand Down
71 changes: 59 additions & 12 deletions src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,45 @@ import { extend } from "./util";
import { IKeyValue } from "./types";

export class Component {
// 当前组件的状态,可以修改
/**
* 默认props
*/
public static defaultProps?: IKeyValue;
/**
* 当前组件的状态,可以修改
*/
public state: IKeyValue;
// 由父级组件传递的状态,不可修改
/**
* 由父级组件传递的状态,不可修改
*/
public props: IKeyValue;
/**
* 组件上下文,由父组件传递
*/
public context: IKeyValue;
// 默认props
public defaultProps?: IKeyValue;
// 组件挂载后的dom
/**
* 组件挂载后的dom
*/
public base?: Element;
// 自定义组件名
/**
* 自定义组件名
*/
public name?: string;
// 修改状态时的原状态
/**
* 上一次的属性
*/
public prevProps?: IKeyValue;
/**
* 上一次的状态
*/
public prevState?: IKeyValue;
/**
* 上一次的上下文
*/
public prevContext?: IKeyValue;
// 被移除时的dom缓存
/**
* 被移除时的dom缓存
*/
public nextBase?: Element;
/**
* 在一个组件被渲染到 DOM 之前
Expand Down Expand Up @@ -63,18 +86,41 @@ export class Component {
* @param { IKeyValue } previousContext
*/
public componentDidUpdate?: (previousProps: IKeyValue, previousState: IKeyValue, previousContext: IKeyValue) => void;
/**
* 获取上下文,会被传递到所有的子组件
*/
public getChildContext?: () => IKeyValue;
/**
* 子组件
*/
public _component?: Component;
/**
* 父组件
*/
public _parentComponent?: Component;
// 能否添加入更新队列
/**
* 是否加入更新队列
*/
public _dirty: boolean;
// render 执行完后的回调队列
/**
* render 执行完后的回调队列
*/
public _renderCallbacks?: any[];
/**
* 当前组件的key用于复用
*/
public _key?: string;
// 是否停用
/**
* 是否停用
*/
public _disable?: boolean;
/**
* react标准用于设置component实例
*/
public _ref?: (component: Component | null) => void;
// 原生如果VNode,root为原生组件就创建并设置它的一些props,event
/**
* VDom暂定用于存放组件根dom的上下文
*/
public child?: any;
constructor(props: IKeyValue, context: IKeyValue) {
// 初始化为true
Expand Down Expand Up @@ -119,6 +165,7 @@ export class Component {
this._renderCallbacks = this._renderCallbacks || [];
this._renderCallbacks.push(callback);
}
// 重新执行render
renderComponent(this, FORCE_RENDER);
}
/**
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const FORCE_RENDER = 2;
// 异步render标记
export const ASYNC_RENDER = 3;

// dom的props属性key
export const ATTR_KEY = "__preactattr_";

// 使用number值的style属性
Expand Down
11 changes: 9 additions & 2 deletions src/create-class.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { Component } from "./component";
import { extend } from "./util";

/**
* 类似React.createClass, 但未bind(this)
* @param obj
*/
export function createClass(obj: any) {
const cl: any = function(props: any, context: any) {
Component.call(this, props, context, {});
Component.call(this, props, context);
};
// 保证后面的实例的constructor指向cl
obj = extend({ constructor: cl }, obj);
if (obj.defaultProps) {
// 获取defaultProps
cl.defaultProps = obj.defaultProps;
}
// prototype链
F.prototype = Component.prototype;
cl.prototype = extend(new F(), obj);

// 组件名
cl.displayName = obj.displayName || "Component";
return cl;
}
Expand Down
87 changes: 81 additions & 6 deletions src/dom/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,40 @@
import { IS_NON_DIMENSIONAL } from "../constants";
import options from "../options";

/**
* 创建一个原生html组件
* @param nodeName 标签名
* @param isSvg 是否为svg
*/
export function createNode(nodeName: string, isSvg: boolean): HTMLElement {
const node: any = isSvg
? document.createElementNS("http://www.w3.org/2000/svg", nodeName)
: document.createElement(nodeName);
// 设置原始的nodeName到dom上normalizedNodeName
node.normalizedNodeName = nodeName;
return node;
}

/**
* 移除dom
* @param node 需要移除的node
*/
export function removeNode(node: HTMLElement) {
const parentNode = node.parentNode;
if (parentNode) {
parentNode.removeChild(node);
}
}

/**
* 通过VNode的props设置真实的dom
* @param node dom节点
* @param name 属性名
* @param old 旧属性值
* @param value 新属性值
* @param isSvg 是否为svg
* @param child VDom原dom上的props,和上下文环境,事件就在其中
*/
export function setAccessor(
node: any,
name: string,
Expand All @@ -25,38 +44,47 @@ export function setAccessor(
child: any,
) {
if (name === "className") {
// 把className重名为class
name = "class";
}
if (name === "key") {
// no set
// 不对key属性做dom设置
} else if ("ref" === name) {
if (old) {
// 对旧的ref设置null保证原方法里的引用移除
old(null);
}
if (value) {
// 给新方法设置dom
value(node);
}
} else if ("class" === name && !isSvg) {
// 直接通过className设置class
node.className = value || "";
} else if ("style" === name) {
if (!value || typeof value === "string" || typeof old === "string") {
// 对于字符串型的直接设置到style.cssText
node.style.cssText = value || "";
}
if (value && typeof value === "object") {
// 如果是一个对象遍历设置
if (typeof old !== "string") {
for (const i in old) {
if (!(i in value)) {
// 清理旧属性且不在新的里
node.style[i] = "";
}
}
}
for (const i in value) {
// 设置新属性
node.style[i] = typeof value[i] === "number"
&& IS_NON_DIMENSIONAL.test(i) === false ? (value[i] + "px") : value[i];
}
}
} else if ("dangerouslySetInnerHTML" === name) {
if (value) {
// innerHTML
node.innerHTML = value.__html || "";
// child.children = [];
// const childNodes = node.childNodes;
Expand All @@ -68,27 +96,35 @@ export function setAccessor(

}
} else if (name[0] === "o" && name[1] === "n") {
// 事件绑定
const oldName = name;
name = name.replace(/Capture$/, "");
// 判断是否 事件代理(事件委托)
const useCapture = oldName !== name;
// 去除前面的on并转换为小写
name = name.toLowerCase().substring(2);
if (value) {
if (!old) {
// 保证只有一次绑定事件
addEventListener(node, name, useCapture, child);
}
} else {
// 移除事件
removeEventListener(node, name, useCapture, child);
}
if (!child._listeners) {
// 在上下文中创建存放绑定的方法的对象
child._listeners = {};
}
child._listeners[name] = value;
} else if (name !== "list" && name !== "type" && !isSvg && name in node) {
// 安全设置属性
setProperty(node, name, value == null ? "" : value);
if (value == null || value === false) {
node.removeAttribute(name);
}
} else {
// 设置Attribute
const ns = isSvg && (name !== (name = name.replace(/^xlink\:?/, "")));
// null || undefined || void 0 || false
if (value == null || value === false) {
Expand Down Expand Up @@ -120,13 +156,26 @@ function setProperty(node: any, name: string, value: string) {
} catch (e) { }
}

function eventProxy(child: any): (e: Event) => void {
/**
* 生成用于绑定事件的方法,保证每次更新props上的事件方法不会重新绑定事件
* @param child 上下文
* @param useCapture 是否冒泡(兼容ie8)
*/
function eventProxy(child: any, useCapture: boolean): (e: Event) => void {
return (e: Event) => {
if (child.isIe8 && !useCapture) {
// ie8事件默认冒泡所以需要阻止
e.cancelBubble = !useCapture;
}
// 取出对于的props事件
const listener = child._listeners[e.type];
// 事件钩子
const event = options.event && options.event(e) || e;
if (options.eventBind) {
if (options.eventBind && child._component) {
// 自动使用所属自定义组件来做this
return listener.call(child._component, event);
}
// 直接调用事件
return listener(event);
};
}
Expand All @@ -139,26 +188,52 @@ export function getLastChild(node: Node): Node| null {
return node.lastChild;
}

/**
* 判断是否为Text节点
* @param node
*/
export function isTextNode(node: Text | any): boolean {
return node.splitText !== undefined;
}

/**
* 绑定代理事件
* @param node dom节点
* @param name 事件名
* @param useCapture 是否冒泡
* @param child 上下文
*/
function addEventListener(node: any, name: string, useCapture: boolean, child: any) {
const eventProxyFun = eventProxy(child);
if (typeof child.isIe8 !== "number") {
// 判断是否为ie9以下的浏览器
child.isIe8 = node.addEventListener ? 0 : 1;
}
// 生成当前事件的代理方法
const eventProxyFun = eventProxy(child, useCapture);
if (!child.event) {
child.event = {};
}
// 把事件代理方法挂载到child.event上等待卸载时使用
child.event[name] = eventProxyFun;
if (node.addEventListener) {
if (!child.isIe8) {
node.addEventListener(name, eventProxyFun, useCapture);
} else {
node.attachEvent("on" + name, eventProxyFun);
}
}

/**
* 移除事件
* @param node dom节点
* @param name 事件名
* @param useCapture 是否冒泡
* @param child 上下文
*/
function removeEventListener(node: any, name: string, useCapture: boolean, child: any) {
// 把上下文中的存储的代理事件解绑
const eventProxyFun = child.event[name];
if (node.removeEventListener) {
child.event[name] = undefined;
if (!child.isIe8) {
node.removeEventListener(name, eventProxyFun, useCapture);
} else {
node.detachEvent("on" + name, eventProxyFun);
Expand Down
7 changes: 7 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ import { VNode } from "./vnode";
import { Component } from "component";

const options: {
// render更新后钩子比componentDidUpdate更后面执行
afterUpdate?: (component: Component) => void;
// dom卸载载前钩子比componentWillUnmount更先执行
beforeUnmount?: (component: Component) => void;
// dom挂载后钩子比componentDidMount更先执行
afterMount?: (component: Component) => void;
// setComponentProps时强制为同步render
syncComponentUpdates?: boolean;
// 自定义异步调度方法,会异步执行传入的方法
debounceRendering?: (render: () => void) => void;
// vnode实例创建时的钩子
vnode?: (vnode: VNode) => void;
// 事件钩子,可以对event过滤返回的会代替event参数
event?: (event: Event) => any;
// 是否自动对事件方法绑定this为组件,默认为true(preact没有)
eventBind?: boolean;
Expand Down

0 comments on commit 1ca7126

Please sign in to comment.