1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. </head>
    7. <body>
    8. <div id='app'>
    9. <h3>姓名</h3>
    10. <p>{{name}}</p>
    11. <h3>年龄</h3>
    12. <p>{{age}}</p>
    13. </div>
    14. </body>
    15. </html>
    16. <script>
    17. document.addEventListener('DOMContentLoaded', function(){
    18. let opt = {el:'#app', data:{name:'检索中...', age:30}}
    19. let vm = new Vue(opt)
    20. setTimeout(() => {
    21. opt.data.name = '王永峰'
    22. }, 2000);
    23. }, false)
    24. class Vue{
    25. constructor(opt){
    26. this.opt = opt
    27. this.observe(opt.data)
    28. let root = document.querySelector(opt.el)
    29. this.compile(root)
    30. }
    31. // 为响应式对象 data 里的每一个 key 绑定一个观察者对象
    32. observe(data){
    33. Object.keys(data).forEach(key => {
    34. let obv = new Observer()
    35. data["_"+key] = data[key]
    36. // 通过 getter setter 暴露 for 循环中作用域下的 obv,闭包产生
    37. Object.defineProperty(data, key, {
    38. get(){
    39. Observer.target && obv.addSubNode(Observer.target);
    40. return data['_'+key]
    41. },
    42. set(newVal){
    43. obv.update(newVal)
    44. data['_'+key] = newVal
    45. }
    46. })
    47. })
    48. }
    49. // 初始化页面,遍历 DOM,收集每一个key变化时,随之调整的位置,以观察者方法存放起来
    50. compile(node){
    51. [].forEach.call(node.childNodes, child =>{
    52. if(!child.firstElementChild && /\{\{(.*)\}\}/.test(child.innerHTML)){
    53. let key = RegExp.$1.trim()
    54. child.innerHTML = child.innerHTML.replace(new RegExp('\\{\\{\\s*'+ key +'\\s*\\}\\}', 'gm'),this.opt.data[key])
    55. Observer.target = child
    56. this.opt.data[key]
    57. Observer.target = null
    58. }
    59. else if (child.firstElementChild)
    60. this.compile(child)
    61. })
    62. }
    63. }
    64. // 常规观察者类
    65. class Observer{
    66. constructor(){
    67. this.subNode = []
    68. }
    69. addSubNode(node){
    70. this.subNode.push(node)
    71. }
    72. update(newVal){
    73. this.subNode.forEach(node=>{
    74. node.innerHTML = newVal
    75. })
    76. }
    77. }
    78. </script>