vue核心原理实现

vue示例

通过一个小案例, 对比vue开发和原生开发的区别

  1. vue的核心就是数据响应式, 数据改变视图跟随改变

  2. 通过数据驱动视图, 我们就不用直接操作DOM元素了, 这就是vue开发与原生开发的区别

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" /> <link rel="stylesheet" href="./css/index.css" /> </head> <body>

    {{title}}

    {{item.name}}
    {{item.choose}}
    {{count}}
    复制代码
     <script src="./js/data.js"></script>
     <script src="./js/vue.js"></script>
     <script src="./js/index.js"></script>
    </body> </html>

    var goods = [
    {
    name: 'iPhone 12',
    choose: 0,
    },
    {
    name: '荣耀X30',
    choose: 1,
    },
    {
    name: 'Redmi Note 11',
    choose: 3,
    },
    {
    name: 'OPPO K9x',
    choose: 0,
    },
    {
    name: '华为Nova10pro',
    choose: 1,
    },
    ];

    var vm = new Vue({
    // 配置对象
    el: '.container',
    data: {
    // 界面数据
    title: '淘东手机',
    goods: goods,
    },
    // 计算属性
    computed: {
    count: function () {
    var sum = 0;
    for (var i = 0; i < this.goods.length; i++) {
    sum += this.goods[i].choose;
    }
    return sum;
    },
    },
    });

vue原理

准备基础代码

复制代码
.card {
  width: 300px;
  border: 2px solid rgb(74, 125, 142);
  border-radius: 10px;
  font-size: 2em;
  padding: 0 20px;
  margin: 0 auto;
  background: lightblue;
  color: #333;
}

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <link rel="stylesheet" href="./index.css" />
</head>

<body>
  <div class="card">
    <p id="firstName"></p>
    <p id="lastName"></p>
    <p id="age"></p>
  </div>
  // <input type="text" oninput="user.name = this.value" />
  // <input type="date" onchange="user.birth = this.value" />
  <script src="./index.js"></script>
</body>

</html>

var user = {
  name: '袁进',
  birth: '2002-5-7',
};

// 显示姓氏
function showFirstName() {
  document.querySelector('#firstName').textContent = '姓:' + user.name[0];
}

// 显示名字
function showLastName() {
  document.querySelector('#lastName').textContent = '名:' + user.name.slice(1);
}

// 显示年龄
function showAge() {
  var birthday = new Date(user.birth);
  var today = new Date();
  today.setHours(0), today.setMinutes(0), today.setMilliseconds(0);
  thisYearBirthday = new Date(
    today.getFullYear(),
    birthday.getMonth(),
    birthday.getDate()
  );
  var age = today.getFullYear() - birthday.getFullYear();
  if (today.getTime() < thisYearBirthday.getTime()) {
    age--;
  }
  document.querySelector('#age').textContent = '年龄:' + age;
}


showFirstName()
showLastName()
showAge()

改造一下程序, 初步实现 "数据驱动视图"

复制代码
var user = {
  name: '袁进',
  birth: '2002-5-7',
};

// 显示姓氏
function showFirstName() {
  document.querySelector('#firstName').textContent = '姓:' + user.name[0];
}

// 显示名字
function showLastName() {
  document.querySelector('#lastName').textContent = '名:' + user.name.slice(1);
}

// 显示年龄
function showAge() {
  var birthday = new Date(user.birth);
  var today = new Date();
  today.setHours(0), today.setMinutes(0), today.setMilliseconds(0);
  thisYearBirthday = new Date(
    today.getFullYear(),
    birthday.getMonth(),
    birthday.getDate()
  );
  var age = today.getFullYear() - birthday.getFullYear();
  if (today.getTime() < thisYearBirthday.getTime()) {
    age--;
  }
  document.querySelector('#age').textContent = '年龄:' + age;
}


showFirstName()
showLastName()
showAge()


/**
 * 当我们改变数据时, 如何让视图更新呢?
 * --重新调用一下方法呗! 这不很简单嘛 
 */
user.name = '张三彭'
showFirstName()
showLastName()

