性能优化-CSS内联

先看实践效果

一、业务背景:

  1. 秒开率目标
  2. 目前运营活动中的CSS文件是犬粮打包进js文件中
    1. 优点:是方便管理,减少首次请求数量
    2. 缺点:首次加载时需要下载整个JS文件增加首次加载时间,以及无法使用css缓存;不能和js并行下载;
    3. 尤其目前我们业务中有一个比较大的公共style文件,页面每个组件中都需要单独引入;
    4. 项目中没有进入css的treeShaking;

二、优化方向

markdown 复制代码
1.  将`src/style`中的公共样式打包成一个单独的css文件压缩并上传到CDN,link到html中进行预加载;
2.  设置全局stylus变量
3.  css拆分,首屏css内联(Critial CSS);其他css延迟加载(或者按需加载)
4.  非首屏css延迟加载(或者按需加载)
5.  引入CSS的treeShaking

三、 具体实施

1. 公共CSS上传CDN并进行预先加载

2. 设置全局stylus变量

项目中用的stylus,里面封装了一套适配项目的一些简化css编写的函数,如设置背景图的工具函数如下:

在使用时,我们需要在页面中引入该函数,如下所示

每次都要引入; 可以释放这部分工作

js 复制代码
module.exports = {
    css: {
        ...,
        loaderOptions: {
            stylus: {
                 // 设置全局stylus变量
                import: [
                  '~@/style/stylusSet/index.styl',
                ]
            }
        }
    }
}

3. CSS拆分,主屏CSC内联

CSS内联需要使用html-inline-css-webpack-plugin

【原理】: 将CSS打包成单独Chunk; 遍历所有的css-chunk,将其复制设置index.html的header头下(默认);然后(默认)删除掉所有的css-chunk;

这种简单粗暴的实现方式有几个隐藏的问题,后面讲到

3.1 引入时要注意有default

js 复制代码
const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").default;
  • 官方文档webpack中配置如下

3.2 在vue-cli中写法

但是实际在我们的vue-cli中并不能这样配置,因为里面已经有很多默认配置了;可以通过vue inspect > webpack.config.js查看

  • 必须将css打包成单独chunk
js 复制代码
module.exports = {
    css: {
        extract: true,
    }
}

多页面HtmlWebpackPlugin优化处理

js 复制代码
const minifyHtmlPlugins = (pages) => {
  return Object.values(pages).map((page) => {
    return new HtmlWebpackPlugin({
      template: page.template,
      filename: page.filename,
      chunks: page.chunks,
      inject: true,
      minify: {
        html5: true,
        minifyCSS: true, // 压缩内联CSS
        minifyJS: true, // 压缩内联JS
        collapseWhitespace:true,
        keepClosingSlash:true,
        removeComments: true, // 去除注释
        removeRedundantAttributes:true,
        removeScriptTypeAttributes:true,
        removeStyleLinkTypeAttributes:true,
        useShortDoctype: true
      },
    })
  })
}

css内联到html中

js 复制代码
module.exports = {
  configureWebpack: config => {
    ...,
    // 单独提取css文件(这里添加后会导致HTMLInlineCSSWebpackPlugin中有2份样式)
    // new MiniCssExtractPlugin({
    //   filename: "[name].css",
    //   // chunkFilename: "[id].css"
    // }),
    config.plugins.push(...[
         // 压缩html以及里面的css/js等
        ...minifyHtmlPlugins(pages),
        // 将单独提取的css文件内联到html中(这行要放在HtmlWebpackPlugin后)
        new HTMLInlineCSSWebpackPlugin()
    ])
  }
}

上述注视注意,vue-cli.js中通过开启css.extract = true时已经处理了MiniCssExtractPlugin; 这里如果再写,将会导致html中copy两份一样的css;所以不要再写;

3.3 问题第一弹

通过build本地打包,并启动http-server后,预览页面,发现页面中样式图片路径均是404

审查html中的样式代码发现如下

里面图片路径均是../

这里我们执行vue inspect --mode product > webpack.config.js

查看到css-chunk默认生成路径在css文件夹下

同时查看,默认设置的单独提取css中设置的publicPath为'../'

