1. 原生计数器
<button id="counter-app">0</button>
<script>
let counterApp = document.getElementById('counter-app');
let number = 0;
counterApp.addEventListener('click', () => {
counterApp.innerHTML = ++number;
})
</script>
2. 类实现计数器
- index.html
<div id="counter-app"></div>
<script src="index.js"></script>
<script>
let counterApp = document.getElementById('counter-app');
counterApp.appendChild( new Counter().render() );
</script>
- index.js
class Counter {
constructor() {
this.state = { number: 0 }
}
createDOMFromDOMString(domString) {
let div = document.createElement('div');
div.innerHTML = domString;
return div.children[0];
}
add() {
this.state = { number: this.state.number + 1 };
let oldElement = this.domElement;
let newElement = this.render();
oldElement.parentElement.replaceChild(newElement, oldElement);
}
render() {
// return `<button id="counter-app">${this.state.number}</button>`
this.domElement = this.createDOMFromDOMString(
`<button id="counter-app">${this.state.number}</button>`
);
this.domElement.addEventListener('click', this.add.bind(this));
return this.domElement;
}
}
3. setState同步
3.1. index.html
<div id="counter-app"></div>
<script src="index.js"></script>
<script>
let counterApp = document.getElementById('counter-app');
new Counter({name:'kft'}).mount(counterApp)
</script>
3.2. index.js
// 公共组件
class Component {
constructor(props) {
this.props = props;
}
createDOMFromDOMString(domString) {
let div = document.createElement('div');
div.innerHTML = domString;
return div.children[0];
}
setState(partcialState) {
this.state = Object.assign(this.state, partcialState);
let oldElement = this.domElement;
let newElement = this.renderElement();
oldElement.parentElement.replaceChild(newElement, oldElement);
}
// 把一个DOM模板字符串转成真实的DOM元素
renderElement() {
let htmlString = this.render();
this.domElement = this.createDOMFromDOMString(htmlString);
this.domElement.addEventListener('click', this.add.bind(this));
return this.domElement;
}
mount(container) {
container.appendChild(this.renderElement());
}
}
window.trigger = function (event, method) {
event.target.component[method].call(event.target.component);
}
class Counter extends Component {
constructor(props) {
super(props);
this.state = { number: 0 }
}
add() {
this.setState({ number: this.state.number + 1 });
}
render() {
return `<button id="counter-app">${this.state.number}${this.props.name}</button>`;
}
}
4. setState如果有异步
- 添加更新器 Updater, 每次更新存起来, 最后合并起来一起计算
- ? 默认非批量更新 : 批量更新方法
- 脏组件数组
- 批量更新脏组件
- 脏组件: 组件的状态和界面显示不一样了;
- 批量更新: true 更新器存起来 : false 直接更新
- 批量更新方法updateComponent
- 事件开启批量更新模式trigger
- 事件结束关闭更新模式 并 执行批量更新
- 0 0 2 3
- 事务 事件执行前开启 事件执行 事件完成后关闭
- 包装器有对象 对象中有初始化和结束两个方法 执行方法
4.1. index.js
// 事务
class Transaction {
constructor(wrappers) {
this.wrappers = wrappers; // {initialize,close}
}
perform(anyMethod) {
this.wrappers.forEach(wrapper => wrapper.initialize());
anyMethod.call();
this.wrappers.forEach(wrapper => wrapper.close());
}
}
// batchingStrategy.isBatchingUpdates batchedUpdates
let batchingStrategy = {
isBatchingUpdates: false, // 默认是非批量更新模式
dirtyComponents: [],
batchedUpdates() { //
this.dirtyComponents.forEach(component => component.updateComponent());
}
}
class Updater {
constructor(component) {
this.component = component;
this.pendingStates = []; // 等待状态更新数组
}
addState(partcialState) {
this.pendingStates.push(partcialState);
batchingStrategy.isBatchingUpdates ? batchingStrategy.dirtyComponents.push(this.component) : this.component.updateComponent();
}
}
// 公共组件
class Component {
constructor(props) {
this.props = props;
this.$updater = new Updater(this);
}
createDOMFromDOMString() {
//this;
let htmlString = this.render();
let div = document.createElement('div');
div.innerHTML = htmlString;
this.domElement = div.children[0];
//让这个BUTTONDOM节点的component属性等于当前Counter组建的实例
this.domElement.component = this;
//this.domElement.addEventListener('click',this.add.bind(this));
return this.domElement;
}
setState(partcialState) {
// 对更新状态进行管理
this.$updater.addState(partcialState);
}
updateComponent() {
this.$updater.pendingStates.forEach(partcialState => Object.assign(this.state, partcialState));
this.$updater.pendingStates.length = 0;
let oldElement = this.domElement;
let newElement = this.createDOMFromDOMString();
oldElement.parentElement.replaceChild(newElement, oldElement);
}
// 把一个DOM模板字符串转成真实的DOM元素
mount(container) {
container.appendChild(this.createDOMFromDOMString());
}
}
let transaction = new Transaction([
{
initialize() {
batchingStrategy.isBatchingUpdates = true;
},
close() {
batchingStrategy.isBatchingUpdates = false;
batchingStrategy.batchedUpdates(); // 进行批量更新
}
}
])
window.trigger = function (event, method) {
let component = event.target.component;//event.target=this.domElement
transaction.perform(component[method].bind(component));
}
class Counter extends Component {
constructor(props) {
super(props);
this.state = { number: 0 }
}
add() {
this.setState({ number: this.state.number + 1 });
console.log(this.state.number);
this.setState({ number: this.state.number + 1 });
console.log(this.state.number);
setTimeout(() => {
this.setState({ number: this.state.number + 1 });
console.log(this.state.number);
this.setState({ number: this.state.number + 1 });
console.log(this.state.number);
}, 1000)
}
render() {
return `<button onclick="trigger(event,'add')">${this.state.number}${this.props.name}</button>`;
}
}
4.2. index.html
<div id="counter-app"></div>
<script src="index.js"></script>
<script>
let counterApp = document.getElementById('counter-app');
new Counter({name:'kft'}).mount(counterApp)
</script>