Vue实现父子组件页面刷新的常用方法

4,584 阅读4分钟

很多时候我们在操作过页面时候,特别是增删改操作之后,数据会有所改变,这个时候我们希望返回的界面中的数据要和数据库中的数据进行同步,就需要刷新当前页面,如果是使用ajax可以使用异步请求实现页面的局部刷新,Vue常用的几种刷新页面方法如下:


CSDN同名账号:[一宿君](https://blog.csdn.net/qq_52596258?spm=1011.2415.3001.5343)

1、原地页面重新加载(不推荐)

this.$router.go(0) //根据路由重新定向到当前页
或者
location.reload() //重新加载当前页

上述两种方法可以数据同步,但是其实都是重新加载当前页面,在刷新的时候会出现屏幕闪动,如果当前页面初始化加载的数据或者请求很多的时候,此种方法严重影响效率和体验感。

2、空白页面作为过渡

新建一个空白页面组件empty.vue,点击确定的时候先跳转到这个空白页,然后再立刻跳转回当前页面。

在需要刷新的页面先引入空白页面组件,再添加路由跳转:

//引入空白页面组件
import empty from '@/views/organization/empty.vue'

//添加路由跳转
this.$router.replace({ path:'/empty' });

此种方法基本和上述两种方法,不会出现一瞬间的空白页(如果网络不好或者数据量大也可能会出现),只是地址栏有个快速的切换的过程,如果数据量不大也可以采用。

3、使用Provide / Inject组合控制显示

详情参考官网指导文档。

通常,当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。


对于这种情况,我们可以使用一对 provideinject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

此种方法最基本的Vue脚手架工程很容易实现:

在App.vue跟组件中定义变量和方法如下:

<template>
  <div id="app">
    <router-view v-if="isShow"/>
  </div>
</template>

<script>
	export default {
	  name: 'App',
	  provide(){   //父组件中通过provide来提供变量,在子组件中通过inject来注入变量。
	    return{
	      reload:this.reload
	    }
	  },
	  data(){
	    return{
	      isShow:true   //控制视图是否显示的变量
	    }
	  },
	  methods:{
	    reload(){
	      this.isShow=false;   //先关闭
	      this.$nextTick(()=>{
	        this.isShow=true  //再打开
	      })
	    }
	  }
	}
</script>

在子组件中使用inject注入根组件的reload方法:

<template>
  <div>
      <div class="header">
          <button @click="update()">刷新页面</button>
      </div>
  </div>
</template>

<script>
	export default {
		data(){
		  return{
		
		  }
		},
		inject:['reload'],//inject注入根组件的reload方法
		methods:{
		  update(){
		    this.reload();
		    console.log('刷新页面')
		  }
		}
	}
</script>

但是我使用的vue项目是基于webpack构建的,整体项目结构有点不同,搞了半天也没搞出来:

<template>
  <div id="app">
    <router-view v-if="isRouterAlive"/>
  </div>
</template>

<script lang="ts">
    import {
        Component,
        Vue
    } from 'vue-property-decorator'

    @Component({
        name: 'App',
        provide() { //父组件中通过provide来提供变量,在子组件中通过inject来注入变量。
            reload: this.reload
        },
        data() {
            return {
                isRouterAlive: true //控制视图是否显示的变量
            }
        },
        method: {
            reload() {
                this.isRouterAlive = false //先关闭
                this.$nextTick(() => {
                    this.isRouterAlive = true  //再打开
                })
            }
        }
    })
    export default class extends Vue {}
</script>

子组件中注入reload:

<script lang="ts">

    import {
        updateStore,//更新门店信息
        getStoresData,//获取门店列表信息
        searchStore//根据门店id和商户id获取门店信息
    } from '@/api/organization.ts'
    
    import App from '@/App.vue'
    
    @Component({
        components: {
            updateStore,
            getStoresData,
            searchStore
        }
    })

	/**
     * 此处的默认输出是集成Vue类(和java中定义实体类跟相似)
     */
    export default class createTasks extends Vue {
    	//此处注入relod
    	inject:['reload'];

		private async ensureDialog() {
            let res = await updateStore(this.form)
            this.syncDialogVisible = false;
            if (res) {
                this.$message.success('修改成功');
            }
            //到达此处说明请求响应成功
            this.reload();//此处调用根组件的reload方法
        }
	}

上述做法不生效,如果有大神知道哪个不对,还望不吝赐教。

4、v-on:param父组件监听子组件事件

我们知道Vue有一个特点,只要vue实例中的数据属性值发生改变,页面中使用v-model或者:data渲染绑定的数据也会随之改变,通常情况下展示数据的界面为父组件,需要对父组件中的某一条数据进行增删改查操作时,都会响应弹出一个组件框悬浮在父组件之上单独展示某一条数据的内容,而这个悬浮组件就可以看做是子组件。

所以我们只要在父组件中监听到子组件的行为事件,就可以在进行当前页面的局部数据刷新(非重新加载)。

父组件:index.vue

<template>
  <div class="data-container">
      <h3>门店管理</h3>

    <el-table
      :data="list"
      fit
      highlight-current-row
      :header-cell-style="{color:'#5373e0',background:'#f3f6fb'}"
      style="width: 100%">
	</el-table>

	<addStore :dialogVisible.sync="dialogVisible" v-on:post="getList" @refreshList="getList"></addStore>
	<setCode :dialogVisible.sync="dialogVisible1" @refreshList="getList" :code="codes"></setCode>
	<updateStore :dialogVisible.sync="dialogVisible2" v-on:put="getList" @refreshList="getList" :storeIds="storeId"></updateStore>
	<setUse :dialogVisible.sync="dialogVisible3" @refreshList="getList" @getAppId="getAppIds"></setUse>
	
	<pagination v-show="total>0" :total="total" :page.sync="pageNo" :limit.sync="pageSize" @pagination="getList" />
	</div>
</template>

<script lang="ts">
    
    import addStore from '@/views/organization/addStore.vue'
    import Pagination from "@/components/Pagination/index.vue";
    import setCode from '@/views/organization/setCode.vue'
    import updateStore from '@/views/organization/updateStore.vue'
    import setUse from '@/views/organization/setUse.vue'
    @Component({
        components: {
            addStore,
            Pagination,
            setCode,
            updateStore,
            setUse
        }
    })
    export default class extends Vue {

        private list: any[] = [];
        private pageNo: number = 1;
        private pageSize: number = 10;
        private total: number = 0;
        private dialogVisible: boolean = false;
        private dialogVisible1: boolean = false;
        private dialogVisible2: boolean = false;
        private dialogVisible3: boolean = false;
        private appId: string = '';
        private codes: string = '';
        private storeId: string = '';
        private storeForm = {
            storeName: '',
            storeNumber: ''
        }
        private form = {
            totalAmount: '',
            body: ''

        }
        created() {
            this.getList()
        }

        private async getList() {
            let data = await getStoresData(this.pageNo, this.pageSize, this.storeForm)
            this.list = data.items; //查询到的门店列表
            this.total = this.list.length; //查询到的总记录数
        }
    }
</script>

可以看到上述父组件中包含了以下子组件:

//新增门店子组件
<addStore :dialogVisible.sync="dialogVisible" v-on:post="getList" @refreshList="getList"></addStore>

//更新门店子组件
<updateStore :dialogVisible.sync="dialogVisible2" v-on:put="getList" @refreshList="getList" :storeIds="storeId"></updateStore>

getList方法的作用就是更新当前父组件要展示的数据:

private async getList() {
     let data = await getStoresData(this.pageNo, this.pageSize, this.storeForm)
     this.list = data.items; //查询到的门店列表
}            

getStoresData方法:

//分页条件查询商户下门店
  export const getStoresData = (pageNo:number,pageSize:number,data:any) =>
    request({
      url: `/merchant/my/stores/merchants/page?pageNo=${pageNo}&pageSize=${pageSize}&tenantId=${UserModule.tenantId}`,
      method: 'post',
      data
  })

渲染数据:

this.list = data.items; //查询到的门店列表

<el-table
      :data="list"
      fit
      highlight-current-row
      :header-cell-style="{color:'#5373e0',background:'#f3f6fb'}"
      style="width: 100%">
    </el-table>

更新数据子组件updaStore.vue:

<template>
  <el-dialog title="修改门店信息" :visible.sync="syncDialogVisible" @open="opend">
    <el-form :inline="false">
      <el-form-item label="门店名称:" :label-width="formLabelWidth" :rules="[{required:true}]">
        <el-input v-model="form.storeName"></el-input>
      </el-form-item >

      <el-form-item label="门店地址:" :label-width="formLabelWidth">
        <el-input v-model="form.storeAddress"></el-input>
      </el-form-item>

      <el-form-item label="门店编号:" :label-width="formLabelWidth">
        <el-input v-model="form.storeNumber"></el-input>
      </el-form-item>

    </el-form>
    <div slot="footer" class="dialog-footer">
      <el-button @click="syncDialogVisible = false">取 消</el-button>
      <el-button type="primary" @click="ensureDialog">确 定</el-button>
    </div>
    
  </el-dialog>
</template>

<script lang="ts">
    @Component({
        components: {
            updateStore,
            getStoresData,
            searchStore
        }
    })

    export default class createTasks extends Vue {
        @PropSync('dialogVisible', {
            type: Boolean,
            default: false
        })
        syncDialogVisible!: boolean
        @PropSync('storeIds', {
            type: String
        })
        storeId!: string

        private form = {
            id: 0,
            storeAddress: '',
            storeName: '',
            storeNumber: 0,
            parentId: 0,
            storeStatus: '',
            merchantId: 0
        }

        private storeForm = {
            storeNumber: '',
            storeName: ''
        }

        list: any[] = [];
        pageNo: number = 1;
        pageSize: number = 10;
        total: number = 0;
        private filterData: any[] = [];
        private opend() {
            this.getList();
        }
        private async getList() {
            let res = await searchStore(this.storeId);
            this.form = res.result;
		}
        private async ensureDialog() {
            let res = await updateStore(this.form)
            this.syncDialogVisible = false;
            if (res) {
                this.$message.success('修改成功');
            }
        }
    }
</script>

实际应用场景: 在这里插入图片描述 在这里插入图片描述 在子组件updateStore.vue中,点击确定修改,修改成功之后在当前组件中进行查询门店列表getStoresData:

private async ensureDialog() {
            let res = await updateStore(this.form)
            this.syncDialogVisible = false;
            if (res) {
                this.$message.success('修改成功');
            }
			//此处查询列表信息
            let ret = await getStoresData(this.pageNo, this.pageSize, this.storeForm);
            this.list = ret.items;
            this.total = this.list.length;
        }

按理论是查询到了修改之后的门店列表list,但是当前是在子组件中,list和父组件index.vue中的list作用域不同,所以父组件中的内容也不会发生改变同步更新,那怎么办呢?

上述我们说了,我们需要父组件监听子组件的事件,当子组件完成相应的操作请求之后可以触发父组件所监听的回调函数,让其父组件index.vue实现list的更新,刷新页面中的数据。

在父组件index.vue中:

//更新门店子组件
<updateStore :dialogVisible.sync="dialogVisible2" v-on:put="getList" @refreshList="getList" :storeIds="storeId"></updateStore>

v-on:put="getList"其中的put就是监听子组件的一个可触发事件,所以我们可以在子组件中完成put更新请求之后,触发该事件,让父组件来完成同步更新渲染数据。

此处完成同步更新有两种方式:

  • 子组件直接触发父组件的监听事件,父组件查询列表信息
  • 子组件将查询好的列表信息,在触发监听事件同时传递给父组件

第一种方式:直接触发监听事件

private async ensureDialog() {
            let res = await updateStore(this.form)
            this.syncDialogVisible = false;
            if (res) {
                this.$message.success('修改成功');
            }
            //触发父组件监听事件put
			this.$emit('put');
        }
//父组件监听到事件put
v-on:put="getList"

//调用getList方法重新查询列表信息(此处的this.list就是渲染界面的绑定属性),这样完成数据刷新,而不用重新加载整个页面
private async getList() {
            let data = await getStoresData(this.pageNo, this.pageSize, this.storeForm)
            this.list = data.items; //查询到的门店列表
            this.total = this.list.length; //查询到的总记录数
        }

第二种方式:触发监听事件的同时传递数据

private async ensureDialog() {
            let res = await updateStore(this.form)
            this.syncDialogVisible = false;
            if (res) {
                this.$message.success('修改成功');
            }
			//此处查询列表信息
            let ret = await getStoresData(this.pageNo, this.pageSize, this.storeForm);
            let = ret.items;
             = this.list.length;
             //触发父组件监听事件put,同时传递数据(后面的皆为参数数据)
			this.$emit('put',ret.items,ret.items.length);
        }
//父组件监听到事件put
v-on:put="getList"

//根据子组件传递过来的数据进行同步更新渲染(此方法的参数列表个数要与所监听的事件传递的参数个数保持一致)
private async getList(param1,param2) {
            this.list = param1; //子组件传递过来的列表数据
            this.total = param2; //子组件传递过来的列表记录数
        }

addStore.vue子组件同样可以用此方法完成同步更新渲染。

参考文档:

好了就到这吧!!!