如何在 Polymer 组件中使用 Sass

12
我目前在使用 Polymer 作为我的前端开发框架,我喜欢 SASS。现在我明白了我可以像平常一样创建一个 Sass 文件并导入它。
然而,我已经养成了在我的 Web 组件中使用样式标签的习惯。
基本上,我正在寻找的工作流程是能够在我的 Web 组件中简单地定义一个脚本标签,可能加上 type='sass',然后让 grunt 编译所有在这些标签中的 SASS,最后输出文件到我的 .tmp 目录中。
像 Grunt 或 Gulp 这样的工具能否实现这样的操作?如果可以,有哪些最好的模块可以帮助我实现这个目标?

你有没有检查我的答案?这个问题很久以前就提出了,但我想与你分享! - David Vega
你可以用更少的工作量来实现你正在做的事情。这个问题的重点是不需要单独的样式表。 - endy
@endy 你是如何解决那个问题的?请与我们分享! - falsarella
大多数想要使用Sass的用例可能最好使用样式模块来完成。Rob Dodson在他最近的Polycast中实际上谈到了这个问题。 - endy
3个回答

8

我的实现是基于替换Polymer html文件中的标签。我正在使用gulp,但可以更改为仅使用fs

文件结构应该像这个例子:

app-view
 |- app-view.html
 |- app-view.scss

app-view.html:

<dom-module id="app-view">
    <template>
        <style>
            <!-- inject{scss} -->
        </style>
    </template>
</dom-module>

app-view.scss:

:host{
    margin-top: 50px;
    justify-content: center;
    display: flex;
}
#container{
    font-size: 12px;
    h1{
        font-size: 20px;
    }
}

gulpfile.js:

var gulp = require('gulp');
var nodeSass = require('node-sass');
var path = require('path');
var fs = require('fs');
var map = require('map-stream');
var srcPath = 'src/';
var buildPath = 'build/';
var buildSrcPath = path.join(buildPath, 'target');

gulp.task('processComponents', function () {
    return gulp.src([srcPath + '/components/**/*.html'])
        .pipe(map(function (file, cb) {
            var injectString = '<!-- inject{scss} -->';
            // convert file buffer into a string
            var contents = file.contents.toString();
            if (contents.indexOf(injectString) >= 0) {
                //Getting scss
                var scssFile = file.path.replace(/\.html$/i, '.scss');
                fs.readFile(scssFile, function (err, data) {
                    if (!err && data) {
                        nodeSass.render({
                            data: data.toString(),
                            includePaths: [path.join(srcPath, 'style/')],
                            outputStyle: 'compressed'
                        }, function (err, compiledScss) {
                            if (!err && compiledScss) {
                                file.contents = new Buffer(contents.replace(injectString, compiledScss.css.toString()), 'binary');
                            }
                            return cb(null, file);
                        });
                    }
                    return cb(null, file);
                });
            } else {
                // continue
                return cb(null, file);
            }
        }))
        .pipe(gulp.dest(path.join(buildSrcPath, 'components')));
});

结果:

<dom-module id="app-view">
    <template>
        <style>
            :host{margin-top:50px;justify-content:center;display:flex}#container{font-size:12px}#container h1{font-size:20px}
        </style>
    </template>
</dom-module>

第一个 return cb(null, file); 只会从 nodeSass.render 回调函数中返回,第二个 return cb(null, file); 必须在 else 块内,否则也会被调用。 - sergimassaneda
@sergimassaneda:我注意到你说的话,令人难以置信的是,如果我们将其删除,文件就无法解析!归根结底,这个脚本会将每个文件返回到管道并替换每个HTML!!! - Jose A
@sergimassaneda:我回来了。我会发布一个更新。我修改了David Vega的解决方案。这样更清晰。实际上,我们需要返回cb(),否则它会复制文件!! - Jose A
我们只需要在解析文件时执行 return cb(null, file)。其他情况下,我们只需执行 return cb(不带参数)。 - Jose A

2
首先,非常感谢和感激David Vega展示了如何完成这个技术!我做了一些调整并且优化了代码。以下是文件的github链接:https://github.com/superjose/polymer-sass/tree/master 嗯,这花费了我一些时间。接下来就是Polymer 1.1版本。从官网(https://www.polymer-project.org/1.0/docs/devguide/styling#style-modules)可以看到:
注意:样式模块在Polymer 1.1中被引入,它们替代了对外部样式表的实验性支持。
相反,现在它们支持“共享样式”。
这意味着我们可以导入包含CSS内容的.html文件。问题是我们不能用正常的方法使用.sass。
幸运的是,这里有一个更简单的解决方案。
以下脚本的作用是获取你的.scss文件,解析它们,并将它们注入到共享样式.html中。
以下是代码。下面是使用和设置步骤:
    var gulp = require('gulp');
