1. // vue 类
  2. class Vue {
  3. /*
  4. 1. 通过属性保存数据
  5. 2. 将data中的数据转换为getter, setter,注入到vue实例中
  6. 3. 调用Observer, 监听data中的属性(响应式)
  7. 4. 调用compiler 解析指令和差值表达式
  8. */
  9. constructor(options) {
  10. // $options 保存 传入的options
  11. this.$options = options || {}
  12. // 保存 data
  13. this.$data = this.$options.data || {}
  14. // $el 绑定根元素
  15. this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
  16. // 将data中的数据转换为getter, setter,注入到vue实例中
  17. this._proxyData(this.$data)
  18. // 调用Observer, 监听data中的属性(响应式)
  19. new Observer(this.$data)
  20. new Compiler(this)
  21. }
  22. _proxyData(data) {
  23. Object.keys(data).forEach(key => {
  24. // 这里的this是vue实例
  25. Object.defineProperty(this, key, {
  26. configurable: true,
  27. enumerable: true,
  28. get() {
  29. return data[key]
  30. },
  31. set(newValue) {
  32. if(newValue === data[key]) {
  33. return
  34. }
  35. data[key] = newValue
  36. }
  37. })
  38. })
  39. }
  40. }

observer.js 监听数据,将数据变成响应式

  1. /*
  2. 1. 负责将传入的data中的属性 转化为响应式数据
  3. 2. 如果 data里面的某个属性也是对象,也需要 处理
  4. 3. 数据变化,发送通知
  5. */
  6. class Observer {
  7. constructor(data) {
  8. this.walk(data)
  9. }
  10. walk(data) {
  11. if(!data || typeof data !== 'object') {
  12. return
  13. }
  14. // 遍历data, 每一个属性都需要处理
  15. Object.keys(data).forEach(key => {
  16. this.defineReactvie(data, key, data[key])
  17. })
  18. }
  19. defineReactvie(obj, key, val) {
  20. const that = this
  21. // 如果val也是对象,其下属的属性也需要处理
  22. this.walk(val)
  23. Object.defineProperty(obj, key,{
  24. configurable: true,
  25. enumerable: true,
  26. get() {
  27. // 如果写 obj[key], 就会进入死循环
  28. return val
  29. },
  30. set(newValue) {
  31. if(newValue === obj[key]) {
  32. return
  33. }
  34. // 如果写 obj[key], 就会进入死循环
  35. val = newValue
  36. // 当赋值为对象时也需要 响应式
  37. that.walk(newValue)
  38. }
  39. })
  40. }
  41. }

compiler.js 编译模板,解析差值表达式 和 指令

  1. // 编译
  2. /*
  3. 1. 负责编译模板,解析指令/差值表达式
  4. 2. 负责页面首次渲染
  5. 3. 当数据变化时重新渲染视图
  6. 结构:
  7. // 变量
  8. el
  9. vm
  10. // 方法
  11. compile(el)
  12. compileText(node)
  13. compileElement(node)
  14. isDirective
  15. isTextNode
  16. */
  17. class Compiler {
  18. constructor(vm) {
  19. this.el = vm.$el
  20. this.vm = vm
  21. this.compile(this.el)
  22. }
  23. // 入口,负责编译
  24. compile(el) {
  25. // 获取所有的节点,伪数组
  26. const childNodes = el.childNodes
  27. Array.from(childNodes).forEach(node => {
  28. if(this.isTextNode(node)) {
  29. // 文本节点
  30. this.compileText(node)
  31. } else if(this.isElementNode(node)) {
  32. this.compileElement(node)
  33. }
  34. // 如果子节点下还有节点,也需要 递归处理
  35. if(node.childNodes && node.childNodes.length) {
  36. this.compile(node)
  37. }
  38. })
  39. }
  40. // 负责编译 文本节点, 解析差值表达式
  41. compileText(node) {
  42. // {{ msg }} , 形式
  43. /*
  44. 1. 取出 msg
  45. 2. 获取msg对应的值,并返回
  46. */
  47. // . 匹配所有字符, + 多个, ? 非贪婪匹配,尽可能结束 () 将匹配到的 分组
  48. const reg = /\{\{(.+?)\}\}/ // 匹配{{}}
  49. const value = node.textContent
  50. if(reg.test(value)) { // 如果获取到 表达式,那么就处理
  51. const key = RegExp.$1.trim()
  52. node.textContent = value.replace(reg, this.vm[key])
  53. }
  54. }
  55. // 负责编译 元素节点,获取其属性,解析 指令
  56. compileElement(node) {
  57. Array.from(node.attributes).forEach(attr => {
  58. let attrName = attr.name
  59. if(this.isDirective(attrName)) {
  60. // 解析指令
  61. // v-text v-model v-html, v-on等
  62. attrName = attrName.slice(2,)
  63. console.log(node, attrName, 'asdf')
  64. const key = attr.value
  65. this.update(node, attrName, key )
  66. }
  67. })
  68. }
  69. update(node, attrName, key) {
  70. // 根据 指令 名不同 调用不同的 函数
  71. const updateFn = this[attrName+'Updater']
  72. updateFn && updateFn(node, this.vm[key])
  73. // v-on 还未处理
  74. }
  75. textUpdater(node, value) {// node,当前node, 指令对应的值
  76. node.textContent = value
  77. }
  78. modelUpdater(node, value) {
  79. node.value = value
  80. }
  81. htmlUpdater(node, value) {
  82. node.innerHTML = value
  83. }
  84. // 是否为指令
  85. isDirective(attrName) {
  86. return attrName.startsWith('v-')
  87. }
  88. // 是否为 文本节点
  89. isTextNode(node) {
  90. return node.nodeType === 3
  91. }
  92. isElementNode(node) {
  93. return node.nodeType === 1
  94. }
  95. }