从前有匹马叫代码
心若没有栖息的地方,到哪里都是流浪

开局不多说,先放两张图

重构前

 

 

重构后

 

 

 

起因:项目之前是jeecg boot的一个开源前端模板二开的,我也搞不明白为什么一个体量不大的项目使用这么重的模板,后来问了那个负责的同事,随便找了一个!!!

然后这个模板打包出来的代码很大,刚开始能达到20MB !!!,然后主管安排我来优化,包括后续的开发重构等也是我来负责,算是我成了这个项目的负责人了,本着一码归一码的

原则,我还是对这个项目进行了一些力所能及的优化,但是性能依旧很差劲,经过webpack打包工具结合控制台分析之后,发现该项目有很严重的性能问题,主要有如下几个方面:

1. jeecg boot 模板自身耦合度特别高,很笨重,而且集成了Antdesign vue

2. 同事全局引入了 element ui 和 各种 我不知道用途的插件

3. JavaScript 部分严重冗余,很多代码明明用不上,但是还是在入口文件中导入了,导致入口文件特别大

4. 滥用动态导入 () => import()

5. 完全没有进行代码拆分

 

然后为了让用户用的放心,用的开心 (违心话语...)

我有必要把这个项目当成自己的项目来打造,于是,开始了以下的过程

要想性能优化,肯定得舍弃掉他原来的模板,所以第一步就是:自己手写一个简单但功能相当的“外壳”,于是有了如下代码:

<template>
    <el-container>
        <el-aside :width="collapse ? '60px' : 'var(--menu-width)'">
            <Menu></Menu>
        </el-aside>
        <el-container>
            <el-header>
                <Navbar></Navbar>
            </el-header>
            <el-main>
                <Navigation></Navigation>
                <AppMain></AppMain>
            </el-main>
        </el-container>
    </el-container>
</template>
//此处省略若干代码片段....

路由权限校验缓存部分全部重写

import router from '@/router/index'
import storage from '@/helpers/storage'
import store from '@/store/index'

router.beforeEach((to, from, next) => {


     //此处省略若干代码片段...

    if (storage.userInfo.find()) {
        insertMenuToStore()
        insertUserinfoToStore()
        insertNavigationToStore()
        if (to.name === 'login') {
            return next({
                name: from.name,
            })
        }
    }

    next()
})

function insertMenuToStore() {
    const userInfo = JSON.parse(storage.userInfo.find())
    if (Array.isArray(userInfo.roleDTO.list)) {
        store.commit('set_menu', userInfo.roleDTO.list)
    }
}

function insertUserinfoToStore() {
    const userInfo = JSON.parse(storage.userInfo.find())
    store.commit('set_userinfo', userInfo)
}

function insertNavigationToStore() {
    const navigations = storage.navigations.find()
    if (navigations) {
        store.commit('set_navigations', JSON.parse(navigations))
    }
}

构造好壳子和路由部分之后,原来项目中我们基本上复写了所有代码,再由于,路由部分的逻辑还是按照原来的逻辑来写的,所以代码可以直接拿过来

第二步,就是 去掉Antdesign Vue,因为在原来项目中,我发现只有菜单和快捷导航部分和布局部分. 也就是说壳子部分是Antdv来写的,他自己又全局引入了Elementui

所以我在第一步用Elementui实现了壳子,此时Antdv已经完全没用了,删掉即可

第三步,我就进行了入口文件提纯,因为原来的项目入口文件中充斥了各种各样的组件,包,代码,大部分都是jeecg带的,还有一部分我也不知道是干嘛的,

然后我基于项目业务对main.js中的内容进行不断对比删除,最终只留下必要的代码和导入

第四步,就是router.config.js中路由规则很多,所有的路由规则全部采用的是 动态导入,打包之后发现有很多 10kb 以下的 js 和 css文件,因为动态导入的.vue文件会被编译成独立的.js和.css文件来引入,所以一个这种小的文件,明明没占多少空间,却白白浪费了两次http请求去加载文件,我首先把一些小文件的动态导入全部改成直接导入,然后再把我项目中的config文件夹下的代码单独分割成一个文件,控制分割代码的生成最小尺寸为30 * 1000 kb,具体配置接下来会说的

