基于range的DOM绘制
    main.js

    1. import {render, Component, createElement} from './toy-react.js'
    2. // window.a = <div id="app" class="head"></div>
    3. class Hello extends Component {
    4. constructor () {
    5. super()
    6. this.state = {
    7. count: 0
    8. }
    9. }
    10. render () {
    11. return <div>
    12. <p>{this.state.count.toString()}</p>
    13. {this.children}
    14. </div>
    15. }
    16. }
    17. window.a = <Hello id="app" class="head">
    18. <span>1</span>
    19. <span>2</span>
    20. <span><h1>3</h1><h1>4</h1></span>
    21. hello
    22. </Hello>
    23. render(window.a, document.body)

    toy-react.js

    1. const RENDER_TO_DOM = Symbol('render to dom')
    2. export class Component {
    3. constructor() {
    4. this.props = Object.create(null);
    5. this.children = [];
    6. this._root = null;
    7. }
    8. setAttribute(name, value) {
    9. this.props[name] = value
    10. }
    11. appendChild(component) {
    12. this.children.push(component)
    13. }
    14. [RENDER_TO_DOM](range) {
    15. this.render()[RENDER_TO_DOM](range)
    16. }
    17. // get root() {
    18. // if (!this._root) {
    19. // this._root = this.render().root
    20. // }
    21. // return this._root
    22. // }
    23. }
    24. class ElementWrapper {
    25. constructor(type) {
    26. this.root = document.createElement(type)
    27. }
    28. setAttribute(name, value) {
    29. this.root.setAttribute(name, value)
    30. }
    31. appendChild(component) {
    32. // this.root.appendChild(component.root)
    33. let range = document.createRange()
    34. range.setStart(this.root, this.root.childNodes.length)
    35. range.setEnd(this.root, this.root.childNodes.length)
    36. component[RENDER_TO_DOM](range)
    37. }
    38. [RENDER_TO_DOM](range){
    39. range.deleteContents()
    40. range.insertNode(this.root)
    41. }
    42. }
    43. class TextWrapper {
    44. constructor(content) {
    45. this.root = document.createTextNode(content)
    46. }
    47. [RENDER_TO_DOM](range){
    48. range.deleteContents()
    49. range.insertNode(this.root)
    50. }
    51. }
    52. export function createElement(tagName, attributes, ...rest) {
    53. let element
    54. if (typeof tagName == 'string') {
    55. element = new ElementWrapper(tagName)
    56. } else {
    57. element = new tagName
    58. }
    59. if (typeof attributes === 'object' && attributes instanceof Object) {
    60. for (const key in attributes) {
    61. element.setAttribute(key, attributes[key])
    62. }
    63. }
    64. let insertChildren = (children) => {
    65. console.log(children)
    66. for(const child of children) {
    67. if (typeof child == 'string') {
    68. child = new TextWrapper(child)
    69. }
    70. if ((typeof child == 'object') && (child instanceof Array)) {
    71. insertChildren(child)
    72. } else {
    73. element.appendChild(child)
    74. }
    75. }
    76. }
    77. insertChildren(rest)
    78. return element
    79. }
    80. export function render(component, parentElement) {
    81. // parentElement.appendChild(component.root)
    82. let range = document.createRange()
    83. range.setStart(parentElement, 0)
    84. range.setEnd(parentElement, parentElement.childNodes.length)
    85. component[RENDER_TO_DOM](range)
    86. }