在我正在工作的应用程序中,我必须在客户端呈现之前动态编译SASS(缓存系统即将到来,不用担心)。目前我正在使用node-sass,一切都很顺利。
这是我到目前为止所做的工作。为简洁起见,已删除其他项目特定代码:
var sass = require('node-sass'),
autoprefixer = require('autoprefixer-core'),
vars = require('postcss-simple-vars'),
postcss = require('postcss'),
function compileCSS() {
var result = sass.renderSync({
file: 'path/to/style.scss'
});
return postcss([autoprefixer]).process(result.css.toString()).css;
}
现在我需要从Node传递动态数据并将其编译为普通的SASS变量。最初我尝试使用PostCSS,因为我注意到变量注入是something it could do。不幸的是,这没有用。 PostCSS在编译阶段开始,在这一点上惨遭失败。
接下来,我尝试使用underscore模板尝试使用node-sass覆盖' importer()
:
var result = sass.renderSync({
file: 'path/to/style.scss',
importer: function(url, prev, done) {
var content = fs.readFileSync(partial_path),
partial = _.template(content.toString());
return {
contents: partial({ test: 'test' })
};
}
});
导致以下错误:
Error: error reading values after :
显然SASS不喜欢下划线的变量语法..
如何从我的Node应用程序中将动态变量传递给SASS?
答案 0 :(得分:58)
我发现自己情况非常相似。我们有很多现有的SASS,现在需要接受要在整个过程中使用的动态值/变量(作为变量)。我最初沿着编写临时目录/文件的路线走下去,基本上创建了一个"代理入口点"这将创建一个proxy_entry.scss
和variables.scss
并使用声明的SASS变量引导实际的entry.scss
。 这很好,并取得了预期的效果,但感觉有点过于复杂......
由于node-sass's options.data
选项,可以使用更简单的解决方案。这接受一个" SASS字符串进行评估"。
类型:字符串默认值:null特殊:必须指定文件或数据
要传递给libsass进行渲染的字符串。建议您将includePaths与此结合使用,以便libsass在使用@import指令时可以找到文件。
这完全消除了编写/管理所有临时目录和文件的需要。
解决方案归结为类似
var sassOptionsDefaults = {
includePaths: [
'some/include/path'
],
outputStyle: 'compressed'
};
options.data
var dataString =
sassGenerator.sassVariables(variables) +
sassGenerator.sassImport(scssEntry);
var sassOptions = _.assign({}, sassOptionsDefaults, {
data: dataString
});
var sass = require('node-sass');
sass.render(sassOptions, function (err, result) {
return (err)
? handleError(err);
: handleSuccess(result.css.toString());
});
注意: 这假设您的entry.scss
导入一些variables.scss
,将变量定义为"默认值为"。
// variables.scss
$someColor: blue !default;
$someFontSize: 13px !default;
// entry.scss
@import 'variables';
.some-selector {
color: $someColor;
font-size: $someFontSize;
}
var sass = require('node-sass');
// 1.) Define sassOptions as usual
var sassOptionsDefaults = {
includePaths: [
'some/include/path'
],
outputStyle: 'compressed'
};
function dynamicSass(scssEntry, variables, handleSuccess, handleError) {
// 2.) Dynamically create "SASS variable declarations"
// then import the "actual entry.scss file".
// dataString is just "SASS" to be evaluated before
// the actual entry.scss is imported.
var dataString =
sassGenerator.sassVariables(variables) +
sassGenerator.sassImport(scssEntry);
var sassOptions = _.assign({}, sassOptionsDefaults, {
data: dataString
});
// 3.) render sass as usual
sass.render(sassOptions, function (err, result) {
return (err)
? handleError(err);
: handleSuccess(result.css.toString());
});
}
// Example usage.
dynamicSass('some/path/entry.scss', {
'someColor': 'red',
'someFontSize': '18px'
}, someSuccessFn, someErrorFn);
" sassGenerator"函数可能看起来像
function sassVariable(name, value) {
return "$" + name + ": " + value + ";";
}
function sassVariables(variablesObj) {
return Object.keys(variablesObj).map(function (name) {
return sassVariable(name, variablesObj[name]);
}).join('\n')
}
function sassImport(path) {
return "@import '" + path + "';";
}
这使您能够像以前一样编写SASS,在任何需要的地方使用SASS变量。它也没有把你束缚到任何特殊的动态sass实现" (即这样可以避免在.scss
个文件中使用"下划线/ lodash模板)。这也意味着你可以利用IDE功能,linting等......就像你现在只是回到写正规SASS 一样。
此外,它可以很好地转换为非节点/ http /即时编译用法,例如通过Gulp预先编译entry.scss
多个变量给定多个值集......
我希望这可以帮助你@ChrisWright(以及其他人)!我知道我很难找到关于这个主题的信息,我想这是一个相当常见的用例(想要从数据库,配置,HTTP参数等将动态值传递给SASS)。
答案 1 :(得分:2)
在我绕过node-sass'importer()
方法后,我能够解决这个问题。我的解决方案包括下划线模板和手动读取文件。它不是最优雅或最有效的解决方案,但每个生成的页面只运行一次。之后,文件将被缩小并缓存以供将来请求使用。
// Other none-sass render parameters omitted for brevity
importer: function (url, prev, done) {
// Manually read each partial file as it's encountered
fs.readFile(url, function (err, result) {
if (err) {
// If there is an error reading the file, call done() with
// no content to avoid a crash
return done({
contents: ''
});
}
// Create an underscore template out of the partial
var partial = _.template(result.toString());
// Compile template and return its contents to node-sass for
// compilation with the rest of the SCSS partials
done({
contents: partial({ data: YOUR_DATA_OBJECT })
});
});
}
使用此解决方案,我们可以在SCSS部分中引用正常的下划线变量语法。举个例子:
body {
color: <%= data.colour %>;
}
答案 2 :(得分:0)
我解决了类似的问题,虽然不是在Node中,而是在Java中。我需要根据访问网站的客户端从数据库中呈现SASS变量以生成网站主题。
我探索了一些解决方案并遇到了第三方服务https://www.grooveui.com。它提供了一种语言无关的解决方案来解决这个问题。