var nodeSass = require('node-sass');
var path = require('path');
var fs = require('fs');
var map = require('map-stream');
var basePath = "app/";
var excludeDir = basePath+"bower_components/";
var ext = "**/*.html";

/**
 * We need to specify to nodeSass the include paths for Sass' @import
 * command. These are all the paths that it will look for it. 
 * 
 * Failing to specify this, will NOT Compile your scss and inject it to 
 * your .html file.
 * 
 */
var includePaths = ['app/elements/**/'];

gulp.task('watchSass', function(){
  gulp.watch(['app/**/*.scss', '!app/bower_components/**/*.scss'], ["injectSass"]);  
});




//This is currently not used. But you can enable by uncommenting 
// " //return gulp.src([basePath+ext,...excludeDirs])" above the return.
var excludeDirs = [`!${basePath}/bower_components/${ext}`,`!${basePath}/images/${ext}`]
/**
 * 
 * Enable for advanced use:
 * 
 * 
 */

gulp.task('injectSass', function () {
    /* Original creator: David Vega. I just modified
    * it to take advantage of the Polymer 1.1's shared styles. 
    * 
    * This will look all the files that are inside:
    * app/elements folder. You can change this to match 
    * your structure.  Note, this gulp script uses convention
    * over configuration. This means that if you have a file called
    * my-element-styles.html you should have a file called 
    * my-element-styles.scss
    * 
    * Note #2: 
    * We use "!" (Exclamation Mark) to exclude gulp from searching these paths. 
    * What I'm doing here, is that Polymer Starter Kit has inside its app folder
    * all the bower dependencies (bower_components). If we don't specify it to 
    * exclude this path, this will look inside bower_components and will take a long time
    * (around 7.4 seconds in my machine) to replace all the files. 
    */
    //Uncomment if you want to specify multiple exclude directories. Uses ES6 spread operator.
    //return gulp.src([basePath+ext,...excludeDirs])
    return gulp.src([basePath+ext, '!'+excludeDir+ext])
        .pipe(map(function (file, cb) {
            //This will match anything between the Start Style and End Style HTML comments. 
            var startStyle = "<!-- Start Style -->";
            var endStyle = "<!-- End Style -->";
            //Creates the regEx this ways so I can pass the variables. 
            var regEx = new RegExp(startStyle+"[\\s\\S]*"+endStyle, "g");

            // Converts file buffer into a string
            var contents = file.contents.toString();


            //Checks if the RegEx exists in the file. If not, 
            //don't do anything and return.

            //Rewrote the if for reduced nesting.
            if (!regEx.test(contents)) {
                //Return empty. if we return cb(null, file). It will add
                //the file that we do not want to the pipeline!!
                return cb();
            }
            /**
             * Getting scss
             * This will get the .html file that matches the current name
             * This means that if you have my-app.component.html 
             * this will match my-app.component.scss. Replace with .sass if you 
             * have .sass files instead. 
             */
                var scssFile = file.path.replace(/\.html$/i, '.scss');

                fs.readFile(scssFile, function (err, data) {

                    //Rewrote the if for reduced nesting.
                    //If error or there is no Sass, return null.
                    if (err || !data) {
                      return cb();
                    }
                    nodeSass.render({
                            data: data.toString(),
                            includePaths: [path.join('app', 'style/'), ...includePaths],
                            outputStyle: 'compressed'
                        }, function (err, compiledScss) {


                            //Rewrote the if for reduced nesting.
                            //If error or there is no Sass, return null.
                            if (err || !compiledScss)
                                return cb();
                                /**
                                 * What we are doing here is simple: 
                                 * We are re-creating the start and end placeholders
                                 * that we had and inject them back to the .html file
                                 * 
                                 * This will allow us to re-inject any changes that we 
                                 * do to our .scss or files. 
                                 * 
                                 */
                                var injectSassContent = startStyle +
                                    "<style>" +
                                    compiledScss.css.toString() +
                                    "</style>" +
                                    endStyle;

                                //This is going to replace everything that was between the <!-- Start Style --> and
                                // "<!-- End Style -->"
                                file.contents = new Buffer(contents.replace(regEx, injectSassContent), 'binary');
                                //This return is necessary, or the modified map will not be modified!
                                return cb(null,file);
                     });
                });
            }))
        .pipe(gulp.dest(basePath));
}); //Ends 

1) 设置你的元素:

假设你有一个名为“hero-tournament”的元素:

