react简单原理

简单DEMO

var state = { value: null };

$('#input').on('keyup', function () {
  state.value = $(this).val().trim();
  render();
})

function render () {
  $('#show').html(state.value);
}

render();

进一步 生成HTML节点

function div (text) {
  return '<div>' + text + '</div>'
}

进一步 生成HTML节点

var state = {items: [], id: 0}

$('#add').on('click', function (e) {
  var value = $('#input').val().trim()
  $('#input').val('')

  state.items.push({
    id: state.id++,
    text: value
  })

  render()
})

function render () {
  var items = state.items.map(function (item) {

    return '<li class="item" 
      id="' + item.id + '">(' +
      item.id + ') ' + item.text + '</li>'

  }).join('')

  var html = '<ul>' + items = '</ul>'

  $('#list').html(html)
}

render()

简化render

function ItemRow (props) {
  var className = props.completed ? ' item completed' : 'item'
  return '<li className="' + className +' ">' + props.text + '</li>'
}

function ItemsList (props) {
  return '<ul>' + props.items.map(ItemRow).join('') + '</ul>'
}

function render () {
  $('#list').html(ItemsList({
    items: state.items
  }))
}

减少全局变量的依赖

function render (props, node) {
  node.html(ItemsList({
    items: props.items
  }))
}

能不能自动触发render?

function createStore (initialState) {
  var _state = initialState || {},
    _listeners = []
  function updateListeners (state) {
    _listeners.forEach(function (listener) {
      listener.cb(state)
    })
  }
  return {
    setState: function (state) {
      _state = state
      updateListeners(state)
    },
    getState: function () {
      return _state
    },
    onUpdate: function (name, cb) {
      _listeners.push({name: name, cb: cb})
    }
  }
}

使用store

var store = createStore(state)

store.onUpdate('rootRender', function (state) {
  render(state, $('#list'))
})

state.item.push({**********})
store.setState(state);

总结一下

1.单向数据流

2. 组件

3. state管理

<div id="app">
        <input id="input" />
        <button id="add">Add</button>
        <div id="list"></div>
</div>
var state = { items: [], id: 0 };

$('#add').on('click', function(e) {
  var value = $('#input').val().trim();
  $('#input').val('');
  state.items.push({id: state.id++, text: value, completed: false});
  store.setState($.extend({}, state));
});

$('#list').on('click', '.item', function() {
  var toggleId = parseInt($(this).attr('id'));
  state.items.forEach(function(el) {
    if (el.id === toggleId) {
      el.completed = !el.completed;
    }
  });
 store.setState($.extend({}, state));
});

function ItemRow(props) { 
    var className = props.completed? ' completed' : '';
    return '<li class="item'+ className +'" id="' + props.id + '">(' + 
            props.id + ') '+ props.text + '</li>';
}

function ItemsList(props) {
    return '<ul>' + props.items.map(ItemRow).join('') + '</ul>';
}

function render(props, node) {
 node.html(ItemsList({items : props.items}));
}

function createStore(initialState) {
  var _state = initialState || {}, _listeners = [];
  function updateListeners(state) {
    _listeners.forEach(function(listener) {
      listener.cb(state);
    });
  }

  return {
    setState: function(state) {
      _state = state;
      updateListeners(state);
    },

    getState: function() {
      return _state;
    },

    onUpdate: function(name, cb) {
      _listeners.push({name: name, cb: cb});
    }
  };
}

var store = createStore(state);

store.onUpdate('rootRender', function(state) {
  render(state, $('#list'));  
});

组件绑定事件

function ItemRow (props) {

  return $('<li>')
    .on('click', props.onUpdate.bind(null, props.id))
    .addClass('item')
    .attr('id', props.id)
    .html(props.text)
}

创建元素独立出来

function createElement (tag, attrs, children) {
  var ele = $('<' + tag + '>')

  for (var key in attrs) {
    var val = attrs[key]
    if (key.indexOf('on') === 0) {
      var event = key.substr(2).toLowerCase()
      ele.on(event, val)
    } else {
      ele.attr(key, val)
    }
  }
  return ele.html(children)
}
function ItemRow (props) {
  var className = props.completed ? 'item completed' : 'item'
  return createElement('li', {
    id: props.id,
    class: props.className,
    onClick: props.onUpdate.bind(null, props.id)
  }, props.text)
}

react中新建元素

return ( 
  <div id='el' className='entry'> 
    Hello 
  </div>
)
var SomeElement = React.createElement('div', {
  id: 'el',
  className: 'entry'
}, 'Hello')
{
  // ...
  type: 'div',
  key: null,
  ref: null,
  props: {
    children: 'Hello',
    className: 'entry',
    id: 'el'
  }
}

onUpdate是哪来的呢?

function render (props, node) {
  function updateState (toggleId) {
    state.items.forEach(function (el) {
      if (el.id === toggleId) {
        el.completed = !el.completed
      }
    })
    store.setState(state)
  }

  node.empty().append([ItemList({
    items: props.items,
    onUpdate: updateState
  })])
}

onUpdate的传递

function extending (base, item) {
  return $.extend({}, item, base)
}

function ItemsList (props) {
  return createElement('ul', {}, props.items
    .map(extending.bind(null, {
      onUpdate: props.onUpdate
    }))
    .map(ItemRow))
}

再次简化

<div id="app">
        <input id="input" />
        <button id="add">Add</button>
        <div id="list"></div>
</div>
<div id="app"></div>

再次简化

function SearchBar(props) {
  function onButtonClick (e) {
    var val = $('#input').val()
    $('#input').val('')
    props.update(val)
    e.preventDefault()
  }

  var input = createElement('input', {id: 'input'})

  var button = createElement('button', {
    id: 'add',
    onClick: onButtonClick.bind(null)
  }, 'Add')

  return createElement('div', {}, {input, button})
}

进一步整合searchbar和ItemList以及状态管理

function render (component, node) {
  node.empty().append(component)
}

render(App(state), $('#app'))

进一步整合searchbar和ItemList以及状态管理

function App (props) {
  function updateSearchBar (value) {
    state.items.push({
      id: state.id++,
      text: value,
      completed: false
    })
    store.setState(state);
  }

  function updateState (toggleId) {
    state.items.forEach(function (el) {
      if (el.id === toggleId) {
        el.completed = !el.completed
      }
    })
    store.setState(state)
  }

  return [
    SearchBar({update: updateSearchBar}),
    ItemsList({items: props.items, onUpdate: updateState})
  ]
}

自行管理state

function App (props) {
  function getInitialState (props) {
    return {
      items: [],
      id: 0
    }
  }

  var _state = getInitialState(),
    _node = null

  function setState (state) {
    _state = state
    render()
  }
  function render(){}
  // ..
}

render实现

function render () {
  var children = [
    SearchBar({update: updateSearchState}),
    ItemList({
      items: _state.items,
      onUpdate: updateState
    })
  ]

  if (!_node) {
    return _node = createElement('div', {class: 'top'}, children)
  } else {
    return _node.html(children)
  }
}

大功告成

function render(component, node) {
  node.empty().append(component)
}
render(App(), $('#app'))
<div id="app"></div>
function createElement(tag, attrs, children) {
  var elem = $('<' + tag + '>');
  for (var key in attrs) {
    var val = attrs[key];
    if (key.indexOf('on') === 0) {
      var event = key.substr(2).toLowerCase();
      elem.on(event, val)
    } else {
      elem.attr(key, val);
    }
  }
  return elem.html(children);
}

function ItemRow(props) {
  var className = props.completed ? ' item completed' : 'item';
  return createElement('li', {
    id: props.id,
    class: className,
    onClick: props.onUpdate.bind(null, props.id)
  }, props.text);
}


function extending(base, item) {
  return $.extend({}, item, base);
}

function ItemsList(props) {
  return createElement('ul', {}, props.items.map(extending.bind(null, {
    onUpdate: props.onUpdate
  })).map(ItemRow));
}

function SearchBar(props) {
  function onButtonClick(e) {
    var val = $('#input').val();
    props.update(val);
    $('#input').val('');
    e.preventDefault();
  }

  var input = createElement('input', {
    id: 'input'
  });
  var button = createElement('button', {
    id: 'add',
    onClick: onButtonClick.bind(null)
  }, 'Add');

  return createElement('div', {
    class: 'serach-bar'
  }, [input, button]);
}

function App(props) {
  function getInitialState(props) {
    return {
      items: [],
      id: 0
    }
  }

  var _state = getInitialState(),
    _node = null;

  function setState(state) {
    _state = state;
    render();
  }

  function updateSearchState(value) {
    _state.items.push({
      id: _state.id++,
      text: value,
      completed: false
    });
    setState(_state);
  }

  function updateState(toggleId) {
    _state.items.forEach(function(el) {
      if (el.id === toggleId) {
        el.completed = !el.completed;
      }
    });
    setState(_state);
  }

  function render() {
    var children = [SearchBar({
        update: updateSearchState
      }), ItemsList({
        items: _state.items,
        onUpdate: updateState
      })];
    if (!_node) {
      return _node = createElement('div', { class: 'main' }, children);
    } else {
      return _node.html(children);
    }
  }
  
  return render();
}

function render(component, node) {
  node.empty().append(component);
}

render(App(), $('#app'));

再次总结一下

1.单向数据流与事件流

2. 组件状态管理

2016-8月-分享

By Joson Chen

2016-8月-分享

  • 352