v-if 和 v-show 的认识

Vue框架中,我们可以通过v-if/v-else-if/v-elsev-show来控制元素或组件的渲染,当v-ifv-show的指令表达式条件返回true的时候就会渲染元素或组件。

  1. <h1 v-if="awesome">Vue is awesome!</h1>

v-if指令是利用了注释节点<-- if -->对文档进行了占位,当v-if指令的值为真的时候,Vue会把相应的注释节点替换为真实的元素,反之就是把真实的元素替换为注释节点。 :::warning ⚠️ 注意
v-if在切换渲染的时候,条件区块内的事件监听器和子组件都会被销毁与重建。 ::: 屏幕录制2023-01-10 14.07.34.gif

v-show则不同,v-show里利用CSS给给元素新增dispaly: none;来实现视觉上的隐藏,并没有真正的从文档中被移除。 :::info 因为v-if频繁的切换显示隐藏会提高性能的开销,所以,我们在开发的时候如果需要频繁的切换显示和隐藏就要使用v-show,因为v-show的性能开销会比较低。 :::

模拟实现 v-if 和 v-show

我们如何模拟实现一个v-ifv-show这样的操作呢?
首先我们需要一个Vue.js文件用于创建Vue实例,再创建一个index.js文件作为实例化入口。

首先,我们用熟悉的Vue2配方进行实例化Vue对象:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <div id="app"></div>
  11. <script type="module" src="./index.js"></script>
  12. </body>
  13. </html>
  1. import VueTest from "./Vue.js";
  2. const vm = new VueTest({
  3. el: "#app",
  4. // 页面模版
  5. template: `
  6. <div>
  7. <img v-if="isShowImg1" src="https://cdn.pixabay.com/photo/2022/11/15/04/54/automotive-7593064__340.jpg" />
  8. <img v-show="isShowImg2" src="https://cdn.pixabay.com/photo/2022/12/15/18/15/christmas-7658297__340.jpg" />
  9. </div>
  10. <button @click="showImg1">显示图片1</button>
  11. <button @click="showImg2">显示图片2</button>
  12. `,
  13. // 数据响应劫持
  14. data() {
  15. return {
  16. isShowImg1: true,
  17. isShowImg2: false,
  18. };
  19. },
  20. // 事件处理
  21. methods: {
  22. showImg1() {
  23. this.isShowImg1 = !this.isShowImg1;
  24. },
  25. showImg2() {
  26. this.isShowImg2 = !this.isShowImg2;
  27. },
  28. },
  29. });
  30. console.log(vm);

这样我们就把一个最基本的实例化配置写好了,下面重点要放在Vue.js文件上面。

Vue文件我们利用一个立即执行函数来实现模块化,立即执行函数执行后返回一个Vue的构造函数,所以我们可以在index.js中进行实例化。

  1. var VueTest = (function () {
  2. function Vue(options){}
  3. return Vue;
  4. })();
  5. export default VueTest;

接着,我们要把options里面的相关属性暴露在实例化对象上:

  1. var VueTest = (function () {
  2. function Vue(options){
  3. this.$el = document.querySelector(options.el);
  4. this.$data = options.data();
  5. }
  6. return Vue;
  7. })();
  8. export default VueTest;

image.png

我们还需要一个函数用于初始化数据:

  1. var VueTest = (function () {
  2. function Vue(options) {
  3. this.$el = document.querySelector(options.el);
  4. this.$data = options.data();
  5. this._init();
  6. }
  7. Vue.prototype._init = function () {
  8. var showPool = new Map(); // 存储dom和数据的对应关系
  9. var eventPool = new Map(); // 存储dom和事件处理的对应关系
  10. initData();
  11. initPool();
  12. bindEvent();
  13. render();
  14. };
  15. // 初始化数据,对 data 数据进行拦截
  16. function initData() {}
  17. // 初始化数据池
  18. function initPool() {}
  19. // 绑定事件处理
  20. function bindEvent() {}
  21. // 对 template 进行渲染
  22. function render() {}
  23. // 更改 data 数据后更新视图
  24. function update() {}
  25. return Vue;
  26. })();
  27. export default VueTest;

