一起来看看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 表单输入框不支持focus及blur事件的解决方案
Nov 17 Vue.js
Vue用mixin合并重复代码的实现
Nov 27 Vue.js
详解Vue的异步更新实现原理
Dec 22 Vue.js
vue中封装axios并实现api接口的统一管理
Dec 25 Vue.js
vue项目如何监听localStorage或sessionStorage的变化
Jan 04 Vue.js
vue组件是如何解析及渲染的?
Jan 13 Vue.js
vue-cli 3如何使用vue-bootstrap-datetimepicker日期插件
Feb 20 Vue.js
Vue SPA 首屏优化方案
Feb 26 Vue.js
vue3中provide && inject的使用
Jul 01 Vue.js
Vue如何清空对象
Mar 03 Vue.js
vue-cli3.0修改打包后的文件名和文件地址,打包后本地运行报错解决
Apr 06 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模拟SQL Server的两个日期处理函数
2006/10/09 PHP
Linux下将excel数据导入到mssql数据库中的方法
2010/02/08 PHP
PHP入门学习笔记之一
2010/10/12 PHP
一些php项目中比较通用的php自建函数的详解
2013/06/06 PHP
如何利用php array_multisort函数 对数据库结果进行复杂排序
2013/06/08 PHP
PHP获取一个字符串中间一部分字符的方法
2014/08/19 PHP
php从字符串创建函数的方法
2015/03/16 PHP
CentOS下PHP7的编译安装及MySQL的支持和一些常见问题的解决办法
2015/12/17 PHP
关于php几种字符串连接的效率比较(详解)
2017/02/22 PHP
Laravel框架实现redis集群的方法分析
2017/09/14 PHP
JavaScript高级程序设计 读书笔记之九 本地对象Array
2012/02/27 Javascript
JS小游戏之宇宙战机源码详解
2014/09/25 Javascript
jQuery实现在列表的首行添加数据
2015/05/19 Javascript
Jquery效果大全之制作电脑健康体检得分特效附源码下载
2015/11/02 Javascript
jQuery常用知识点总结以及平时封装常用函数
2016/02/23 Javascript
js方法数据验证的简单实例
2016/09/17 Javascript
学习vue.js条件渲染
2016/12/03 Javascript
JS中解决谷歌浏览器记住密码输入框颜色改变功能
2017/02/13 Javascript
vue下跨域设置的相关介绍
2017/08/26 Javascript
[01:02:53]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第二局
2016/03/06 DOTA
Python用模块pytz来转换时区
2016/08/19 Python
Python基于回溯法子集树模板实现8皇后问题
2017/09/01 Python
python的unittest测试类代码实例
2017/12/07 Python
PyQt4实现下拉菜单可供选择并打印出来
2018/04/20 Python
详解python做UI界面的方法
2019/02/27 Python
Python代码块及缓存机制原理详解
2019/12/13 Python
python中xlutils库用法浅析
2020/12/29 Python
python urllib和urllib3知识点总结
2021/02/08 Python
python中time tzset()函数实例用法
2021/02/18 Python
信息专业个人的自我评价
2013/12/27 职场文书
婚礼答谢宴主持词
2014/03/14 职场文书
环保建议书500字
2014/05/14 职场文书
十佳标兵事迹材料
2014/08/18 职场文书
行政专员岗位职责说明书
2014/09/01 职场文书
中学生勤俭节约倡议书
2015/04/29 职场文书
校园音乐节目广播稿
2015/08/19 职场文书