一起来看看Vue的核心原理剖析


Posted in Vue.js onMarch 24, 2022

前言:

  • 相信大家阅读过很多关于Vue2的文章,我也阅读过很多,但是大部分文章介绍的都是如何在项目中进行应用,技术点如果使用,功能如何实现;
  • 今天小编为大家带来这篇Vue2的核心原理剖析就是为大家介绍我们常用的Vue2他是如何实现的核心内容,我们简单代码的背后究竟他做了哪些,让大家能够 知其然,知其所以然

学习目标:

  • 了解Object.defineProperty原理
  • 了解set、get关联使用
  • 了解数据反应到识图的过程
  • 了解视图更换如何影响数据
  • 掌握MVVM

Object.defineProperty

<script>
    // 1. 字面量定义
    let data = {
      name: 'aa'
    }
    data.name = 'bb' // 这种情况下我们并不能知道name属性发生了变化

    // 2. Object.defineProperty  
    let data1 = {}
    Object.defineProperty(data1, 'name', {
      // 当我们访问data1的name属性的时候自动调用的方法
      // 并且get函数的返回值就是你拿到的值
      get() {
        console.log('你访问了data1的name属性')
        return 'aa'
      },
      // 当我们设置修改name属性的时候自动调用的函数
      // 并且属性最新的值会被当成实参传入进来
      set(newValue) {
        console.log('你修改了data1的name属性最新的值为', newValue)
        // 这个位置 只要你修改了name属性就会得到执行
        // 所以如果你想要在name变化的时候 完成一些自己的事情
        // 都可以放到这里来执行
        // 1. ajax()
        // 2. 操作一块dom区域
      }
    })
    // 以上是js中对象定义的另外一种方案,可以在访问属性和设置属性的时候自动调用对应的函数
    // 访问属性:data.name  data['name']  
    // 设置属性:data.name = 'bb'  data['name'] = 'bb'
  </script>

 响应式的核心API   

get、set

<script>
    // let data = {
    //   name: 'aa'
    // }
    let data = {}
    let _name = 'aa'
    Object.defineProperty(data, 'name', {
      get() {
        console.log('你访问了data1的name属性')
        return _name
      },

      set(newValue) {
        console.log('你修改了data1的name属性最新的值为', newValue)
        _name = newValue
      }
    })
    // 问题产生的原因:get中直接返回了一个固定的值,并且set函数中新值拿到了但是没有做任何事情
    // 解决方案:通过声明一个中间变量,让get函数中return出去这个变量
    // 并且在set函数中把最新的值设置到这个中间变量身上,起到一个set和get操作的一个
    // 数据的效果
  </script>

数据反应到视图

数据的变化可以引起视图的变化(通过操作dom把数据放到对应的位置上去 如果数据变化之后就用数据最新的值再重新放一次)

方案一:命令式操作