initPool方法主要有两个作用:
1、存储dom和指令+数据的对应关系
2、存储dom和事件处理函数的对应关系
我们利用的是Map数据结构进行存储,因为MapKey可以是任意的数据类型,这样我们在后续处理的时候就能根据对应关系去处理数据啦。
v-if 和 v-show - 图3

第一件事:数据拦截

到这里,基本的架子已经差不多了,下面我们先做第一件事,那就是对数据进行拦截:

  1. var VueTest = (function () {
  2. function Vue(options) {
  3. this.$el = document.querySelector(options.el);
  4. this.$data = options.data();
  5. this._init();
  6. }
  7. Vue.prototype._init = function () {
  8. var showPool = new Map();
  9. var eventPool = new Map();
  10. // 把当前实例、dom和数据的对应关系传递过去
  11. initData(this, showPool);
  12. initPool();
  13. bindEvent();
  14. render();
  15. };
  16. function initData(vm, showPool) {
  17. var _data = vm.$data;
  18. for (const key in _data) {
  19. // 判断属性是不是 _data 的原生属性
  20. if (Object.hasOwnProperty.call(_data, key)) {
  21. // 对当前实例进行拦截,例如访问 vm.isShowImg1 就会被拦截
  22. Object.defineProperty(vm, key, {
  23. get: function () {
  24. return _data[key];
  25. },
  26. set: function (newVal) {
  27. _data[key] = newVal;
  28. // 当更改 data 的数据后,需要调用方法去更新数据和对应关系
  29. update(vm, key, showPool);
  30. }
  31. });
  32. }
  33. }
  34. }
  35. function initPool() {}
  36. function bindEvent() {}
  37. function render() {}
  38. function update() {}
  39. return Vue;
  40. })();
  41. export default VueTest;

第二件事:存储数据池

第二件事情,对数据池进行初始化(可以对应上面的思维导图去理解):

  1. var VueTest = (function () {
  2. function Vue(options) {
  3. this.$el = document.querySelector(options.el);
  4. this.$data = options.data();
  5. this._init(options.template, options.methods);
  6. }
  7. // 把 template, methods 传递进来
  8. Vue.prototype._init = function (template, methods) {
  9. var showPool = new Map();
  10. var eventPool = new Map();
  11. // 创建一个 div 元素,把 template 的内容放进去
  12. var container = document.createElement("div");
  13. container.innerHTML = template;
  14. initData(this, showPool);
  15. // 把 template、methods 和数据池传递进去,绑定关系
  16. initPool(container, methods, showPool, eventPool);
  17. bindEvent();
  18. render();
  19. };
  20. function initData(vm, showPool) {
  21. var _data = vm.$data;
  22. for (const key in _data) {
  23. if (Object.hasOwnProperty.call(_data, key)) {
  24. Object.defineProperty(vm, key, {
  25. get: function () {
  26. return _data[key];
  27. },
  28. set: function (newVal) {
  29. _data[key] = newVal;
  30. update(vm, key, showPool);
  31. },
  32. });
  33. }
  34. }
  35. }
  36. function initPool(container, methods, showPool, eventPool) {
  37. var _allNodes = container.getElementsByTagName("*");
  38. var dom = null;
  39. console.log(container);
  40. console.log(_allNodes); // 获取 container 下所有的内容
  41. for (let i = 0; i < _allNodes.length; i++) {
  42. dom = _allNodes[i];
  43. // 获取所有元素上的 v-if、v-show 和 @click 属性
  44. var vIfData = dom.getAttribute("v-if");
  45. var vShowData = dom.getAttribute("v-show");
  46. var vEvent = dom.getAttribute("@click");
  47. }
  48. }
  49. function bindEvent() {}
  50. function render() {}
  51. function update() {}
  52. return Vue;
  53. })();
  54. export default VueTest;

