Vue之数据传递
基础:vue的响应式规则
简单的props更新
父组件
<template>
<div>
<block-a :out-data="x"></block-a>
</div>
</template> <script>
import blockA from './block-a'; export default {
name: "App",
components:{
blockA
},
data(){
return {
x:123
}
},
mounted(){
setTimeout(()=>{
this.x = 789;
},1000)
}
}
</script>
子组件
<template>
<div>
这是子组件
{{outData}}
</div>
</template> <script>
export default {
name: "block-a",
props:['outData'],
watch:{
outData(newVal){
console.log("新的值是:" + newVal)
}
} }
</script>
运行效果,界面先展示123,一秒后展示789,控制台仅输出了“新的值:789”。
结论:简单的数值类型能通过props动态反映到子组件内,而且能被子组件watch检测。
props对象的更新
父组件
<template>
<div>
<block-a :out-data="x"></block-a>
</div>
</template> <script>
import blockA from './block-a'; export default {
name: "App",
components:{
blockA
},
data(){
return {
x:{
a:123,
b:999
}
}
},
mounted(){
setTimeout(()=>{
this.x.a = 789;
},1000)
}
}
</script>
子组件
<template>
<div>
这是子组件
{{outData}}
</div>
</template> <script>
export default {
name: "block-a",
props:['outData'],
watch:{
outData(newVal){
console.log("新的值是:" + newVal)
}
}
}
</script>
这个例子对比上一个例子,将传递的数据改为了对象类型。对对象属性的变更,能动态反映到子组件中(子组件的界面一秒后发生正确的变化),但watch函数却没有执行(控制台无输出)。
为了监听对象属性的变化,将子组件中的watch设置修改如下:
watch:{
outData:{
handler(newVal){
console.log("新的值是:" + newVal)
},
deep:true
}
}
这样代码运行起来,不仅界面会发生变化,而且控制台也会有输出了(属性变化能被子组件watch检测到了),虽然检测出来有属性变更,却没办法知道到底是哪个属性发生了变更。
补充测试1:修改原有属性,同时添加新的属性(this.x.c=123),则子组件中检测出对象原有属性发生变更后,读取到的对象中也包含了新的属性(c=123)。
补充测试2:父组件中直接为属性赋值一个新的对象,如
setTimeout(()=>{
this.x = {
kkk:123
};
},1000)
则,不管子组件中采用深层watch还是普通的watch检测,都检测出变更(控制台有输出),而且界面也会发生正确变化。
补充测试3:将父组件中的响应式对象删除
<template>
<div>
<block-a :out-data="x"></block-a>
</div>
</template> <script>
import blockA from './block-a'; export default {
name: "App",
components:{
blockA
},
data(){
return { }
},
mounted(){
setTimeout(()=>{
this.x = {
kkk:123
};
},1000)
}
}
</script>
则代码运行会报错,提示渲染期间使用了x,而x未定义。而且后续子组件中没有发生任何变化,控制台也无输出。
结论:当父组件中的响应式对象通过props与子组件中的属性建立了关联后,父组件中的响应式对象发生变更(原属性发生变化和对象被重新赋值,前提是对应的setter存在,这样的赋值才是响应式的),都会通知到被关联子组件去重新渲染(与这个对象相关的属性部分dom渲染),而且watch函数会进入判断(如果是深层watch,则对比对象的属性,如果是普通watch,则简单对应对象的引用),如果没有判断出来发生变化,则不执行watch回调函数。
props对象属性的更新
以上发现一个问题,子组件能监听出对象属性发生了变化,却不知道变化了哪个属性。解决办法是使用简单watch,但是写法得改变一下,要直接定位到我关心的属性上,将子组件的watch修改如下
watch:{
['outData.a'](newVal){
console.log("新的值是:" + newVal)
}
}
对于父组件中的x被重新赋值:新对象不存在a属性或者a属性的值与原来的值不一致时,子组件中的watch才会执行,如果重新赋值后,新对象的a属性与原对象的a属性值相同,则watch回调不执行。
结论:在上一条结论的基础上,多加一点,这个例子中的这种watch写法,仅仅是简单地再次读取对象的某个属性值(a),与原来记录的值进行对比,如果不一致,则执行watch回调。而不在乎外部的对象是否被重新赋值了,还是属性被修改了。
修改props
父组件
<template>
<div>
<block-a :out-data="x"></block-a>
</div>
</template> <script>
import blockA from './block-a'; export default {
name: "App",
components:{
blockA
},
data(){
return {
x:{
a:123,
b:999
}
}
},
mounted(){
setTimeout(()=>{
console.log("父组件中输出")
console.log(this.x)
},2000)
}
}
</script>
子组件
<template>
<div>
这是子组件
{{outData}}
</div>
</template> <script>
export default {
name: "block-a",
props:['outData'],
watch:{
['outData.a'](newVal){
console.log("新的值是:" + newVal)
}
},
mounted(){
this.outData.a = 888;
setTimeout(()=>{
console.log("子组件中输出")
console.log(this.outData)
},1000)
}
}
</script>
运行效果:子组件内的watch回调执行,然后子组件内输出了修改后的对象,接着父组件也输出了修改后的对象(a=888)。
虽然props传递(文档中解释props是单向传递的),但因为传递的是对象,所以子组件内对对象的修改等同于父组件中对对象的修改。子组件内通过props获取到的对象和父组件中的对象是一样的,打印出来,发现对象属性上都有getter和setter。
将对象类型改为普通类型,然后在子组件中对props进行修改,会有如下警告:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "outData"
虽然有这样的警告,却不会影响代码的执行(与出异常不一样,修改语句后面的代码依然会继续执行),子组件内props的变化依然是响应式的(界面显示也会跟着变化),但这样的修改却不会反映到父组件中,父组件中的输出依然是旧的值。
props的转换
上一个例子提示:不能直接修改props值,而应该使用基于props的data或computed属性。这里来演示一下。
父组件
<template>
<div>
<block-a :out-data="x"></block-a>
</div>
</template> <script>
import blockA from './block-a'; export default {
name: "App",
components:{
blockA
},
data(){
return {
x:{
a:123,
b:999
}
}
},
mounted(){
setTimeout(()=>{
console.log("父组件中输出")
console.log(this.x)
},2000)
}
}
</script>
子组件中将props赋值给data属性
<template>
<div>
这是子组件
{{outData}}
</div>
</template> <script>
export default {
name: "block-a",
props:['outData'],
watch:{
['outData.a'](newVal){
console.log("新的值是:" + newVal)
}
},
data(){
return {
localData:this.outData,
}
},
mounted(){
this.localData.a = 888;
setTimeout(()=>{
console.log("子组件中输出")
console.log(this.localData)
},1000)
}
}
</script>
代码运行的效果:与直接修改props对象效果一致,因为虽然修改的是本地的data属性对象,而它指向的是与props一样的对象,所以执行起来,子组件的watch函数会输出,而且子组件和父组件中的定时器输出是相同的对象。
父组件将传递的值从对象类型修改为基本类型,将子组件代码修改如下
<template>
<div>
这是子组件
{{localData}}
</div>
</template> <script>
export default {
name: "block-a",
props:['outData'],
watch:{
['outData.a'](newVal){
console.log("新的值是:" + newVal)
}
},
data(){
return {
localData:this.outData,
}
},
mounted(){
this.localData = 888;
setTimeout(()=>{
console.log("子组件中输出")
console.log(this.localData)
},1000)
}
}
</script>
将基本类型从props属性转为data属性后,修改data属性,而不修改props属性,就不会提示之前的警告了,而且也不会影响父组件中的值。
但是这样修改之后,当父组件修改了传入的数据,因为子组件中直接读取的是data属性,而不是props属性,所以子组件没办法感知props的变更,解决办法是:在子组件中添加一个对props的watch,回调函数中更新data的值。接下来演示一下父子组件交替修改值的示例。
父组件
<template>
<div>
<block-a :out-data="x"></block-a>
</div>
</template> <script>
import blockA from './block-a'; export default {
name: "App",
components:{
blockA
},
data(){
return {
x:"123"
}
},
mounted(){
let i = 0;
setInterval(()=>{
this.x = "父组件中设置新值:" + i++;
},5000,100)
}
}
</script>
子组件
<template>
<div>
这是子组件
{{localData}}
</div>
</template> <script>
export default {
name: "block-a",
props:['outData'],
watch:{
['outData'](newVal){
this.localData = newVal;
}
},
data(){
return {
localData:this.outData,
}
},
mounted(){
let i = 0;
setInterval(()=>{
this.localData = "子组件中设置新值:" + i++;
},1000)
}
}
</script>
运行效果:子组件既能接收到父组件中的值,也能使用自己本地的值。
可以发现,才用这种“监听props + props转data”的办法,组件内的data数据来源就有两种了:来源于当前组件 + 来源于父组件。这种特点可以实现一个带缓存的分页组件,简单描述如下:
组件内维护一个data属性dataList,用于存储要展示的数据,当有组件内有数据缓存时,则将缓存赋值给dataList,来展示缓存的数据,当没有缓存时,则读取外部的props传入的值来显示,通过将这个新的数据缓存起来【这一步读取props的值时需要注意props变化的异步性】。
ps:实现props到data的双向绑定,可以查看这个代码 https://github.com/xxcanghai/cnblogsFiles/blob/master/vue-mixins/propsync.js
props变化的异步性
父组件中修改数据后,子组件中立马读取到的props值,并不是父组件中修改后的值。演示如下:
父组件
<template>
<div>
<block-a ref="subBlock" :out-data="x"></block-a>
</div>
</template> <script>
import blockA from './block-a'; export default {
name: "App",
components:{
blockA
},
data(){
return {
x:"123"
}
},
mounted(){
this.x = 888;
this.$refs.subBlock.getProps(); // 123
}
}
</script>
子组件
<template>
<div>
这是子组件
{{outData}}
</div>
</template> <script>
export default {
name: "block-a",
props: ['outData'],
methods: {
getProps() {
console.log("获取到的props是:" + this.outData)
}
}
}
</script>
运行效果:控制台输出123,而界面显示888 。这是因为JS是单线程的,父组件中修改完后,马上调用子组件的方法读取props值,这时响应式的代码还没执行,而应该将读取响应式的变化放到下一次vue循环中读取即可,子组件代码修改如下
getProps() {
this.$nextTick(()=>{
console.log("获取到的props是:" + this.outData)
})
}
这样控制台也输出888了。
数组对象的props更新
还没测试,但应该与对象的情况一致,只不过数组与对象的响应式变化有些区别而已【注意有些情况下数组没办法去检测变化】
Vue之数据传递的更多相关文章
- vue教程3-05 vue组件数据传递、父子组件数据获取,slot,router路由
vue教程3-05 vue组件数据传递 一.vue默认情况下,子组件也没法访问父组件数据 <!DOCTYPE html> <html lang="en"> ...
- vue 组件数据传递
vue组件化开发 主要为了把一个大功能拆分成若干个小的功能,解决高耦合问题,同时也方便开发人员维护. 从功能上组件可以分为木偶组件和功能组件. 木偶组件(为了接收数据,渲染数据,基本上是没有逻辑的 ...
- Vue 爬坑之路(二)—— 组件之间的数据传递
Vue 的组件作用域都是孤立的,不允许在子组件的模板内直接引用父组件的数据.必须使用特定的方法才能实现组件之间的数据传递. 首先用 vue-cli 创建一个项目,其中 App.vue 是父组件,com ...
- vue.js之数据传递和数据分发slot
一.组件间的数据传递 1.父组件获取子组件的数据 *子组件把自己的数据,发送到父级 *vm.$emit(事件名,数据); *v-on: @ 示例用法:当点击send按钮的时候,"111&qu ...
- Vue之单文件组件的数据传递,axios请求数据及路由router
1.传递数据 例如,我们希望把父组件的数据传递给子组件. 可以通过props属性来进行传递. 传递数据三个步骤: 步骤1:在父组件中,调用子组件的组名处,使用属性值的方式往下传递数据 <Menu ...
- vue再次入手(数据传递①)
准备 之前使用vue.js完成一个项目之后,对其还是充满着无限兴趣,于是不妨利用碎片时间再次研究一下这个“令人着迷”的js框架. 1.新建一个基于vue的项目,具体方法不再赘述,请看这里:http:/ ...
- vue兄弟组件传递数据
在main.js里面设置data{eventHub:new Vue() } new Vue({ el: '#app', router, store, template: '<App/>', ...
- Vue基础知识之组件及组件之间的数据传递(五)
vue中的组件是自定的标签,可以扩展的原生html元素,封装可复用的代码 note: 1.在标签命中不要使用大写,标签名字必须用短横线隔开 2.模板中只能有一个根元素,不能使用并列标签. 定义组件 全 ...
- vue 2.x之组件的数据传递(一)
这是根据官方提供的脚手架vue-cli搭建,通过简单的案例来介绍vue数据的传递的方式,根据自己平时用到的,来做简单的总结: 1.父组件传递数据给子组件 父组件传递数据给子组件,需要把子组件引入,并挂 ...
随机推荐
- [題解](函數下整點個數?)luogu_P4132_BZOJ_2659_算不出的等式
兩個都是一次函數,下取整就是整點個數,兩個函數k剛好成倒數,所以最後發現會組合成一個矩形 (為啥要考慮重複與否的問題???) 然而這樣會不會重複計算點數呢 我們發現因為取的是圖像下的整數點 所以要想重 ...
- mysql5.7安装部署后初始密码查看以及修改
一.查看初始密码以下两种方法: 1.找到自己的error.log日志文件,执行自己的命令,红色标记的部分为初始化密码. grep 'temporary password' /data/mysql/er ...
- @Primary 使用
造轮子的一个小小的发现 当一个接口被两个service实现时,controller调用接口实现功能,会报错,提示开发者指定service,官方是建议你使用@Qualifier来区分的,但是,总有另一种 ...
- nodejs 实践:express 最佳实践 (一) 项目结构
express 最佳实践 (一) 第二篇: express 最佳实践(二):中间件 最近,一直在使用 nodejs 做项目,对 nodejs 开发可以说深有体会. 先说说 nodejs 在业务中的脚色 ...
- webpack分开打包和合并打包的瘦身
webpack.config.js 记录一下优化webpack的几个点: 1. devtool: false, //产品阶段不应该有devtool entry: { bundle : pa ...
- Spring MVC 示例
Srping MVC项目结构如下: 一.首先创建一个Dynamic Web Project 二.WebContent/WEB-INF/文件夹下新增 web.xml,配置servlet 容器对于web. ...
- fleet-运行一个全局的单元
运行一个全局的单元 正如前面所提到的,全局单元是有用的,用于在您的集群中的所有机器上运行一个单元.它不会比一个普通的单元差太多,而是一个新的x-fleet参数称为Global=true.这是一个示例单 ...
- 使用 swift3.0高仿新浪微博
项目地址:https://github.com/SummerHH/swift3.0WeBo 使用 swift3.0 高仿微博,目前以实现的功能有,添加访客视图,用户信息授权,首页数据展示(支持正文中连 ...
- springboot集成shiro实现权限缓存和记住我
到这节为止,我们已经实现了身份验证和权限验证.但是,如果我们登录之后多次访问http://localhost:8080/userInfo/userDel的话,会发现权限验证会每次都执行一次.这是有问题 ...
- 电话号码 马赛克*号 string类扩展
/// <summary> /// 字符串马赛克 /// </summary> /// <param name="source"></pa ...