1.document.querySelector(’#app’).innerText = data.name

2.set函数中重新执行一下document.querySelector(’#app’).innerText = data.name

方案二:声明式渲染

v-text指令的实现

<p v-text="name"></p>

核心逻辑:通过‘模板编译’找到标记了v-text的元素,然后把对应的数据通过操作domapi放上去

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

1.通过app根元素找到所有的子节点 (元素节点,文本节点…) -> dom.nodeChilds

2.通过节点类型筛选出元素节点 (p) -> nodeType 1元素节点 3文本节点

3.通过v-text找到需要设置的具体的节点 <p v-text></p>4.找到绑定了v-text标记的元素 拿到它身上所有的属性 id class v-text=“name”

5.通过v-text=“name” 拿到指令类型 ‘v-text’ 拿到需要绑定的数据的属性名 ‘name’

6.判断当前是v-text指令 然后通过操作domapi 把name属性对应的值放上去 node.innerText = data[name]

以上整个过程可以称作‘模板编译’

视图的变化反映到数据

input元素 v-model双向绑定
M -> V
V -> M

M -> V

1.通过app根元素找到所有的子节点 (元素节点,文本节点…) -> dom.nodeChilds

2.通过节点类型筛选出元素节点 (p) -> nodeType 1元素节点 3文本节点

3.通过v-text找到需要设置的具体的节点 <p v-text></p>4.找到绑定了v-text标记的元素 拿到它身上所有的属性 id class v-text=“name”

5.通过v-model=“name” 拿到指令类型 ‘v-model’ 拿到需要绑定的数据的属性名 ‘name’

6.判断当前是v-model指令 然后通过操作domapi 把name属性对应的值放上去node.value = data[name]

v-model和v-text除了指令类型不一致,使用的dom api不一致 其它的步骤是完全一致的

V -> M

本质:事件监听在回调函数中拿到input中输入的最新的值然后赋值给绑定的属性

node.addEventListener('input',(e)=>{
   data[name] = e.target.value
 })

以上总结:

1.数据的响应式

2.数据变化影响视图

3.视图变化影响数据

4.指令是如何实现的(常规实现逻辑)

优化工作:

1.通用的数据响应式处理

data(){
       return {
          name:'cp',
          age:28
      }
   }

基于现成的数据,然后都处理成响应式 

Object.keys(data) // 由所有的对象的key组成的数组
    Object.keys(data).forEach(key=>{
      // key 属性名
      // data[key]  属性值
      // data 原对象
      // 将所有的key都转成get和set的形式
      defineReactive(data,key,data[key])
    })
    function defineReactive(data,key,value){
      Oject.defineProperty(data, key, {
        get(){
          return value
        },
        set(newValue){
          value = newValue
        }
      })
    }

2.发布订阅模式

问题:

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

name发生变化之后 我需要做的事情是更新俩个p标签,而现在不管你更新了哪个数据,所有的标签都会被重新操作赋值,无法做到精准更新

解决问题的思路:

1.数据发生变化之后最关键的代码是什么?

node.innerText = data[name]

2.设计一个存储结构

每一个响应式数据可能被多个标签绑定 是一个‘一对多’的关系

{
        name: [()=>{ node(p1).innerText = data[name]},()=>{ node(p2).innerText = data[name]}...]
      }

发布订阅(自定义事件) 解决的问题就是 ‘1对多’的问题

实现简单的发布订阅模式:

浏览器的事件模型

dom.addEventLister(‘click’,()=>{})

只要调用click事件,所有绑定的回调函数都会执行 显然是一个1对多的关系

const Dep = {
      map:{},
      collect(eventName,fn){
        // 如果从来没有收集过当前事件就先初始化成数组
        if(!this.map[eventName]){
          this.map[eventName] = []
        }
        // 已经初始化好了就直接往里面push添加
        this.map[eventName].push(fn)
      },
      trigger(eventName){
        this.map[eventName].forEach(fn=>fn())
      }
    }

使用发布订阅模式优化现存问题

先前的写法 不管是哪个数据发生变化我们都是粗暴的执行一下compile函数即可

现在的写法 我们在compile函数初次执行的时候 完成更新函数的收集 然后在数据变化的时候

通过数据的key找到相对应的更新函数 依次执行 达到精准更新的效果

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注三水点靠木的更多内容!  

Vue.js 相关文章推荐
vue中音频wavesurfer.js的使用方法
Feb 20 Vue.js
解决vue页面刷新,数据丢失的问题
Nov 24 Vue.js
Vue如何实现验证码输入交互
Dec 07 Vue.js
Vue 组件注册全解析
Dec 17 Vue.js
Vue实现简易购物车页面
Dec 30 Vue.js
vue 中this.$set 动态绑定数据的案例讲解
Jan 29 Vue.js
如何管理Vue中的缓存页面
Feb 06 Vue.js
vue中axios封装使用的完整教程
Mar 03 Vue.js
vue项目实现分页效果
Mar 24 Vue.js
Element-ui Layout布局(Row和Col组件)的实现
Dec 06 Vue.js
vue3.0 数字翻牌组件的使用方法详解
Apr 20 Vue.js
Vue2项目中对百度地图的封装使用详解
Jun 16 Vue.js
深入讲解Vue中父子组件通信与事件触发
Mar 22 #Vue.js
关于Vue中的options选项
Mar 22 #Vue.js
vue+echarts实现多条折线图
vue使用echarts实现折线图
浅谈Vue的computed计算属性
VUE中的v-if与v-show区别介绍
Mar 13 #Vue.js
Vue.js中v-bind指令的用法介绍
Mar 13 #Vue.js
You might like
PHP 数组排序方法总结 推荐收藏
2010/06/30 PHP
PHP操作mysql函数详解,mysql和php交互函数
2011/05/19 PHP
基于php设计模式中单例模式的应用分析
2013/05/15 PHP
php读取3389的脚本
2014/05/06 PHP
PHP中使用hidef扩展代替define提高性能
2015/04/09 PHP
PHP互换两个变量值的方法(不用第三变量)
2016/11/14 PHP
js动态生成指定行数的表格
2013/07/11 Javascript
JQuery判断radio是否选中并获取选中值的示例代码
2014/10/17 Javascript
手机端转盘抽奖代码分享
2015/09/10 Javascript
图解js图片轮播效果
2015/12/20 Javascript
基于javascript实现样式清新图片轮播特效
2016/03/30 Javascript
探讨跨域请求资源的几种方式(总结)
2016/12/02 Javascript
javascript中闭包概念与用法深入理解
2016/12/15 Javascript
原生javascript上传图片带进度条【实例分享】
2017/04/06 Javascript
JS实现左边列表移到到右边列表功能
2018/03/28 Javascript
Vue项目中使用Vux的安装过程
2018/05/01 Javascript
基于jQuery使用Ajax动态执行模糊查询功能
2018/07/05 jQuery
JS 事件机制完整示例分析
2020/01/15 Javascript
浅谈javascript如何获取文件后缀名
2020/08/07 Javascript
ES6中的类(Class)示例详解
2020/12/09 Javascript
[01:33]PWL开团时刻DAY2-开雾与反开雾
2020/10/31 DOTA
python实现点对点聊天程序
2018/07/28 Python
用Python实现读写锁的示例代码
2018/11/05 Python
Python3的unicode编码转换成中文的问题及解决方案
2019/12/10 Python
Python计算公交发车时间的完整代码
2020/02/12 Python
TensorFlow实现模型断点训练,checkpoint模型载入方式
2020/05/26 Python
解决keras加入lambda层时shape的问题
2020/06/11 Python
python中pivot()函数基础知识点
2021/01/03 Python
用CSS3来实现社交分享按钮
2014/11/11 HTML / CSS
HTML5中图片之间的缝隙完美解决方法
2017/07/07 HTML / CSS
Beach Bunny Swimwear官网:设计师泳装和性感比基尼
2019/03/13 全球购物
ANINE BING官方网站:奢华的衣橱基本款和时尚永恒的单品
2019/11/26 全球购物
介绍一下SOA和SOA的基本特征
2016/02/24 面试题
财政专业大学生职业生涯规划书
2014/09/17 职场文书
2014年前台接待工作总结
2014/12/05 职场文书
php解析非标准json、非规范json的方式实例
2022/05/10 PHP