{"id":19177300,"url":"https://github.com/liuxing/css-modules-demo","last_synced_at":"2025-10-08T15:51:10.547Z","repository":{"id":83544694,"uuid":"131967268","full_name":"liuxing/css-modules-demo","owner":"liuxing","description":"CSS modules 入门教程","archived":false,"fork":false,"pushed_at":"2018-05-03T11:10:43.000Z","size":18,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-07T20:41:33.823Z","etag":null,"topics":["css-modules"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liuxing.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-03T08:47:00.000Z","updated_at":"2020-07-31T01:43:45.000Z","dependencies_parsed_at":"2023-07-07T17:32:19.581Z","dependency_job_id":null,"html_url":"https://github.com/liuxing/css-modules-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/liuxing/css-modules-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuxing%2Fcss-modules-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuxing%2Fcss-modules-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuxing%2Fcss-modules-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuxing%2Fcss-modules-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liuxing","download_url":"https://codeload.github.com/liuxing/css-modules-demo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuxing%2Fcss-modules-demo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278972330,"owners_count":26078017,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css-modules"],"created_at":"2024-11-09T10:32:51.875Z","updated_at":"2025-10-08T15:51:10.525Z","avatar_url":"https://github.com/liuxing.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"前端发展越来越快，这应该是每个前端开发者的切身感受，但是CSS 是前端领域中进化最慢的一块。ES678快速普及前端工程发愈发成熟，CSS 被被远远甩在了后面，JS语言模块已经标准化，CSS还是在不断探索，同时这也是一个急需解决的问题。\n\n要是你以前还从未听说过[CSS Modules](https://github.com/css-modules/css-modules)，那么这篇就是专门写给你的。如果你了解过它，你就没必要再看了，因为他真的很简单(很强大)。\n\n## CSS的痛点\n\n我们都知道，CSS入门简单，深入就比较难，样式简单维护难。CSS痛点也很多\n\n1、CSS 的规则是全局的，任何一个组件的样式规则，都对整个页面有效。相信写css的人都会遇到样式冲突（污染）的问题。\n\n2、为了解决全局污染的问题，那就把class命名写长一点吧、加一层父级选择器、降低冲突的几率，那么CSS命名混乱了\n\n3、组件依赖管理不彻底，组件应该相互独立，引入一个组件时，应该只引入它所需要的 CSS 样式。\n\n4、代码难以复用，出现了sass less 之类的工具\n\n## CSS模块化方案分类\n\n前端发展是飞速的天天有新轮子。自然CSS 模块化的解决方案有很多，但主要有三类：\n\n### 1、命名约定\n\n规范化CSS的解决方案如：BEM、OOCSS、AMCSS、SMACSS\n\n### 2、CSS in JS\n\n彻底抛弃 CSS，用 JavaScript 写 CSS 规则，[styled-components](https://github.com/styled-components/styled-components) 就是其中代表。\n\n### 3、使用JS 来管理样式模块\n\n使用JS编译原生的CSS文件，使其具备模块化的能力，代表是 CSS Modules。\n\n但是这些模块化方案都是各有优缺点，如命名约定：命名复杂、CSS in JS：缺乏扩展、 CSS Modules当然也有一些缺点(*你得先学会它再去谈优劣*)。在众多解决方案中，没有绝对的优劣。还是要结合自己的场景来决定。\n\n## 使用 CSS Modules\n\nCSS Modules不是将CSS改造的具有编程能力，而是加入了局部作用域、依赖管理，这恰恰解决了最大的痛点。可以有效避免全局污染和样式冲突，能最大化地结合现有 CSS 生态和 JS 模块化能力。\n\n### 启用 CSS Modules\n\nCSS Modules 很容易学。webpack 自带的 `css-loader` 组件，自带了 CSS Modules，通过简单的配置即可使用。\n\n```js\n// webpack.config.js\nconst path = require('path')\n\nmodule.exports = {\n  entry: __dirname + '/src/index.js',\n  output: {\n    filename: 'bundle.js',\n    path: path.resolve(__dirname, 'dist')\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.css$/,\n        use: [\n          'style-loader',\n          {\n            loader: 'css-loader',\n            options: {\n              modules: true,\n            }\n          }\n        ]\n       }\n    ]\n  }\n}\n// 也可以使用下面这种写法\n// loader: \"style-loader!css-loader?modules\"\n```\n\n现在我们来写个Button组件\n\n```css\n/* Button.css */\n.primary {\n    background-color: #1aad19;\n    color: #fff;\n    border: none;\n    border-radius: 5px;\n}\n```\n\n\n\n```js\n// Button.js\nimport styles from './Button.css';\n\nconsole.log(styles); // -\u003e {primary: \"yTXmm0isaXExoYiZUvKxH\"}\nconst Button = document.createElement('div')\nButton.innerHTML = `\u003cbutton class=${styles.primary}\u003eSubmit\u003c/button\u003e`\n\nexport default Button\n\n// index.js\nimport Button from './components/Button'\n\nconst app = document.getElementById('root')\napp.appendChild(Button)\n```\n\n生成HTML为\n\n```html\n\u003cdiv id=\"root\"\u003e\n    \u003cdiv\u003e\n        \u003cbutton class=\"yTXmm0isaXExoYiZUvKxH\"\u003eSubmit\u003c/button\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- yTXmm0isaXExoYiZUvKxH为CSS Modules自动生成的class类名 --\u003e\n```\n\nCSS Modules 对 CSS 中的 class 名都做了处理，使用对象来保存原 class 和混淆后 class 的对应关系。CSS Modules自动生成的class类名基本就是唯一的，大大降低了项目中样式覆盖冲突的几率。\n\nGitHub 示例库 [https://github.com/liuxing/css-modules-demo](https://github.com/liuxing/css-modules-demo) 通过commit 记录来查看\n\n### 定制class类名\n\n`css-loader`默认的哈希算法是`[hash:base64]`，从前面我们可以发现`.primary` 被编译成了 yTXmm0isaXExoYiZUvKxH 这样的字符串。这名字也没风格了别担心，css-loader 为我们提供了`localIdentName ` 参数指定生成的名字格式。`localIdentName`的默认值是`[hash:base64]`。\n\n```js\n...\n\n{\n    loader: 'css-loader',\n    options: {\n        modules: true,\n        localIdentName: '[name]__[local]--[hash:base64:5]'\n    }\n}\n\n// 或者\nloader: 'style-loader!css-loader?modules\u0026localIdentName=[name]__[local]--[hash:base64:5]'\n...\n```\n\n**BEM代码规范不再是必须的 ** 熟悉BEM的同学可能发现了上面的命名和BEM有些神似， 虽然它的命名有点奇特，但是 BEM 被非常多的大型项目和团队采用，这是一种很好的实践。当然了随便怎么写都可以结合自己的场景来决定，在CSS module中不再需要遵守BEM规范。\n\n### 作用域\n\n通过前面的例子可以感受到CSS module处理CSS的方式。现在我们从头来说作用域。\n\n### 默认局部作用\n\nCSS很多问题都是因为全局作用域引起的，怎么样才能产生局部作用域？通过前面CSS module的例子我们发现它思路很简单就是生成唯一的class类名。CSS module将class转换成对应的全局唯一hash值来形成局部作用域。使用了 CSS Modules 后，就相当于给每个 class 名外加了一个 `:local` 这是默认的，也可以显式使用\n\n当然，如果你想切换到全局模式，CSS Modules 允许使用`:global(.className)`的语法，声明一个全局规则。凡是这样声明的`class`，都不会被编译成哈希字符串。\n\n```css\n/* Button.css */\n:global(.btn) {\n    color: #fff;\n    border: none;\n    border-radius: 5px;\n}\n\n.primary {\n    background-color: #1aad19;\n}\n\n/* \n与上面不加`:local`等价 \n显式的局部作用域语法\n*/\n:local(.warn) {\n    background-color: #e64340\n}\n```\n\n通过 GitHub 示例库 [https://github.com/liuxing/css-modules-demo ](https://github.com/liuxing/css-modules-demo)的commit 记录来查看\n\n### CSS Modules下的样式复用\n\n对于样式复用，CSS Modules 提供了 `composes` 组合 的方式来处理。一个选择器可以继承另一个选择器的规则\n\n```css\n/* Button.css */\n\n.btn {\n    /* 所有通用的样式 */\n    color: #fff;\n    border: none;\n    border-radius: 5px;\n    box-sizing: border-box；\n}\n\n.primary {\n    composes: btn;\n    background-color: #1aad19;\n}\n```\nButton.js\n```js\nimport styles from './Button.css';\n\nconsole.log(styles);\nconst Button = document.createElement('div')\nButton.innerHTML = `\u003cbutton class=\"${styles.primary}\"\u003eSubmit\u003c/button\u003e`\n\nexport default Button\n```\n\n生成的 HTML 变为\n\n```html\n\u003cdiv id=\"root\"\u003e\n    \u003cdiv\u003e\n        \u003cbutton class=\"Button__primary--yTXmm Button__btn--nx67B\"\u003eSubmit\u003c/button\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\n我们发现在 `.primary` 中 composes 了 `.btn`，编译后 `.primary` 会变成两个 class。\n\ncomposes 还可以也可以继承组合其他CSS文件里面的规则\n\n```css\n/* author.css */\n\n.shadow {\n    box-shadow: 0 0 20px rgba(0, 0, 0, .2)\n}\n```\n\nButton.css\n\n```css\n···\n.primary {\n    composes: btn;\n    composes: shadow from './author.css';\n    background-color: #1aad19;\n}\n···\n```\n\n这是个很强大方便的功能，CSS Modules团队成员认为`composes`是CSS Modules里最强大的功能：\n\n\u003e For me, the most powerful idea in CSS Modules is composition, where you can deconstruct your visual inventory into atomic classes, and assemble them at a module level, without duplicating markup or hindering performance.\n\n### 一些建议\n\n为了追求**简单可控**，作者建议遵循如下原则：\n\n- 不使用选择器，只使用 class 名来定义样式\n- 不层叠多个 class，只使用一个 class 把所有样式定义好\n- 所有样式通过 `composes` 组合来实现复用\n- 不嵌套\n\n但是建议只是建议，CSS Modules 并不强制你一定要这么做。怎么舒服怎么来\n\n\n\nCSS Modules 很好的解决了 CSS 目前面临的一些痛点以及模块化难题，同时也支持与 Sass/Less/PostCSS 等搭配使用。\n\n无论是通过遵循的命名标准化的规范，还是使用本文介绍的CSS Modules，目的都是一样：可维护的css代码。具体使用不是有还是要结合自己的场景来决定。适合的才是最好的","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliuxing%2Fcss-modules-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliuxing%2Fcss-modules-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliuxing%2Fcss-modules-demo/lists"}