├── src
├── config.js
├── index.js
├── parse-class-expression.js
├── utils.js
└── create-element.js
├── .gitignore
├── assets
└── logo.png
├── test
└── index.test.js
├── lib
├── config.js
├── index.js
├── parse-class-expression.js
├── utils.js
└── create-element.js
├── .travis.yml
├── demo
├── styles
│ └── button.module.css
├── template-with-outer-modules.vue
├── jsx.vue
├── template-with-inline-modules-global.vue
├── template-with-inline-modules.vue
├── renderFn.vue
├── renderFn-functional.vue
└── index.js
├── .editorconfig
├── bdr.config.js
├── LICENSE
├── CHANGELOG.md
├── package.json
├── dist
├── vue-css-modules.min.js
├── vue-css-modules.es.js
├── vue-css-modules.cjs.js
└── vue-css-modules.js
├── README_zh-CN.md
└── README.md
/src/config.js:
--------------------------------------------------------------------------------
1 | export const INJECT_ATTR = 'styleName'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | *.log
4 | coverage
5 | .vscode
6 |
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fjc0k/vue-css-modules/HEAD/assets/logo.png
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 | import app from '../src'
2 |
3 | test('test', () => {
4 | expect(app).toBe(app)
5 | })
6 |
--------------------------------------------------------------------------------
/lib/config.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 | exports.INJECT_ATTR = void 0;
5 | var INJECT_ATTR = 'styleName';
6 | exports.INJECT_ATTR = INJECT_ATTR;
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 9
4 | cache:
5 | directories:
6 | - node_modules
7 | install:
8 | - npm install
9 | script:
10 | - yarn test
11 | after_success:
12 | - yarn codecov
13 |
--------------------------------------------------------------------------------
/demo/styles/button.module.css:
--------------------------------------------------------------------------------
1 | .button {
2 | display: inline-block;
3 | font-size: 18px;
4 | padding: 10px;
5 | border: 1px solid green;
6 | }
7 |
8 | .disabled {
9 | opacity: .5;
10 | color: black;
11 | }
12 |
13 | .mini {
14 | font-size: 12px;
15 | }
16 |
17 | .primary {
18 | color: green;
19 | }
20 |
21 | .danger {
22 | color: red;
23 | }
24 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | # The JSON files contain newlines inconsistently
13 | [*.json]
14 | insert_final_newline = ignore
15 |
16 | # Minified JavaScript files shouldn't be changed
17 | [**.min.js]
18 | indent_style = ignore
19 | insert_final_newline = ignore
20 |
21 | [*.md]
22 | trim_trailing_whitespace = false
23 |
--------------------------------------------------------------------------------
/demo/template-with-outer-modules.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 | BUTTON
6 |
7 |
8 |
9 |
33 |
--------------------------------------------------------------------------------
/demo/jsx.vue:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* eslint camelcase: 0 */
2 | import createElement from './create-element'
3 |
4 | const CSSModules = styles => ({
5 | beforeCreate() {
6 | this.original$createElement = this.original$createElement || this.$createElement
7 | this.original_c = this.original_c || this._c
8 | this.$createElement = createElement.bind(this, {
9 | createElement: this.original$createElement,
10 | context: this,
11 | styles
12 | })
13 | this._c = createElement.bind(this, {
14 | createElement: this.original_c,
15 | context: this,
16 | styles
17 | })
18 | }
19 | })
20 |
21 | CSSModules.install = Vue => {
22 | Vue.mixin(CSSModules())
23 | }
24 |
25 | export default CSSModules
26 |
--------------------------------------------------------------------------------
/demo/template-with-inline-modules-global.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 | BUTTON
6 |
7 |
8 |
9 |
26 |
27 |
43 |
--------------------------------------------------------------------------------
/demo/template-with-inline-modules.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 | BUTTON
6 |
7 |
8 |
9 |
32 |
33 |
49 |
--------------------------------------------------------------------------------
/bdr.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: {
3 | 'vue-css-modules': [
4 | 'src/index.js',
5 | 'VueCSSModules'
6 | ]
7 | },
8 | getUmdMinSize(rawSize, gzippedSize) {
9 | const path = require('path')
10 | const fs = require('fs')
11 |
12 | const tasks = [
13 | ['README.md', /[^-]+(?=-blue\.svg\?MIN)/, /[^-]+(?=-blue\.svg\?MZIP)/],
14 | ['README_zh-CN.md', /[^-]+(?=-blue\.svg\?MIN)/, /[^-]+(?=-blue\.svg\?MZIP)/]
15 | ]
16 |
17 | tasks.forEach(task => {
18 | const filePath = path.resolve(__dirname, task[0])
19 | const content = fs.readFileSync(filePath)
20 | fs.writeFileSync(
21 | filePath,
22 | String(content)
23 | .replace(task[1], encodeURIComponent(rawSize))
24 | .replace(task[2], encodeURIComponent(gzippedSize))
25 | )
26 | })
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/demo/renderFn.vue:
--------------------------------------------------------------------------------
1 |
44 |
--------------------------------------------------------------------------------
/demo/renderFn-functional.vue:
--------------------------------------------------------------------------------
1 |
42 |
--------------------------------------------------------------------------------
/src/parse-class-expression.js:
--------------------------------------------------------------------------------
1 | import { includes, camelCase } from './utils'
2 |
3 | const cache = Object.create(null)
4 |
5 | export default expression => {
6 | if (cache[expression]) return cache[expression]
7 |
8 | let className
9 | let binding
10 | let bindingValue
11 | let role
12 |
13 | if (includes(expression, '=', 1)) { // eg: disabled=isDisabled
14 | [className, binding] = expression.split('=')
15 | } else {
16 | const modifier = expression[0]
17 | if (modifier === '$') { // eg: $type
18 | binding = expression.substr(1)
19 | bindingValue = true
20 | } else if (modifier === '@') { // eg: @button
21 | className = expression.substr(1)
22 | role = className
23 | } else if (modifier === ':') { // eg: :disabled
24 | className = expression.substr(1)
25 | binding = camelCase(className)
26 | } else {
27 | className = expression
28 | }
29 | }
30 |
31 | cache[expression] = {
32 | className,
33 | binding,
34 | bindingValue,
35 | role
36 | }
37 |
38 | return cache[expression]
39 | }
40 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 | exports.default = void 0;
5 |
6 | var _createElement = _interopRequireDefault(require("./create-element"));
7 |
8 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9 |
10 | /* eslint camelcase: 0 */
11 | var CSSModules = function CSSModules(styles) {
12 | return {
13 | beforeCreate: function beforeCreate() {
14 | this.original$createElement = this.original$createElement || this.$createElement;
15 | this.original_c = this.original_c || this._c;
16 | this.$createElement = _createElement.default.bind(this, {
17 | createElement: this.original$createElement,
18 | context: this,
19 | styles: styles
20 | });
21 | this._c = _createElement.default.bind(this, {
22 | createElement: this.original_c,
23 | context: this,
24 | styles: styles
25 | });
26 | }
27 | };
28 | };
29 |
30 | CSSModules.install = function (Vue) {
31 | Vue.mixin(CSSModules());
32 | };
33 |
34 | var _default = CSSModules;
35 | exports.default = _default;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 fjc0k
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 |
6 | # [1.2.0](https://github.com/fjc0k/vue-css-modules/compare/v1.1.0...v1.2.0) (2018-06-29)
7 |
8 |
9 | ### Features
10 |
11 | * support global installation ([0e6174d](https://github.com/fjc0k/vue-css-modules/commit/0e6174d))
12 |
13 |
14 |
15 |
16 | # [1.1.0](https://github.com/fjc0k/vue-css-modules/compare/v1.0.5...v1.1.0) (2018-05-18)
17 |
18 |
19 | ### Features
20 |
21 | * extract styles from context ([b846fb8](https://github.com/fjc0k/vue-css-modules/commit/b846fb8))
22 |
23 |
24 |
25 |
26 | ## [1.0.5](https://github.com/fjc0k/vue-css-modules/compare/v1.0.4...v1.0.5) (2018-04-18)
27 |
28 |
29 |
30 |
31 | ## [1.0.4](https://github.com/fjc0k/vue-css-modules/compare/v1.0.3...v1.0.4) (2018-04-18)
32 |
33 |
34 | ### Bug Fixes
35 |
36 | * postbump ([1de199b](https://github.com/fjc0k/vue-css-modules/commit/1de199b))
37 |
38 |
39 |
40 |
41 | ## 1.0.3 (2018-04-18)
42 |
43 |
44 | ### Bug Fixes
45 |
46 | * prepend the class names to data.staticClass ([277423f](https://github.com/fjc0k/vue-css-modules/commit/277423f))
47 |
--------------------------------------------------------------------------------
/lib/parse-class-expression.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 | exports.default = void 0;
5 |
6 | var _utils = require("./utils");
7 |
8 | var cache = Object.create(null);
9 |
10 | var _default = function _default(expression) {
11 | if (cache[expression]) return cache[expression];
12 | var className;
13 | var binding;
14 | var bindingValue;
15 | var role;
16 |
17 | if ((0, _utils.includes)(expression, '=', 1)) {
18 | // eg: disabled=isDisabled
19 | var _expression$split = expression.split('=');
20 |
21 | className = _expression$split[0];
22 | binding = _expression$split[1];
23 | } else {
24 | var modifier = expression[0];
25 |
26 | if (modifier === '$') {
27 | // eg: $type
28 | binding = expression.substr(1);
29 | bindingValue = true;
30 | } else if (modifier === '@') {
31 | // eg: @button
32 | className = expression.substr(1);
33 | role = className;
34 | } else if (modifier === ':') {
35 | // eg: :disabled
36 | className = expression.substr(1);
37 | binding = (0, _utils.camelCase)(className);
38 | } else {
39 | className = expression;
40 | }
41 | }
42 |
43 | cache[expression] = {
44 | className: className,
45 | binding: binding,
46 | bindingValue: bindingValue,
47 | role: role
48 | };
49 | return cache[expression];
50 | };
51 |
52 | exports.default = _default;
--------------------------------------------------------------------------------
/demo/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import CSSModules from '../src'
3 |
4 | import renderFn from './renderFn'
5 | import renderFnFunctional from './renderFn-functional'
6 | import templateWithOuterModules from './template-with-outer-modules'
7 | import templateWithInlineModules from './template-with-inline-modules'
8 | import templateWithInlineModulesGlobal from './template-with-inline-modules-global'
9 |
10 | Vue.use(CSSModules)
11 |
12 | const demos = {
13 | 'renderFn-functional': renderFnFunctional,
14 | renderFn: renderFn,
15 | 'template-with-inline-modules': templateWithInlineModules,
16 | 'template-with-inline-modules-global': templateWithInlineModulesGlobal,
17 | 'template-with-outer-modules': templateWithOuterModules
18 | }
19 |
20 | // eslint-disable-next-line
21 | new Vue({
22 | el: '#app',
23 | data: {
24 | currentDemo: renderFn
25 | },
26 | render(h) {
27 | return (
28 | h('div', [
29 | h('div', 'Click button'),
30 | h('hr'),
31 | h(this.currentDemo, {
32 | class: 'custom'
33 | }),
34 | h('hr'),
35 | Object.keys(demos).map(demo => (
36 | h('button', {
37 | style: 'margin:10px;font-size:18px;' + (
38 | this.currentDemo === demos[demo] ? 'border-color:red;' : ''
39 | ),
40 | on: {
41 | click: () => {
42 | this.currentDemo = demos[demo]
43 | }
44 | }
45 | }, demo)
46 | )),
47 | h('hr'),
48 | h('pre', [JSON.stringify(
49 | this.currentDemo,
50 | null,
51 | 2
52 | )])
53 | ])
54 | )
55 | }
56 | })
57 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export function includes(arrayLike, element, fromIndex = 0) {
2 | for (let i = fromIndex, len = arrayLike.length; i < len; i++) {
3 | if (arrayLike[i] === element) {
4 | return true
5 | }
6 | }
7 | return false
8 | }
9 |
10 | export function isObject(value) {
11 | return value !== null && typeof value === 'object'
12 | }
13 |
14 | export function isFunction(value) {
15 | return typeof value === 'function'
16 | }
17 |
18 | export function isString(value) {
19 | return typeof value === 'string'
20 | }
21 |
22 | const camelCaseCache = Object.create(null)
23 | export function camelCase(value) {
24 | if (camelCaseCache[value]) return camelCaseCache[value]
25 | let result = ''
26 | let shouldUpperCase = false
27 | for (let i = 0, len = value.length; i < len; i++) {
28 | const char = value[i]
29 | if (char === '-') {
30 | shouldUpperCase = true
31 | } else {
32 | result += (result && shouldUpperCase) ? char.toUpperCase() : char
33 | shouldUpperCase = false
34 | }
35 | }
36 | camelCaseCache[value] = result
37 | return result
38 | }
39 |
40 | const dashCaseCache = Object.create(null)
41 | export function dashCase(value) {
42 | if (dashCaseCache[value]) return dashCaseCache[value]
43 | let result = ''
44 | let shouldAddDash = false
45 | for (let i = value.length - 1; i >= 0; i--) {
46 | const char = value[i]
47 | const charCode = char.charCodeAt(0)
48 | if (charCode >= 65 && charCode <= 90) {
49 | shouldAddDash = true
50 | result = char.toLowerCase() + result
51 | } else {
52 | result = char + (shouldAddDash ? '-' : '') + result
53 | shouldAddDash = false
54 | }
55 | }
56 | dashCaseCache[value] = result
57 | return result
58 | }
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-css-modules",
3 | "version": "1.2.0",
4 | "description": "Seamless mapping of class names to CSS modules inside of Vue components.",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "unpkg": "dist/vue-css-modules.min.js",
8 | "jsdelivr": "dist/vue-css-modules.min.js",
9 | "homepage": "https://github.com/fjc0k/vue-css-modules",
10 | "author": {
11 | "name": "fjc0k",
12 | "email": "fjc0kb@gmail.com",
13 | "url": "https://github.com/fjc0k"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git@github.com:fjc0k/vue-css-modules.git"
18 | },
19 | "bugs": {
20 | "url": "https://github.com/fjc0k/vue-css-modules/issues"
21 | },
22 | "keywords": [
23 | "vue",
24 | "vuejs",
25 | "css",
26 | "css-modules",
27 | "postcss",
28 | "sass",
29 | "less",
30 | "stylus"
31 | ],
32 | "files": [
33 | "dist",
34 | "lib"
35 | ],
36 | "scripts": {
37 | "demo": "bdr dev demo/index.js --css.modules",
38 | "test": "jest --coverage",
39 | "build": "bdr transform & bdr build",
40 | "release": "standard-version -a",
41 | "postrelease": "git push --follow-tags origin master && npm publish"
42 | },
43 | "standard-version": {
44 | "scripts": {
45 | "postbump": "yarn build && git add -A"
46 | }
47 | },
48 | "eslintConfig": {
49 | "root": true,
50 | "extends": "@fir-ui/fir"
51 | },
52 | "eslintIgnore": [
53 | "dist",
54 | "lib"
55 | ],
56 | "babel": {
57 | "presets": [
58 | [
59 | "@bdr/bdr"
60 | ]
61 | ]
62 | },
63 | "devDependencies": {
64 | "@babel/core": "^7.0.0-beta.44",
65 | "@fir-ui/eslint-config-fir": "^0.3.2",
66 | "babel-core": "^7.0.0-bridge.0",
67 | "babel-jest": "^22.4.3",
68 | "bdr": "^1.3.4",
69 | "codecov": "^3.0.0",
70 | "eslint": "^4.19.1",
71 | "jest": "^22.4.3",
72 | "standard-version": "^4.3.0"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 | exports.includes = includes;
5 | exports.isObject = isObject;
6 | exports.isFunction = isFunction;
7 | exports.isString = isString;
8 | exports.camelCase = camelCase;
9 | exports.dashCase = dashCase;
10 |
11 | function includes(arrayLike, element, fromIndex) {
12 | if (fromIndex === void 0) {
13 | fromIndex = 0;
14 | }
15 |
16 | for (var i = fromIndex, len = arrayLike.length; i < len; i++) {
17 | if (arrayLike[i] === element) {
18 | return true;
19 | }
20 | }
21 |
22 | return false;
23 | }
24 |
25 | function isObject(value) {
26 | return value !== null && typeof value === 'object';
27 | }
28 |
29 | function isFunction(value) {
30 | return typeof value === 'function';
31 | }
32 |
33 | function isString(value) {
34 | return typeof value === 'string';
35 | }
36 |
37 | var camelCaseCache = Object.create(null);
38 |
39 | function camelCase(value) {
40 | if (camelCaseCache[value]) return camelCaseCache[value];
41 | var result = '';
42 | var shouldUpperCase = false;
43 |
44 | for (var i = 0, len = value.length; i < len; i++) {
45 | var char = value[i];
46 |
47 | if (char === '-') {
48 | shouldUpperCase = true;
49 | } else {
50 | result += result && shouldUpperCase ? char.toUpperCase() : char;
51 | shouldUpperCase = false;
52 | }
53 | }
54 |
55 | camelCaseCache[value] = result;
56 | return result;
57 | }
58 |
59 | var dashCaseCache = Object.create(null);
60 |
61 | function dashCase(value) {
62 | if (dashCaseCache[value]) return dashCaseCache[value];
63 | var result = '';
64 | var shouldAddDash = false;
65 |
66 | for (var i = value.length - 1; i >= 0; i--) {
67 | var char = value[i];
68 | var charCode = char.charCodeAt(0);
69 |
70 | if (charCode >= 65 && charCode <= 90) {
71 | shouldAddDash = true;
72 | result = char.toLowerCase() + result;
73 | } else {
74 | result = char + (shouldAddDash ? '-' : '') + result;
75 | shouldAddDash = false;
76 | }
77 | }
78 |
79 | dashCaseCache[value] = result;
80 | return result;
81 | }
--------------------------------------------------------------------------------
/dist/vue-css-modules.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * vue-css-modules v1.2.0
3 | * (c) 2018-present fjc0k (https://github.com/fjc0k)
4 | * Released under the MIT License.
5 | */
6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueCSSModules=e()}(this,function(){"use strict";var t=Object.create(null);var e=Object.create(null),n=function(n){if(e[n])return e[n];var i,r,s,a;if(function(t,e,n){void 0===n&&(n=0);for(var i=n,r=t.length;i
18 |
19 | ## CSS Modules:局部作用域 & 模块化
20 |
21 | [`CSS Modules`](https://github.com/css-modules/css-modules) 为每一个局部类赋予全局唯一的类名,这样组件样式间就不会相互影响了。如:
22 |
23 | ```css
24 | /* button.css */
25 | .button {
26 | font-size: 16px;
27 | }
28 | .mini {
29 | font-size: 12px;
30 | }
31 | ```
32 |
33 | 它会被转换为类似这样:
34 |
35 | ```css
36 | /* button.css */
37 | .button__button--d8fj3 {
38 | font-size: 16px;
39 | }
40 | .button__mini--f90jc {
41 | font-size: 12px;
42 | }
43 | ```
44 |
45 | 当导入一个 CSS 模块文件时,它会将局部类名到全局类名的映射对象提供给我们。就像这样:
46 |
47 | ```javascript
48 | import styles from './button.css'
49 | // styles = {
50 | // button: 'button__button--d8fj3',
51 | // mini: 'button__mini--f90jc'
52 | // }
53 |
54 | element.innerHTML = ''
55 | ```
56 |
57 | ## `vue-css-modules`:简化类名映射
58 |
59 | 下面是一个使用了 CSS Modules 的按钮组件:
60 |
61 | ```html
62 |
63 |
68 |
69 |
70 |
78 | ```
79 |
80 | 的确,CSS Modules 对于 Vue 组件是一个不错的选择。但也存在以下几点不足:
81 |
82 | - 你必须在 `data` 中传入 `styles`
83 | - 你必须使用 `styles.localClassName` 导入全局类名
84 | - 如果有其他全局类名,你必须将它们放在一起
85 | - 如果要和组件的属性值绑定,就算局部类名和属性名一样,也要显式指定
86 |
87 | 对于上面的按钮组件,使用 `vue-css-modules` 后:
88 |
89 | ```html
90 |
91 |
96 |
97 |
98 |
107 | ```
108 |
109 | 现在:
110 |
111 | - 你不必在 `data` 中传入 `styles`,但得在 `mixins` 中传入 `styles` 🌝
112 | - 你可以跟 `styles.localClassName` 说拜拜了
113 | - 将局部类名放在 `styleName` 属性,全局类名放在 `class` 属性,规整了许多
114 | - 局部类名绑定组件同名属性,只需在其前面加上 `:` 修饰符
115 |
116 | ## 修饰符
117 |
118 | ### @button
119 |
120 | ```html
121 |
122 | ```
123 |
124 | 这等同于:
125 |
126 | ```html
127 |
128 | ```
129 |
130 | 这让你能在外部重置组件的样式:
131 |
132 | ```css
133 | .form [data-component-button] {
134 | font-size: 20px;
135 | }
136 | ```
137 |
138 | ### $type
139 |
140 | ```html
141 |
142 | ```
143 |
144 | 这等同于:
145 |
146 | ```html
147 |
148 | ```
149 |
150 | ### :mini
151 |
152 | ```html
153 |
154 | ```
155 |
156 | 这等同于:
157 |
158 | ```html
159 |
160 | ```
161 |
162 | ### disabled=isDisabled
163 |
164 | ```html
165 |
166 | ```
167 |
168 | 这等同于:
169 |
170 | ```html
171 |
172 | ```
173 |
174 | ## 使用方法
175 |
176 | ### 在 Vue 模板中使用
177 |
178 | #### 引入模板外部的 CSS 模块
179 |
180 | ```html
181 |
182 |
187 |
188 |
189 |
198 | ```
199 |
200 | #### 使用模板内部的 CSS 模块
201 |
202 | ```html
203 |
204 |
209 |
210 |
211 |
219 |
220 |
228 | ```
229 |
230 | ### 在 Vue JSX 中使用
231 |
232 | ```javascript
233 | import CSSModules from 'vue-css-modules'
234 | import styles from './button.css'
235 |
236 | export default {
237 | mixins: [CSSModules(styles)],
238 | props: { mini: Boolean },
239 | render() {
240 | return (
241 |
242 | )
243 | }
244 | }
245 | ```
246 |
247 | ### 在 Vue 渲染函数中使用
248 |
249 | ```javascript
250 | import CSSModules from 'vue-css-modules'
251 | import styles from './button.css'
252 |
253 | export default {
254 | mixins: [CSSModules(styles)],
255 | props: { mini: Boolean },
256 | render(h) {
257 | return h('button', {
258 | styleName: '@button :mini'
259 | }, '点我')
260 | }
261 | }
262 | ```
263 |
264 | ## 实现原理
265 |
266 | `vue-css-modules` 注册了 [`beforeCreate`](https://cn.vuejs.org/v2/api/#beforeCreate) 钩子,在钩子中劫持了组件的渲染函数。对于传给渲染函数的参数,将会解析其 `data` 或 `data.attrs` 中的 `styleName` 属性生成全局类名字符串,并将它附着在 `data.staticClass` 值的后面。
267 |
--------------------------------------------------------------------------------
/dist/vue-css-modules.es.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * vue-css-modules v1.2.0
3 | * (c) 2018-present fjc0k (https://github.com/fjc0k)
4 | * Released under the MIT License.
5 | */
6 | function includes(arrayLike, element, fromIndex) {
7 | if (fromIndex === void 0) {
8 | fromIndex = 0;
9 | }
10 |
11 | for (var i = fromIndex, len = arrayLike.length; i < len; i++) {
12 | if (arrayLike[i] === element) {
13 | return true;
14 | }
15 | }
16 |
17 | return false;
18 | }
19 | function isObject(value) {
20 | return value !== null && typeof value === 'object';
21 | }
22 | function isFunction(value) {
23 | return typeof value === 'function';
24 | }
25 | function isString(value) {
26 | return typeof value === 'string';
27 | }
28 | var camelCaseCache = Object.create(null);
29 | function camelCase(value) {
30 | if (camelCaseCache[value]) return camelCaseCache[value];
31 | var result = '';
32 | var shouldUpperCase = false;
33 |
34 | for (var i = 0, len = value.length; i < len; i++) {
35 | var char = value[i];
36 |
37 | if (char === '-') {
38 | shouldUpperCase = true;
39 | } else {
40 | result += result && shouldUpperCase ? char.toUpperCase() : char;
41 | shouldUpperCase = false;
42 | }
43 | }
44 |
45 | camelCaseCache[value] = result;
46 | return result;
47 | }
48 |
49 | var cache = Object.create(null);
50 | var parseClassExpression = (function (expression) {
51 | if (cache[expression]) return cache[expression];
52 | var className;
53 | var binding;
54 | var bindingValue;
55 | var role;
56 |
57 | if (includes(expression, '=', 1)) {
58 | // eg: disabled=isDisabled
59 | var _expression$split = expression.split('=');
60 |
61 | className = _expression$split[0];
62 | binding = _expression$split[1];
63 | } else {
64 | var modifier = expression[0];
65 |
66 | if (modifier === '$') {
67 | // eg: $type
68 | binding = expression.substr(1);
69 | bindingValue = true;
70 | } else if (modifier === '@') {
71 | // eg: @button
72 | className = expression.substr(1);
73 | role = className;
74 | } else if (modifier === ':') {
75 | // eg: :disabled
76 | className = expression.substr(1);
77 | binding = camelCase(className);
78 | } else {
79 | className = expression;
80 | }
81 | }
82 |
83 | cache[expression] = {
84 | className: className,
85 | binding: binding,
86 | bindingValue: bindingValue,
87 | role: role
88 | };
89 | return cache[expression];
90 | });
91 |
92 | var INJECT_ATTR = 'styleName';
93 |
94 | /* eslint max-depth: 0 guard-for-in: 0 */
95 |
96 | function createElement(_) {
97 | var args = [].slice.call(arguments, 1); // for functional component
98 |
99 | if (isFunction(_)) {
100 | return createElement.bind(_, {
101 | functional: true,
102 | createElement: _,
103 | styles: args[0],
104 | context: args[1]
105 | });
106 | }
107 |
108 | var _$functional = _.functional,
109 | functional = _$functional === void 0 ? false : _$functional,
110 | h = _.createElement,
111 | _$context = _.context,
112 | context = _$context === void 0 ? {} : _$context,
113 | _$styles = _.styles,
114 | styles = _$styles === void 0 ? context.$style || {} : _$styles;
115 |
116 | if (isString(styles)) {
117 | styles = (functional ? (context.injections || {})[styles] : context[styles]) || {};
118 | }
119 |
120 | if (functional) {
121 | context = context.props || {};
122 | }
123 |
124 | var data = args[1];
125 |
126 | if (isObject(data)) {
127 | if (!data.staticClass) {
128 | data.staticClass = '';
129 | }
130 |
131 | if (!data.attrs) {
132 | data.attrs = {};
133 | }
134 |
135 | var modules = data[INJECT_ATTR] || data.attrs[INJECT_ATTR] || '';
136 |
137 | if (modules.length) {
138 | var _modules = Array.isArray(modules) ? modules : [modules];
139 |
140 | for (var i in _modules) {
141 | var module = _modules[i];
142 |
143 | if (module && typeof module === 'string') {
144 | var classExpressions = module.split(/\s+/g);
145 |
146 | for (var _i in classExpressions) {
147 | var classExpression = classExpressions[_i];
148 |
149 | var _parseClassExpression = parseClassExpression(classExpression),
150 | className = _parseClassExpression.className,
151 | binding = _parseClassExpression.binding,
152 | bindingValue = _parseClassExpression.bindingValue,
153 | role = _parseClassExpression.role;
154 |
155 | if (bindingValue) {
156 | className = context[binding];
157 | binding = undefined;
158 | }
159 |
160 | if ((binding ? context[binding] : true) && styles[className]) {
161 | data.staticClass += " " + styles[className];
162 | data.staticClass = data.staticClass.trim();
163 | }
164 |
165 | if (role) {
166 | data.attrs["data-component-" + role] = '';
167 | }
168 | }
169 | }
170 | }
171 | } // remove styleName attr
172 |
173 |
174 | delete data[INJECT_ATTR];
175 | delete data.attrs[INJECT_ATTR];
176 | }
177 |
178 | return h.apply(null, args);
179 | }
180 |
181 | /* eslint camelcase: 0 */
182 |
183 | var CSSModules = function CSSModules(styles) {
184 | return {
185 | beforeCreate: function beforeCreate() {
186 | this.original$createElement = this.original$createElement || this.$createElement;
187 | this.original_c = this.original_c || this._c;
188 | this.$createElement = createElement.bind(this, {
189 | createElement: this.original$createElement,
190 | context: this,
191 | styles: styles
192 | });
193 | this._c = createElement.bind(this, {
194 | createElement: this.original_c,
195 | context: this,
196 | styles: styles
197 | });
198 | }
199 | };
200 | };
201 |
202 | CSSModules.install = function (Vue) {
203 | Vue.mixin(CSSModules());
204 | };
205 |
206 | export default CSSModules;
207 |
--------------------------------------------------------------------------------
/dist/vue-css-modules.cjs.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * vue-css-modules v1.2.0
3 | * (c) 2018-present fjc0k (https://github.com/fjc0k)
4 | * Released under the MIT License.
5 | */
6 | 'use strict';
7 |
8 | function includes(arrayLike, element, fromIndex) {
9 | if (fromIndex === void 0) {
10 | fromIndex = 0;
11 | }
12 |
13 | for (var i = fromIndex, len = arrayLike.length; i < len; i++) {
14 | if (arrayLike[i] === element) {
15 | return true;
16 | }
17 | }
18 |
19 | return false;
20 | }
21 | function isObject(value) {
22 | return value !== null && typeof value === 'object';
23 | }
24 | function isFunction(value) {
25 | return typeof value === 'function';
26 | }
27 | function isString(value) {
28 | return typeof value === 'string';
29 | }
30 | var camelCaseCache = Object.create(null);
31 | function camelCase(value) {
32 | if (camelCaseCache[value]) return camelCaseCache[value];
33 | var result = '';
34 | var shouldUpperCase = false;
35 |
36 | for (var i = 0, len = value.length; i < len; i++) {
37 | var char = value[i];
38 |
39 | if (char === '-') {
40 | shouldUpperCase = true;
41 | } else {
42 | result += result && shouldUpperCase ? char.toUpperCase() : char;
43 | shouldUpperCase = false;
44 | }
45 | }
46 |
47 | camelCaseCache[value] = result;
48 | return result;
49 | }
50 |
51 | var cache = Object.create(null);
52 | var parseClassExpression = (function (expression) {
53 | if (cache[expression]) return cache[expression];
54 | var className;
55 | var binding;
56 | var bindingValue;
57 | var role;
58 |
59 | if (includes(expression, '=', 1)) {
60 | // eg: disabled=isDisabled
61 | var _expression$split = expression.split('=');
62 |
63 | className = _expression$split[0];
64 | binding = _expression$split[1];
65 | } else {
66 | var modifier = expression[0];
67 |
68 | if (modifier === '$') {
69 | // eg: $type
70 | binding = expression.substr(1);
71 | bindingValue = true;
72 | } else if (modifier === '@') {
73 | // eg: @button
74 | className = expression.substr(1);
75 | role = className;
76 | } else if (modifier === ':') {
77 | // eg: :disabled
78 | className = expression.substr(1);
79 | binding = camelCase(className);
80 | } else {
81 | className = expression;
82 | }
83 | }
84 |
85 | cache[expression] = {
86 | className: className,
87 | binding: binding,
88 | bindingValue: bindingValue,
89 | role: role
90 | };
91 | return cache[expression];
92 | });
93 |
94 | var INJECT_ATTR = 'styleName';
95 |
96 | /* eslint max-depth: 0 guard-for-in: 0 */
97 |
98 | function createElement(_) {
99 | var args = [].slice.call(arguments, 1); // for functional component
100 |
101 | if (isFunction(_)) {
102 | return createElement.bind(_, {
103 | functional: true,
104 | createElement: _,
105 | styles: args[0],
106 | context: args[1]
107 | });
108 | }
109 |
110 | var _$functional = _.functional,
111 | functional = _$functional === void 0 ? false : _$functional,
112 | h = _.createElement,
113 | _$context = _.context,
114 | context = _$context === void 0 ? {} : _$context,
115 | _$styles = _.styles,
116 | styles = _$styles === void 0 ? context.$style || {} : _$styles;
117 |
118 | if (isString(styles)) {
119 | styles = (functional ? (context.injections || {})[styles] : context[styles]) || {};
120 | }
121 |
122 | if (functional) {
123 | context = context.props || {};
124 | }
125 |
126 | var data = args[1];
127 |
128 | if (isObject(data)) {
129 | if (!data.staticClass) {
130 | data.staticClass = '';
131 | }
132 |
133 | if (!data.attrs) {
134 | data.attrs = {};
135 | }
136 |
137 | var modules = data[INJECT_ATTR] || data.attrs[INJECT_ATTR] || '';
138 |
139 | if (modules.length) {
140 | var _modules = Array.isArray(modules) ? modules : [modules];
141 |
142 | for (var i in _modules) {
143 | var module = _modules[i];
144 |
145 | if (module && typeof module === 'string') {
146 | var classExpressions = module.split(/\s+/g);
147 |
148 | for (var _i in classExpressions) {
149 | var classExpression = classExpressions[_i];
150 |
151 | var _parseClassExpression = parseClassExpression(classExpression),
152 | className = _parseClassExpression.className,
153 | binding = _parseClassExpression.binding,
154 | bindingValue = _parseClassExpression.bindingValue,
155 | role = _parseClassExpression.role;
156 |
157 | if (bindingValue) {
158 | className = context[binding];
159 | binding = undefined;
160 | }
161 |
162 | if ((binding ? context[binding] : true) && styles[className]) {
163 | data.staticClass += " " + styles[className];
164 | data.staticClass = data.staticClass.trim();
165 | }
166 |
167 | if (role) {
168 | data.attrs["data-component-" + role] = '';
169 | }
170 | }
171 | }
172 | }
173 | } // remove styleName attr
174 |
175 |
176 | delete data[INJECT_ATTR];
177 | delete data.attrs[INJECT_ATTR];
178 | }
179 |
180 | return h.apply(null, args);
181 | }
182 |
183 | /* eslint camelcase: 0 */
184 |
185 | var CSSModules = function CSSModules(styles) {
186 | return {
187 | beforeCreate: function beforeCreate() {
188 | this.original$createElement = this.original$createElement || this.$createElement;
189 | this.original_c = this.original_c || this._c;
190 | this.$createElement = createElement.bind(this, {
191 | createElement: this.original$createElement,
192 | context: this,
193 | styles: styles
194 | });
195 | this._c = createElement.bind(this, {
196 | createElement: this.original_c,
197 | context: this,
198 | styles: styles
199 | });
200 | }
201 | };
202 | };
203 |
204 | CSSModules.install = function (Vue) {
205 | Vue.mixin(CSSModules());
206 | };
207 |
208 | module.exports = CSSModules;
209 |
--------------------------------------------------------------------------------
/dist/vue-css-modules.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * vue-css-modules v1.2.0
3 | * (c) 2018-present fjc0k (https://github.com/fjc0k)
4 | * Released under the MIT License.
5 | */
6 | (function (global, factory) {
7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8 | typeof define === 'function' && define.amd ? define(factory) :
9 | (global.VueCSSModules = factory());
10 | }(this, (function () { 'use strict';
11 |
12 | function includes(arrayLike, element, fromIndex) {
13 | if (fromIndex === void 0) {
14 | fromIndex = 0;
15 | }
16 |
17 | for (var i = fromIndex, len = arrayLike.length; i < len; i++) {
18 | if (arrayLike[i] === element) {
19 | return true;
20 | }
21 | }
22 |
23 | return false;
24 | }
25 | function isObject(value) {
26 | return value !== null && typeof value === 'object';
27 | }
28 | function isFunction(value) {
29 | return typeof value === 'function';
30 | }
31 | function isString(value) {
32 | return typeof value === 'string';
33 | }
34 | var camelCaseCache = Object.create(null);
35 | function camelCase(value) {
36 | if (camelCaseCache[value]) return camelCaseCache[value];
37 | var result = '';
38 | var shouldUpperCase = false;
39 |
40 | for (var i = 0, len = value.length; i < len; i++) {
41 | var char = value[i];
42 |
43 | if (char === '-') {
44 | shouldUpperCase = true;
45 | } else {
46 | result += result && shouldUpperCase ? char.toUpperCase() : char;
47 | shouldUpperCase = false;
48 | }
49 | }
50 |
51 | camelCaseCache[value] = result;
52 | return result;
53 | }
54 |
55 | var cache = Object.create(null);
56 | var parseClassExpression = (function (expression) {
57 | if (cache[expression]) return cache[expression];
58 | var className;
59 | var binding;
60 | var bindingValue;
61 | var role;
62 |
63 | if (includes(expression, '=', 1)) {
64 | // eg: disabled=isDisabled
65 | var _expression$split = expression.split('=');
66 |
67 | className = _expression$split[0];
68 | binding = _expression$split[1];
69 | } else {
70 | var modifier = expression[0];
71 |
72 | if (modifier === '$') {
73 | // eg: $type
74 | binding = expression.substr(1);
75 | bindingValue = true;
76 | } else if (modifier === '@') {
77 | // eg: @button
78 | className = expression.substr(1);
79 | role = className;
80 | } else if (modifier === ':') {
81 | // eg: :disabled
82 | className = expression.substr(1);
83 | binding = camelCase(className);
84 | } else {
85 | className = expression;
86 | }
87 | }
88 |
89 | cache[expression] = {
90 | className: className,
91 | binding: binding,
92 | bindingValue: bindingValue,
93 | role: role
94 | };
95 | return cache[expression];
96 | });
97 |
98 | var INJECT_ATTR = 'styleName';
99 |
100 | /* eslint max-depth: 0 guard-for-in: 0 */
101 |
102 | function createElement(_) {
103 | var args = [].slice.call(arguments, 1); // for functional component
104 |
105 | if (isFunction(_)) {
106 | return createElement.bind(_, {
107 | functional: true,
108 | createElement: _,
109 | styles: args[0],
110 | context: args[1]
111 | });
112 | }
113 |
114 | var _$functional = _.functional,
115 | functional = _$functional === void 0 ? false : _$functional,
116 | h = _.createElement,
117 | _$context = _.context,
118 | context = _$context === void 0 ? {} : _$context,
119 | _$styles = _.styles,
120 | styles = _$styles === void 0 ? context.$style || {} : _$styles;
121 |
122 | if (isString(styles)) {
123 | styles = (functional ? (context.injections || {})[styles] : context[styles]) || {};
124 | }
125 |
126 | if (functional) {
127 | context = context.props || {};
128 | }
129 |
130 | var data = args[1];
131 |
132 | if (isObject(data)) {
133 | if (!data.staticClass) {
134 | data.staticClass = '';
135 | }
136 |
137 | if (!data.attrs) {
138 | data.attrs = {};
139 | }
140 |
141 | var modules = data[INJECT_ATTR] || data.attrs[INJECT_ATTR] || '';
142 |
143 | if (modules.length) {
144 | var _modules = Array.isArray(modules) ? modules : [modules];
145 |
146 | for (var i in _modules) {
147 | var module = _modules[i];
148 |
149 | if (module && typeof module === 'string') {
150 | var classExpressions = module.split(/\s+/g);
151 |
152 | for (var _i in classExpressions) {
153 | var classExpression = classExpressions[_i];
154 |
155 | var _parseClassExpression = parseClassExpression(classExpression),
156 | className = _parseClassExpression.className,
157 | binding = _parseClassExpression.binding,
158 | bindingValue = _parseClassExpression.bindingValue,
159 | role = _parseClassExpression.role;
160 |
161 | if (bindingValue) {
162 | className = context[binding];
163 | binding = undefined;
164 | }
165 |
166 | if ((binding ? context[binding] : true) && styles[className]) {
167 | data.staticClass += " " + styles[className];
168 | data.staticClass = data.staticClass.trim();
169 | }
170 |
171 | if (role) {
172 | data.attrs["data-component-" + role] = '';
173 | }
174 | }
175 | }
176 | }
177 | } // remove styleName attr
178 |
179 |
180 | delete data[INJECT_ATTR];
181 | delete data.attrs[INJECT_ATTR];
182 | }
183 |
184 | return h.apply(null, args);
185 | }
186 |
187 | /* eslint camelcase: 0 */
188 |
189 | var CSSModules = function CSSModules(styles) {
190 | return {
191 | beforeCreate: function beforeCreate() {
192 | this.original$createElement = this.original$createElement || this.$createElement;
193 | this.original_c = this.original_c || this._c;
194 | this.$createElement = createElement.bind(this, {
195 | createElement: this.original$createElement,
196 | context: this,
197 | styles: styles
198 | });
199 | this._c = createElement.bind(this, {
200 | createElement: this.original_c,
201 | context: this,
202 | styles: styles
203 | });
204 | }
205 | };
206 | };
207 |
208 | CSSModules.install = function (Vue) {
209 | Vue.mixin(CSSModules());
210 | };
211 |
212 | return CSSModules;
213 |
214 | })));
215 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | English | [🇨🇳中文](./README_zh-CN.md)
2 |
3 | # Vue CSS Modules
4 |
5 | [](https://travis-ci.org/fjc0k/vue-css-modules)
6 | [](https://github.com/fjc0k/vue-css-modules/blob/master/dist/vue-css-modules.min.js)
7 | [](https://github.com/fjc0k/vue-css-modules/blob/master/dist/vue-css-modules.min.js)
8 |
9 | Seamless mapping of class names to CSS modules inside of Vue components.
10 |
11 | ```shell
12 | yarn add vue-css-modules
13 | ```
14 |
15 | CDN: [jsDelivr](//www.jsdelivr.com/package/npm/vue-css-modules) | [UNPKG](//unpkg.com/vue-css-modules/) (Avaliable as `window.VueCSSModules`)
16 |
17 |
18 |
19 | ## CSS Modules: local scope & modular
20 |
21 | [`CSS Modules`](https://github.com/css-modules/css-modules) assigns a local class a global unique name, so a component styles will not affect other components. e.g.
22 |
23 | ```css
24 | /* button.css */
25 | .button {
26 | font-size: 16px;
27 | }
28 | .mini {
29 | font-size: 12px;
30 | }
31 | ```
32 |
33 | It's will transformed to something similar to:
34 |
35 | ```css
36 | /* button.css */
37 | .button__button--d8fj3 {
38 | font-size: 16px;
39 | }
40 | .button__mini--f90jc {
41 | font-size: 12px;
42 | }
43 | ```
44 |
45 | When importing the CSS Module from a JS Module, it exports an object with all mappings from local names to global names. Just like this:
46 |
47 | ```javascript
48 | import styles from './button.css'
49 | // styles = {
50 | // button: 'button__button--d8fj3',
51 | // mini: 'button__mini--f90jc'
52 | // }
53 |
54 | element.innerHTML = ''
55 | ```
56 |
57 | ## `vue-css-modules`: simplify mapping name
58 |
59 | Here's a button component with CSS Modules:
60 |
61 | ```html
62 |
63 |
68 |
69 |
70 |
78 | ```
79 |
80 | Surely, CSS Modules is a good choice for Vue components. But here are a few disadvantages:
81 |
82 | - You have to pass `styles` object into `data` function.
83 | - You have to use `styles.localClassName` importing a global class name.
84 | - If there are other global class names, you have to put them together.
85 | - If you want to bind a class name to a component property value, you have to explicitly specify the property name, even if the class name is equals the property name.
86 |
87 | Now, you can use `vue-css-modules` to remake it:
88 |
89 | ```html
90 |
91 |
96 |
97 |
98 |
107 | ```
108 |
109 | Using `vue-css-modules`:
110 |
111 | - You don't need pass `styles` object into `data` function, but the `CSSModules` mixin. 🌝
112 | - You can completely say byebye to `styles.localClassName`.
113 | - There is clear distinction between global CSS and CSS Modules.
114 | - You can use the `:` modifier to bind the property with the same name.
115 |
116 | ## Modifiers
117 |
118 | ### @button
119 |
120 | ```html
121 |
122 | ```
123 |
124 | This is the equivalent to:
125 |
126 | ```html
127 |
128 | ```
129 |
130 | This allows you to override component styles in context:
131 |
132 | ```css
133 | .form [data-component-button] {
134 | font-size: 20px;
135 | }
136 | ```
137 |
138 | ### $type
139 |
140 | ```html
141 |
142 | ```
143 |
144 | This is the equivalent to:
145 |
146 | ```html
147 |
148 | ```
149 |
150 | ### :mini
151 |
152 | ```html
153 |
154 | ```
155 |
156 | This is the equivalent to:
157 |
158 | ```html
159 |
160 | ```
161 |
162 | ### disabled=isDisabled
163 |
164 | ```html
165 |
166 | ```
167 |
168 | This is the equivalent to:
169 |
170 | ```html
171 |
172 | ```
173 |
174 | ## Usage
175 |
176 | ### In templates
177 |
178 | #### CSS Modules outside the template
179 |
180 | ```html
181 |
182 |
187 |
188 |
189 |
198 | ```
199 |
200 | #### CSS Modules inside the template
201 |
202 | ```html
203 |
204 |
209 |
210 |
211 |
219 |
220 |
228 | ```
229 |
230 | ### In JSX
231 |
232 | ```javascript
233 | import CSSModules from 'vue-css-modules'
234 | import styles from './button.css'
235 |
236 | export default {
237 | mixins: [CSSModules(styles)],
238 | props: { mini: Boolean },
239 | render() {
240 | return (
241 |
242 | )
243 | }
244 | }
245 | ```
246 |
247 | ### In render functions
248 |
249 | ```javascript
250 | import CSSModules from 'vue-css-modules'
251 | import styles from './button.css'
252 |
253 | export default {
254 | mixins: [CSSModules(styles)],
255 | props: { mini: Boolean },
256 | render(h) {
257 | return h('button', {
258 | styleName: '@button :mini'
259 | }, 'Click me')
260 | }
261 | }
262 | ```
263 |
264 | ## The implementation
265 |
266 | `vue-css-modules` extends `$createElement` method of the current component. It will use the value of `styleName` in `data` or `data.attrs` to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to the `data.staticClass` value.
267 |
--------------------------------------------------------------------------------