├── .gitignore
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
├── favicon.ico
└── index.html
└── src
├── App.vue
├── assets
└── logo.png
├── components
├── HelloWorld.vue
├── f1.vue
├── f2.vue
└── iframe-router-view.vue
└── main.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 | 最近一个需求,需要在**Vue**项目中加入**含有iframe**的页面,同时在路由切换的过程中,要求iframe的内容**不会被刷新**。一开始使用了Vue自带的keep- alive发现没有用,于是自己研究了一下解决方案。。。。。。
3 |
4 | ## Vue的keep-alive原理
5 | 要实现对保持iframe页的状态。我们先搞清楚为什么Vue的keep-alive不能凑效。keep-alive原理是把组件里的节点信息保留在了**VNode**(在内存里),在需要渲染时候从Vnode渲染到真实DOM上。iframe页里的内容并不属于节点的信息,所以使用keep-alive依然会重新渲染iframe内的内容。**另外**,我也尝试有过想法:如果把整个iframe节点保存起来,然后需要切换时把它渲染到目标节点上,能否实现iframe页不被刷新呢?————也是不可行的,iframe每一次渲染就相当于打开一个新的网页窗口,即使把节点保存下来,在渲染时iframe页还是刷新的。
6 |
7 | ## 实现的思路
8 | 既然保持iframe页里的状态很难实现,在这个时候我想到了一个别的方法。能否在Vue的route-view节点上动点手脚?使得在切换**非iframe页**的时候使用Vue的路由,当切换**iframe页**时则使用**v-show**切换显示与隐藏,使得iframe节点**一直不被删除**,这样就能保持iframe的状态了。
9 |
10 | 我们简陋的实现一下以上的效果,上代码:
11 |
12 | 入口main.js:
13 | ```
14 | import Vue from 'vue/dist/vue.js'
15 | import App from './App.vue'
16 | import VueRouter from 'vue-router';
17 |
18 | const Index = { template: '
Index
' }
19 | const routes = [
20 | // 含有iframe的两个页面
21 | {
22 | path: '/f1',
23 | name: 'f1'
24 | },
25 | // 含有iframe的两个页面
26 | {
27 | path: '/f2',
28 | name: 'f2'
29 | },
30 | {
31 | path: '/index',
32 | component: Index
33 | }
34 | ]
35 |
36 | const router = new VueRouter({
37 | routes
38 | });
39 |
40 | Vue.use(VueRouter);
41 | new Vue({
42 | render: h => h(App),
43 | router
44 | }).$mount('#app')
45 |
46 | ```
47 | 根组件:
48 |
49 | ```
50 |
51 |
52 |
53 | Go to F1
54 | Go to F2
55 | Go to Index
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
81 | ```
82 | 上面代码简单来说,关键的地方首先是main.js初始化路由时,对iframe页不填写属性component,这样页面就是空白的。然后在**router-view**节点旁边渲染iframe页组件,使用$route.path判断当前路由的指向,控制iframe页的**显示与隐藏**。
83 |
84 | 上面代码简单的解决了问题,但还有一些地方可以优化:
85 | 1. iframe页在根节点App.vue一渲染时**已经渲染**了,对此iframe页可以做成**懒加载**,只有在进入过相应页面了触发渲染,并且渲染过之后就用v-show切换显示与隐藏
86 | 2. 每当增加一个iframe页都要增加一段的组件引入注册和调用的代码。比较**繁琐**。我们目标应该做到每增加一个iframe页,只需要添加尽量少的代码。这里思路是:
87 | 1. 在路由配置中定义一个属性,用于**标识该页面是否含有iframe**的页面
88 | 2. 根据标识,iframe页组件**自动动态注册和渲染**,无需再手写额外的代码
89 | 3. router-view和iframe切换的逻辑封装成**新组件**,用它**替代原有的router-view**
90 |
91 | 我们先修改router的配置,增加一个属性名iframeComponent,用于标识是否包含iframe,该属性的值是组件文件引用
92 |
93 | main.js:
94 | ```
95 | import F1 from './components/f1';
96 | import F2 from './components/f2';
97 |
98 | const routes = [
99 | {
100 | path: '/f1',
101 | name: 'f1',
102 | iframeComponent: F1 // 用于标识是否含有iframe页
103 | },
104 | {
105 | path: '/f2',
106 | name: 'f2',
107 | iframeComponent: F2 // 用于标识是否含有iframe页
108 | },
109 | {
110 | path: '/index',
111 | component: { template: 'Index
' }
112 | }
113 | ]
114 |
115 | const router = new VueRouter({
116 | routes // (缩写)相当于 routes: routes
117 | });
118 |
119 | new Vue({
120 | render: h => h(App),
121 | router
122 | }).$mount('#app')
123 |
124 | ```
125 |
126 | 接下来我们第二步和第三步结合在一起,封装新的组件iframe-router-view.vue:
127 | ```
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
142 |
143 |
144 |
145 |
204 | ```
205 | 1. 该组件主要做的是根据main.ja里的routes生成一个只含有iframe页的数组对象。
206 | 2. watch上监听$route,判断当前页面在iframe页列表里的话就设置hasOpen属性为true,渲染该组件
207 | 3. 用v-show="$route.path === item.path"切换iframe页的显示与隐藏。
208 |
209 | 逻辑并不复杂,这里就不多赘述。
210 |
211 | ## 结语
212 | 大家如果有更好的实现方法,或者我上面还有什么需要更正的错误,欢迎交流。
213 | 上面demo的代码放在了个人github上[https://github.com/jmx164491960/vue-iframe-demo](https://github.com/jmx164491960/vue-iframe-demo)
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello-world",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "core-js": "^2.6.5",
12 | "vue": "^2.6.10",
13 | "vue-router": "^3.0.7"
14 | },
15 | "devDependencies": {
16 | "@vue/cli-plugin-babel": "^3.9.0",
17 | "@vue/cli-plugin-eslint": "^3.9.0",
18 | "@vue/cli-service": "^3.9.0",
19 | "babel-eslint": "^10.0.1",
20 | "eslint": "^5.16.0",
21 | "eslint-plugin-vue": "^5.0.0",
22 | "vue-template-compiler": "^2.6.10"
23 | },
24 | "eslintConfig": {
25 | "root": true,
26 | "env": {
27 | "node": true
28 | },
29 | "extends": [
30 | "plugin:vue/essential",
31 | "eslint:recommended"
32 | ],
33 | "rules": {},
34 | "parserOptions": {
35 | "parser": "babel-eslint"
36 | }
37 | },
38 | "postcss": {
39 | "plugins": {
40 | "autoprefixer": {}
41 | }
42 | },
43 | "browserslist": [
44 | "> 1%",
45 | "last 2 versions"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmx164491960/vue-iframe-demo/9af5104009044c4335c3a817165b061a25073691/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | hello-world
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Go to F1
5 | Go to F2
6 | Go to Index
7 |
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 |
39 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmx164491960/vue-iframe-demo/9af5104009044c4335c3a817165b061a25073691/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
5 | For a guide and recipes on how to configure / customize this project,
6 | check out the
7 | vue-cli documentation.
8 |
9 |
Installed CLI Plugins
10 |
14 |
Essential Links
15 |
22 |
Ecosystem
23 |
30 |
31 |
32 |
33 |
41 |
42 |
43 |
59 |
--------------------------------------------------------------------------------
/src/components/f1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/components/f2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/components/iframe-router-view.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
77 |
78 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue/dist/vue.js'
2 | import App from './App.vue'
3 | import VueRouter from 'vue-router';
4 | import F1 from './components/f1';
5 | import F2 from './components/f2';
6 |
7 | const Index = { template: 'Index
' }
8 | const routes = [
9 | {
10 | path: '/f1',
11 | name: 'f1',
12 | iframeComponent: F1
13 | },
14 | {
15 | path: '/f2',
16 | name: 'f2',
17 | iframeComponent: F2
18 | },
19 | {
20 | path: '/index',
21 | component: Index,
22 | children: [
23 | {
24 | path: '/f3',
25 | iframe: true
26 | }
27 | ]
28 | }
29 | ]
30 |
31 | const router = new VueRouter({
32 | routes // (缩写)相当于 routes: routes
33 | });
34 | Vue.config.productionTip = false
35 |
36 | Vue.use(VueRouter);
37 | new Vue({
38 | render: h => h(App),
39 | router
40 | }).$mount('#app')
41 |
--------------------------------------------------------------------------------