好几步优化超大单页应用启动速度,3分钟=>30秒

em,要怎么才能不让文章那么枯燥呢?

序言

一篇好的文章,技术是不是不太必要,关键是不是要有好的故事呢?


image

故事开始:从前从前山上有座庙,庙里有个老和尚,和一群小和尚。那天老和尚对小和尚说:系统大了,速度慢了,臃肿了,是时候优化了。有个趋势叫“微前端”,就你了,搞着去吧。
小和尚:...
小和尚很努力的查找资料,查看书籍,准备demo,请教大佬。日子一天天的过去,眼瞅事情要黄了...
小和尚收拾收拾东西,赶紧下山...
end

image

故事环境

祖传代码,一代传一代。

  • 1.好的,我们先分析一下当前项目环境吧
# 进入项目
cd project/
# 查看项目下有多少文件
ls -lR| grep "^-" | wc -l
=> 43305 # 眉头一紧,事情不对,这么多文件
# 删除项目依赖
rm -rf node_modules
# 在看一次
ls -lR| grep "^-" | wc -l
=> 1408 # 眉头又是一紧,事情没有不对,就是文件真的多
  • 2.然后,em,我们统计一下有多少路由?em,怎么分析呢?
const projectRoute = {
    path: "project",
    component: (resolve) => require(["@/vankely/sub-layout.vue"], resolve),
    children: [ {
        path: "shopConditions",
        meta: "/admin/project/shopConditions",
        component: ShopConditionsIndex
    }]
};

就简单的统计一下‘path’出现的次数吧,虽然不怎么严谨,但是,我们不是写技术文章的呀

grep -rn "path" src/router/ | wc -l
=> 474  # em,四百多个页面,教练,我要去学做菜
  • 3.我们统计一下编译时间吧
    打开命令行,一顿操作。
yarn  dev # 开始编译
=> 项目启动:2019-06-14 04:14:13
=> ...
=> 期间:我去上了个厕所
=> 期间:我去到了杯水
=> 期间:我发了个呆,比特币上100w美金了
=> ....
=> 项目成功:2019-06-14 04:18:56
=> 项目耗时:4分钟43秒
=> Listening at http://0.0.0.0:8080/

em,就只看我的电脑,是不是不严谨,找隔壁的小姐姐问了一下他的启动时间。

p2.jpg

11分钟,em.
em,对不起,公司发的我的笔记本,再也不嫌弃你性能差了。

后面发现是因为node版本太低,老年车,拖拉机。升级到最新的后,也是3分多钟。
em,垃圾笔记本,性能真差(嫌弃的敲了一下笔记本,结果删了点代码,隔天真开心)。

image

正文

本文不讲那些webpack升级,happyPack,DLLPlugin等等很多技巧。

主要是作用不大,量变引起质变,算以上能带来50%以上的时间优化,但是,文件数量在那,总和的时间也不会低。

关键原因:不会。

我们找到根本原因是文件数量太多,那我们启动的时候给他减肥减肥

  • 1.找到路由文件,删,代码全删了

/src/router/index.js
只留页面启动必要的页面,其他的删


const router = new Router({
    mode: "history",
    routes: [
        {
            path: "/login",
            meta: { permission: "public" },
            component: Login
        },
      {
            path: "/user",
            meta: { permission: "public" },
            component: User
        }
        
    ]
});
export default router;

启动一下:18秒。
em,快是真的快了,可怎么开发呢?
啥页面都没有。

image
  • 2.写个脚本动态修改路由文件,动态添加路由模块

哎呦,这可真的秀 ~~. ლ(・ω・ლ)

em,那就建文件专门存放路由模块吧!
然后,我们就写个脚本专门修改这个文件吧
src/router/routes-modules.js

import routes1 from "./routes1";
import routes2 from "./routes2";
import routes3 from "./routes3";
const syncRoutes = [
            ...routes1,
            ...routes2,
            ...routes3
];
export default syncRoutes;

修改 src/router/index.js

import syncRoutes from './routes-modules'
const router = new Router({
    mode: "history",
    routes: [
        {
            path: "/login",
            meta: { permission: "public" },
            component: Login
        },
      ... syncRoutes
    ]
});
export default router;

em,那我要怎么配置加载什么文件呢?
新增 src/router/router-config.json,为什么用json呢,因为开心。(主要是为了方便以后,方便es模块导入和node模块导入)