user.birth = '1999-1-1'
showAge()


/**
 * 可以实现自动调用方法吗? 
 * 当我改变了数据, 就自动调用方法更新视图.岂不美哉?
 * --我来试试吧
 */
user.name = '张三彭'
// 如何自动调用依赖该属性的函数?
// showFirstName()
// showLastName()

/**
 * 思路分析
 * 1.如何感知属性的读取和设置呢? -- 属性访问器
 * 2.自动调用依赖属性的方法
 */
let internalName = user.name;
Object.defineProperty(user, 'name', {
  get: function () {
    console.log('有人在读取 name 属性');
    return internalName;
  },
  set: function (val) {
    internalName = val
    console.log('有人修改 name 属性, 值为' + val);
    showFirstName()
    showLastName()
  }
})
user.name = '李四康'
  1. 现在, 无论是通过编码还是通过控制台, 只要修改了user对象的name属性, 就可以自动调用该属性的方法, 从而自动更新视图

上面的代码还只是玩具, 如果需要通用, 就要封装

复制代码
/**
 * 观察对象的所有属性
 */
// 问题1: 如何调用依赖该属性的函数???
// --在get函数中依赖收集
// --在set函数中派发更新
function observe(obj) {
  for (const key in obj) {
    let internalValue = obj[key]
    let funcs = [];
    Object.defineProperty(obj, key, {
      get: function () {
        // 依赖收集:  记录是哪些函数在用我
        if (window.__func && !funcs.includes(window.__func)) {
          funcs.push(window.__func);
        }
        return internalValue
      },
      set: function (val) {
        internalValue = val
        // 派发更新: 运行依赖我的函数
        for (let index = 0; index < funcs.length; index++) {
          funcs[index]()
        }
      }
    })
  }
}


// 问题2: 依赖怎么收集呢?
// --封装一个代理方法, 方法执行前记录方法
// --方法不要直接调用, 使用代理方法间接调用
// autorun <- showFristName
function autorun(fn) {
  window.__func = fn
  fn()
  window.__func = null
}

var user = {
  name: '袁进',
  birth: '2002-5-7',
};

// 显示姓氏
function showFirstName() {
  document.querySelector('#firstName').textContent = '姓:' + user.name[0];
}

// 显示名字
function showLastName() {
  document.querySelector('#lastName').textContent = '名:' + user.name.slice(1);
}

// 显示年龄
function showAge() {
  var birthday = new Date(user.birth);
  var today = new Date();
  today.setHours(0), today.setMinutes(0), today.setMilliseconds(0);
  thisYearBirthday = new Date(
    today.getFullYear(),
    birthday.getMonth(),
    birthday.getDate()
  );
  var age = today.getFullYear() - birthday.getFullYear();
  if (today.getTime() < thisYearBirthday.getTime()) {
    age--;
  }
  document.querySelector('#age').textContent = '年龄:' + age;
}

/**
 * 封装代码
 */
observe(user) // 观察对象

autorun(showFirstName)
autorun(showLastName)
autorun(showAge)

<!DOCTYPE html>
<html lang="en">

<head>
  ... ... 
</head>

<body>
  <div class="card">
    <p id="firstName"></p>
    <p id="lastName"></p>
    <p id="age"></p>
  </div>
  <input type="text" oninput="user.name = this.value" />
  <input type="date" onchange="user.birth = this.value" />
  <script src="./iuv.js"></script>
  <script src="./index.js"></script>
</body>

</html>
  1. 整个代码的健壮性还是不够的, 但是代码的思想跟vue源码是一致的
  2. 数据响应式的本质是数据变化后自动调用依赖数据的方法
相关推荐
每天吃饭的羊19 小时前
JSZip的使用
开发语言·javascript
EnCi Zheng19 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen19 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技19 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人20 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实20 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha20 小时前
三目运算符
linux·服务器·前端
晓晨的博客20 小时前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect20 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript
donecoding20 小时前
别再让 pnpm 跟着 nvm 跑了!独立安装终极指南
前端·node.js·前端工程化