Vue组件传值及页面缓存问题

一、父子组件传值

  • 基本概念
    在Vue中,父子组件间的数据流向可以总结为prop向下传递,事件向上传递,即父组件通过prop给子组件下发数据,子组件通过自定义事件给父组件发送信息。


    vue父子组件传值.png

    (1) 父组件给子组件传值

  • 父组件通过属性传值,子组件利用props接收值
// 给子组件传入一个静态的值:
<child title="哈哈" />
// 传动态的值,需要v-bind绑定:
<child :title="data" />

// 子组件接收值
export default {
  props: {
    title: {
      type: String,
      default: 'hello world'
    }
  }
}

关于父组件的传值类型和props更多的定义详见官网 :vue官网
(2)子组件向父组件传值

<!-- 父组件 -->
<template>
    <div class="test">
      <test-com @childFn="parentFn"></test-com>
      <br/> 
      子组件传来的值 : {{message}}
    </div>
</template>

<script>
export default {
    // ...
    data: {
        message: ''
    },
    methods: {
       parentFn(payload) {
        this.message = payload;
      }
    }
}
</script>
<!-- 子组件 -->
<template> 
<div>
    <input type="text" v-model="message" />
    <button @click="click">Send</button>
</div>
</template>
<script>
export default {
    // ...
    data() {
        return {
          message: '子组件数据'
        }
    },
    methods: {
      click() {
            this.$emit('childFn', this.message);
        }
    }    
}
</script>

(3)通过parent,chlidren等方法调取用层级关系的组件内的数据和方法。

// 获取子组件data中的id 
const id = this.$children.$data.id
// 获取父组件data中的id 
const id = this.$parent.$data.id
  • 注意:获取父元素data中的id 这样用起来比较灵活,但是容易造成代码耦合性太强,导致维护困难

二、非父子组件间传值

有很多时候根据业务需求要在同级组件或页面间传值,此处提供以下几种方法作为参考:
(1)通过router-link进行跳转

<router-link   
    :to="{  
        path: '/test',     
        params: {   
            key: 'value', 
        },  
        query: {  
           key: 'value', 
        }  
    }">  
    <button type="button">跳转</button> 
</router-link> 
  1. path -> 是要跳转的路由路径
  2. name ->路由文件里面配置的name,必须一致,这两者都可以进行路由导航
  3. params -> 是要传送的参数,参数可以直接key:value形式传递
  4. query -> 是通过 url 来传递参数的同样是key:value形式传递
    跳转页面接受参数:
let value1 = this.$route.query.key
let value2 = this.$route.params.key

(2) this.$router.push()
此方法同样是有path+query和name+params两种方式:

  • this.$router.push({name:'路由命名',params:{参数名:参数值,参数名:参数值}})
  • this.$router.push({path:'跳转路由',query:{参数名:参数值,参数名:参数值}})
