Featured image of post 自己写一个小小vue - getter与setter

自己写一个小小vue - getter与setter

Javascript中getter与setter的使用

我相信各位前端大佬和小佬们都听过这句话:

vue2使用getter与setter来控制数据,vue3则直接使用proxy。

肯定有很多人像我一样,只管爽用vue就行,我管你什么getter、proxy的。不过这个东西在实际开发中是非常实用的,可以实现一些非常优雅的封装。不过首先,让我们先了解一下这俩东西。

getter与setter

这俩东西就相当于一个监听器,当某个数据被修改或者被读取时,你设置的getter和setter就会运行。

例如我现在定义了一个box(盒子),里边的content(内容)是一个‘banana🍌’

1
2
3
let box = {
  content: 'banana🍌'
}

我们试试监听这个盒子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let box = {
  content: 'banana🍌',
  // 定义getter非常简单,就是一个函数,函数名就是需要被监听的变量
  get boxContent() {
    alert('有人查看了盒子里的东西,是个'+this.content)
  },
  // 定义setter也是一样
  set boxContent(newContent) {
    this.content = newContent
    alert('有人换掉了盒子里的东西,换成了'+newContent)
  }
}

这里有个需要注意的点就是getter和setter的名字不能和原变量相同,不过在firefox浏览器中却是可以这么写的,不过鉴于兼容性原因我们不建议这么写

这时候一旦有人尝试读取和修改boxContent,就会触发getter和setter。

1
2
3
4
5
//如果盒子里有东西
if(box.boxContent){
  //就把它换成苹果
  box.boxContent = 'apple🍎'
}

第一行的if读取了box.boxContent,触发getter,然后弹出提示。 第一行的if读取了box.boxContent,触发getter,然后弹出提示。 紧接我们修改了box.boxContent,触发setter,然后弹出提示。 紧接着第二行代码修改了box.boxContent,触发setter,然后弹出提示。

好了,你已经学会了getter和setter了,让我们来实现一个vue把。

实现一个小小vue

来个经典案例,小黑记事本:

样式布局我不多赘述,都挺简单的。具体代码可以上码上掘金或者来github查看

说是实现小小vue,实际上只是通过getter和setter来尝试实现v-model的功能,毕竟咱也不是尤雨溪,咱就来简单点的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="style.css">
    <title>Demo</title>
  </head>
  <body>
    <div class="outter">
      <div class="title">Demo</div>
      <div class="list">
        <div class="item">
          <span>2333</span>
          <button class="delete">Delete</button>
        </div>
      </div>
      <div class="add">
        <!-- 我们设置一个bind属性,它就相当于v-model,绑定到input变量 -->
        <input bind="input"/>
        <button onclick="add()" class="add-btn">Add</button>
      </div>
      <!-- 这里也来一个bind,也绑定到input变量 -->
      <div>input的值:<span bind="input"></span></div>
    </div>
  </body>
  <script src="index.js"></script>
</html>

js部分如下,我们只关注前面部分

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 我们先定义好数据
let data = {
  list:[],
  input:''
}

// 实现绑定
// 此处获得所有拥有bind属性的元素
let binds = document.querySelectorAll('[bind]');
// 遍历这些元素
binds.forEach((item) => {
  // 绑定input事件
  item.oninput = (e) => {
    // 当input时更新其值
    data[item.getAttribute('bind')] = e.target.value
  }
})

// 实现数据监听
let myData = {}
// 因为getter和setter不能和原变量同名,咱把原变量包裹起来一下
myData.data = data
// 我们遍历data里的每个对象
for(let item in data){
  //为每个item都定义一个getter和setter
  Object.defineProperty(myData, item, {
    // 这里通过defineProperty来设置getter和setter
    get(){
      return myData.data[item]
    },
    set(val){
      myData.data[item] = val
      // 当值更新时,我们遍历所有有bind属性的元素
      binds.forEach((bindsItem) => {
        // 如果这个元素有bind属性,并且属性名等于item,就修改它的值
        if(bindsItem.getAttribute('bind') === item){
          bindsItem.value = val
          bindsItem.innerHTML = val
        }
      })
      // 当 list 更新时,重新渲染列表
      if(item === 'list') {
        renderList()
      }
    }
  })
}
// 把myData赋值给data,这样我们的data就变成一个代理对象了
data = myData

//之后的代码感兴趣的可以看看

// 渲染列表函数
function renderList() {
  const listContainer = document.querySelector('.list')
  listContainer.innerHTML = data.list.map((item, index) => `
    <div class="item">
      <span>${item}</span>
      <button class="delete" onclick="deleteItem(${index})">Delete</button>
    </div>
  `).join('')
}

// 添加项目函数
function add() {
  if(data.input.trim()) {
    data.list = [...data.list, data.input]
    data.input = '' // 清空输入框
  }
}

// 删除项目函数
function deleteItem(index) {
  data.list = data.list.filter((_, i) => i !== index)
}

看看效果

这样我们就用原生js实现了一个v-modle,是不是成就感满满呢 最终效果

相关知识

getter
setter
defineProperty

Licensed under CC BY-NC-SA 4.0
最后更新于 Mar 13, 2025 00:00 UTC
使用 Hugo 构建
主题 StackJimmy 设计