我的项目引入bricks
根底组件库,并不是独自引入每一个所依赖的根底组件款式,而是在入口文件全局引入所有款式import '@casstime/bricks/lib/styles/bricks.scss';
,这就导致一些没有被应用的组件款式被打包到最终产物中,须要对款式做树摇解决。
接下来就该 PurgeCSS
上场了。PurgeCSS
是一个用来删除未应用的 CSS
代码的工具。能够将它作为你的开发流程中的一个环节。 当你构建一个网站时,你可能会决定应用一个 CSS
框架,例如 TailwindCSS、Bootstrap、MaterializeCSS、Foundation
等,然而,你所用到的也只是框架的一小部分而已,大量 CSS
款式并未被应用。PurgeCSS
通过剖析你的内容和 CSS
文件,首先它将 CSS
文件中应用的选择器与内容文件中的选择器进行匹配,而后它会从 CSS
中删除未应用的选择器,从而生成更小的 CSS
文件。
webpack
对应插件purgecss-webpack-plugin
,该插件的应用依赖款式抽离插件mini-css-extract-plugin
,只有先将款式抽离成独立文件后能力将 CSS
文件中应用的选择器与内容文件中的选择器进行匹配,而后它会从 CSS
中删除未应用的选择器,从而生成更小的 CSS
文件。
插件purgecss-webpack-plugin
的应用须要指定paths
属性,通知purgecss
须要剖析的文件列表,这些文件中应用的选择器与抽离的款式文件中的选择器进行匹配,从而剔除未应用的选择器。
因为我的项目中应用到款式的文件有src
目录和引入的业务组件以及bricks
根底组件,所以须要将这些文件目录指定为被剖析的列表,这些文件中应用到的款式选择器不会被剔除掉
// config-overrides.jsconst fs = require('fs');const path = require('path');const glob = require('glob-all');const webpack = require('webpack');const PurgeCSSPlugin = require('purgecss-webpack-plugin');const paths = require('react-scripts/config/paths');/** css tree shaking */function CssTreeShaking(config) { /** 开发环境下不做非凡解决 */ if (process.env.NODE_ENV === 'development') return; /** 有一些动静款式须要手动匹配保留,形如classNames=`icon-${type}` classNames=`mall-sidebar__${name}-icon` */ function collectSafelist() { return { standard: ['icon', /^icon-/, /^mall-sidebar__/, /^mall-right-trial-modal/], deep: [/^icon-/, /^mall-sidebar__/, /^mall-right-trial-modal/], greedy: [/^icon-/, /^mall-sidebar__/, /^mall-right-trial-modal/], }; } /** 收集我的项目中应用到的bricks组件(引入的业务组件中可能也有应用bricks组件) */ var files = glob.sync([ path.join(paths.appSrc, '/**/*.{ts,tsx}'), path.resolve(__dirname, `./node_modules/@casstime/bre-*/**/*.tsx`), path.resolve(__dirname, `./node_modules/@casstime/mall-*/**/*.tsx`), ]); /** 正则匹配我的项目中应用了哪些bricks组件 */ let usedComps = []; files.forEach((filePath) => { const code = fs.readFileSync(filePath, 'utf-8'); const reg = new RegExp(/import\s+\{(.*)\}\s+from\s+'@casstime\/bricks'/); const ret = code.match(reg); if (ret) { const comps = ret[1].replace(/\s+/g, '').toLowerCase().split(','); usedComps.push(...comps); } }); /** 应用到bricks组件的文件列表 */ const usedBrsPaths = [...new Set(usedComps)].map((comp) => { return path.resolve(__dirname, `./node_modules/@casstime/bricks/lib/components/${comp}/*.js`); }); /** 收集我的项目中应用到业务组件的门路 */ const usedBrePaths = [ path.resolve(__dirname, `./node_modules/@casstime/bre-*/**/*.tsx`), path.resolve(__dirname, `./node_modules/@casstime/mall-*/**/*.tsx`), ]; /** 款式树摇 */ const purgeCSSPlugin = new PurgeCSSPlugin({ /** paths示意这些文件中应用的款式须要保留,没有应用的款式将会被剔除 */ paths: glob.sync( [paths.appHtml, path.join(paths.appSrc, '/**/*.{ts,tsx}', ...usedBrsPaths, ...usedBrePaths], { nodir: true }, ), // fontFace: true, safelist: collectSafelist, }); config.plugins.push(purgeCSSPlugin);}module.exports = function override(config, env) { CssTreeShaking(config); return config;};
应用上述purgecss-webpack-plugin
解决会有一个问题,那就是没有思考到css modules
的影响,因为款式选择器被哈希化,与剖析文件中应用的款式选择器不能匹对,导致被剔除掉了
// src/page/components/Main.tsximport styles from './index.module.scss';const Main = () => { return <div className={styles.container}></div>}// 如果构建后款式产物如下(main.5d986f58.chunk.css).container__3PRC3 {}// main.5d986f58.chunk.css在与Main.tsx款式选择器匹配时发现选择器container与container__3PRC3不统一,就把container__3PRC3款式选择器剔除掉了
所以,在css modules
场景下应用purgecss-webpack-plugin
做款式荡涤会有问题。PurgeCSS官网思考到此问题,给出了相干loader
做款式荡涤计划。css modules
是在css-loader
解决后的后果,为了防止css modules
对款式荡涤的影响,能够在css-loader
之前,sass-loader
之后引入postCSS
插件@fullhuman/postcss-purgecss
解决
// config-overrides.jsconst fs = require('fs');const path = require('path');const glob = require('glob-all');const webpack = require('webpack');const paths = require('react-scripts/config/paths');/** css tree shaking */function CssTreeShaking(config) { /** 开发环境下不做非凡解决 */ if (process.env.NODE_ENV === 'development') return; /** 有一些动静款式须要手动匹配保留,形如classNames=`icon-${type}` classNames=`mall-sidebar__${name}-icon` */ function collectSafelist() { return { standard: ['icon', /^icon-/, /^mall-sidebar__/, /^mall-right-trial-modal/], deep: [/^icon-/, /^mall-sidebar__/, /^mall-right-trial-modal/], greedy: [/^icon-/, /^mall-sidebar__/, /^mall-right-trial-modal/], }; } /** 收集我的项目中应用到的bricks组件(引入的业务组件中可能也有应用bricks组件) */ var files = glob.sync([ path.join(paths.appSrc, '/**/*.{ts,tsx}'), path.resolve(__dirname, `./node_modules/@casstime/bre-*/**/*.tsx`), path.resolve(__dirname, `./node_modules/@casstime/mall-*/**/*.tsx`), ]); let usedComps = []; files.forEach((filePath) => { const code = fs.readFileSync(filePath, 'utf-8'); const reg = new RegExp(/import\s+\{(.*)\}\s+from\s+'@casstime\/bricks'/); const ret = code.match(reg); if (ret) { const comps = ret[1].replace(/\s+/g, '').toLowerCase().split(','); usedComps.push(...comps); } }); const usedBrsPaths = [...new Set(usedComps)].map((comp) => { return path.resolve(__dirname, `./node_modules/@casstime/bricks/lib/components/${comp}/*.js`); }); /** 收集我的项目中应用到的业务组件 */ const usedBrePaths = [ path.resolve(__dirname, `./node_modules/@casstime/bre-*/**/*.tsx`), path.resolve(__dirname, `./node_modules/@casstime/mall-*/**/*.tsx`), ]; const pureCSSPaths = [...usedBrsPaths, ...usedBrePaths]; const targetRules = config.module.rules.find((rule) => !!rule.oneOf); if (targetRules && targetRules.oneOf) { const sassModuleRule = targetRules.oneOf.find( (rule) => rule.test && new RegExp(rule.test).test('.module.scss'), ); if (sassModuleRule) { const postCssLoader = sassModuleRule.use.find((item) => /postcss-loader/.test(item.loader || ''), ); if (postCssLoader) { postCssLoader.options.plugins = () => [ require('postcss-flexbugs-fixes'), require('postcss-preset-env')({ autoprefixer: { flexbox: 'no-2009', }, stage: 3, }), require('@fullhuman/postcss-purgecss')({ content: [ paths.appHtml, ...glob.sync([path.join(paths.appSrc, '/**/*.{ts,tsx}')].concat(pureCSSPaths), { nodir: true, }), ], safelist: collectSafelist(), }), require('postcss-normalize'), ].filter(Boolean); } } }}module.exports = function override(config, env) { CssTreeShaking(config); return config;};
优化前后产物体积比照,减小体积还是很主观的~
在理论优化过程中,因为生产环境node@10.18.0
版本太低,如果装置的@fullhuman/postcss-purgecss
或purgecss-webpack-plugin
版本过高,其外部依赖的postcss@8.0.0
在低版本node
环境下装置不了,须要升高版本@fullhuman/postcss-purgecss@3.1.3
和purgecss-webpack-plugin@3.1.3
,必要时可能还需配置resolutions
对立postcss
版本
{ "resolutions": { "commander": "7.2.0", "postcss": "7.0.39" }}