47、48 行代码打印结果,获取模版的元素

获取到这些属性之后,我们就可以存储到数据池啦:

  1. var VueTest = (function () {
  2. function Vue(options) {
  3. this.$el = document.querySelector(options.el);
  4. this.$data = options.data();
  5. this._init(options.template, options.methods);
  6. }
  7. Vue.prototype._init = function (template, methods) {
  8. var showPool = new Map();
  9. var eventPool = new Map();
  10. var container = document.createElement("div");
  11. container.innerHTML = template;
  12. initData(this, showPool);
  13. initPool(container, methods, showPool, eventPool);
  14. bindEvent();
  15. render();
  16. };
  17. function initData(vm, showPool) {
  18. var _data = vm.$data;
  19. for (const key in _data) {
  20. if (Object.hasOwnProperty.call(_data, key)) {
  21. Object.defineProperty(vm, key, {
  22. get: function () {
  23. return _data[key];
  24. },
  25. set: function (newVal) {
  26. _data[key] = newVal;
  27. update(vm, key, showPool);
  28. },
  29. });
  30. }
  31. }
  32. }
  33. function initPool(container, methods, showPool, eventPool) {
  34. var _allNodes = container.getElementsByTagName("*");
  35. var dom = null;
  36. for (let i = 0; i < _allNodes.length; i++) {
  37. dom = _allNodes[i];
  38. var vIfData = dom.getAttribute("v-if");
  39. var vShowData = dom.getAttribute("v-show");
  40. var vEvent = dom.getAttribute("@click");
  41. if (vIfData) {
  42. showPool.set(dom, {
  43. type: "if",
  44. prop: vIfData,
  45. });
  46. dom.removeAttribute("v-if");
  47. } else if (vShowData) {
  48. showPool.set(dom, {
  49. type: "show",
  50. prop: vShowData,
  51. });
  52. dom.removeAttribute("v-show");
  53. }
  54. if (vEvent) {
  55. eventPool.set(dom, methods[vEvent]);
  56. dom.removeAttribute("@click");
  57. }
  58. }
  59. console.log(showPool);
  60. console.log(eventPool);
  61. }
  62. function bindEvent() {}
  63. function render() {}
  64. function update() {}
  65. return Vue;
  66. })();
  67. export default VueTest;

70、71行代码结果,数据池的对应关系

第三件事:绑定事件

到目前为止,数据拦截已经完成,dom和数据的对应关系也已经完成,接下来我们要绑定事件:

  1. var VueTest = (function () {
  2. function Vue(options) {
  3. this.$el = document.querySelector(options.el);
  4. this.$data = options.data();
  5. this._init(options.template, options.methods);
  6. }
  7. Vue.prototype._init = function (template, methods) {
  8. var showPool = new Map();
  9. var eventPool = new Map();
  10. var container = document.createElement("div");
  11. container.innerHTML = template;
  12. initData(this, showPool);
  13. initPool(container, methods, showPool, eventPool);
  14. // 传递当前实例、事件池
  15. bindEvent(this, eventPool);
  16. render();
  17. };
  18. function initData(vm, showPool) {
  19. var _data = vm.$data;
  20. for (const key in _data) {
  21. if (Object.hasOwnProperty.call(_data, key)) {
  22. Object.defineProperty(vm, key, {
  23. get: function () {
  24. return _data[key];
  25. },
  26. set: function (newVal) {
  27. _data[key] = newVal;
  28. update(vm, key, showPool);
  29. },
  30. });
  31. }
  32. }
  33. }
  34. function initPool(container, methods, showPool, eventPool) {
  35. var _allNodes = container.getElementsByTagName("*");
  36. var dom = null;
  37. for (let i = 0; i < _allNodes.length; i++) {
  38. dom = _allNodes[i];
  39. var vIfData = dom.getAttribute("v-if");
  40. var vShowData = dom.getAttribute("v-show");
  41. var vEvent = dom.getAttribute("@click");
  42. if (vIfData) {
  43. showPool.set(dom, {
  44. type: "if",
  45. prop: vIfData,
  46. });
  47. dom.removeAttribute("v-if");
  48. } else if (vShowData) {
  49. showPool.set(dom, {
  50. type: "show",
  51. prop: vShowData,
  52. });
  53. dom.removeAttribute("v-show");
  54. }
  55. if (vEvent) {
  56. eventPool.set(dom, methods[vEvent]);
  57. dom.removeAttribute("@click");
  58. }
  59. }
  60. }
  61. function bindEvent(vm, eventPool) {
  62. for (var [dom, handler] of eventPool) {
  63. // 把方法挂载到实例上
  64. vm[handler.name] = handler;
  65. // 给 dom 添加事件处理
  66. // 利用 bind 把 this 指向当前实例对象
  67. dom.addEventListener("click", vm[handler.name].bind(vm), false);
  68. }
  69. }
  70. function render() {}
  71. function update() {}
  72. return Vue;
  73. })();
  74. export default VueTest;

