IView Modal 结合 Vue-Router 命名路由实现页面嵌套,但是无法实现视图更新?

问题描述

我在基于Vue全家桶的单页项目中使用了IView, 页面类似于一个监控页面,当点击不同的图标的时候需要在一个模态框内(Modal)内展示不同的内容,所以我考虑将监控一面作为一个路由,这里命名为路由 A,并且在 A 路由下添加一个子路由,这里命名为路由 B,且 B 为一个命名路由,本想 A 组件在 mounted 之后直接跳转到子组件,利用命名路由动态控制页面的模态框展示,但是第一次控制模态框展示成功了,第二次却无效!!!

项目中的各种依赖信息

  • 下面是 package.json 配置类容
{
  "name": "test4",
  "version": "1.0.0",
  "description": "webpack test",
  "main": "webpack.config.js",
  "scripts": {
    "dev": "webpack-dev-server --config config.js --content-base dist/ --port 8080 --color --hot --inline ",
    "build": "webpack --config config.js --progress --display-modules --display-reasons --colors",
    "auto-build": "webpack --config config.js --progress --colors --watch"
  },
  "keywords": [
    "webpack",
    "test"
  ],
  "author": "yangxiuchu",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-env": "^1.6.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-es2016": "^6.24.1",
    "babel-preset-stage-2": "^6.24.1",
    "compression-webpack-plugin": "^1.0.1",
    "css-loader": "^0.28.7",
    "extract-text-webpack-plugin": "^3.0.1",
    "file": "^0.2.2",
    "file-loader": "^1.1.5",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^2.30.1",
    "style-loader": "^0.19.0",
    "url": "^0.11.0",
    "url-loader": "^0.6.2",
    "vee-validate": "^2.1.3",
    "vue-loader": "^13.5.0",
    "vue-router": "^3.0.1",
    "vue-template-compiler": "^2.5.5",
    "webpack": "^3.8.0",
    "webpack-dev-server": "^2.9.2"
  },
  "dependencies": {
    "axios": "^0.17.1",
    "bootstrap": "^3.3.7",
    "echarts": "^4.1.0",
    "iview": "^3.1.5",
    "jquery": "^3.2.1",
    "moment": "^2.22.2",
    "toastr": "^2.1.2",
    "underscore": "^1.9.1",
    "vue": "^2.5.5",
    "vue-axios": "^2.0.2",
    "vuex": "^3.0.1",
    "ztree": "^3.5.24"
  }
}

相关代码

相关代码如下:

  • A 组件相关代码
<style scoped>
...
</style>
<template>      
       ...
        <!--
            此处使用命名路由,当前跳转到子路由的时候,此视图会根据 currentView 的名字渲染,
            未了强制刷新 <router-view> 组件,在每次修改 currentView 的时候都修改 key 的值
            未当前时间戳,即 this.key = new Date().getTime(); 具体见 detail 方法。 
        -->
        <button @click="detail('dynamoDetail')">clike me</button>
        <router-view :name="currentView" :key="key"></router-view>
    </Layout>
</template>
<script>
    export default {
        data() {
            return {
                currentView : '',
                key : ''
            }
        },
        watch : {
        },
        created() {

        },
        mounted() {
            // 加载完成之后直接跳转到子路由,使命名路由生效
            this.$router.push({name : 'dynamic-power-systme-detail'});
        },
        beforeDestroy() {
        },
        methods: {
            detail(target) {
                // 更改 key, 已强制刷新 <router-view>。
                this.key = new Date().getTime();
                if(target !== this.currentView) {
                    this.currentView = target;
                }
                console.log('key is updated...');
            }
        }
    }
</script>
  • 相应路由定义部分
...
 {
    path : 'dynamic-oil-electric-system-index',
    name : 'dynamic-oil-electric-system-index',
    component : (resolve) => require(['./monitor/dynamicSystem/oilElectricSystem/index.vue'], resolve),
    children : [
        {
            path : 'detail',
            name : 'dynamic-oil-electric-system-detail',
            components : {
                'pressureGaugeDetail' : (resolve) => require(['./monitor/dynamicSystem/oilElectricSystem/pressureGaugeDetail.vue'], resolve)
            }
        }
        
    ]
}
...
  • B 组件定义如下
