<body>
<button id="counter-btn">
</button>
<script>
let counterBtn = document.getElementById('counter-btn');
let number = 0;
counterBtn.addEventListener('click',function(){
counterBtn.innerHTML = ++number;
});
</script>
</body>
<body>
<div id="counter-app"></div>
<script src="index.js"></script>
<script>
let counterApp = document.getElementById('counter-app');
counterApp.innerHTML = new Counter().render();
</script>
</body>
class Counter{
render(){
return (
`
<button id="counter-btn">
0
</button>
`
)
}
}
<body>
<div id="counter-app"></div>
<script src="index.js"></script>
<script>
let counterApp = document.getElementById('counter-app');
counterApp.appendChild(new Counter().render());
</script>
</body>
class Counter{
constructor(){
this.state = {number:0};
}
createDOMFromString(domString){
const div = document.createElement('div');
div.innerHTML = domString;
return div.children[0];
}
increment (){
this.state = {number:this.state.number+1};
let oldElement = this.domElement;
let newElement = this.render();
oldElement.parentElement.replaceChild(newElement,oldElement);
}
render(){
this.domElement = this.createDOMFromString(`
<button id="counter-btn">
${this.state.number}
</button>
`);
this.domElement.addEventListener('click',this.increment.bind(this));
return this.domElement;
}
}
<body>
<div id="counter-app"></div>
<script src="index.js"></script>
<script>
let counterApp = document.getElementById('counter-app');
new Counter({name:'珠峰架构'}).mount(counterApp);
</script>
</body>
window.trigger = function(event,name){
let component = event.target.component;
component[name].call(component,event);
}
class Component{
constructor(props){
this.props = props;
}
createDOMFromString(domString){
const div = document.createElement('div');
div.innerHTML = domString;
return div.children[0];
}
setState(partialState){
this.state = Object.assign(this.state,partialState);
let oldElement = this.domElement;
let newElement = this.renderElement();
oldElement.parentElement.replaceChild(newElement,oldElement);
}
renderElement(){
let renderString = this.render();
this.domElement = this.createDOMFromString(renderString);
this.domElement.component = this;
return this.domElement;
}
mount(container){
container.appendChild(this.renderElement());
}
}
class Counter extends Component{
constructor(props){
super(props);
this.state = {number:0};
}
increment(){
this.setState({number:this.state.number+1});
console.log(this.state);
this.setState({number:this.state.number+1});
console.log(this.state);
setTimeout(()=>{
this.setState({number:this.state.number+1});
console.log(this.state);
this.setState({number:this.state.number+1});
console.log(this.state);
},1000);
}
render(){
return (
`
<button id="counter-btn" onclick="trigger(event,'increment')">
${this.props.name}:${this.state.number}
</button>
`
)
}
}
<body>
<div id="counter-app"></div>
<script src="index.js"></script>
<script>
let counterApp = document.getElementById('counter-app');
new Counter({name:'珠峰架构'}).mount(counterApp);
</script>
</body>
let batchingStrategy = {
isBatchingUpdates:false,
updaters:[],
batchedUpdates(){
this.updaters.forEach(updater => {
updater.component.updateComponent();
});
}
}
class Updater{
constructor(component){
this.component = component;
this.pendingStates = [];
}
addState(particalState){
this.pendingStates.push(particalState);
batchingStrategy.isBatchingUpdates?batchingStrategy.updaters.push(this):this.component.updateComponent();
}
}
let transaction = new Transaction({
initialize() {
batchingStrategy.isBatchingUpdates = true;
},
close() {
batchingStrategy.isBatchingUpdates = false;
batchingStrategy.batchedUpdates();
}
});
window.trigger = function(event,name){
batchingStrategy.isBatchingUpdates = true;
let component = event.target.component;
component[name].bind(component,event);
batchingStrategy.isBatchingUpdates = false;
batchingStrategy.batchedUpdates();
}
class Component{
constructor(props){
this.props = props;
this.$updater = new Updater(this);
}
createDOMFromString(domString){
const div = document.createElement('div');
div.innerHTML = domString;
return div.children[0];
}
setState(particalState){
this.$updater.addState(particalState);
}
updateComponent(){
let pendingStates = this.$updater.pendingStates;
pendingStates.forEach(particalState=>Object.assign(this.state,particalState));
this.$updater.pendingStates.length = 0;
let oldElement = this.domElement;
let newElement = this.renderElement();
oldElement.parentElement.replaceChild(newElement,oldElement);
}
renderElement(){
let renderString = this.render();
this.domElement = this.createDOMFromString(renderString);
this.domElement.component = this;
return this.domElement;
}
mount(container){
container.appendChild(this.renderElement());
}
}
class Counter extends Component{
constructor(props){
super(props);
this.state = {number:0};
}
increment(){
this.setState({number:this.state.number+1});
console.log(this.state);
this.setState({number:this.state.number+1});
console.log(this.state);
setTimeout(()=>{
this.setState({number:this.state.number+1});
console.log(this.state);
this.setState({number:this.state.number+1});
console.log(this.state);
},1000);
}
render(){
return (
`
<button id="counter-btn" onclick="trigger(event,'increment')">
${this.props.name}:${this.state.number}
</button>
`
)
}
}
一个所谓的 Transaction 就是将需要执行的 method 使用 wrapper 封装起来,再通过 Transaction 提供的 perform 方法执行
* wrappers (injected at creation time) * + + * | | * +-----------------|--------|--------------+ * | v | | * | +---------------+ | | * | +--| wrapper1 |---|----+ | * | | +---------------+ v | | * | | +-------------+ | | * | | +----| wrapper2 |--------+ | * | | | +-------------+ | | | * | | | | | | * | v v v v | wrapper * | +---+ +---+ +---------+ +---+ +---+ | invariants * perform(anyMethod) | | | | | | | | | | | | maintained * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|--------> * | | | | | | | | | | | | * | | | | | | | | | | | | * | | | | | | | | | | | | * | +---+ +---+ +---------+ +---+ +---+ | * | initialize close | * +-----------------------------------------+ *
function setState() {
console.log('setState')
}
class Transaction {
constructor(wrappers) {
this.wrappers = wrappers;
}
perform(func) {
this.wrappers.forEach(wrapper=>wrapper.initialize())
func.call();
this.wrappers.forEach(wrapper=>wrapper.close())
}
}
let transaction = new Transaction([
{
initialize() {
console.log('before1');
},
close() {
console.log('after1');
}
},
{
initialize() {
console.log('before2');
},
close() {
console.log('after2');
}
}
]);
transaction.perform(setState);
class Transaction {
constructor(wrapper){
this.wrapper = wrapper;
}
perform(func){
this.wrapper.initialize();
func.call();
this.wrapper.close();
}
}
let batchingStrategy = {
isBatchingUpdates:false,
updaters:[],
batchedUpdates(){
this.updaters.forEach(updater => {
updater.component.updateComponent();
});
}
}
class Updater{
constructor(component){
this.component = component;
this.pendingStates = [];
}
addState(particalState){
this.pendingStates.push(particalState);
batchingStrategy.isBatchingUpdates?batchingStrategy.updaters.push(this):this.component.updateComponent();
}
}
let transaction = new Transaction({
initialize() {
batchingStrategy.isBatchingUpdates = true;
},
close() {
batchingStrategy.isBatchingUpdates = false;
batchingStrategy.batchedUpdates();
}
});
window.trigger = function(event,name){
let component = event.target.component;
transaction.perform(component[name].bind(component,event));
}
class Component{
constructor(props){
this.props = props;
this.$updater = new Updater(this);
}
createDOMFromString(domString){
const div = document.createElement('div');
div.innerHTML = domString;
return div.children[0];
}
setState(particalState){
this.$updater.addState(particalState);
}
updateComponent(){
let pendingStates = this.$updater.pendingStates;
pendingStates.forEach(particalState=>Object.assign(this.state,particalState));
this.$updater.pendingStates.length = 0;
let oldElement = this.domElement;
let newElement = this.renderElement();
oldElement.parentElement.replaceChild(newElement,oldElement);
}
renderElement(){
let renderString = this.render();
this.domElement = this.createDOMFromString(renderString);
this.domElement.component = this;
return this.domElement;
}
mount(container){
container.appendChild(this.renderElement());
}
}
class Counter extends Component{
constructor(props){
super(props);
this.state = {number:0};
}
increment(){
this.setState({number:this.state.number+1});
console.log(this.state);
this.setState({number:this.state.number+1});
console.log(this.state);
setTimeout(()=>{
this.setState({number:this.state.number+1});
console.log(this.state);
this.setState({number:this.state.number+1});
console.log(this.state);
},1000);
}
render(){
return (
`
<button id="counter-btn" onclick="trigger(event,'increment')">
${this.props.name}:${this.state.number}
</button>
`
)
}
}