一起来看看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中的生命周期
Dec 30 Vue.js
vue中activated的用法
Jan 03 Vue.js
Vue中强制组件重新渲染的正确方法
Jan 03 Vue.js
Vue 实例中使用$refs的注意事项
Jan 29 Vue.js
vue 根据选择的月份动态展示日期对应的星期几
Feb 06 Vue.js
Vue单页面应用中实现Markdown渲染
Feb 14 Vue.js
使用vue-element-admin框架从后端动态获取菜单功能的实现
Apr 29 Vue.js
详解Vue项目的打包方式(生成dist文件)
Jan 18 Vue.js
vue中控制mock在开发环境使用,在生产环境禁用方式
Apr 06 Vue.js
vue打包时去掉所有的console.log
Apr 10 Vue.js
vue特效之翻牌动画
Apr 20 Vue.js
vue @click.native 绑定原生点击事件
Apr 22 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生成网页快照 不用COM不用扩展.
2010/02/11 PHP
php 利用array_slice函数获取随机数组或前几条数据
2015/09/30 PHP
php将数组存储为文本文件方法汇总
2015/10/28 PHP
JavaScript 闭包深入理解(closure)
2009/05/27 Javascript
纯js实现背景图片切换效果代码
2010/11/14 Javascript
js 浏览本地文件夹系统示例代码
2013/10/24 Javascript
JS获得QQ号码的昵称,头像,生日的简单实例
2013/12/04 Javascript
jQuery中$.get、$.post、$.getJSON和$.ajax的用法详解
2014/11/19 Javascript
JavaScript中setUTCMilliseconds()方法的使用详解
2015/06/12 Javascript
JavaScript 常见安全漏洞和自动化检测技术
2015/08/21 Javascript
JS实现图片平面旋转的方法
2016/03/01 Javascript
BootStrap日期控件在模态框中选择时间下拉菜单无效的原因及解决办法(火狐下不能点击)
2016/08/18 Javascript
详解React-Todos入门例子
2016/11/08 Javascript
JavaScript实现水平进度条拖拽效果
2017/01/18 Javascript
js实现4个方向滚动的球
2017/03/06 Javascript
JS简单获取当前日期时间的方法(如:2017-03-29 11:41:10 星期四)
2017/03/29 Javascript
解决百度Echarts图表坐标轴越界的方法
2018/10/17 Javascript
vue从零实现一个消息通知组件的方法详解
2020/03/16 Javascript
Vue2.0 ES6语法降级ES5的操作
2020/10/30 Javascript
Python中asyncore的用法实例
2014/09/29 Python
python的staticmethod与classmethod实现实例代码
2018/02/11 Python
谈一谈基于python的面向对象编程基础
2019/05/21 Python
Django框架静态文件使用/中间件/禁用ip功能实例详解
2019/07/22 Python
python Plotly绘图工具的简单使用
2020/03/03 Python
Python基于当前时间批量创建文件
2020/05/07 Python
pycharm远程连接vagrant虚拟机中mariadb数据库
2020/06/05 Python
浅析rem和em和px vh vw和% 移动端长度单位
2016/04/28 HTML / CSS
HTML块级标签汇总(小篇)
2016/07/13 HTML / CSS
澳大利亚领先的亚麻品牌:Bed Threads
2019/12/16 全球购物
北京捷通华声语音技术有限公司Java软件工程师笔试题
2012/04/10 面试题
周年庆典邀请函范文
2014/01/24 职场文书
个性发展自我评价
2014/02/11 职场文书
班子四风对照检查材料思想汇报
2014/09/29 职场文书
幼儿园家长工作总结2015
2015/04/25 职场文书
博士论文答辩开场白
2015/06/01 职场文书
推广普通话主题班会
2015/08/17 职场文书