vue深入响应式原理

数据模型–》 vm中 的data选项
状态管理
什么叫做状态?什么叫做状态管理?
我们使用一个数据去管理视图中的一个部分, 那么这条数据就叫做状态, 这种管理模式就叫做状态管理
Object.defineProperty( obj, obj.attr , descriptor )
* 存储器:
* get函数 设置了当前对象的初始值
* get函数 要求必须要有返回值
*
* set函数 修改了当前对象的key的值
* set函数 set( val,old ) val 代表当前的值 , old 表示上一次改变时的值

     Object.defineProperty(obj,'name',{
   //存储器
   get(){
     //要求必须有返回值
     return 'hello'
   },
   set(value){ //value就是修改后的对象的key的value
     console.log('set')
     console.log('====================================');
     console.log(value);
     console.log('====================================');
     document.querySelector('#app').innerHTML = value
   }
  • 非响应式情况
  • 在vm模型的data外定义的属性, 就是非响应式属性, 非响应式属性, 属性值更新, 视图不会更新
  • 非响应式属性如何变成响应式属性
  • 思维: 将非响应式属性合并到响应式属性身上
  • 解决: 利用了Vue提供的 Vue.set/this.$set(vm.dataattr,prop,value)
  • Vue.set(vm.someObj,prop,value)
  • this.$set(vm.someObj,prop,value)
  • Vue.set底层原生是什么?
  • Object.assign(目标对象,对象1, 对象2,对象3)
  • 如果要求非相应数据进行相应, 需要将非相应的属性合并到data中
  • 如果是在Vue中去合并的话 使用 this.$set( vm.someobj , prop , value )
  • 如果实在Vue外部合并的话 使用 Vue.set( vm.someobj , prop , value )

总结:

1.  什么是深入响应式原理?
  *  深入响应式原理是利用了数据劫持和订阅发布的模式, 当数据模型发生改变的时候,
  *  视图就会响应的进行更新, 那么深入响应式原理是利用es5的Object.defineProperty中getter/setter来进行数据的劫持的
  
  *  Vue通过watcher将data中的属性全部使用Object.definePropery变成getter和setter,当属性值发生
  改变的时候, 就会触发, 然后wather就会触发, 告诉视图(V)进行重新渲染	
  
  名称解释: 
    数据劫持:  Object.defineProperty中的getter/setter , 然后在执行watcher
    订阅发布:事件(自定义事件) 
      订阅: 事件的声明  vm.$on    
      发布: 事件的触发  vm.$emit

vue双向数据绑定原理

v-model 双向数据绑定

  1. 效果
    数据改 , 视图更
    视图改, 数据更
    
  2. 实现
    使用v-model实现
    
  3. 缺点
    v-model默认绑定value属性, 所以v-model只能在表单使用
    
  4. 原理
    1. 为什么数据能直接在视图显示
      v-model默认绑定了DOM对象的value属性, 当它初次绑定的时候, 
      就会触发getter,watcher就会触发, watcher通知Vue生成新的VDOM树, 
      再通过render函数进行渲染,生成真实DOM
      
    2. 为什么视图修改数据就会修改
      当视图修改是, 意味着DOM的value属性值改变,就会触发setter,watcher监听机制就会执行
      watcher通知Vue生成新的VDOM树,再通过render函数进行渲染,生成真实DOM

vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

具体步骤:

第一步:需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter?
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化

第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:

1、在自身实例化时往属性订阅器(dep)里面添加自己

2、自身必须有一个update()方法

3、待属性变动dep.notice()通知时,能调用自身的 update() 方法,并触发Compile中绑定的回调,则功成身退。

第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

  1. axios fetch 数据请求

watch 监听

  1. 作用:
    用来监听数据的变换, 当数据模型 (data选项 M)发生改变时, watch就会触发
  2. 使用
    两种用法:
    1. key的value值是一个函数
    new Vue({
        watch: {
          key(value,oldvalue){}          
             }
           })
         ```
         2. key的value值是一个对象             
           ```javascript
     new Vue({
               watch: {
                 key: {
             deep: true/false 深入监听,
             handler(value,oldvalue){}    // 监听的处理函数              
                 }       
               }
             })
           ```
  用法:
 ```javascript
new Vue({
   el: '#app',
   data: {
     msg: 'hello vue.js'
   },
   watch: {
     msg(){
       alert('数据改变了')
     }
   }
 })
  watch中的key指的就是data选项中key
  对比 watch computed methods 
  methods : 用于时间
  watch : 1.异步操作   2. 开销较大
  computed : 1. 有逻辑 2. 要像变量一样使用

mixins

组件即实例, 实例即组件

1. 概念: 
  mixins; 混合 ,将  根实例或是组件中的配置项 抽离出来, 单独管理 
2. 类型
  A:局部混入
     var mixin = {
       methods: {
         sum(){
           alert( 10*10 )
         }
       },
     }

     new Vue({
       el: '#app',
       data: {},
       watch: {},
       mixins: [mixin],
       computed: {}
     })
  注意: 
    1. 即使分离出去, 我们的配置项中也可以继续写分离出去的配置
    2. 如果说分离出去中的内容有冲突, 以组件中的配置项为准
    3. 配置项中的方法执行时是最优先的
  B: 全局混入
    注意: 全局混入慎用(不建议你使用)
    理由:全局混入会影响所有的组件(实例)
     Vue.mixin({
       watch: {},
       methods: {
 	yes():{
     alert(123);
    }
 }
     })

axios与fetch(原生的方法)

    1.get方法
    A: 无参数
        axios.get(url).then(res=>console.log(res).catch(error=>conosle.log(error))
    B: 有参数
    
        axios({
            url: 'http://xxx',
            method: 'get' //默认就是get,这个可以省略,
            params: {
                key: value
            }
        })

    2.post
        注意: axios中post请求如果你直接使用npmjs.com官网文档, 会有坑
        解决步骤: 
                1. 先设置请求头 
                2. 实例化 URLSearchParams的构造器函数得到params对象
                3. 使用params对象身上的append方法进行数据的传参
                4. new URLserachParams对象有一个toString()方法可以将对象转换为类似a=1&b=2的形式
 
// 统一设置请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; 
let params = new URLSearchParams()

// params.append(key,value)

params.append('a',1)
params.append('b',2)

axios({
    url: 'http://localhost/post.php',
    method: 'post',
    data: params,
      headers: {  //单个请求设置请求头
       'Content-Type': "application/x-www-form-urlencoded"
    }
})
.then(res => {
    console.log( res )
})
.catch( error => {
    if( error ){
    throw error
}
})

Fetch
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch#进行_fetch_请求
https://blog.csdn.net/hefeng6500/article/details/81456975

列表渲染中的key的作用

给VDOM添加标记

如果没有key会产生的问题:
VDOM是惰性的, 它有一个原则, 这个原则叫做’就地复用’ , 它认为我的第一个就是红色的, 删除了第二个之后, 第二个就变成了第一个, 它印象中第一个是红的, 所以就我们认为的第二个变成红的了

解决: 使用key属性

使用:

  <li v-for = " (item,index) in list " :key = "item.id">

注意:
1. 优先使用数据中能够识别的, 比如 : id
2. 最差在用 index

虚拟DOM和diff算法

  1. 虚拟DOM 是什么?
    虚拟DOM是利用 了js的对象的Object的对象模型来模拟真实DOM, 那么它的结构是一个树形结构
    2. diff算法
    diff算法是用来比较两个或是多个文件, 返回值是文件的不同点

    diff算法是同级比较的

    diff思维也是来自后端

    diff算法的比较思维
    比较后会出现四种情况:
    1、此节点是否被移除 -> 添加新的节点
    2、属性是否被改变 -> 旧属性改为新属性
    3、文本内容被改变-> 旧内容改为新内容
    4、节点要被整个替换 -> 结构完全不相同 移除整个替换

    1. 整个VDOM的使用流程(Vue)
    • 创建VDOM树
    • 利用render函数渲染页面
    • 数据改变,生成新的vDOM
    • 通过diff算法比较 新 旧 两个VDOM , 将不同的地方进行修改, 相同的地方就地复用 , 最后在通过render函数渲染页面
//用js实现的一个简单的redner函数封装
<script>
    var vdom = {
    vnode: {
      tag: 'div',
      attr: {
        idName: '#app'
      },
      content: [
        {
          tag: 'header',
          content: [
            '头部'
          ]
        },
        {
          tag: 'section',
          content: [
            '内容'
          ]
        },
        {
          tag: 'footer',
          content: [
            '底部',
          ]
        }
      ]
    }
  }
  function render (parentNode){
      //父节点的元素类型和属性
    var parent = document.createElement(parentNode.tag);
    parent.id = parentNode.attr.idName;
    //添加到body中
    document.body.appendChild(parent);
    // 保存所有文本
    var content = vdom.vnode.content;
    //循环遍历生成标签
    content.forEach((ele,i)=>{
        var vno = document.createElement(ele.tag);
        ele.content.forEach((ele)=>{
            var vtext = document.createTextNode(ele);
            vno.appendChild(vtext);
        })
        
        parent.appendChild(vno);
    })
  }
  render(vdom.vnode);
</script>
Logo

前往低代码交流专区

更多推荐