cha*_*lee 11 javascript css angularjs css-modules
我现在正在使用带有AngularJS 1.5组件的css-modules.堆栈是webpack+ css-loader?modules=true+ angular 1.5 components+ pug.
目前,我必须执行以下步骤才能在我的pug模板中使用css模块.
// my-component.js
import template from 'my-component.pug';
import styles from 'my-component.css';
class MyComponent {
constructor($element) {
$element.addClass('myComponent'); // ------ (1)
this.styles = styles; // ------ (2)
}
}
angular.module(name, deps)
.component('my-component', {
controller: MyComponent,
template: template,
});
// my-component.pug
div(class={{ ::$ctrl.styles.fooBar }}) FooBar // ----- (3)
// my-component.css
.myComponent { background: green; }
.fooBar { color: red; }
Run Code Online (Sandbox Code Playgroud)
有两个问题:
每个组件都必须$element手动注入和设置其类名.这样做的原因是,AngularJS组件标签本身存在于没有任何类的结果HTML中,这使得CSS变得困难.例如,如果我MyComponent像上面这样使用:
<div>
<my-component></my-component>
</div>
Run Code Online (Sandbox Code Playgroud)
它将生成以下HTML:
<div>
<my-component>
<div class="my-component__fooBar__3B2xz">FooBar</div>
</my-component>
</div>
Run Code Online (Sandbox Code Playgroud)
与ReactJS相比,<my-component>在上面的结果中HTML是一个额外的,有时它使CSS难以编写.所以我的解决方案是(1),为它添加一个类.
模板中的类太长(3).我知道这是正确的参考方式,$ctrl.styles.fooBar但这太长了.
我理想的解决方案是这样的:
// my-component.js
angular.module(name, deps)
.component('my-component', {
controller: MyComponent,
template: template,
styles: styles,
});
// my-component.css
div(css-class="fooBar") FooBar
Run Code Online (Sandbox Code Playgroud)
这个想法是:
angular.module().component支持一个额外的styles属性,它将this.styles = styles;在控制器中自动执行(2),并同时应用(1)$element.addClass().css-class适用$ctrl.styles于元素的指令.我的问题是,我不知道如何实现上面的想法1(2很容易).我很感激,如果有人能分享一些关于此的话.
我想出了一个我不太满意的解决方案.
Angular组件可以接受函数作为模板并注入$element.
DOC
如果template是一个函数,那么它将注入以下locals:
- $ element - 当前元素
- $ attrs - 元素的当前属性对象
因此,我们可以.myComponent在模板函数中附加component()的主类,然后正则表达式用编译的类名替换所有类名的出现.
// utils.js
function decorateTemplate(template, styles, className) {
return ['$element', $element => {
$element.addClass(styles[className]);
return template.replace(/\$\{(\w+)\}/g, (match, p1) => styles[p1]);
}];
}
// my-component.js
import style from './my-component.css';
import template from './my-component.pug';
import { decorateTemplate } from 'shared/utils';
class MyComponent {
// NO NEED to inject $element in constructor
// constructor($element) { ...
}
angular.module(name, deps)
.component('myComponent', {
// decorate the template with styles
template: decorateTemplate(template, styles, 'myComponent'),
});
// my-component.pug, with special '${className}' notation
div(class="${fooBar}") FooBar
Run Code Online (Sandbox Code Playgroud)
还有一个地方需要改进,decorateTemplate使用正则表达式替换,模板必须使用特殊表示法${className}来指定css-modules类名.
欢迎任何建议.
我更新了我的decorateTemplate()函数以利用pug功能,以便可以将本地类名称写为._localClassName.
// utils.js
function decorateTemplate(template, styles, className) {
return ['$element', ($element) => {
$element.addClass(styles[className]);
return template.replace(/\sclass="(.+?)"/g, (match, p1) => {
let classes = p1.split(/\s+/);
classes = classes.map(className => {
if (className.startsWith('_')) {
let localClassName = className.slice(1);
if (styles[localClassName]) {
return styles[localClassName];
} else {
console.warn(`Warning: local class name ${className} not found`);
return className;
}
} else {
return className;
}
});
return ' class="' + classes.join(' ') + '"';
});
}];
}
// my-component.pug
._fooBar FooBar
Run Code Online (Sandbox Code Playgroud)
虽然这更容易,但它并没有消除古怪的符号(从_本地类名开始)和正则表达式替换.
欢迎任何建议.