[
    {
        "name": "模块1",
        "key": "route1",
        "path": "./route1/index",
        "use": true
    },
    {
        "name": "模块1",
        "key": "route2",
        "path": "./route2/index",
        "use": false
    },
{
        "name": "模块1",
        "key": "route2",
        "path": "./route2/index",
        "use": false
    }
]

em,这个到底是干嘛用的?
经过脚本加工,然后修改src/router/routes-modules.js文件的内容,将“use”为true的输出到文件里面
得到:

import routes1 from "./routes1";
const syncRoutes = [
           ...routes1
];
export default syncRoutes;

以上:就是优化的原理。

  • 3.脚本实现修改文件

在webpack配置文件里,新增一个脚本文件,伪代码,因为细节全上就太多了。
/build/init-route.js

const template = `
$$$___import___$$$
const syncRoutes = [
       $$$___routes___$$$
];
export default syncRoutes;
`;
const fs = require("fs");
const path = require("path");
const isProd = process.env.NODE_ENV === "production";
// 从项目根目录起
const resolve = dir => path.join(__dirname, "../..", dir);
const routesConf = resolve("src/router/router-config.json")
const routeModules = resolve("src/router/routes-modules.js")
const initRoute = (modules = []) => {
      let  content = template;
      // 文件是否存在, 不存在就生成
     if (fs.existsSync(routeModules)) {
           content=content.replace("$$$___import___$$$", "").replace("$$$___routes___$$$", "");
           // 创建文件,并写入内容
           createFile();
           return;
     }
     const contObj = {
        "$$$___import___$$$": "",
        "$$$___routes___$$$": ""
    }
    routesConf.forEach(r => {
        if (r.use || modules.includes(r.key)) {
             contObj["$$$___import___$$$"] += `\nimport ${rou.key} from "${rou.path}";`;
             contObj["$$$___routes___$$$"] += `\n ...${rou.key},`;
        }
    })
 content=content.replace("$$$___import___$$$", contObj["$$$___import___$$$"])
                     .replace("$$$___routes___$$$", contObj["$$$___routes___$$$"]);
    // 将文字内容替换到  src/router/routes-modules.js
    updateFile(content);
};
module.exports = initRoute;

说明:
脚本暴露的方法,用于传入数组,然后更新src/router/routes-modules.js,然后webpack文件系统,监听到文件变化自动重启。

  • 4.脚本怎么用呢?需要的时候node build/init-router?

为了更有好的开发,就想着写个快捷入口,放页面。

p3.png

biubiu,biubiu生成
组件上的选项来自"src/router/router-config.json"
当我点击 ‘生效’ 的时候,将中的key传给后端?
em,没后端!
当我点击生成的时候,将中的key传给谁?
em,那我当个后端,牛逼前后端,全栈(不接受反驳)。
em,webpack是express起的服务。

找到启动webpack的express文件,我的叫devServer.js。
你的叫什么,不要问我,我也不知道。

/*eslint no-console: 0 */

const express = require("express");
const webpack = require("webpack");
const webpackConfig = require("./webpack.config");
const bodyParser = require("body-parser");

const initRoute = require("./sync-router/init-router");
initRoute([]);


const app = express();
app.use(bodyParser.json());

// 添加一个post拦截请求  用来监听是否要修改  src/router/routes-modules.js  文件
app.post("/__setting", async (req, res) => {
    await initRoute(req.body.routes);
    res.send("hello world"});
});

const compiler = webpack(webpackConfig);
const port = appConfig.devServer.port;
const devMiddleware = require("webpack-dev-middleware")(compiler,);
const hotMiddleware = require("webpack-hot-middleware")(compiler);

app.use(require("connect-history-api-fallback")());
app.use(devMiddleware);
app.use(hotMiddleware);

app.use("/", express.static(utils.resolve("static")));

app.listen(port, (err) => {
    console.log(`Listening at http://0.0.0.0:${appConfig.devServer.port}/`);
    opn(`http://127.0.0.1:${port}`);
});
image
  • 5.最终效果

em,累死我了。

p4.png

上点优化后的启动时间变化:

  • 1.window1o
    优化前:
    1975557014.jpg

优化后:

335275594.jpg
  • 2.linux-manjaro
    优化前:
优化前

优化后:

优化后

总结

微前端没做成,倒是达成一项新成就,逗逼的写了一篇文章。

和尚是没头发的。

em。

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