前言
咱们先不讲什么语法原理, 先依据 API 成果强行模仿语法应用, 实现一个简易版的 React.
上面代码跟 React 源码没有关系,只是单纯模仿思路实现
render
第一步咱们先用类创立一个元素返回, 并且绑定点击事件, 代码如下, 能够失常看到一个按钮呈现了.
class AddButton {
// 创立 Dom
createDOM(domString) {const div = document.createElement("div");
div.innerHTML = domString;
return div;
}
// 输入实例
render() {this.dom = this.createDOM(`<button>0</button>`);
this.dom.addEventListener("click", () => console.log("click"), false);
return this.dom;
}
}
// 插入页面
document.body.appendChild(new AddButton().render());
state && setState
实现类状态和批改状态办法
class AddButton {
// 内置状态
constructor() {this.state = { num: 0};
}
// 创立 Dom
createDOM(domString) {const div = document.createElement("div");
div.innerHTML = domString;
return div;
}
// 惟一批改状态更新视图
setState(state) {
this.state = state;
this.dom = this.render();}
// 批改数据
handleAdd() {
const num = this.state.num + 1;
this.setState({num: num});
}
// 输入实例
render() {this.dom = this.createDOM(`<button id="btn">${this.state.num}</button>`);
this.dom.addEventListener("click", () => this.handleAdd(), false);
return this.dom;
}
}
// 插入页面
document.body.appendChild(new AddButton().render());
渲染之后看到 this.dom
输入曾经发现扭转了, 然而视图并没有渲染, 那是因为这是结尾一次性插入, 上面就渲染视图这块往下走
从新渲染
咱们当初把插入数据的操作内置到 class 外面, 新增一个办法插入新元素移除旧元素.
class AddButton {
// 内置状态
constructor() {this.state = { num: 0};
}
// 创立 Dom
createDOM(domString) {const div = document.createElement("div");
div.innerHTML = domString;
return div;
}
// 只是一个替换元素的办法
changeDom() {
const oDom = this.dom;
this.dom = this.render();
document.body.insertBefore(this.dom, oDom);
document.body.removeChild(oDom);
}
// 惟一批改状态更新视图
setState(state) {
this.state = state;
this.changeDom();}
// 批改数据
handleAdd() {
const num = this.state.num + 1;
this.setState({num: num});
}
// 输入实例
render() {this.dom = this.createDOM(`<button id="btn">${this.state.num}</button>`);
this.dom.addEventListener("click", () => this.handleAdd(), false);
return this.dom;
}
}
// 插入页面
document.body.appendChild(new AddButton().render());
当初成果尽管实现, 然而还是得结尾手动把元素插入视图
抽取专用类
咱们先将一些共有办法提取到一个独自类,另外补全一下 props 属性传参
class React {
// 内置状态
constructor(props = {}) {
// 实例
this.wrapper = null
// 状态
this.state = {}
// 属性
this.props = {}}
// 创立 Dom
createDOM(domString) {const div = document.createElement("div");
div.innerHTML = domString;
return div;
}
// 只是一个替换元素的办法
changeDom() {
const oDom = this.dom;
this.dom = this._render();
this.wrapper.insertBefore(this.dom, oDom);
this.wrapper.removeChild(oDom);
}
// 惟一批改状态更新视图
setState(state) {
this.state = state;
this.changeDom();}
// 输入实例
_render(wrapper) {if (wrapper) this.wrapper = wrapper;
this.dom = this.createDOM(this.render());
this.dom.addEventListener("click", () => console.log('增加自定义事件'), false);
return this.dom;
}
}
而后组件只须要间接继承 Component
而后解决本人逻辑即可
class AddButton extends React {constructor() {super();
this.state = {num: 0};
}
handleAdd() {
const num = this.state.num + 1;
this.setState({num: num});
}
render() {return `<button id="btn">${this.state.num}</button>`;
}
}
下面做了几件事:
- 抽取通用的逻辑到 React 类
- 自定义组件 AddButton
还有一个问题是点击事件临时还是耦合进 Component
外面, 下一章节实现
增加自定义事件
因为事件是由组件自定义的,所以咱们思路是在组件实例定义好之后,通用类绑定事件
class React {
// 内置状态
constructor(props = {}) {
// 实例
this.wrapper = null
// 状态
this.state = {}
// 属性
this.props = {}
// 事件
this.event = {}}
// 创立 Dom
createDOM(domString) {const div = document.createElement("div");
div.innerHTML = domString;
return div;
}
// 只是一个替换元素的办法
changeDom() {
const oDom = this.dom;
this.dom = this._render();
this.wrapper.insertBefore(this.dom, oDom);
this.wrapper.removeChild(oDom);
}
// 惟一批改状态更新视图
setState(state) {
this.state = state;
this.changeDom();}
// 初始化事件
initEvent() {const events = Object.keys(this.event)
events.forEach(key => {this.dom.addEventListener(key, this.event[key].bind(this), false);
})
}
// 输入实例
_render(wrapper) {if (wrapper) this.wrapper = wrapper;
this.dom = this.createDOM(this.render());
// 须要创立实例后能力初始化
this.dom && this.initEvent()
return this.dom;
}
}
同时组件代码也须要绝对应的批改
class AddButton extends React {constructor() {super();
this.state = {num: 0};
this.event = {click: this.handleAdd}
}
handleAdd() {
const num = this.state.num + 1;
this.setState({num: num});
}
render() {return `<button id="btn">${this.state.num}</button>`;
}
}
ReactDom.render
大家都晓得 React 会提供这么一个办法将组件插入一个指定元素, 咱们间接模仿
const ReactDom = {
// 指定元素内插入组件实例
render(component, wrapper) {wrapper.appendChild(component._render(wrapper));
}
};
最终运行代码
咱们下面曾经实现了几个性能:
- 负责创立元素的 React 类,包含单向数据流,自定义状态,事件,替换元素等
- 负责挂载的 ReactDom 类
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
</head>
<body>
<div id="root"></div>
<script>
class React {
// 内置状态
constructor(props = {}) {
// 实例
this.wrapper = null
// 状态
this.state = {}
// 属性
this.props = {}
// 事件
this.event = {}}
// 创立 Dom
createDOM(domString) {const div = document.createElement("div");
div.innerHTML = domString;
return div;
}
// 只是一个替换元素的办法
changeDom() {
const oDom = this.dom;
this.dom = this._render();
this.wrapper.insertBefore(this.dom, oDom);
this.wrapper.removeChild(oDom);
}
// 惟一批改状态更新视图
setState(state) {
this.state = state;
this.changeDom();}
// 初始化事件
initEvent() {const events = Object.keys(this.event)
events.forEach(key => {this.dom.addEventListener(key, this.event[key].bind(this), false);
})
}
// 输入实例
_render(wrapper) {if (wrapper) this.wrapper = wrapper;
this.dom = this.createDOM(this.render());
// 须要创立实例后能力初始化
this.dom && this.initEvent()
return this.dom;
}
}
const ReactDom = {
// 指定元素内插入组件实例
render(component, wrapper) {wrapper.appendChild(component._render(wrapper));
}
};
class AddButton extends React {constructor() {super();
this.state = {num: 0};
this.event = {click: this.handleAdd}
}
handleAdd() {
const num = this.state.num + 1;
this.setState({num: num});
}
render() {return `<button id="btn">${this.state.num}</button>`;
}
}
ReactDom.render(new AddButton(), document.getElementById("root"));
</script>
</body>
</html>
至此, 抛开理论思路不说, 咱们曾经简略模仿进去 React 的根本语法实现了.