CRA配置SASS全局mixin报Undefined mixin错误如何解决
CRA项目SASS全局Mixin/变量配置方案(解决Undefined mixin报错)
问题背景
- 基于Create React App搭建的React项目,需使用SASS预处理器,期望组件样式就近在对应组件文件中引入,同时全局复用公共mixin、变量,无需每个组件样式文件单独引入
- 项目目录结构:
|── package.json ├── README.md ├── src │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── React │ ├── component │ │ ├── Example │ │ ├── Example.component.js │ │ ├── Example.container.js │ │ ├── Example.style.scss │ └── style │ ├── abstract │ │ ├── _abstract.scss │ │ └── _media.scss │ ├── _reset.scss │ └── style.scss ├── webpack.config.js └── yarn.lock
- 初始实现逻辑:在
_media.scss定义媒体查询等公共mixin,_abstract.scss通过@import 'media'引入_media.scss,style.scss引入抽象层后在src/index.js中全局引入 - 报错信息:组件样式文件
Example.style.scss中调用全局mixin时构建失败:
Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: Undefined mixin.
现有配置问题
- CRA默认内置webpack构建配置,项目根目录手动创建的
webpack.config.js不会被react-scripts识别,自定义的loader配置完全没有生效 sass-resources-loader配置的资源路径使用相对路径,且错误将包含实际样式输出的style.scss加入注入列表,既会导致资源加载失败,还会引发样式重复打包问题- loader顺序存在逻辑问题,全局资源没有在SASS编译前正确注入到每个组件样式文件中
解决步骤
步骤1:使用CRACO接管CRA配置(无需eject)
CRA默认不暴露webpack配置,直接修改根目录webpack文件无效,使用@craco/craco可以在不执行不可逆的eject操作前提下修改构建配置:
- 安装依赖:
yarn add @craco/craco -D
- 删除根目录下手动创建的无效
webpack.config.js - 修改
package.json中的scripts字段,将react-scripts替换为craco:
"scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "react-scripts eject" }
步骤2:配置全局SASS资源注入
项目根目录新建craco.config.js,两种配置方案二选一即可:
方案1:使用sass-resources-loader(和原有技术选型一致)
const path = require('path'); module.exports = { webpack: { configure: (webpackConfig) => { // 定位内置的scss处理规则 const scssRule = webpackConfig.module.rules.find( rule => rule.test?.toString().includes('scss') ); // 追加全局资源loader,必须放在sass-loader之后 scssRule.use.push({ loader: 'sass-resources-loader', options: { // 仅注入存放变量、mixin的抽象层文件,不要加入带实际样式输出的文件 resources: [ path.resolve(__dirname, 'src/React/style/abstract/_media.scss'), path.resolve(__dirname, 'src/React/style/abstract/_abstract.scss'), ] } }); return webpackConfig; } } };
方案2:直接使用sass-loader内置能力(无需额外安装sass-resources-loader)
sass-loader本身提供additionalData配置,可以在每个scss文件编译前自动注入内容,配置更轻量:
const path = require('path'); // 公共抽象样式入口 const abstractEntry = path.resolve(__dirname, 'src/React/style/abstract/_abstract.scss'); module.exports = { webpack: { configure: (webpackConfig) => { const scssRule = webpackConfig.module.rules.find( rule => rule.test?.toString().includes('scss') ); // 定位sass-loader const sassLoader = scssRule.use.find(item => item.loader?.includes('sass-loader')); // 配置自动注入 sassLoader.options = { ...sassLoader.options, // 现代SASS推荐使用@use替代@import,避免变量污染和重复打包 additionalData: `@use "${abstractEntry.replace(/\\/g, '/')}" as *;` }; return webpackConfig; } } };
步骤3:调整样式引入逻辑
src/React/style/style.scss中移除对abstract层的引入,仅保留_reset.scss这类实际输出全局样式的引入,避免重复打包- 组件样式文件直接在对应组件js文件中引入即可,例如在
Example.component.js顶部加import './Example.style.scss',无需手动引入mixin/变量文件,可直接调用全局定义的mixin、变量
注意:绝对不要把
_reset.scss、style.scss这类会输出实际CSS代码的文件加入全局注入列表,否则每个组件样式文件都会重复打包这部分内容,导致最终产物体积异常膨胀。
内容的提问来源于stack exchange,提问作者Andrii Antoniuk
相关产品推荐
相关产品推荐