这里我这边做测试修改; 直接在plugin中试图覆盖

再次执行vue inspect --mode product > webpack.config.js命令查看改动没有生效;所以得换方式

js 复制代码
module.exports = { 
    chainWebpack: config => {
        config.plugin('extract-css')
        .use(MiniCssExtractPlugin, [
            {
                filename: '[name].[contenthash:8].css',
                chunkFilename: '[name].[contenthash:8].css'
            }
       ]);
   }
 }

再次执行vue inspect --mode product > webpack.config.js

已经更新成功

BUT 执行build时报错:Error: No module factory available for dependency type: CssDependency; 原因待查......

这里尝试不改变css生成的路径,直接改css中引入的背景图路径的法子; 如上直接改publicPath

js 复制代码
module.exports = { 
    chainWebpack: config => {
        config.module
          .rule('stylus')
          .oneOf('vue')
          .use('extract-css-loader')
          .tap(options => {
            options.publicPath = '/'
            return options;
          });
      }
    }
}

执行vue inspect --mode product > webpack.config.js 和执行build后图片路径变成如下

4. 非首屏css按需加载

按需加载需要vue写的时候用组件异步加载的写法写;

js 复制代码
const GuardTreasure = () => import('./components/treasure/guard-treasure.vue')

这样引入的css文件会被link到JavaScript文件中,在运行时需要时加载,而不会inline到html中;

4.1 问题第二弹

但是我们这样写了后发现异步组件加载时会报https://xxxxx/css/chunk-0950ed4p.css404;

查找原因就是因为先前提到的html-inline-css-webpack-plugin原理中,在copy了依赖的CSS后,在删除时是将整个CSS文件夹删除,导致异步组件中link的css-chunk也被意外删除;

看源码以及官网说明,我们绝对直接保留css文件夹;这样唯一的后果就是打包后的dist/css中多了一些已经被inline到html中的无用样式,浪费存储,但其实并不会引入和读取,没有其他副作用了; 所以我们要设置一个属性

js 复制代码
      new HTMLInlineCSSWebpackPlugin({
        leaveCSSFile: true,
      }),

5. CSS的Tree Shaking 擦除无用的样式

js 复制代码
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
module.exports = { 
    chainWebpack: config => {
    config.plugin.push(
        new PurgeCSSPlugin({
            paths: globAll.sync(
              [...Object.keys(pages).map(pageDirName => `${path.join(__dirname, '../src')}/banners/${pageDirName}/**/*`),`${path.join(__dirname, '../src')}/comps/**/*`],
              { nodir: true }
            ),
            // paths: glob.sync(`${path.join(__dirname, '../src')}/banners/${pageDirName}/**/*`,  { nodir: true }),
            whitelist: ['webp'], // 白名的 
            whitelistPatterns: [/^kp/], // 以kp-开头的 不擦除
      })
    )

   }
 }
相关推荐
韩楚风7 小时前
【linux 多进程并发】linux进程状态与生命周期各阶段转换,进程状态查看分析,助力高性能优化
linux·服务器·性能优化·架构·gnu
长天一色16 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
裴云飞2 天前
鸿蒙性能优化之布局优化
性能优化·harmonyos
kingapex12 天前
性能优化-数据库分区技术深入解析
数据库·oracle·性能优化·数据库设计
胡耀超3 天前
知识图谱入门——6:Cypher 查询语言高级组合用法(查询链式操作、复杂路径匹配、条件逻辑、动态模式创建,以及通过事务控制和性能优化处理大规模数据。
性能优化·知识图谱·cypher
EterNity_TiMe_3 天前
【Linux进程间通信】Linux匿名管道详解:构建进程间通信的隐形桥梁
linux·运维·redis·缓存·性能优化·学习方法
PangPiLoLo4 天前
高性能架构—存储高性能
java·数据库·redis·性能优化·架构
Hello Dam4 天前
【文件增量备份系统】MySQL百万量级数据量分页查询性能优化
java·mysql·性能优化·springboot·深分页优化
黑马金牌编程4 天前
nginx常用的性能优化
运维·服务器·nginx·性能优化