【注意】最后更新于 June 15, 2020,文中内容可能已过时,请谨慎使用。
vue 不使用 webpack,vue_router 怎么异步
公司项目都是后台管理项,肯定是导航加主显示区的方式切换,然后就想着用 vue_router 看看。
然后发现 vue_router 的 component 是同步加载的。。要是后台功能多 js 就很大了,这个就是所谓的单页应用。
但是单页应用不用 webpack 打包手动把 js 写一个也太蛋疼了。然后发现了 webpack 用 chunk 可以异步会把每个路由的 js 单独打包。
具体的原理下面也会讲解。
零、一些注意事项
- 源码在这里
一、vue_router_demo
先来个 vue_router 的 demo 用 requirejs 加载其它 js。
vue_router_demo.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Title</title>
<script
type="text/javascript"
data-main="js/main.js"
src="./bower_components/requirejs/require.js"
></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
|
js/main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| (function(require, define) {
require.config({
paths: {
vue: "https://cdn.bootcss.com/vue/2.0.5/vue",
"vue-router": "https://cdn.bootcss.com/vue-router/2.0.1/vue-router"
}
});
define(function(require, exports, module) {
var Vue = require("vue");
var VueRouter = require("vue-router");
Vue.use(VueRouter);
var router = new VueRouter({
routes: [
{
path: "/component1",
component: function(resolve) {
resolve(require("js/component1.js"));
}
},
{
path: "/component2",
component: function(resolve) {
resolve(require("js/component2.js"));
}
}
]
});
var app = new Vue({
el: "#app",
router: router,
template: '<div class="content">\
<h1>Hello Content!</h1>\
<p>\
<router-link to="/component1">Go to Component1</router-link>\
<router-link to="/component2">Go to Component2</router-link>\
</p>\
<router-view></router-view>\
</div>'
});
});
})(require, define);
|
js/component1.js
1
2
3
4
5
6
7
| (function(define) {
define(function(require, exports, module) {
return {
template: "<span>component1</span>"
};
});
})(define);
|
我看了 webpack 的.vue 编译完后导出的对象也不过是有特定属性的对象,必须有template
或者render
其中一个。
js/component2.js
1
2
3
4
5
6
7
| (function(define) {
define(function(require, exports, module) {
return {
template: "<h1>component2</h1>"
};
});
})(define);
|
再来一个不一样的。
示例效果图
源码
在 git 仓库的 vue_router_demo 分支。
总结
看了图我想你已经发现了问题明明component
用的 require 但是怎么回事第一次刷新首页都引入了。
实际上因为 requirejs 的 require 会收集所有使用 require 的地方并一同加载甚至定义一个方法里就 require 一个模块,也会预先加载 js。
这个结果照成我们的异步变成了多文件 SPA 在一开始就会把所有模块导入。如果想做这样的 SPA 不要使用这样的方法,请使用 webpack 打包成一个文件减少请求。
二、vue_router_async_demo
现在我们就来一步步分析怎么做到。
查找资料
通过搜索引擎的查找翻到了一篇博文分享(Angular 和 Vue)按需加载的项目实践优化方案
里面给了他的参考资料:
- 异步组件
- webpack
然后我写了一个测试效果是我想要的异步。然后看了生成的代码异步用的 js 的开头webpackJsonp([1,3],[...])
。
而导入模块的地方使用__webpack_require__([1,3], resolve)
还有__webpack_require__
中有这样一段代码:
1
2
3
4
5
6
7
| var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.charset = "utf-8";
script.async = true;
script.src = ""; // del;
head.appendChild(script);
|
这下就明白了__webpack_require__(chunkIds, callback)
调用后查找是否已有chunkIds
的模块有的话通过callback
调用返回。
没有就通过chunkIds
找到文件并在head
中添加一个script
加载新 js 然后 js 中调用webpackJsonp
并通过callback
返回模块。
好了这下找到方法了我们自己写个webpackJsonp
和webpack_require
就好了。代码可能有点多用注释的方式来解释。
js/webpackjsonp.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| (function(define) {
define(function() {
// 模块缓存区。
var webpackJsonpModule = {};
// 回调方法缓存区。
var webpackInstallModule = {};
// 引入的js所需调用的方法。
var webpackJsonpCallback = function webpackJsonpCallback_(
chunkName,
Module
) {
// 判断模块名
if (webpackJsonpModule[chunkName]) {
console.warn(
"模块名: " +
chunkName +
"已存在!为其旧模块重命名到:" +
chunkName +
"_"
);
webpackJsonpModule[chunkName + "_"] =
webpackJsonpModule[chunkName];
}
// 调用模块的方法获得模块并设置到模块缓存区。
webpackJsonpModule[chunkName] = Module();
// 取出回调方法
resolve = webpackInstallModule[chunkName];
if (resolve) {
// 调用回调方法
resolve(webpackJsonpModule[chunkName]);
// 删除已有回调方法
webpackInstallModule[chunkName] = undefined;
}
};
// 添加script标签.
var webpackJsonp = function webpackJsonp_(chunkName, resolve) {
webpackInstallModule[chunkName] = resolve;
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.charset = "utf-8";
script.async = true;
script.src = chunkName;
head.appendChild(script);
};
// 获取模块方法。
var webpack_require = function webpack_require_(chunkName, resolve) {
if (chunkName) {
var Module = webpackJsonpModule[chunkName];
if (Module) {
resolve(Module);
} else {
webpackJsonp(chunkName, resolve);
}
}
};
// 将webpackJsonpCallback设置到全局上。
window["webpackJsonpCallback"] = webpackJsonpCallback;
// 返回webpack_require给requirejs调用。
return webpack_require;
});
})(define);
|
好了有了这个就可以做下面的了。
js/main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| (function(require, define) {
require.config({
paths: {
vue: "https://cdn.bootcss.com/vue/2.0.5/vue",
"vue-router": "https://cdn.bootcss.com/vue-router/2.0.1/vue-router",
webpackjsonp: "/js/webpackjsonp"
}
});
define(function(require, exports, module) {
var Vue = require("vue");
var VueRouter = require("vue-router");
Vue.use(VueRouter);
var webpack_require = require("webpackjsonp");
var router = new VueRouter({
routes: [
{
path: "/component1",
component: function(resolve) {
webpack_require("/js/component1.js", resolve);
}
},
{
path: "/component2",
component: function(resolve) {
webpack_require("/js/component2.js", resolve);
}
}
]
});
var app = new Vue({
el: "#app",
router: router,
template: '<div class="content">\
<h1>Hello Content!</h1>\
<p>\
<router-link to="/component1">Go to Component1</router-link>\
<router-link to="/component2">Go to Component2</router-link>\
</p>\
<router-view></router-view>\
</div>'
});
});
})(require, define);
|
html 不变把里面的 component 改好即可
js/component1.js
1
2
3
4
5
6
7
| (function(define) {
define("/js/component1.js", function(require, exports, module) {
return {
template: "<span>component1</span>"
};
});
})(webpackJsonpCallback);
|
这个改动就比较大了。
js/component2.js
1
2
3
4
5
6
7
| (function(define) {
define("/js/component2.js", function(require, exports, module) {
return {
template: "<h1>component2</h1>"
};
});
})(webpackJsonpCallback);
|
多了一个模块名。。这样效果不是很好希望下回能研究出不需要模块名的。
效果图
源码
在 git 仓库的 vue_router_async_demo 分支。
文章作者
上次更新
2020-06-15 18:12:42 +08:00
(9c054d8)
许可协议
CC BY-NC-ND 4.0