style-loader 的核心作用是将 css-loader 处理后的样式以 javascript 方式动态插入 dom 的 标签,本身不解析 css 语法;必须与 css-loader 配合使用,顺序为 ['style-loader', 'css-loader'],且生产环境应替换为 minicssextractplugin.loader。

style-loader 的核心作用不是“加载 CSS 文件”,而是把 css-loader 处理后的样式内容,以 JavaScript 方式动态插入到 DOM 中的 <style></style> 标签里。它本身不解析 @import、url() 或模块化类名,这些全靠 css-loader 提前处理好。
为什么只配 style-loader 会报 Module parse failed: Unexpected token
Webpack 默认不认识 .css 文件,遇到就直接当 JS 解析——自然在 . 或 { 处报错。必须让 css-loader 先接手,把它转成 JS 模块对象(含 toString() 方法),style-loader 才能后续调用该对象注入 DOM。
- 错误配置:
use: ['style-loader']→ 缺少css-loader,CSS 原始文本直传给style-loader,后者无法处理 - 正确顺序(执行顺序从右到左):
use: ['style-loader', 'css-loader'] - 若启用 CSS Modules,
css-loader必须带modules: true,且 JS 中要import styles from './Button.css'才生效;仅import './Button.css'不触发模块逻辑
injectType: "styleTag" 是开发环境默认行为,但要注意 DOM 节点位置
默认注入方式会在 末尾追加 <style></style> 标签,但这个行为受 insert 选项控制。如果不指定,它不会插到你期望的容器里,比如 Shadow DOM 或特定 <div id="app"> 内。
<ul>
<li>想插到某个元素内部:<code>insert: '#app'(字符串选择器)或 insert: (element) => document.getElementById('app').appendChild(element)
<style></style> 标签,热更新时旧标签会被移除、新标签插入,这是 HMR 正常表现
lazyStyleTag 模式需要手动触发,不是“自动懒加载”
名字容易误导——lazyStyleTag 并不会根据滚动或视口自动加载,它只是把注入时机延迟到你显式调用 use() 方法那一刻。JS 模块导出的是一个对象,不是立即生效的样式。
- 配置写法:
{ loader: 'style-loader', options: { injectType: 'lazyStyleTag' } } - JS 中必须这样用:
const styles = await import('./modal.css'); styles.use(); - 如果忘记调用
use(),样式永远不会出现;调用多次也只会注入一次(内部有标记) - 适合弹窗、Tab 切换等明确“按需”的场景,不适合首屏关键样式
开发环境用 style-loader,上线前必须换成 MiniCssExtractPlugin.loader
两者不能共存,也不能在 mode: 'development' 下使用 MiniCssExtractPlugin.loader——它依赖 chunk graph,而开发模式下该结构未就绪,会报 Chunk.entryModule is not available。
- 开发配置片段:
['style-loader', 'css-loader'] - 生产配置片段:
[MiniCssExtractPlugin.loader, 'css-loader'],且需在plugins中添加new MiniCssExtractPlugin() - 注意:切换后,CSS 不再走 JS 注入流程,而是生成独立
.css文件 +<link>标签,浏览器可缓存、无 FOUC
真正容易被忽略的是 insert 和 attributes 的组合效果——比如设置了 nonce 却没配 CSP 策略,或者用了 insert: 'body' 却在 里初始化脚本,结果样式插入失败却没报错。这些细节不跑一遍真实 DOM 插入过程,光看文档很难察觉。