<dom-module id="hero-tournament">
  <template>
    <style>

    </style>

  </template>

  <script>
    (function() {
      'use strict';

      Polymer({
        is: 'hero-tournament',
      });
    })();
  </script>
</dom-module>

您想将您的.scss文件注入其中。

在此之外,创建两个新文件:

hero-tournament-style.html
hero-tournament-style.scss

在第一个文件 hero-tournament-style.html 中写入以下内容:
<!-- hero-tournament-style.html -->
<dom-module id="hero-tournament-style">
  <template>
  <!-- Start Style -->
 <style>
 </style>
 <!-- End Style -->
  </template>
</dom-module>

请注意:

<!-- Start Style --> <!-- End Style -->

评论?

这些是非常重要的,因为所有的CSS都将放在这些标签内。它们对大小写敏感,但不对位置敏感。一定要在您的模板标签内部样式标签外部包含它们。

然后,在您的hero-tournament-style.scss文件中,包含您的Sass CSS: (示例)

 .blank{
      display: none;
    }

2) 运行 Gulp:

gulp watchSass

Bam!你会发现你的“hero-tournament-style.scss”文件已被覆盖为CSS!!!
    <!-- -hero-tournament-style.html -->
<dom-module id="-hero-tournament-style">
  <template>
<!-- Start Style -->
<style>.blank{display:none}
</style><!-- End Style -->
  </template>
</dom-module>

现在,您可以在任何地方引用该文件!!! 还记得您的第一个元素,原始元素(“hero-tournament.html”)吗? 对它执行以下操作:
<!-- import the module  -->
<link rel="import" href="../path-to-my-element/.html">
<dom-module id="hero-tournament">
  <template>
    <!-- include the style module by name -->
<style include="hero-tournament-styles"></style>

  </template>

  <script>
    (function() {
      'use strict';

      Polymer({
        is: 'hero-tournament',
      });
    })();
  </script>
</dom-module>

一些最后的说明:

使用SASS导入 使用Sass导入很容易,只需要注意以下几点:

在gulpfile中有一个名为“includePaths”的变量。这是一个数组,nodeSass将查找所有导入文件。如果未指定导入文件的任何位置,将阻止您的文件注入和编译。默认情况下,在脚本中有一个'app/style'目录,它将查找文件。

文件夹结构 文件夹结构很重要,可以根据您的喜好进行调整。 假设您的元素位于与gulpfile相同层次的“app”文件夹内:

-gulpfile.js
/app
    /element
         /hero-tournament
             -hero-tournament.html
             -hero-tournament-styles.html
             -hero-tournament-styles.scss
    /maybe-other-folder

如果您想更改文件夹结构,请更改“basePath”变量。请务必检查前导“/”,以免破坏您的结构!
如何运行gulpfile? 很容易: 调用“watchSass”方法进行监视,或者使用“injectSass”方法使用一次。
gulp watchSass

gulp injectSass

更多信息请查看Github页面!!!

这个更新了吗?我尝试过,但没有成功。也许是我的问题。 - Andrea Santana
@AndreaSantana:你好!你当前遇到什么问题?你使用的是哪个版本的Polymer?你是否使用了检查任何类型错误的linter? - Jose A
抱歉,我太匆忙了,最终使用了其他东西。不过我记不清具体细节了。我正在使用Poymer CLI 0.18.1。 - Andrea Santana
非常感谢Jose提供的解决方案。多亏了你的代码库,我做出了一种独立的解决方案和一个webpack插件。https://github.com/drdreo/StyleModuleInjectPlugin - AHahn
@AHahn 太好了! :) 我很感激!!!我将在我的 GitHub 问题中发布这个,看看它是否有帮助。 - Jose A

0
在 Polymer 2.0 中,也可以像这样在元素模板中导入样式表:

<dom-module id="your-module-name">
    <template>
        <style><!-- you can also add additional styling in here --></style>
        <link rel="stylesheet" href="link_to_stylesheet.css">
        <!-- your template in here -->
    </template>
    <script>
    //your element class + registration here
    </script>
</dom-module>

在样式表中,您可以像在style标签中一样为内容设置样式。这些样式仅影响元素及其内容。 如果您想使用SASS、Stylus、LESS或类似的东西,您只需要在Express中使用一个中间件(如何:Stack Overflow),该中间件会在每个请求中将SASS代码呈现为CSS。我更喜欢这种解决方案而不是GULP/GRUNT任务,因为我认为它更容易,因为由于中间件的缘故,只要需要编译,它就会自动编译。

希望这能帮到您


但这需要用户使用express.js。我想,一个独立的样式模块解决方案更合适。 - AHahn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接