React系列
React系列 — 简单模拟语法(一)
React系列 — Jsx, 合成事件与Refs(二)
React系列 — virtualdom diff算法实现分析(三)
React系列 — 从Mixin到HOC再到HOOKS(四)
React系列 — createElement, ReactElement与Component部分源码解析(五)
React系列 — 从使用React了解Css的各种使用方案(六)
前言
我们先不讲什么语法原理,先根据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的一般语法实现了.