<style scoped>
...
</style>

<template>
    <Layout>
        <Modal
            :value="show"
            @on-visible-change="change"
            title="foo"
            :styles="{width: '50%', verticalAlign: 'center', marginTop: '5%'}">
            <div class="tz-table-wrapper">
                <Table :columns="cols" :data="rows"></Table>
            </div>
            <span slot="footer">
                {{new Date().getTime()}}
            </span>
        </Modal>
    </Layout>
</template>
<script>
    export default {
        data() {
            return {
                show : true,
                cols : [
                        {title : '编号', key : 'id'},
                        {title : '名称', key : 'name'},
                        {title : '内容', key : 'content'}
                    ],
                rows : []
            }
        },
        created() {
            console.log('dynamoDetail Component created...');
        },
        updated() {
            console.log('dynamoDetail Component updated...');
        },
        mounted() {
            console.log('dynamoDetail Component mounted...');
        },
        destroyed() {
            console.log('dynamoDetail Component destoryed...');
        },
        methods: {
            ok() {
            },
            cancel() {
            },
            change(state) {
                this.show = state;
            }
        }
    }
</script>
此组件是 A 组件的子组件,且定义为命名路由

实际情况说明

在 A 组件页面中, 点击按钮,则 B 命名路由匹配到的组件会弹出一个模态框,而实际情况也是如此,但是我关闭模态框之后,再次在 A 组件页面中点击按钮却无法弹出该模态框,打印了生命周期,输出如下:

// 第一次点击按钮之后
key is updated...
dynamoDetail Component created...
dynamoDetail Component mounted...
// 关闭模态框之后
dynamoDetail Component updated...
// 再次点击按钮之后, 这里生命周期和我了解 Vue 组件生命周期顺序不同,不知道为什么!!!???
// 这都是其次的,最重要的是这个模态框的组件不是钩子函数都触发了吗? 为什么显示不出呢?
// 而且页面上也没有实际创建 dom 元素!!!???
key is updated...
dynamoDetail Component created...
dynamoDetail Component destoryed...
dynamoDetail Component mounted...
// 再次点击和上一步是一样的结果

解决办法

  • 修改组件 B 代码如下:
<style scoped>
...
</style>

<template>
    <!--
        套一层组件,也尝试过直接用普通的 HTML 标签,如 div, span, 都能避免上述出现的问题
        而且点击按钮打印的效果是一模一样的。
    -->
    <Layout>
        <Modal
            :value="show"
            @on-visible-change="change"
            title="foo"
            :styles="{width: '50%', verticalAlign: 'center', marginTop: '5%'}">
            <div class="tz-table-wrapper">
                <Table :columns="cols" :data="rows"></Table>
            </div>
            <span slot="footer">
                {{new Date().getTime()}}
            </span>
        </Modal>
    </Layout>
</template>
<script>
    export default {
        data() {
            return {
                show : true,
                cols : [
                        {title : '编号', key : 'id'},
                        {title : '名称', key : 'name'},
                        {title : '内容', key : 'content'}
                    ],
                rows : []
            }
        },
        created() {
            console.log('dynamoDetail Component created...');
        },
        updated() {
            console.log('dynamoDetail Component updated...');
        },
        mounted() {
            console.log('dynamoDetail Component mounted...');
        },
        destroyed() {
            console.log('dynamoDetail Component destoryed...');
        },
        methods: {
            ok() {
            },
            cancel() {
            },
            change(state) {
                this.show = state;
            }
        }
    }
</script>

疑问

1、Vue 组件正常的生命周期是 brforeCreaded -> created -> mounted -> [beforeUpdated -> updated] -> beforeDestoryed -> destoryed, 而上述问题中为什么会出现 created -> destoryed -> updated 的顺序,而且在解决方案中调用的顺序也是如此?
2、为什么会出现上述的问题,而用上述的解决方案就能够解决呢? 是 IView Modal 组件自身的问题吗?

小弟在此跪求过路大佬指点!!!

阅读 3.7k
1 个回答

这是因为你加了key的原因,vue检测到key不一样就会销毁子组件,重新加载,销毁的过程中就触发了destory

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题