共计 3533 个字符,预计需要花费 9 分钟才能阅读完成。
前言
我们先不讲什么语法原理, 先根据 API 效果强行模拟语法使用, 实现一个简易版的 React.
render
第一步我们先用类创建一个元素返回, 并且绑定点击事件, 代码如下, 可以正常看到一个按钮出现了.
class AddButton {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};
}
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);
console.log(this.dom);
return this.dom;
}
}
document.body.appendChild(new AddButton().render());
渲染之后看到 this dom
输出已经发现改变了, 但是视图并没有渲染, 那是因为这是结尾一次性插入, 下面就渲染视图这块往下走
重新渲染
我们现在把插入数据的操作内置到 class 里面, 新增一个方法插入新元素移除旧元素.
class AddButton {constructor() {this.state = { num: 0};
}
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());
现在效果虽然实现, 但是还是得开头手动把元素插入视图
抽取公用类
我们先将一些共有方法提取到一个单独类
class Component {constructor() {}
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", () => this.handleAdd(), false);
return this.dom;
}
}
然后组件只需要直接继承 Component
然后处理自己逻辑即可
class AddButton extends Component {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>`;
}
}
还有一个问题是点击事件暂时还是耦合进 Component
里面, 先略过不提.
ReactDom.render
大家都知道 React 会提供这么一个方法将组件插入一个指定元素, 我们直接模拟
const ReactDom = {render(component, wrapper) {wrapper.appendChild(component._render(wrapper));
}
};
ReactDom.render(new AddButton(), document.getElementById("root"));
Props
还有一个重要的传输数据实现如下
const ReactDom = {render(component, wrapper) {wrapper.appendChild(component._render(wrapper))
}
}
class Component {constructor(props = {}) {this.props = props}
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", () => this.handleAdd(), false)
return this.dom
}
}
class AddButton extends Component {constructor(props) {super(props)
this.state = {num: 0}
}
handleAdd() {
const num = this.state.num + 1
this.setState({num: num})
}
render() {console.log(this.props)
return `<button id="btn">${this.state.num}</button>`
}
}
ReactDom.render(new AddButton({a:1}), document.getElementById("root"))
至此, 抛开实际思路不说, 我们已经简单模拟出来 React 的一般语法实现了.
正文完
发表至: javascript
2019-08-07