image.png
可以看到实例对象上有了showImg1showImg2这两个方法啦。

第四件事:渲染 dom

最重要的事情来了,那就是渲染dom,我们要判断dom对应的指令是v-if还是v-show来决定如何隐藏dom

  1. var VueTest = (function () {
  2. function Vue(options) {
  3. this.$el = document.querySelector(options.el);
  4. this.$data = options.data();
  5. this._init(options.template, options.methods);
  6. }
  7. Vue.prototype._init = function (template, methods) {
  8. var showPool = new Map();
  9. var eventPool = new Map();
  10. var container = document.createElement("div");
  11. container.innerHTML = template;
  12. initData(this, showPool);
  13. initPool(container, methods, showPool, eventPool);
  14. bindEvent(this, eventPool);
  15. // 把当前实例、数据池、dom 内容传递过去
  16. render(this, showPool, container);
  17. };
  18. function initData(vm, showPool) {
  19. var _data = vm.$data;
  20. for (const key in _data) {
  21. if (Object.hasOwnProperty.call(_data, key)) {
  22. Object.defineProperty(vm, key, {
  23. get: function () {
  24. return _data[key];
  25. },
  26. set: function (newVal) {
  27. _data[key] = newVal;
  28. update(vm, key, showPool);
  29. },
  30. });
  31. }
  32. }
  33. }
  34. function initPool(container, methods, showPool, eventPool) {
  35. var _allNodes = container.getElementsByTagName("*");
  36. var dom = null;
  37. for (let i = 0; i < _allNodes.length; i++) {
  38. dom = _allNodes[i];
  39. var vIfData = dom.getAttribute("v-if");
  40. var vShowData = dom.getAttribute("v-show");
  41. var vEvent = dom.getAttribute("@click");
  42. if (vIfData) {
  43. showPool.set(dom, {
  44. type: "if",
  45. prop: vIfData,
  46. });
  47. dom.removeAttribute("v-if");
  48. } else if (vShowData) {
  49. showPool.set(dom, {
  50. type: "show",
  51. prop: vShowData,
  52. });
  53. dom.removeAttribute("v-show");
  54. }
  55. if (vEvent) {
  56. eventPool.set(dom, methods[vEvent]);
  57. dom.removeAttribute("@click");
  58. }
  59. }
  60. }
  61. function bindEvent(vm, eventPool) {
  62. console.log(eventPool);
  63. for (var [dom, handler] of eventPool) {
  64. vm[handler.name] = handler;
  65. dom.addEventListener("click", vm[handler.name].bind(vm), false);
  66. }
  67. }
  68. function render(vm, showPool, container) {
  69. var _data = vm.$data;
  70. var _el = vm.$el;
  71. // 迭代 showPool 对象
  72. for (const [dom, info] of showPool) {
  73. // 判断dom和数据的对应关系
  74. switch (info.type) {
  75. // 如果是 if 指令
  76. case "if":
  77. // 创建一个注释节点
  78. info.comment = document.createComment(["v-if"]);
  79. // 如果数据为假,也就是 isShowImg1,那么就把 dom 替换为注释节点
  80. !_data[info.prop] && dom.parentNode.replaceChild(info.comment, dom);
  81. break;
  82. case "show":
  83. // 如果数据为假,也就是 isShowImg2,那么就把 dom 的样式设置为隐藏
  84. !_data[info.prop] && (dom.style.display = "none");
  85. break;
  86. }
  87. }
  88. // 最后把 container 渲染到 el 节点上
  89. _el.appendChild(container);
  90. }
  91. function update() {}
  92. return Vue;
  93. })();
  94. export default VueTest;

