>[success] # 配置css模块解析 1. css 是在实际开发中最常见的模块,在解析时候需要配置其对应 loader 才能成功打包,一般常见的配置 css loader 如下 * `css-loader` 用于**加载.css 文件**,并且转化成**commonjs对象**,安装`npm install css-loader -D` * `style-loader` 将样式通过`<style>` 标签插入到**head中**,安装`npm install style-loader -D` * `less-loader` 将用于**less 转换成css**,安装`npm install less-loader -D` * `postcss-loader`,一套将 CSS 源码解析为 AST 结构,并传入 PostCSS 插件做处理的流程框架,具体功能都由插件实现,因此可以安装`autoprefixer` 插件(用来告诉PostCSS 那些css用来加浏览器前缀,解析CSS并使用'**[Can I Use 网站](https://caniuse.com/)**'中的值向CSS规则添加供应商前缀,[CSS属性前缀的作用是什么](https://m.html.cn/qa/css3/16979.html)),安装`npm install postcss postcss-loader autoprefixer -D`,更多配合使用插件 * [`mini-css-extract-plugin`](https://link.juejin.cn/?target=https%3A%2F%2Fwebpack.js.org%2Fplugins%2Fmini-css-extract-plugin "https://webpack.js.org/plugins/mini-css-extract-plugin"):该插件会将 CSS 代码抽离到单独的`.css`文件,并将文件通过`<link>`标签方式插入到页面中,安装`npm install --save-dev mini-css-extract-plugin` >[info] ## 使用 1. 如果单独使用`css-loader` 会发现执行完后对应css 样式并没有生效,因为 `css-loader` 的作用是将 **CSS 模块转换为一个 JS 模块**,具体的实现方法是将 **CSS 代码 push 到一个数组中,这个数组是由 css-loader 内部的一个模块提供的,但是整个过程并没有任何地方使用** 正是因为 **css-loader 只会把 CSS 模块加载到 JS 代码中,而并不会使用这个模块** 因此不生效 ![](https://img.kancloud.cn/ba/50/ba500238f0e2506da041ee6a719b231f_1311x451.png) ![](https://img.kancloud.cn/6f/47/6f4783e47d831c430a6455032036d817_1159x375.png) 2. `style-loader`,style-loader的作用是在js执行时,动态的创建style标签,然后将 **css-loader 转换的样式插入到这个style标签里去的** ![](https://img.kancloud.cn/68/ce/68ce3b8e4f5e2ad54ae134b6f998be35_391x210.png) 3. 使用 `postcss-loader` 需要配置插件来达到css一些特殊配置 * [autoprefixer](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fpostcss%2Fautoprefixer "https://github.com/postcss/autoprefixer"):基于[Can I Use](https://link.juejin.cn/?target=https%3A%2F%2Fcaniuse.com%2F "https://caniuse.com/")网站上的数据,自动添加浏览器前缀 * [postcss-preset-env](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fjonathantneal%2Fpostcss-preset-env "https://github.com/jonathantneal/postcss-preset-env"):一款将最新 CSS 语言特性转译为兼容性更佳的低版本代码的插件 * [postcss-less](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshellscape%2Fpostcss-less "https://github.com/shellscape/postcss-less"):兼容 Less 语法的 PostCSS 插件,类似的还有:[postcss-sass](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FAleshaOleg%2Fpostcss-sass "https://github.com/AleshaOleg/postcss-sass")、[poststylus](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fmadeleineostoja%2Fpoststylus "https://github.com/madeleineostoja/poststylus") * [stylelint](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fstylelint%2Fstylelint "https://github.com/stylelint/stylelint"):一个现代 CSS 代码风格检查器,能够帮助识别样式代码中的异常或风格问题 >[danger] ##### 使用配置 ![](https://img.kancloud.cn/ed/fa/edfac40da471bf7d5c161178579ca642_539x431.png) ~~~ /** @type {import('webpack').Configuration} */ const config = { mode: 'production', entry: './src/index.js', output: { filename: 'bundle.js', }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: ['autoprefixer'], }, }, }, ], }, { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', 'postcss-loader', ], }, ], }, } module.exports = config ~~~ * 关于`postcss-loader` 的 `options` 可以通过配置`postcss.config.js`,来指定用`autoprefixer` ~~~ // postcss.config.js module.exports = { plugins: ['autoprefixer'], } ~~~ ~~~ /** @type {import('webpack').Configuration} */ const config = { mode: 'production', entry: './src/index.js', output: { filename: 'bundle.js', }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'], }, { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', 'postcss-loader', ], }, ], }, } module.exports = config ~~~ * 有时候并不是所有浏览器都需要配置 `.browserslistrc` 文件来执行需要通过`postcss`和其插件具体要适配的浏览器(Browserslist这个东西单独是没用的,browserslist字段会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀) ~~~ last 2 version >1% ~~~ * 直接使用 `postcss-preset-env`,它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环境添加所需的`polyfill`,也包括会自动帮助我们添加autoprefixer(所以相当于已经**内置了autoprefixer**)安装`npm install postcss-preset-env -D`,直接在`postcss.config.js` 文件使用 ~~~ // postcss.config.js module.exports = { plugins: ['postcss-preset-env'], } ~~~ >[info] ## 将css 文件抽离 1. 当 Webpack 版本低于 5.0 时,请使用[`extract-text-webpack-plugin`] (https://link.juejin.cn/?target=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fextract-text-webpack-plugin "https://www.npmjs.com/package/extract-text-webpack-plugin")代替`mini-css-extract-plugin`。 2. `mini-css-extract-plugin` 不能与 style-loader 混用,否则报错,所以需要判断 `process.env.NODE_ENV` 环境变量决定使用那个 Loader ~~~ const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HTMLWebpackPlugin = require('html-webpack-plugin') module.exports = { module: { rules: [{ test: /\.css$/, use: [ // 根据运行环境判断使用那个 loader (process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader), 'css-loader' ] }] }, plugins: [ new MiniCssExtractPlugin(), new HTMLWebpackPlugin() ] } ~~~ >[danger] ##### 执行顺序 1. `postcss-loader` 执行顺序必须保证在 `css-loader` 之前,建议还是放在` less`或者 `sass `等预处理器之后更好。即 `loader `顺序:**less-loader -> postcss-loader -> css-loader -> style-loader 或者 MiniCssExtractPlugin.loader** 其实 postcss-loader 放在 less-loader 之前问题也不大,平时使用的 less 里面的语法基本不会和 autoprefixer 处理产生冲突的。 >[danger] ##### postcss-loader中的options怎么还会有个plugins 1. 这个是通过loader的options传递postcss所需要用到的插件,这个插件是postcss生态下的。和webpack插件没有关联 >[danger] ##### 为什么css 要向js 一样动态引入 1. **在JS中通过import方式引入CSS文件根据代码的,好处不像以前的代码,我们将所有的css 文件都沉积在业务里**,很多情况都是触发了某个js代码,然后从而需要某种资源,如果业务开发后续阶段删除了某个js业务逻辑,**而没有清空他的对应css代码就会产生资源浪费**,现在通过webpack 将他们关联到一起,也方便管理 * 举一个代码案例 ~~~ // 注意这里是因为用了webpack 和css -- loader,才可以将css作为模块导入 // 这样导入后的效果就是只有当触发了下面的js 情况才会引入一个css文件 import './heading.css' export default () => { const element = document.createElement('h2') element.textContent = 'Hello world' element.classList.add('heading') element.addEventListener('click', () => { alert('Hello webpack') }) return element } ~~~ >[danger] ##### 补充 常见主流浏览器内核 * **Trident内核**: 主要代表为IE浏览器 -'ms' * **Gecko内核**: 主要代表为Firefox -'moz' * **Presto内核**': 主要代表为Opera -'o' * **Webkit内核**: 产要代表为Chrome和Safari - 'webkit' >[danger] ##### 总结 1. `css-loader`提供了很多处理 CSS 代码的基础能力,包括 CSS 到 JS 转译、依赖解析、Sourcemap、css-in-module 等,基于这些能力,Webpack 才能像处理 JS 模块一样处理 CSS 模块代码,经过`css-loader`处理后,样式代码最终会被转译成一段 JS 字符串,**只是被当作普通 JS 模块处理,并不会实际影响到页面样式** * **开发环境**:使用`style-loader`将样式代码注入到页面`<style>`标签; * **生产环境**:使用`mini-css-extract-plugin`将样式代码抽离到单独产物文件,并以`<link>`标签方式引入到页面中。 ![](https://img.kancloud.cn/58/60/586039df00a792fe55e392395ec43851_696x496.png) 2. `style-loader`并不会对代码内容做任何修改,而是简单注入一系列运行时代码,用于将`css-loader`转译出的 JS 字符串插入到页面的`style`标签,由于通过`style`标签注入到页面。这种将 JS、CSS 代码合并进同一个产物文件的方式有几个问题: * JS、CSS 资源无法并行加载,从而降低页面性能; * 资源缓存粒度变大,JS、CSS 任意一种变更都会致使缓存失效。 ![](https://img.kancloud.cn/c6/93/c693ead2466c28e6150ff5cbc77867eb_563x185.png) 3. 因此生产环境中通常会用[`mini-css-extract-plugin`](https://link.juejin.cn/?target=https%3A%2F%2Fwebpack.js.org%2Fplugins%2Fmini-css-extract-plugin "https://webpack.js.org/plugins/mini-css-extract-plugin")插件替代`style-loader`,抽离成单独的 CSS 文件 * `mini-css-extract-plugin`库同时提供 Loader、Plugin 组件,需要同时使用 * `mini-css-extract-plugin`不能与`style-loader`混用,否则报错,所以需要判断`process.env.NODE_ENV`环境变量决定使用那个 Loader * `mini-css-extract-plugin`需要与`html-webpack-plugin`同时使用,才能将产物路径以`link`标签方式插入到 html 中 >[info] ## 参考 [Webpack5 核心原理与应用实践](https://juejin.cn/book/7115598540721618944) [webpack4 css 提取插件 mini-css-extract-plugin,当想提取出css 样式文件时候](http://www.tensweets.com/article/5c8cad80362e5434baf6335d) [style-loader](http://www.fly63.com/article/detial/5741) [前端工程化精讲](https://kaiwu.lagou.com/course/courseInfo.htm?courseId=416&sid=20-h5Url-0#/detail/pc?id=4419) [链接](https://github.com/fe-efficiency/lessons_fe_efficiency/blob/master/05_coding_efficiency/webpack.config.js)