this.$router.push({  
    path: 'yourPath',   
    name: '要跳转的路径的 name,在 router 文件夹下的 index.js 文件内找',  
    params: {   
        key: 'key',   
        value: this.msg  
     }  
    /*query: {  
           key: 'key',   
            value: this.msg  
     }*/
})  
// 接收参数
export default{
    data () {
      return {
        msg: '',
        // 保存传递过来的index
        index: ''
      }
    mounted() {
      this.msg = this.$route.params.value
      // this.index = this.$route.query.key
    }
}

总结:使用query,传输的值会在url后面以参数的形式显示出来,可以刷新页面,数据不变,但会是页面路由过长;而params只要一刷新传递的参数就没了。
(3)LocalStorage缓存传值

//保存userid至内存
const userId = 1;
localStorage.setItem('storeId',JSON.stringify(userId));
//取,注意这里取出的是字符串。
this.userId= JSON.parse(localStorage.userId);

注意:简单的小项目可以这么做,如果项目很大,建议直接用vuex。
(4)通过Vuex进行传值

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import data from './modules/data'
Vue.use(Vuex)
export default new Vuex.Store({
  modules: {
    data
  },
  state: {},
  getters: {},
  mutations: {},
  actions: {},
  strict: process.env.NODE_ENV !== 'production', // 严格模式
})

// store/modules/data.js
const state = {
  checkedCars: [],
}
const getters = {}
Object.keys(state).forEach(prop => {
  getters[prop] = state => state[prop]
})
const mutations = {
  loadCheckedCars: (state, payload) => {
    state.checkedCars = [].concat(payload)
  },
  deleteCheckedCar: (state, payload) => {
    const result = state.checkedCars.findIndex(item => {
      // eslint-disable-next-line no-unused-expressions
      item.id === payload.id
    })
    if (result > -1) {
      state.checkedCars.splice(result, 1)
    }
  },
}
const actions = {}
export default {
  state,
  getters,
  mutations,
  actions,
}

(5)发布订阅模式(也叫eventBus或事件总线)
在Vue的原型上定义一个变量eventBus,所有所有Vue的实例或组件都将共享这个eventBus,可以用eventBus来发布自定义事件,然后在组件中用eventBus订阅自定义事件。就可以实现传值。

<body>
    <div id="app">
        <child :content="'heqi'"></child>
        <child :content="'20'"></child>
    </div>
    <script type="text/javascript">
        Vue.prototype.eventBus= new Vue();
        var child = {
            props: {
                content: String
            },
            template: '<div @click="handleClick">{{content}}</div>',
            methods: {
                handleClick: function() {
                    // 使用 this.eventBus.$emit 发布广播
                    this.eventBus.$emit('change', this.content); // 此处参数可为字符串,对象
                }
            },
            mounted: function() {
                // 使用 this.eventBus.$on 订阅事件
                this.eventBus.$on('change', msg => {
                    this.content = msg;
                });
            }
           beforeDestroy(){ 
               //组件销毁前移除监听
               this.eventBus.$off('change');
           }
        }
        var app = new Vue({
            el: '#app',
            components: {
                child
            },
            data: {
                content: 'Hello World'
            }
        });
    </script>
  • 注意:当使用this.eventBus.$emitthis.eventBus.$on时,this的值不是指向vue的实例或组件,而是指向Vue。可以用常规操作解决:
mounted: function() {
    var that = this; 
    this.eventBus.$on('change', function(msg) {
       that.content = msg;
    });
}
beforeDestroy(){ 
    //组件销毁前移除监听
    this.eventBus.$off('change');
    }

详细讲解可看链接
(6)Vue.observable

  • Vue.observable为v2.6.0版本中新增的一个组件通信方案,让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。返回的对象可以直接用于渲染函数methods和计算属性computed内,并且会在发生改变时触发相应的更新。也可以作为最小化的跨组件状态存储器。
    创建store.js文件
import Vue from 'vue'
export const state = Vue.observable({
  screenCars: {},
})
export const mutations = {
  updateScreenCars (payload) {
    state.screenCars = Object.assign({}, payload)
  },
}

index.vue组件中触发:

<template>
    <div>
        <el-button @click="toSave">保存</el-button>
    </div>
</template>

<script>
import {state, mutations} from './store.js'
export default {
  name: 'table_form',
  computed: {
    screenCars() {
      return state.screenCars
    },
  },
  methods: {
    setTest: mutations.updateScreenCars ,
    toSave () {
      this.setTest({a:1})
    },
  },
}
</script>

三、页面缓存问题

Vue中如何在切换组件过程中,将状态保存到内存中,防止DOM重新渲染,通俗的讲就是实现如何在一个页面输入部分数据后到了另一个页面再返回该页面,数据还在。
需求分析:Page1中录入信息,页面跳转带Page2,然后再返回Page1,之前Page1录入的信息还存在。

  • keepAlive组件
    此处结合router-view,将整个路由页面缓存,使用$route.meta的keepAlive属性:
    在APP.Vue文件中 :
<keep-alive> 
      <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive> 
<router-view v-if="!$route.meta.keepAlive"></router-view> 
routes: [
  { 
    path: '/page2',
    name: 'Page2', 
    component: Hello, 
    meta: { 
        keepAlive: false // 不需要缓存 
    } 
  }, 
  {
    path: '/page1’,
    name: 'Page1’,
    component: Page1,
    meta: {
      keepAlive: true // 需要被缓存 
    }
  }
] 

现在更改需求为:
首页是A页面
A页面跳转到B,B页面不需要缓存
B页面跳转到C,C页面不需要被缓存
C页面返回到B,B页面需要缓存
B页面返回到A,
A再次跳转到B

(1)此时思路是在每个路由的beforeRouteLeave(to, from, next)钩子中设置to.meta.keepAlive
beforeRouteLeave讲解

// B的路由:
{
  path: '/',
  name: ‘B',
  component: B,
  meta: {
    keepAlive: true // 需要被缓存
  }
}

PageA页面:

// 重新加载A页面
let isRefresh = sessionStorage.getItem('isRefresh’);
if(isRefresh == '1’){
  sessionStorage.setItem('isRefresh',null);
    window.location.reload();
  } else {
    sessionStorage.setItem('isRefresh',1);
}
// 并非刷新页面,而是重置页面数据达到页面刷新效果
beforeRouteEnter:function(to,form,next){
  next(vm => {
    let calculateReload = sessionStorage.getItem('calculateReload’);
      if(calculateReload == 'reload’){
        sessionStorage.setItem('calculateReload',null);
        vm.clearFinancing();vm.load();
      } else {
        sessionStorage.setItem('calculateReload','reload');
      }
  })
},

PageB页面:

beforeRouteLeave (to, from, next) {
    if (to.name === 'CheckedCars' || to.name === 'CarInfo') {
      from.meta.keepAlive = true
      next()
    } else {
      to.meta.keepAlive = false
      from.meta.keepAlive = false
      this.$destroy()
      next()
    }
  },
methods:{
    clearFinancing(){
  this.financing = {};
  },
load(){
  this.financing.product_name_write = 
  this.$route.params.financing.product_name_write;
  this.financing.invoice_price = null;
  this.financing.annual_pay_times_n = '月';//还款频率
  this.financing.annual_pay_times = 12;//还款频率
  this.financing.lease_charge = 0;//手续费
},
}

(2)用eventBus解决此问题
需要注意的一点是发布订阅第一次会无效,因为订阅的组件还没创建。解决方法就是首次进入pageB页面时接收pageA页面params里传递的参数。

// pageA
cars = res.rows
this.$router.push({
    name: 'BrowseCars',
    params: { rentType: rt, cars, selectResult      
})
this.bus.$emit('change', { rentType: rt, cars, selectResult })
}
// pageB
mounted () {
    this.rentType = this.$route.params.rentType
    this.carsList = this.$route.params.cars // 筛选或推荐车辆
    this.carNumber = this.$route.params.cars.length
    this.selectResult = this.$route.params.selectResult // 筛选结果数

    this.bus.$on('change', msg => {  // 发布订阅方式,但第一次无效
         console.log(msg)
    })
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342