image.png
这样页面第一次加载就会触发render函数,该函数内负责对dom的隐藏/显示进行控制。

第五件事:更新视图

最后一件事就是在调用methods的方法后,去更新视图,更改isShowImg1isShowImg2的时候就会触发initData里面的拦截器,拦截器在set处理函数中又调用了update函数。

  1. new VueTest({
  2. // 其他的配置选项
  3. methods: {
  4. showImg1() {
  5. this.isShowImg1 = !this.isShowImg1;
  6. },
  7. showImg2() {
  8. this.isShowImg2 = !this.isShowImg2;
  9. }
  10. }
  11. })
  1. var VueTest = (function () {
  2. // ...
  3. function initData(vm, showPool) {
  4. var _data = vm.$data;
  5. for (const key in _data) {
  6. if (Object.hasOwnProperty.call(_data, key)) {
  7. Object.defineProperty(vm, key, {
  8. get: function () {
  9. return _data[key];
  10. },
  11. set: function (newVal) {
  12. _data[key] = newVal;
  13. // 更新视图
  14. update(vm, key, showPool);
  15. },
  16. });
  17. }
  18. }
  19. }
  20. // ...
  21. function update(vm, key, showPool) {}
  22. return Vue;
  23. })();
  24. export default VueTest;

update方法和render方法基本类似:

  1. var VueTest = (function () {
  2. function Vue(options) {
  3. this.$el = document.querySelector(options.el);
  4. this.$data = options.data();
  5. this._init(options.template, options.methods);
  6. }
  7. Vue.prototype._init = function (template, methods) {
  8. var showPool = new Map();
  9. var eventPool = new Map();
  10. var container = document.createElement("div");
  11. container.innerHTML = template;
  12. initData(this, showPool);
  13. initPool(container, methods, showPool, eventPool);
  14. bindEvent(this, eventPool);
  15. render(this, showPool, container);
  16. };
  17. function initData(vm, showPool) {
  18. var _data = vm.$data;
  19. for (const key in _data) {
  20. if (Object.hasOwnProperty.call(_data, key)) {
  21. Object.defineProperty(vm, key, {
  22. get: function () {
  23. return _data[key];
  24. },
  25. set: function (newVal) {
  26. _data[key] = newVal;
  27. update(vm, key, showPool);
  28. },
  29. });
  30. }
  31. }
  32. }
  33. function initPool(container, methods, showPool, eventPool) {
  34. var _allNodes = container.getElementsByTagName("*");
  35. var dom = null;
  36. for (let i = 0; i < _allNodes.length; i++) {
  37. dom = _allNodes[i];
  38. var vIfData = dom.getAttribute("v-if");
  39. var vShowData = dom.getAttribute("v-show");
  40. var vEvent = dom.getAttribute("@click");
  41. if (vIfData) {
  42. showPool.set(dom, {
  43. type: "if",
  44. prop: vIfData,
  45. });
  46. dom.removeAttribute("v-if");
  47. } else if (vShowData) {
  48. showPool.set(dom, {
  49. type: "show",
  50. prop: vShowData,
  51. });
  52. dom.removeAttribute("v-show");
  53. }
  54. if (vEvent) {
  55. eventPool.set(dom, methods[vEvent]);
  56. dom.removeAttribute("@click");
  57. }
  58. }
  59. }
  60. function bindEvent(vm, eventPool) {
  61. console.log(eventPool);
  62. for (var [dom, handler] of eventPool) {
  63. vm[handler.name] = handler;
  64. dom.addEventListener("click", vm[handler.name].bind(vm), false);
  65. }
  66. }
  67. function render(vm, showPool, container) {
  68. var _data = vm.$data;
  69. var _el = vm.$el;
  70. for (const [dom, info] of showPool) {
  71. switch (info.type) {
  72. case "if":
  73. info.comment = document.createComment(["v-if"]);
  74. !_data[info.prop] && dom.parentNode.replaceChild(info.comment, dom);
  75. break;
  76. case "show":
  77. !_data[info.prop] && (dom.style.display = "none");
  78. break;
  79. }
  80. }
  81. _el.appendChild(container);
  82. }
  83. function update(vm, key, showPool) {
  84. var _data = vm.$data;
  85. // 遍历 showPool 对象
  86. for (const [dom, info] of showPool) {
  87. // 如果 dom 的数据和被拦截到的 key 相等
  88. if (info.prop === key) {
  89. switch (info.type) {
  90. case "show":
  91. // 如果为假那么就设置样式为隐藏,否则就移除样式
  92. !_data[key] ? (dom.style.display = "none") : dom.removeAttribute("style");
  93. break;
  94. case "if":
  95. // 如果为假就用注释节点替换dom,否则就用dom替换注释节点
  96. !_data[key]
  97. ? dom.parentNode.replaceChild(info.comment, dom)
  98. : info.comment.parentNode.replaceChild(dom, info.comment);
  99. break;
  100. }
  101. }
  102. }
  103. }
  104. return Vue;
  105. })();
  106. export default VueTest;

