V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
lesismal
V2EX  ›  JavaScript

尝试用 pm.js spa 方案改造了一个原生 js 的 admin 模板项目,性能大幅提升

  •  
  •   lesismal · 2021-01-22 14:55:04 +08:00 · 2661 次点击
    这是一个创建于 1400 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前阵子发布过 pm.js ,原帖在这里:

    发布 pm.js ,包括但不限于帮助构建 web 原生单页面

    非专业前端,用词未必准确,脸皮防御值高,欢迎来喷。。。

    最近随便 fork 了个 admin 的模板项目,用 pm.js 改造了下,去掉了多个页面大量的重复内容、避免不必要的重复加载、重复下载之类的,对比 fork 原作,切换内容顺滑、性能要好太多,不谦虚地说,就是性能基本能达到极致了

    我对前端不算熟悉,所以暂时主要研究研究框架相关

    有兴趣的朋友欢迎多来指点、给些批评建议(性能提升跟 fork 原作对比就可以了):

    https://github.com/3rdrepo/adminkit

    10 条回复    2021-01-25 15:36:15 +08:00
    bmwh123
        1
    bmwh123  
       2021-01-22 17:28:40 +08:00
    有一个问题就是设置成 display none 的页面也是会加载的,那首屏的加载时间随着项目膨胀也会变长很多,这个怎么解决的呀
    lesismal
        2
    lesismal  
    OP
       2021-01-22 18:13:28 +08:00
    @bmwh123 有个 lazy 选项,lazy: true 的子页面是等到需要展示的时候才进行异步加载的,比如:

    https://github.com/3rdrepo/adminkit/blob/dev/static/js/init.js#L18

    每个页面可以配置:

    {
    src: "pages-profile.src", // 这个 id 的元素 onclick 时会切换内容到 dst
    dst: "pages-profile.html",
    url: "page/pages-profile.html",
    lazy: true, //如果 url 不为空,lazy 为 true 则等到首次显示 dst 内容时加载 url 对应的子页面
    init: function() {...}, //首次显示时调用
    onshow: function(){...}, //被显示时调用
    onhide: function(){...}, //被隐藏时调用
    }
    lesismal
        3
    lesismal  
    OP
       2021-01-22 18:17:22 +08:00
    @bmwh123 还提供了$pm.release(page),可以用于释放单个 page 的内容,如果应用层子页面内容太多想减少总资源占用压力,可以根据自己的需要、在 onhide(page) 中选择是否对该子页面内容释放,就是对该 dom 元素的 innerHTML 设置为""。
    lesismal
        4
    lesismal  
    OP
       2021-01-22 21:04:22 +08:00
    @bmwh123 抱歉才看懂问题,前面的回答默认以为大家知道实际的机制了呢。。。

    实际上单个大页面分为主页面和子页面,主页面里大概是这样子的:

    <div class="main">
    <div id="nav"></div>

    <div id="dashboard.html"></div>
    <div id="charts-chartjs.html"></div>
    <div id="forms-basic-inputs.html"></div>
    <div id="forms-layouts.html"></div>
    <div id="icons-feather.html"></div>
    <div id="maps-google.html"></div>
    <div id="pages-blank.html"></div>
    <div id="pages-invoice.html"></div>
    <div id="pages-profile.html"></div>
    <div id="pages-settings.html"></div>
    <div id="pages-sign-in.html"></div>
    <div id="pages-sign-up.html"></div>
    <div id="tables-bootstrap.html"></div>
    <div id="ui-alerts.html"></div>
    <div id="ui-buttons.html"></div>
    <div id="ui-cards.html"></div>
    <div id="ui-general.html"></div>
    <div id="ui-grid.html"></div>
    <div id="ui-modals.html"></div>
    <div id="ui-typography.html"></div>

    <div id="footer"></div>
    </div>

    每个子 div 的 id 对应一个子页面,然后才是上面 lazy 相关的回答,如果 lazy 默认是不加载的、而等用户首次点击触发 $pm.select(...) 或者代码 $pm.select(...) 时才去加载的,并不是进到主页默认就把所有加载进来
    lesismal
        5
    lesismal  
    OP
       2021-01-22 21:05:45 +08:00
    not lazy 的是进来就加载的,都可配,业务层自由管理
    alan0liang
        6
    alan0liang  
       2021-01-23 11:43:13 +08:00 via Android
    原作问题真的是太大了……基本怎么优化都能好很多
    一个 1.6M 的 app.js (优化:split chunks )被所有页面使用(优化:分 entrypoint 按需加载),还没有缓存(优化:immutable + 缓存),还没有 CDN (优化:静态资源单独走 CDN ),这能不慢吗🤣
    lesismal
        7
    lesismal  
    OP
       2021-01-23 12:34:03 +08:00
    @alan0liang 是啊,我找了好几个 bootstrap admin template,都差不多的情况。可能这些主要是为了展示 template 功能、没做优化,但是既然是作为 template,别人拿来用也是希望快速实现功能,所以性能问题还是太不友好了。
    还有很大程度上其实应该是使用传统原生 js 方式的的老技术、工程体系的锅,react vue 崛起之前的前端社区的大部分人缺少工程思维,react vue 崛起后框架强行工程优化了
    bmwh123
        8
    bmwh123  
       2021-01-25 09:28:09 +08:00
    @lesismal 听起来感觉蛮不错的 项目有文档嘛 我再研究研究,要是可以用到公司的老项目上就好了
    lesismal
        9
    lesismal  
    OP
       2021-01-25 15:34:52 +08:00
    @bmwh123 其实就是把老方案的内容摘出来放到单独的 html 子页面,index.html 里放上对应的 dom 元素,用 pm.bindPages 绑定这些 dom 元素的 id 和对应的子页面 url,可以手动 $pm.select 切换页面 也可以带上 src 配置项在对应的 dom onclick 时自动切换内容,或者配置的 dst 值就用 router,然后 $pm.listenRouter 用路由的方式自动切换,比如:

    <div id="menu" class="side_bar">
    <div style="height: 2px;">&nbsp;</div>
    <button class="button">
    <a href="#page_1" class="button">Page 1<br>Click Me</a>
    </button>
    <div style="height: 2px;">&nbsp;</div>
    <button class="button">
    <a href="#page_2">Page 2<br>Click Me</a>
    </button>
    </div>

    dom 树上两个 <a> 分别路由到:
    href="#page_1"
    href="#page_2"

    // 页面配置 && 绑定
    let pages = [
    {
    dst: "page_1",
    url: "page/page_1.html",
    },
    {
    dst: "page_2",
    url: "page/page_2.html",

    // lazy 为 true 时则第一次显示时才进行加载,否则进入页面就开始加载
    lazy: true,
    },
    ];
    $pm.bindPages(pages);

    // 显示 page_1
    $pm.select("page_1");

    // 监听路由变化、自动切换页面,比如路由切换到 #page_2 时,相当于执行 $pm.select("page_2")
    $pm.listenRouter();
    lesismal
        10
    lesismal  
    OP
       2021-01-25 15:36:15 +08:00
    完整的例子可以看这里:
    https://github.com/lesismal/pm/tree/master/examples/bind_src_dst

    还有个项目是 fork 了个别人的 admin template,用 pm.js 改造了下,可以直接对比 fork 的原作,性能提升太明显了。。
    https://github.com/3rdrepo/adminkit
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1072 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 168ms · UTC 19:00 · PVG 03:00 · LAX 11:00 · JFK 14:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.