从Props源码分析vue组件传值的过程

props一般用来进行组件间传值(父组件->子组件)

其中整个过程大致可以分成三个部分:

父组件通过绑定属性传值给子组件的props
知识点:with()的使用
子组件读取props
知识点:vue的双向绑定
父组件 data 更新时,子组件的props也进行更新
知识点:watcher原理示例代码:

  <div class="a" >
    <testb :child-name="parentName" ></testb>
    父组件传人的值: {{parentName}}
</div>

new Vue({    
el:".a",        
name:"A",    
components:{        
    testb:{            
        props:{                
            childName:""
        },            
    template: '

子组件接收的值: {{childName}}

'
, } }, data(){ return { parentName:"我是父组件" } }, })

效果:从Props源码分析vue组件传值的过程_第1张图片

流程分析:

1. 父组件通过绑定属性传值给子组件的 props

根据上面的场景设置,testb 是一个子组件,接收一个 props(child-name),然后 根组件 A 把 自身的 parentName 绑定到 子组件的属性 child-name 上

tip:关于大小写
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名 ,否则会报错:
在这里插入图片描述

(如果不使用驼峰法命名就无所谓了)

父组件在传值前会产生这样页面渲染函数:

(function() {    
    with(this){  
        return _c('div',{staticClass:"a"},[
            _c('testb',{attrs:{"child-name":parentName}})
        ],1)
    }
})

特点1:它是个立即执行函数

特点2: with关键字的作用在于改变作用域,效果如下:
从Props源码分析vue组件传值的过程_第2张图片

平时不建议使用,因为会导致局部数据泄露全局!

特点3:_c 是渲染组件的函数,_c(‘testb’) 表示渲染 testb 这个子组件

之后这个函数会被执行,执行时会绑定父组件为作用域 ,渲染函数内部所有的变量,都会从父组件对象 上去获取,绑定了父作用域之后, parentName 自然会从父组件获取,类似这样

{ attrs: { child-name: parentVm.parentName } }

函数执行了,内部的 _c(‘testb’) 第一个执行,然后传入了 赋值后的 attrs
父组件赋值之后的 attrs 就是下面这样:

{ attrs: { child-name: "我是父组件" } }

此时,父组件就正式利用 props 把 parentName 传给了 子组件的props 的child-name

2. 子组件读取props

而子组件拿到父组件赋值过后的 attr 而 attrs 包含普通属性和 props,所以需要 筛选出 props,然后保存起来
从Props源码分析vue组件传值的过程_第3张图片
如果在这里打印一下,我们可以发现:
从Props源码分析vue组件传值的过程_第4张图片

props 会被保存到实例的_props 中,并且 会逐一复制到实例上,并且每一个属性会被设置为响应式的
其中props的访问和更改源码大概是这个样子:

Object.defineProperty(childVm, childName, {      
get() {        
        return this._props.childName
    },    
set(val) {        
        this._props.childName = val
    }
});

Set 时和Get时访问的this.childName,会默认找到this._props.childName

3. 父组件 data 更新时,子组件的props也进行更新

watcher就是vue的专用监听器
watcher的作用:

1.用于实例自己更新视图

2.用于给 依赖的属性保存,然后属性变化的时候,通知实例更新

props更新流程:

1.parentName 会收集 父组件的 watcher

2.父组件重新渲染,重新赋值 props

关于在子组件里修改props时:

父组件传递的参数 :数组和对象,子组件接受之后可以直接进行修改,并且会传递给父组件相应的值也会修改(不报错)
如果传递的值是字符串和数字(非引用类型),直接修改会报错

当参数为非引用类型时:
从Props源码分析vue组件传值的过程_第5张图片

不推荐子组件直接修改父组件中的参数,不是因为会影响父组件(毕竟是单向数据流),而是避免这个参数被多个子组件引用,无法找到造成数据不正常的原因

当参数为引用类型时:

<div class="a" >
    <testb :child-name="parentName" ></testb>
    父组件:<input type="text" v-model='parentName.name'>
</div>

从Props源码分析vue组件传值的过程_第6张图片

此时修改子组件会影响父组件!最好方法就是在data里再建一个值拷贝一下再处理

为什么对象和数组会出现这个情况呢:

js数据储存方式:

堆空间 栈空间
Object Number/String/Object在堆中的地址…

父组件会给子组件传递对象在栈空间的地址数据,子组件如果试图修改props里的对象,修改的就是这个地址上在堆空间的实际数据,并且即使修改了地址也不会发生变化,所以vue检测不到props发生了修改,也就不会报错了

你可能感兴趣的:(vue,javascript)