到这里我们的模拟就已经完成了,下面去看看效果。
屏幕录制2023-01-10 16.37.19.gif

添加声明周期函数

我们还可以添加一些生命周期函数,在相应的阶段进行执行。

  1. import VueTest from "./Vue.js";
  2. const vm2 = new VueTest({
  3. el: "#app",
  4. template: `
  5. <div>
  6. <img v-if="isShowImg1" src="https://cdn.pixabay.com/photo/2022/11/15/04/54/automotive-7593064__340.jpg" />
  7. <img v-show="isShowImg2" src="https://cdn.pixabay.com/photo/2022/12/15/18/15/christmas-7658297__340.jpg" />
  8. </div>
  9. <button @click="showImg1">显示图片1</button>
  10. <button @click="showImg2">显示图片2</button>
  11. `,
  12. data() {
  13. return {
  14. isShowImg1: false,
  15. isShowImg2: false,
  16. };
  17. },
  18. beforeCreate() {
  19. console.log("beforeCreate", this);
  20. },
  21. created() {
  22. console.log("created", this);
  23. },
  24. beforeMount() {
  25. console.log("beforeMount", this);
  26. },
  27. mounted() {
  28. console.log("mounted", this);
  29. this.isShowImg1 = false;
  30. },
  31. methods: {
  32. showImg1() {
  33. this.isShowImg1 = !this.isShowImg1;
  34. },
  35. showImg2() {
  36. this.isShowImg2 = !this.isShowImg2;
  37. },
  38. },
  39. });
  40. console.log(vm2);
  1. var VueTest = (function () {
  2. function Vue(options) {
  3. // 保存所有的生命周期函数,并且改变 this 指向为 当前实例
  4. var recycles = {
  5. beforeCreate: options.beforeCreate.bind(this),
  6. created: options.created.bind(this),
  7. beforeMount: options.beforeMount.bind(this),
  8. mounted: options.mounted.bind(this),
  9. };
  10. // 初始化数据之前调用
  11. recycles.beforeCreate();
  12. this.$el = document.querySelector(options.el);
  13. this.$data = options.data();
  14. // 传递 recycles
  15. this._init(options.template, options.methods, recycles);
  16. }
  17. Vue.prototype._init = function (template, methods, recycles) {
  18. // 初始化数据之后调用
  19. recycles.created();
  20. var showPool = new Map();
  21. var eventPool = new Map();
  22. var container = document.createElement("div");
  23. container.innerHTML = template;
  24. initData(this, showPool);
  25. initPool(container, methods, showPool, eventPool);
  26. bindEvent(this, eventPool);
  27. // 传递 recycles
  28. render(this, showPool, container, recycles);
  29. };
  30. function initData(vm, showPool) {
  31. var _data = vm.$data;
  32. for (const key in _data) {
  33. if (Object.hasOwnProperty.call(_data, key)) {
  34. Object.defineProperty(vm, key, {
  35. get: function () {
  36. return _data[key];
  37. },
  38. set: function (newVal) {
  39. _data[key] = newVal;
  40. update(vm, key, showPool);
  41. },
  42. });
  43. }
  44. }
  45. }
  46. function initPool(container, methods, showPool, eventPool) {
  47. var _allNodes = container.getElementsByTagName("*");
  48. var dom = null;
  49. for (let i = 0; i < _allNodes.length; i++) {
  50. dom = _allNodes[i];
  51. var vIfData = dom.getAttribute("v-if");
  52. var vShowData = dom.getAttribute("v-show");
  53. var vEvent = dom.getAttribute("@click");
  54. if (vIfData) {
  55. showPool.set(dom, {
  56. type: "if",
  57. prop: vIfData,
  58. });
  59. dom.removeAttribute("v-if");
  60. } else if (vShowData) {
  61. showPool.set(dom, {
  62. type: "show",
  63. prop: vShowData,
  64. });
  65. dom.removeAttribute("v-show");
  66. }
  67. if (vEvent) {
  68. eventPool.set(dom, methods[vEvent]);
  69. dom.removeAttribute("@click");
  70. }
  71. }
  72. }
  73. function bindEvent(vm, eventPool) {
  74. for (var [dom, handler] of eventPool) {
  75. vm[handler.name] = handler;
  76. dom.addEventListener("click", vm[handler.name].bind(vm), false);
  77. }
  78. }
  79. function render(vm, showPool, container, recycles) {
  80. var _data = vm.$data;
  81. var _el = vm.$el;
  82. for (const [dom, info] of showPool) {
  83. switch (info.type) {
  84. case "if":
  85. info.comment = document.createComment(["v-if"]);
  86. !_data[info.prop] && dom.parentNode.replaceChild(info.comment, dom);
  87. break;
  88. case "show":
  89. !_data[info.prop] && (dom.style.display = "none");
  90. break;
  91. }
  92. }
  93. // 挂载之前调用
  94. recycles.beforeMount();
  95. _el.appendChild(container);
  96. // 挂载之后调用
  97. recycles.mounted();
  98. }
  99. function update(vm, key, showPool) {
  100. var _data = vm.$data;
  101. for (const [dom, info] of showPool) {
  102. if (info.prop === key) {
  103. switch (info.type) {
  104. case "show":
  105. !_data[key] ? (dom.style.display = "none") : dom.removeAttribute("style");
  106. break;
  107. case "if":
  108. !_data[key]
  109. ? dom.parentNode.replaceChild(info.comment, dom)
  110. : info.comment.parentNode.replaceChild(dom, info.comment);
  111. break;
  112. }
  113. }
  114. }
  115. }
  116. return Vue;
  117. })();
  118. export default VueTest;

image.png
这些生命周期方法的this都指向当前实例!!!

最后,源码地址献上:
https://github.com/xiechen1201/JSPlusPlus/blob/main/%E8%85%BE%E8%AE%AF%E8%AF%BE%E5%A0%82/Vue%20%E6%9C%AC%E5%B0%8A02/05-If%26Show/main.js