• 【js&vue】联合gtp仿写一个简单的vue框架,以此深度学习JavaScript


    用 gtp 学习 Vue 生命周期的原理

    lifecycle.js

    1. function Vue(options) {
    2. // 将选项保存到实例的 $options 属性中
    3. this.$options = options;
    4. // 若存在 beforeCreate 钩子函数,则调用之
    5. if (typeof options.beforeCreate === 'function') {
    6. options.beforeCreate.call(this);
    7. }
    8. // 判断并保存 data 数据对象
    9. this._data = typeof options.data === 'function' ? options.data() : options.data;
    10. // 将 data 对象中的属性代理到 Vue 实例上
    11. this._proxyData();
    12. // 若存在 created 钩子函数,则调用之
    13. if (typeof options.created === 'function') {
    14. options.created.call(this);
    15. }
    16. // 执行挂载操作
    17. this.$mount(options.el);
    18. }
    19. Vue.prototype.$mount = function(el) {
    20. // 将目标元素保存到实例的 $el 属性中
    21. this.$el = document.querySelector(el);
    22. // 若存在 beforeMount 钩子函数,则调用之
    23. if (typeof this.$options.beforeMount === 'function') {
    24. this.$options.beforeMount.call(this);
    25. }
    26. // 调用 render 方法渲染模板
    27. this.render();
    28. // 若存在 mounted 钩子函数,则调用之
    29. if (typeof this.$options.mounted === 'function') {
    30. this.$options.mounted.call(this);
    31. }
    32. };
    33. Vue.prototype._proxyData = function() {
    34. var self = this;
    35. // 遍历 data 对象的属性,并将其代理到 Vue 实例上
    36. Object.keys(this._data).forEach(function(key) {
    37. Object.defineProperty(self, key, {
    38. get: function() {
    39. return self._data[key];
    40. },
    41. set: function(newValue) {
    42. self._data[key] = newValue;
    43. // 若存在 beforeUpdate 钩子函数,则调用之
    44. if (typeof self.$options.beforeUpdate === 'function') {
    45. self.$options.beforeUpdate.call(self);
    46. }
    47. // 重新渲染模板
    48. self.render();
    49. // 若存在 updated 钩子函数,则调用之
    50. if (typeof self.$options.updated === 'function') {
    51. self.$options.updated.call(self);
    52. }
    53. }
    54. });
    55. });
    56. };
    57. Vue.prototype.render = function() {
    58. // 调用 render 函数生成模板字符串,并更新目标元素的内容
    59. if (typeof this.$options.render === 'function') {
    60. this.$el.innerHTML = this.$options.render.call(this);
    61. }
    62. };
    63. // 使用示例
    64. var app = new Vue({
    65. el: '#app', // Vue 实例挂载的目标元素
    66. data: { // 数据对象
    67. message: 'Hello, Vue!' // 文本数据
    68. },
    69. beforeCreate: function() {
    70. console.log('beforeCreate hook');
    71. },
    72. created: function() {
    73. console.log('created hook');
    74. },
    75. beforeMount: function() {
    76. console.log('beforeMount hook');
    77. },
    78. mounted: function() {
    79. console.log('mounted hook');
    80. },
    81. beforeUpdate: function() {
    82. console.log('beforeUpdate hook');
    83. },
    84. updated: function() {
    85. console.log('updated hook');
    86. },
    87. render: function() {
    88. return '

      ' + this.message + '

      '
      ;
    89. }
    90. });

    注解:
    this.$options.beforeMount.call(this);与 this.$options.beforeMount();有什么区别:

    • call(this) 的作用是将当前对象(this)作为参数传递给 beforeMount 方法,使得在 beforeMount 方法内部可以通过 this 访问到当前对象的上下文
    • 直接调用了 beforeMount 方法,没有指定上下文 

    index.html

    1. 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. <title>Vuetitle>
    7. head>
    8. <body>
    9. <div id="app">div>
    10. <script src="./lifecycle.js">script>
    11. body>
    12. html>

    在浏览器查看渲染结果,并在控制台查看日志输出

    另外,我们可以在控制输入 app.message = 'ChatGPT' 来验证数据绑定以及页面更新机制

    效果图:

    用 gtp 学习 Vue 模板语法和指令的原理

    index.html

    1. 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. <title>Documenttitle>
    7. head>
    8. <body>
    9. <div id="app">div>
    10. <script>
    11. // 定义 Vue 类
    12. function Vue(options) {
    13. // 保存选项为实例的属性
    14. this.$options = options;
    15. // 判断传入的 data 是函数还是对象,并保存到 _data 属性上
    16. this._data = typeof options.data === 'function' ? options.data() : options.data;
    17. // 调用编译模板的方法
    18. this._compileTemplate();
    19. }
    20. // 原型方法:编译模板
    21. Vue.prototype._compileTemplate = function () {
    22. var self = this;
    23. // 获取模板字符串
    24. var template = this.$options.template || '';
    25. // 定义一个函数用于对表达式进行求值
    26. var evalExpression = function (expression) {
    27. // 使用 with 关键字将 data 对象的属性添加到作用域中,并求解表达式
    28. with (self._data) return eval(expression);
    29. }
    30. // 将模板中的双括号表达式替换成 data 对应属性的值
    31. var compiledTemplate = template.replace(/\{\{(.*?)\}\}/g, function (match, expression) {
    32. var value = evalExpression(expression);
    33. return value !== undefined ? value : '';
    34. });
    35. // 获取目标元素,并将编译后的模板插入其中
    36. var element = document.querySelector(this.$options.el);
    37. element.innerHTML = compiledTemplate.trim();
    38. // 处理带有 v-model 属性的元素,实现数据的双向绑定
    39. element.querySelectorAll('[v-model]').forEach(function (element) {
    40. var value = element.getAttribute('v-model');
    41. element.value = self._data[value];
    42. element.addEventListener('input', function (event) {
    43. self._data[value] = event.target.value;
    44. });
    45. });
    46. // 处理带有 v-text 属性的元素,实现数据的单向绑定
    47. element.querySelectorAll('[v-text]').forEach(function (element) {
    48. var value = element.getAttribute('v-text');
    49. element.textContent = self._data[value];
    50. // 使用 defineProperty 方法定义 data 对象对应属性的 getter 和 setter
    51. Object.defineProperty(self._data, value, {
    52. get: function () {
    53. return this[value]
    54. },
    55. set: function (newValue) {
    56. element.textContent = newValue;
    57. }
    58. });
    59. });
    60. };
    61. // 使用示例
    62. var app = new Vue({
    63. el: '#app', // Vue 实例挂载的目标元素
    64. data: { // 数据对象
    65. message: 'Hello, Vue!', // 文本数据
    66. inputValue: 'ChatGPT' // 输入数据
    67. },
    68. template: // 模板字符串
    69. `
    70. {{ message }}

  • `
  • });
  • script>
  • body>
  • html>
  • 效果图:

    注解:

    • js中with 语句的作用

    with语句的作用是简化代码,使得可以在该作用域内直接访问对象的属性和方法,而无需重复使用对象名字的前缀

    1. var person = {
    2. name: 'Alice',
    3. age: 25,
    4. greet: function() {
    5. console.log('Hello, ' + this.name + '!');
    6. }
    7. };
    8. with (person) {
    9. console.log(name); // 直接访问属性,输出: Alice
    10. console.log(age); // 直接访问属性,输出: 25
    11. greet(); // 直接调用方法,输出: Hello, Alice!
    12. }
    • template.replace(/\{\{(.*?)\}\}/g, function (match, expression) { ... })

    是一个正则表达式替换的方法,用于处理模板中的双花括号表达式 {{expression}},回调函数接收两个参数:

      match:匹配到的整个字符串,即 {{expression}}

      expression:匹配到的表达式,即 expression

     用 gtp 学习 Vue 数据监听和计算属性的原理

    index.html

    1. 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. <title>Documenttitle>
    7. head>
    8. <body>
    9. <div id="app">div>
    10. <script>
    11. // 定义 Vue 类
    12. function Vue(options) {
    13. // 将 data、computed 和 watch 选项保存到实例中
    14. this._data = options.data;
    15. this._computed = options.computed;
    16. this._watch = options.watch;
    17. // 数据代理
    18. this._proxyData();
    19. // 创建计算属性
    20. this._createComputed();
    21. // 创建监听器
    22. this._createWatchers();
    23. }
    24. // 数据代理,将 data 中的属性代理到 Vue 实例上,实现直接访问和修改数据
    25. Vue.prototype._proxyData = function () {
    26. var self = this;
    27. Object.keys(this._data).forEach(function (key) {
    28. Object.defineProperty(self, key, {
    29. get: function () {
    30. return self._data[key];
    31. },
    32. set: function (newValue) {
    33. self._data[key] = newValue;
    34. }
    35. });
    36. });
    37. };
    38. // 创建计算属性
    39. Vue.prototype._createComputed = function () {
    40. var self = this;
    41. var computed = this._computed || {};
    42. Object.keys(computed).forEach(function (key) {
    43. Object.defineProperty(self, key, {
    44. get: function () {
    45. return computed[key].call(self);
    46. }
    47. });
    48. });
    49. };
    50. // 创建监听器
    51. Vue.prototype._createWatchers = function () {
    52. var self = this;
    53. var watch = this._watch || {};
    54. Object.keys(watch).forEach(function (key) {
    55. var callback = watch[key];
    56. var value = self._data[key];
    57. Object.defineProperty(self._data, key, {
    58. get: function () {
    59. return value;
    60. },
    61. set: function (newValue) {
    62. value = newValue;
    63. callback.call(self, newValue);
    64. }
    65. });
    66. });
    67. };
    68. // 使用示例
    69. // 创建一个 Vue 实例
    70. var app = new Vue({
    71. // 初始化数据
    72. data: {
    73. message: 'Hello, Vue!',
    74. firstName: 'John',
    75. lastName: 'Doe'
    76. },
    77. // 定义计算属性
    78. computed: {
    79. fullName: function () {
    80. return this.firstName + ' ' + this.lastName;
    81. }
    82. },
    83. // 定义监听器
    84. watch: {
    85. message: function (newValue) {
    86. console.log('Message changed:', newValue);
    87. }
    88. }
    89. });
    90. console.log(app.message); // 输出: Hello, Vue!
    91. app.message = 'Hello, Vue.js!'; // 输出: Message changed: Hello, Vue.js!
    92. console.log(app.message); // 输出: Hello, Vue.js!
    93. console.log(app.fullName); // 输出: John Doe
    94. app.message = 'New message'; // 输出: Message changed: New message
    95. script>
    96. body>
    97. html>

    效果图:

    用 gtp 学习 Vue 事件处理和方法的原理

    1. 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. <title>Documenttitle>
    7. head>
    8. <body>
    9. <div id="app">div>
    10. <script>
    11. // 定义事件总线类
    12. function EventBus() {
    13. this._events = {};
    14. }
    15. // 事件总线订阅方法,用于注册事件回调函数
    16. EventBus.prototype.on = function (eventName, callback) {
    17. if (!this._events[eventName]) {
    18. this._events[eventName] = [];
    19. }
    20. this._events[eventName].push(callback);
    21. };
    22. // 事件总线触发方法,用于触发事件并调用相应的回调函数
    23. EventBus.prototype.emit = function (eventName, payload) {
    24. if (this._events[eventName]) {
    25. this._events[eventName].forEach(function (callback) {
    26. callback(payload);
    27. });
    28. }
    29. };
    30. // 定义 Vue 类
    31. function Vue(options) {
    32. // 初始化数据
    33. this._data = typeof options.data === 'function' ? options.data() : options.data;
    34. // 记录方法
    35. this._methods = options.methods;
    36. // 创建事件总线实例
    37. this._eventBus = new EventBus();
    38. // 对数据进行代理,使得可以直接通过 this.xxx 访问和修改数据
    39. this._proxyData();
    40. // 对方法进行代理,使得可以通过 this.xxx 调用方法
    41. this._proxyMethods();
    42. }
    43. // 数据代理,将 data 中的属性添加到 Vue 实例中,实现直接访问和修改数据
    44. Vue.prototype._proxyData = function () {
    45. var self = this;
    46. Object.keys(this._data).forEach(function (key) {
    47. Object.defineProperty(self, key, {
    48. get: function () {
    49. return self._data[key];
    50. },
    51. set: function (newValue) {
    52. self._data[key] = newValue;
    53. }
    54. });
    55. });
    56. };
    57. // 方法代理,将 methods 中的方法添加到 Vue 实例中,实现通过 this.xxx 调用方法
    58. Vue.prototype._proxyMethods = function () {
    59. var self = this;
    60. var methods = this._methods;
    61. if (methods) {
    62. Object.keys(methods).forEach(function (key) {
    63. self[key] = methods[key].bind(self);
    64. });
    65. }
    66. };
    67. // 发布事件,触发相应的事件回调函数
    68. Vue.prototype.$emit = function (eventName, payload) {
    69. this._eventBus.emit(eventName, payload);
    70. };
    71. // 订阅事件,注册事件回调函数
    72. Vue.prototype.$on = function (eventName, callback) {
    73. this._eventBus.on(eventName, callback);
    74. };
    75. // 创建一个 Vue 实例
    76. var app = new Vue({
    77. // 初始化数据
    78. data: {
    79. message: 'Hello, Vue!'
    80. },
    81. // 定义方法
    82. methods: {
    83. greet: function () {
    84. this.$emit('greet', this.message);
    85. },
    86. updateMessage: function (newMessage) {
    87. this.message = newMessage;
    88. }
    89. },
    90. });
    91. // 注册 greet 事件的回调函数
    92. app.$on('greet', function (message) {
    93. console.log('Greet:', message);
    94. });
    95. // 调用 greet 方法,触发 greet 事件
    96. app.greet(); // 输出: Greet: Hello, Vue!
    97. // 调用 updateMessage 方法,修改 message 的值
    98. app.updateMessage('Hello, World!');
    99. // 再次调用 greet 方法,触发 greet 事件,并输出修改后的 message
    100. app.greet(); // 输出: Greet: Hello, World!
    101. script>
    102. body>
    103. html>

    用 gtp 学习 Vue 插槽(slot)的原理

    1. 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. <title>Documenttitle>
    7. head>
    8. <body>
    9. <div id="app">div>
    10. <script>
    11. // 定义 Vue 构造函数
    12. function Vue(options) {
    13. this.$options = options;
    14. this._data = typeof options.data === 'function' ? options.data() : options.data;
    15. this._components = options.components || {};
    16. // 代理 data 属性到 Vue 实例上
    17. this._proxyData();
    18. // 编译模板
    19. this._compileTemplate();
    20. // 代理组件
    21. this._proxyComponents();
    22. }
    23. // 将 data 对象的属性代理到 Vue 实例上
    24. Vue.prototype._proxyData = function () {
    25. var self = this;
    26. Object.keys(this._data).forEach(function (key) {
    27. Object.defineProperty(self, key, {
    28. get: function () {
    29. return self._data[key];
    30. },
    31. set: function (newValue) {
    32. self._data[key] = newValue;
    33. }
    34. });
    35. });
    36. };
    37. // 编译模板
    38. Vue.prototype._compileTemplate = function () {
    39. var self = this;
    40. var el = this.$options.el;
    41. var template = this.$options.template || '';
    42. // 使用 evalExpression 函数执行模板中的表达式
    43. var evalExpression = function (expression) {
    44. with (self) return eval(expression);
    45. }
    46. // 替换模板中的双花括号表达式为对应的数据值
    47. var compiledTemplate = template.replace(/\{\{(.*?)\}\}/g, function (match, expression) {
    48. var value = evalExpression(expression);
    49. return value !== undefined ? value : '';
    50. });
    51. // 将编译后的模板插入目标元素中
    52. var element = el ? document.querySelector(el) : document.createElement('div');
    53. element.innerHTML = compiledTemplate.trim();
    54. this.$el = el ? element : element.childNodes[0];
    55. };
    56. // 代理组件
    57. Vue.prototype._proxyComponents = function () {
    58. var self = this;
    59. var components = this._components;
    60. // 遍历组件对象,创建组件实例并进行代理
    61. Object.keys(components).forEach(function (componentName) {
    62. var component = new Vue(components[componentName]);
    63. // 查询所有组件标签,并将子组件的内容替换到对应的插槽中
    64. self.$el.querySelectorAll(componentName).forEach(function (element) {
    65. component.$el.querySelectorAll('slot').forEach(function (slot) {
    66. slot.innerHTML = element.innerHTML;
    67. });
    68. element.innerHTML = component.$el.outerHTML;
    69. });
    70. });
    71. };
    72. // 使用示例
    73. var HelloComponent = {
    74. data: function () {
    75. return {
    76. name: 'John'
    77. };
    78. },
    79. template: `
    80. {{ name }}

  • `
  • };
  • // 创建 Vue 实例
  • var app = new Vue({
  • el: '#app',
  • data: {
  • message: 'Hello, Vue!'
  • },
  • components: {
  • HelloComponent
  • },
  • template: `
  • {{ message }}

  • `
  • });
  • script>
  • body>
  • html>
  • 相关阅读:
    遥测、遥信、遥控、定值?IEC61850?-----极简记录
    【考研线代】六. 二次型
    MySQL主从同步
    SpringCloud01
    23种设计模式(四)单例模式(阁瑞钛伦特软件-九耶实训)
    Pytorch 实战 LESSON 8 单层回归神经网络 & Tensor新手避坑指南
    【PAT甲级 - C++题解】1078 Hashing
    浅谈postman设置token依赖步骤
    Mysql的基本命令
    [附源码]Python计算机毕业设计Django高校体育场馆管理系统
  • 原文地址:https://blog.csdn.net/weixin_52479803/article/details/132290331