前端Vue之响应式那些事

目录

1.数据响应式

2.数据变化反应到视图

3.视图变化反映到数据


1.数据响应式

1.1响应式是什么

    数据发生变化时,立刻做出反应并完成一些想做的事。例如数据交互,网络请求。

1.2如何实现

    两种方案:在vue2.x中,通过Object.defineProperty对对象属性进行拦截(数据劫持)

                      在vue3.x中,通过Proxy对对象整体代理

1.3实现拦截

   无论用哪种方式,道理都是相通的。

let data = {
    name:'老师'
}

  object.defineproperty对象拦截

Object.defineProperty(data,'name',{
    // 访问name属性就会执行此方法 返回值就是获取到的值
    get(){
       console.log('name属性被获取了')
       return '老师'
    },
    // 设置新值就会执行此方法 newVal就是设置的新值
    set(newVal){
       console.log('name属性被设置新值了')
       console.log(newVal)
    }
})

若运行实例,会发现get方法中返回值始终是老师,那么如何取得最新值?就是创建一个中间变量,实现get/set之间的联动。建议验证,体会一下。

let _name = '老师'
Object.defineProperty(data,'name',{
    // 访问name属性就会执行此方法 返回值就是获取到的值
    get(){
       return _name
    },
    // 设置新值就会执行此方法 newVal就是设置的新值
    set(newVal){
       _name = newVal
    }
})

1.4优化

若是对象中所有属性都变成响应式,怎么办?利用闭包的特性,自己看吧。

let data = {
    name: '老师',
    age: 26,
    height:175
}

// 遍历每一个属性
Object.keys(data).forEach((key)=>{
    // key 属性名
    // data[key] 属性值
    // data 原对象
    defineReactive(data,key,data[key])
})
// 响应式转化方法
function defineReactive(data,key,value){
    Object.defineProperty(data,key,{
        get(){
           return value
        },
        set(newVal){
          value = newVal 
        }
    })
}

所谓响应式其实就是拦截对象属性的访问和设置,插入一些自己想做的事。

2.数据变化反应到视图

 数据变化后,操作视图。还是离不开dom操作。分两种,自行体会。

命令式操作视图。

<div id="app">
  <p></p>
</div>
<script>
   let data = {
        name: '老师',
        age: 26,
        height:175
    }
    // 遍历每一个属性
    Object.keys(data).forEach((key)=>{
        // key 属性名
        // data[key] 属性值
        // data 原对象
        defineReactive(data,key,data[key])
    })
    function defineReactive(data,key,value){
        Object.defineProperty(data,key,{
            get(){
               return value
            },
            set(newVal){
              value = newVal 
              // 数据发生变化,操作dom进行更新
              document.querySelector('#app p').innerHTML = data.name
            }
        })
    }
  // 首次渲染
  document.querySelector('#app p').innerHTML = data.name
</script>

声明式操作视图。

<div id="app">
  <p v-text="name"></p>
</div>
<script>
  let data = {
    name: '老师',
    age: 26,
    height: 175
  }
  // 遍历每一个属性
  Object.keys(data).forEach((key) => {
    // key 属性名
    // data[key] 属性值
    // data 原对象
    defineReactive(data, key, data[key])
  })
  function defineReactive(data, key, value) {
    Object.defineProperty(data, key, {
      get() {
        return value
      },
      set(newVal) {
        value = newVal
        // 数据发生变化,操作dom进行更新
        compile()
      }
    })
  }
  // 获取并操作dom
  function compile() {
    let app = document.getElementById('app')
    // 1.拿到app下所有的子元素
    const nodes = app.childNodes   //  [text, input, text]
    //2.遍历所有的子元素
    nodes.forEach(node => {
      // nodeType为1为元素节点
      if (node.nodeType === 1) {
        const attrs = node.attributes
        // 遍历所有的attrubites找到 v-model
        Array.from(attrs).forEach(attr => {
          const dirName = attr.nodeName
          const dataProp = attr.nodeValue
          if (dirName === 'v-text') {
            node.innerText = data[dataProp]
          }
        })
      }
    })
  }
  // 首次渲染
  compile()
</script>

3.视图变化反映到数据

将视图变化通过事件监听实现反向修改。自行体会。

// 编译函数
  function compile() {
    let app = document.getElementById('app')
    // 1.拿到app下所有的子元素
    const nodes = app.childNodes   //  [text, input, text]
    //2.遍历所有的子元素
    nodes.forEach(node => {
      // nodeType为1为元素节点
      if (node.nodeType === 1) {
        const attrs = node.attributes
        // 遍历所有的attrubites找到 v-model
        Array.from(attrs).forEach(attr => {
          const dirName = attr.nodeName
          const dataProp = attr.nodeValue
          if (dirName === 'v-model') {
            node.value = data[dataProp]
            // 视图变化反应到数据 无非是事件监听反向修改
            node.addEventListener('input', (e) => {
              data[dataProp] = e.target.value
            })
          }
        })
      }
    })
  }

到此,响应式结束。但还有一个问题就是无法做到精准更新,进一步优化则是发布订阅模式。(无论在前端还是后端,都是经典。)

<div id="app">
  <p v-text="name"></p>
  <p v-text="age"></p>
  <p v-text="name"></p>
</div>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值