前言
咱们先不讲什么语法原理,先依据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的根本语法实现了.