Angular12+Webpack5导入CSS返回字符串而非对象,调用use方法报错
我在TypeScript模块中导入CSS文件,导入结果解析为字符串而非对象,请问为什么无法获取到对象?
// preview.ts import test from './src/assets/test.theme.css'; // 也尝试过该写法: // import * as test from './src/assets/test.theme.css'; console.log('typeof test: ', typeof test); console.log(test);
控制台输出
我正在为我的Angular12组件库搭建Storybook。
为了提供多主题能力,我计划使用@etchteam/storybook-addon-css-variables-theme插件,其文档中使用了Webpack的内联loader语法:
import myTheme from '!!style-loader?injectType=lazyStyleTag!css-loader!./assets/my-theme.css';
我将该写法应用到自己的代码后,浏览器控制台抛出错误:
Error: myTheme.use is not a function
排查过程中我发现,导入的样式表不是可执行的JavaScript对象,而是包含style-loader生成的源码的字符串。
我还发现该问题并不局限于style-loader,css-loader、raw-loader等其他loader也会出现相同问题。
该问题也和内联loader语法无关,即便是在最简webpack配置中定义loader也可复现。
运行环境:
- Angular 12
- Webpack 5
我已搭建了复现该问题的GIT仓库,README文件中说明了复现步骤和问题详情。
问题原因
- Angular 12 默认在Webpack配置中加入了全局资源模块(Asset Modules)规则,所有
.css后缀文件会被默认匹配到type: 'asset/source'规则,直接导出文件原始内容字符串,该规则优先级高于你自行配置的style-loader/css-loader处理链,导致你的loader没有实际生效。 - 你使用的双感叹号开头的内联loader语法会禁用所有前置/后置loader,和全局规则叠加后会把loader输出的JS代码再次当成纯文本导出,最终拿到的自然是字符串而非带
use方法的对象。
解决方案
方案一:修改Storybook的Webpack配置(推荐)
打开.storybook/main.js,调整Webpack规则,给全局CSS规则添加排除项,单独处理主题类CSS文件:
module.exports = { // 其余配置保持不变 webpackFinal: async (config) => { // 查找默认处理CSS的规则 const cssRule = config.module.rules.find(rule => rule.test.toString().includes('.css')); // 排除你自定义的主题CSS文件,这里按你的文件名规则匹配.theme.css后缀 cssRule.exclude = /\.theme\.css$/; // 新增专属主题CSS的处理规则 config.module.rules.push({ test: /\.theme\.css$/, use: [ { loader: 'style-loader', options: { injectType: 'lazyStyleTag' } }, 'css-loader' ] }); return config; } }
配置完成后直接按普通方式导入即可,不需要写内联loader:
import test from './src/assets/test.theme.css';
此时导入的test对象会自带use()和unuse()方法,可直接调用切换主题。
方案二:调整内联loader写法
如果不想改全局配置,把内联loader开头的双感叹号改成单感叹号即可,单感叹号会禁用全局配置的默认资源规则,让你的loader链正常生效:
import myTheme from '!style-loader?injectType=lazyStyleTag!css-loader!./assets/my-theme.css';
补充TypeScript类型适配
添加类型声明文件避免TS报错,在项目根目录新建typings.d.ts:
declare module '*.theme.css' { interface LazyStyleSheet { use: () => void; unuse: () => void; } const styles: LazyStyleSheet; export default styles; }
内容的提问来源于stack exchange,提问作者Markus Windhager