第五步,就是项目构建优化部分,分析如下:

项目用了很多elementui的组件,没有意义再去按需加载,项目用了lodash,项目用了echarts,代码合理分割,减小入口文件体积,

所以基于以上分析,我决定对 elementui echarts vue 采用 cdn的方式引入,然后代码分割,将入口文件代码中的大文件,分割出来,让浏览器并行加载

代码如下:

// vue.config.js
const { defineConfig } = require('@vue/cli-service') const LodashModuleReplacementPlugin = require('lodash-webpack-plugin') const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') module.exports = defineConfig({ productionSourceMap: false, transpileDependencies: true, chainWebpack: (config) => { config.externals({ echarts: 'echarts', vue: 'Vue', 'element-ui': 'ELEMENT', }) config.plugin('loadshReplace').use( new LodashModuleReplacementPlugin({ shorthands: true, }) ) if (process.env.NODE_ENV === 'development') config.plugin().use(new BundleAnalyzerPlugin()) config.output.chunkFilename('[name].[hash:4].js') config.optimization.splitChunks({ minSize: 30 * 1000, cacheGroups: { srcConfig: { test: /[\\/]src[\\/]config[\\/]/, name: 'srcConfig', chunks: 'all', priority: 5, }, vendors: { test: /[\\/]node_modules[\\/]/, name: 'verdors', chunks: 'all', priority: 5, }, }, }) }, })

 

<!DOCTYPE html>
<html lang="">
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width,initial-scale=1.0" />
        <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
        <title>*********</title>
        <script src="https://cdn.bootcss.com/echarts/4.7.0/echarts-en.common.min.js"></script>

        <script src="//cdn.staticfile.org/vue/2.6.11/vue.min.js" defer></script>
        <script
            defer
            crossorigin="anonymous"
            integrity="sha512-BFTa+pOeQxEAgwQyr6fiiE0obFmW6DxY5lRV+yamYVJVgbfQKK5ZYs9wpprA51If77YXnqPvbmXNMmStfctlqA=="
            src="//lib.baomitu.com/element-ui/2.15.5/index.js"
        ></script>
        <link
            crossorigin="anonymous"
            integrity="sha512-D0p8FADInHcCyZ+ZKKz2A3xHSWnzJZWdmNfQIVOETyJa/ohn5FpD8/AqD46mwDeSPl9hfFHKDKxhogTiWQyv3g=="
            href="//lib.baomitu.com/element-ui/2.15.5/theme-chalk/index.min.css"
            rel="stylesheet"
            media="bogus"
        />
    </head>
    <body>
        <noscript>
            <strong
                >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't
                work properly without JavaScript enabled. Please enable it to
                continue.</strong
            >
        </noscript>
        <div id="app"></div>
        <!-- built files will be auto injected -->
        <link
            crossorigin="anonymous"
            integrity="sha512-D0p8FADInHcCyZ+ZKKz2A3xHSWnzJZWdmNfQIVOETyJa/ohn5FpD8/AqD46mwDeSPl9hfFHKDKxhogTiWQyv3g=="
            href="//lib.baomitu.com/element-ui/2.15.5/theme-chalk/index.min.css"
            rel="stylesheet"
        />
    </body>
</html>

注意:这里ElementUI的css cdn 引入方式,为什么要这样写,因为是想在页面渲染之前跳过 css 的 render

Meanwhile, way up in the <head>, browsers insist on downloading stylesheets with media attributes they could never fulfill, like media="print" without a printer connected

文章在此

意思就是:这个css其实就会异步的去下载,并应用,不会引起 render blocking,然后我们这里指定的media是 bogus,所以浏览器不会进行 render blocking

关于media属性

然后我们在body属性再插入一个相同的但是没有 media属性的link标签,这样就可以等DOM渲染完成再去 执行css文件了,又因为在head中css文件已经被下载好了,所以这里直接就能用,不会去再次请求该资源

以上就是我对该项目进行优化的方式

22:13:36

 

posted on 2022-03-15 22:14  从前有匹马叫代码  阅读(346)  评论(0编辑  收藏  举报