webpack与SPA实践之管理CSS等资源

栏目: 编程工具 · 发布时间: 8年前

内容简介:webpack与SPA实践之管理CSS等资源

上一篇介绍了如何使用webpack搭建一个稳定的支持本地服务、自动刷新、模块热替换、使用ES6编写JavaScript的开发环境,本篇主要介绍webpack如何处理HTML应用三大元素的另一元素 – CSS及其他诸如图片、字体文件或者数据配置文件等资源。

前言

在学习使用webpack时,我们需要明白无论它怎么设计,它的工作原理、流程是什么,最根本的它处理的还是HTML文档中的HTML标签、JavaScript、CSS、图片等资源,而且最终的处理结果依然必须是一个HTML文档,包括DOM、JavaScript、CSS,而CSS在文档中的存在方式,有三种:行内样式,内联样式,外链样式,行内样式使用方式早已不推荐,所以webpack处理CSS方式也就两种:

  • 内联样式: 以 <style> 标签方式在HTML文档中嵌入样式;
  • 外链样式: 打包生成CSS文件,通过 <link> 标签引入样式;

webpack与CSS

我们知道,webpack本质是只能处理JavaScript的,而对于其他资源,需要使用加载器和插件将其处理成JavaScript模块,然后进行模块依赖管理。webpack提供 style-loadercss-loader 两个加载器支持我们模块化CSS,因此可以在其他模块内直接引入。

  • 安装
    npm install --save-dev style-loader css-loader
  • 配置

在webpack配置文件的模块加载器选项中添加如下配置:

    module: {
        loaders: [
            { test: /\.css$/, loader: "style-loader!css-loader" }
        ]
    }

当然为了方便使用引用路径,还可以配置路径片段别名:

    alias: {
        styles: path.resolve(__dirname, 'src/styles/')
    }

此时, import 'styles/index.css'; 等同于使用相对路径,如 import '../src/styles/indx.css';

  • 使用

配置好以后,假如我们在 styles 目录下创建了一个 index.css 文件,现在可以在JavaScript文件中直接引入该CSS: import 'styles/index.css';require('styles/index.css');

css内容如下:

    html, body {
        width: 100%;
        height: 100%;
    }
    .container {
        color: red;
    }

页面展示如图:

webpack与SPA实践之管理CSS等资源

内联样式

前面提到了webpack处理CSS的方式有两种,第一种是以内联方式在页面 <head> 标签内动态插入 <style> 内联样式,这种方式也是webpack的默认处理方式,只需要简单配置如下加载器:

    {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: 'style-loader!css-loader'
        // or 
        // loaders: ['style-loader', 'css-loader']
    }

webpack加载器解析顺序

如上面代码所示,无论是字符串语法 style-loader!css-loader ,亦或是数组语法 ['style-loader', 'css-loader'] ,webpack解析规则都是从右至左,依次解析并执行加载器处理文件,前一加载器处理的输出就是下一加载器处理的输入,直到最后加载器处理完成;此处即webpack先调用 css-loader 加载器处理css文件,然后将处理结果传递给 style-loader 加载器, style-loader 接受该输入继续处理。

css-loader

我们已经反复强调,webpack只能处理JavaScript,所以对于其他诸如css或图片等资源需要使用加载器将其转换输出为JavaScript模块,webpack才能继续处理。

css-loader 加载器的作用就是支持我们像使用JavaScript模块一样在JavaScript文件中引用CSS文件,如 require ('./index.css') ,所以你可以认为其作用是将CSS文件转换成JavaScript模块,于是我们可以直接通过引入JavaScript模块的方式直接引用。

参数

css-loader 有两个常用参数:

  • modules: {boolean}指定是否使用CSS模块(如:local和:global设置局部或全局样式规则),默认是false,开启设置如 css-loader?modules ;
  • importLoaders: {number}指定 css-loader 加载器之前使用的加载器数量,默认是0,设置如 css-loader?importLoaders=1

style-loader

无论webpack怎么处理CSS文件,最终都需要将其输出到页面,才能实际使用该CSS规则, style-loader 加载器就是将CSS以内联方式插入到页面文档,即:针对每一个输入(通过 require 引入,使用 css-loader 转换为JavaScript模块,传递给 style-loader 作为输入), style-loader 在页面 <head> 标签内插入一个 <style> 标签,该标签内样式规则即此输入内容,如下实例:

webpack与SPA实践之管理CSS等资源

外链样式

当然,我们并不总是希望所有样式都以内联方式存在页面中,很多时候我们也希望通过外链方式使用样式表,特别是样式规则较多的时候,webpack开发者们当然考虑了这样的需求。

webpack提供的 style-loader 加载器默认是以内联方式将样式插入文档,我们需要使用webpack extract-text-webpack-plugin 插件以实现输出单独CSS文件。

Extract Text Plugin

  • 安装

首先安装该插件:

    npm install --save-dev extract-text-webpack-plugin
  • 配置

然后添加如下配置:

    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    ...
    module: {
        loaders: [
            {
                test: /\.css$/,
                exclude: /node_modules/,
                // 老版本 loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
                loader: ExtractTextPlugin.extract({
                    fallback:'style-loader',
                    use: 'css-loader'
                })
            }
        ]
    },
    plugins: [
        // 生成独立css文件
        new ExtractTextPlugin({
            filename: 'css/[name].css'
        })
    ]

运行 webpack 命令,我们会看到在 dist/css/ 文件夹下生成相应的CSS文件。

  • 参数

    • filename {String | Function}

      Extract Text Plugin为每个入口生成一个CSS文件,所以对于多入口项目需要指定 filename 参数 [name]或[id]或[contenthash] 生成唯一识别文件名;

    • disable {Boolean}

      禁用此插件;

    • allChunks {Boolean}

      allChunks: true; 时指定从所有模块中抽取CSS输出至单独CSS文件,包括异步引入的额外模块;此插件默认是只抽取初始模块内引入的CSS;

  • extract方法

该方法可以以参数指定加载器,然后接受该加载器的输出,进行处理。需要在加载器和插件配置中同时声明相关配置,才能实现效果;在加载器配置中调用其 extract 方法传入通常以下两个参数:

1. use: 将CSS转换成模块的加载器;
2. fallback: 对于不被抽取输出至单独CSS文件的CSS模块使用的加载器,上例中`style-loader`即说明以内联方式使用,该加载器通常在`allChunks: false`时处理额外的模块;

filename与output

在上一篇介绍了输出文件配置 output 相关内容,其中:

  • output.path 是webpack处理文件后输出的路径,对于CSS文件输出依然适用,即CSS文件也将输出至该目录;
  • output.publicPath 是指浏览器访问资源时的相对基础路径,规则是: output.publicPath + output.filename ;

你可以看到在本系列文章实例中filename都添加了前缀目录,如 cssscripts ,你可能看到很多项目是不添加的,但文件入口较多时建议分类型目录输出,而且需要记得在浏览器访问资源时也需要添加该目录层级。

CSS预处理器

通常在开发较复杂的应用时,我们都会选择一种CSS的强化辅助工具,以更高效,更方便的使用CSS开发应用样式,这些拓展 工具 就是所说的CSS预处理器.

CSS预处理器(preprocessors)在CSS语法的基础上增加了变量 (variables)、嵌套 (nested rules)、混合 (mixins)、导入 (inline imports) 等高级功能,令CSS更加强大与优雅,有助于更好地组织管理样式文件,以及更高效地开发项目。

目前最常见的CSS预处理器有LESS,SASS,Stylus,个人用过的是前两种,使用SASS的还是居多。

SASS

  • 安装
    npm install --save-dev sass-loader

安装 sass-loader 以后会发现,package.json中多了一个 node-sass 依赖,这是使用SASS必须的。

  • 配置

然后添加以下配置:

    {
        test: /\.s[ac]ss$/,
        exclude: /node_modules/,
        loader: 'style-loader!css-loader!sass-loader'
    }

如上,配置中传递了三个加载器,相对于前文处理CSS文件的加载器,在最后面多了一个 sass-loader ,首先加载 sass-loader 加载器处理SASS文件成CSS代码,然后继续按照前文描述流程处理CSS文件。

Extract Text Plugin

和处理CSS文件一样,上述配置最终通过 style-loader 将转换后的CSS代码内联到页面,我们需要使用 Extract Text Plugin 生成单独CSS文件,以外链方式引用:

    {
         test: /\.s[ac]ss$/,
         exclude: /node_modules/,
         loader: ExtractTextPlugin.extract({
         fallback:'style-loader',
             use: [
                  'css-loader',
                  'sass-loader'
             ]
         })
    }

    ...

    // 生成独立css文件
        new ExtractTextPlugin({
            filename: 'css/[name].css'
        })

CSS后处理器

前面讲到CSS预处理器,如SASS,他们提供对CSS的拓展,包括语法拓展,高级特性拓展,如嵌套,变量,自动处理添加属性前缀等,使得我们可以以其定义的语法与模板方式更高效的编写CSS,然而这些预处理器都是另外对CSS进行拓展,各自定义了语法和模板,其处理流程是对代码进行解析处理,然后转换成CSS代码。

不同预处理器有各自的定义和规范,假如你需要从LESS转到SASS,源代码转换成本和学习成本颇高,而接下来要介绍的CSS后处理器并没有这个问题。

不同于预处理器预定义好一个语法和模板,然后对按照该语法和模板编写的代码进行处理转换成CSS,其输入是自定义语法文件,输出是CSS代码; 后处理器(postprocessor) 是对原生CSS代码根据配置进行处理,其输入输出依然是CSS代码。

postcss

现在最受欢迎的CSS后处理器,就是postcss:

PostCSS is a tool for transforming styles with JS plugins. These plugins can lint your CSS, support variables and mixins, transpile future CSS syntax, inline images, and more.

PostCSS是一个使用Js插件转换样式的根据,插件支持拓展CSS,如变量,混合,CSS属性语法兼容,行内图片等等功能。

特性

不同于SASS提供一个功能性拓展工具,postcss更多的是提供一个CSS高效开发工具解决方式,其本身只包含CSS解析器只能将CSS处理成一棵抽象语法树(AST),同时提供一个丰富的CSS节点树API,可以对语法树进行操作,另外它有一个高拓展性的插件系统支持我们通过引入不同插件对CSS进行处理,一个插件的输出还可以是下一个插件的输入,更值得一提的是,这些插件都是JavaScript插件,前端开发者们很容易就能根据项目需求定制自己的插件,所以可以总结几点一以下特性:

  1. postcss只处理CSS,概念简洁;
  2. 提供高拓展性的插件系统支持按需引入不同插件,实现不同处理;
  3. 使用JavaScript插件,开发者可以很方便定制项目插件;
  4. 提供CSS节点树API,可以高效的进行CSS处理;
  • 安装

在webpack中使用,需要先安装对应加载器:

    npm install --save-dev postcss-loader

插件

postcss目前有200+插件,足够满足绝大部分项目开发需求,可以查看 postcss插件 ,我们介绍几个主要使用的插件。

Autoprefixer

回顾一下在预处理器中,如果我们需要为CSS代码添加属性前缀,需要这么实现呢?对于Sass,我们通常使用mixin,即混合宏,处理CSS属性前缀,如:

    // 定义
    @mixin prefix-animation($animation-name){
        animation:$animation-name;
        -webkit-animation:$animation-name;
    }

    // 使用
    body{
        @include prefix-animation(loading .5s linear infinite);
    }

如上,我们需要按照定义的语法和模板:先定义一个mixin,然后通过 @include 方式使用,最后才能输出添加前缀的CSS代码,当代码越来越多时,我们需要定义的mixin也会越来越多,而且不同预处理器定义的语法和模板都有差异,学习成本、转换成本都很可能令人难以接受。

那么postcss插件怎么处理的呢?postcss提供了 Autoprefixer 插件处理CSS属性前缀:

Autoprefixer插件基于 Can I Use 的数据,对CSS规则进行前缀处理。

  • 安装

首先还是要安装 Autoprefixer :

    npm install --save-dev autoprefixer
  • 配置

添加如下配置:

    module: {
        loaders: [
            {
                test: /\.css$/,
                exclude: /node_modules/,
                loaders: [
                    'style-loader',
                    'css-loader',
                    { 
                        loader: 'postcss-loader', 
                        options: {
                            plugins: [
                                require('autoprefixer')({
                                    browsers: ['last 2 versions']
                                })
                            ]  
                        } 
                    }
                ]
            }
        ]
    }

如上,我们知道 postcss 是一个样式开发解决方案,其特定功能需要引入插件实现,上例中在指定 postcss-loader 加载器时为其设置了插件配置 autoprefixer ;当然webpack还支持直接设置一个 postcss 配置文件,然后在项目根目录创建 postcss.config.js 配置文件,内容格式如下:

    module.exports = {
        plugins: [
            require('autoprefixer')({
                browsers: ['last 2 versions']
            })
            // or just require('autoprefixer')
        ]
    }

使用 autoprefixer 插件时可选传入 browsers 参数,可以设置添加前缀的适配范围,详细可查阅 browsers配置说明

混合使用CSS预处理器与后处理器 – PreCSS

也许你迫不及待想在项目中引入 postcss ,又希望能继续使用CSS预处理器语法,而且需要保证以前按照某预处理器预定语法和模板(如SASS)编写的源代码继续稳定使用,不需要太多的迁移和学习成本,可以做到吗?当然可以,可以使用预处理器 PreCSS 插件包,另外我们需要安装一个 postcss 的scss解析器,因为 postcss 默认只包含一个CSS解析器, postcss 配置文件更新如下:

    module.exports = {
        parser: require('postcss-scss'),
        plugins: [
            require('autoprefixer')({
                browsers: ['last 2 versions']
            }),
            require('precss')
        ]
    }

webpack配置文件更新配置:

    modules: {
        loaders: [
            {
                test: /\.s?[ac]ss$/,
                exclude: /node_modules/,
                // or 内联方式 loader: 'style-loader!css-loader!postcss-loader'
                loader: ExtractTextPlugin.extract({
                    fallback:'style-loader',
                    use: [
                        'css-loader',
                        'postcss-loader'
                    ]
                })
             }
        ]
    }

可以看到文件匹配规则,修改为 /\.s?[ac]ss$/ ,可以匹配包括 .sass, .scss, .css 样式文件;在 css-loader 加载器之前添加了 postcss-loader 加载器(webpack加载器解析顺序为从右至左)。

当然你可以不使用 precss ,依然使用 sass-loader ,则只需要修改配置:

    loader: 'style-loader!css-loader!postcss-loader!sass-loader'

对于如下SCSS代码:

    $column: 200px;
    .menu {
        display: flex;
        width: calc(4 * $column);
    }

转换生成如下CSS代码:

    .menu {
        display: -webkit-box;
        display: -ms-flexbox;
        display: flex; 
        width: calc(4 * 200px);
    }

处理图片与字体文件

对于一个应用而言,除了需要开发HTML、CSS、JavaScript,通常还好使用到图片,字体文件,配置文件等诸多资源,那么前端工程化流程也就必然需要对这些资源进行处理与优化,最典型的说处理图片和字体文件。

在Grunt或Gulp中,我们对图片和字体文件的处理通常是将其从源目录压缩优化处理后输出至输出目录,通常是以文件目录整体进行处理,每次构建时,对所有资源,包括未使用的图片均进行处理,效率是有局限的;而webpack中一切资源文件都可以处理成模块,然后在编译时管理模块依赖,可以做到只处理存在依赖的资源(即,使用了的资源)。

图片与字体

当我们在Js模块中引入CSS文件时,其中样式规则中的背景图片,字体文件如何处理呢?webpack只能管理模块化的东西,需要将其模块化,然后使用webpack管理依赖,webpack提供了 file-loader 加载器:

File Loader

Instructs webpack to emit the required object as file and to return its public url.

通知webpack将引入的对象输出为文件并返回其公开资源路径。

  • 配置
    module: {
        loaders: [
            {
                test: /\.(png|svg|jpe?g|gif)$/,
                loader: [
                    'file-loader'
                ]
            }
        ]
    }
  • 说明

当我们在js文件中 import Image from '../images/test.png' 或在CSS文件中 url('../images/test.png') 时, file-loader 将处理该图片并在 output.path 目录下输出文件,然后将 ../images/test.png 路径替换成该输出文件路径。

注,对于html中引用的图片,需要使用[html-loader]加载器处理(http://npm.taobao.org/package/html-loader)。

  • 参数

    1. emitFile: 是否输出文件;
    2. name: 指定输出文件的文件名,有几个可用内置变量:
      1. [name]: 引用资源的名称;
      2. [path]: 引用资源的相对路径;
      3. [ext]: 资源拓展名;
      4. [hash]: 资源内容的hash值,默认使用md5算法计算得到,可以指定长度值,如 [hash:7] 表示返回hash值前7个字符;
      5. [hashType:hash:digestType:length]: 指定hash值计算算法类型和摘要类型,及摘要长度,如 sha512:hash:base64:7 表示使用sha512加密算法计算hash值并且返回7个字符的base64编码字符
  • 实例

在配置时可以指定参数: file-loader?name=[name].[ext]?[hash:8] 或者以配置对象方式:

    {
        test: /\.(png|svg|jpe?g|gif)$/,
        loaders: [
            // 'file-loader?name=[path][name].[ext]?[hash:8]'
            {
                loader: 'file-loader',
                query: {
                    name: '[path][name].[ext]?[hash:8]'
                }
            }
        ]
    }

对于CSS源代码:

    .wrapper {
        font-size: 18px;
        background: url('../images/test.png') no-repeat 0 0;
    }

输出CSS代码如下:

    .wrapper {
        font-size: 18px;
        background: url(assets/images/test.png?59427321) no-repeat 0 0;
    }

assetsoutput.publicPath 指定值, images/test.png?59427321 为配置文件中指定的 name 模板,在 output.path 目录下输出 images/test.png ,区别是,不会携带 ? 后的参数。

另外,你也可以在js模板中这样使用:

    <img src={imgSrc} />

    ...
    import imgSrc from 'path/xxx.png';

Url Loader

你可能会发现前面并没有安装 file-loader ,因为有更好用的加载器 url-loaderurl-loader 加载器是 file-loader 的升级版,他们唯一的不同之处在于:

url-loader 可以通过 limit 参数指定一个尺寸值,加载器会对小于该值的资源处理返回一个Data URL,以base64的方式嵌入HTML或CSS,如 url-loader?limit=65000 ;对于大于该尺寸的资源将使用 file-loader 处理并且传递所有参数。

  • mimetype

还可以设置mimetype对处理文件进行过滤,如 url-loader?mimetype=image/png 将只处理png类型的资源。

  • 安装
    npm install --save-dev url-loader
  • 配置

该加载器对于图片和字体文件资源都适用:

    {
         test: /\.(png|svg|jpe?g|gif)$/,
         loaders: [
             // 'url-loader?name=[path][name].[ext]?[hash:8]'
             {
                 loader: 'url-loader',
                 query: {
                     limit: 6000,
                     name: '[path][name].[ext]?[hash:8]'
                 }
             }
          ]
    }, {
         test: /\.(woff|woff2|eot|ttf|otf)$/,
         loaders: [{
             loader: 'url-loader',
             query: {
                 limit: 10000,
                 name: '[path][name].[ext]?[hash:8]'
             }
         }]
    }

资源优化

完成以上配置后,已经可以在项目中很方便的引用各自资源了,但是通常我们还需要对图片字体等文件进行压缩优化处理,如Grunt中使用的imagemin插件一样压缩资源,webpack则提供了相关加载器 img-loader

  • 安装
    npm install --save-dev img-loader
  • 配置
    {
        test: /\.(jpe?g|png|gif|svg)$/i,
        loaders: [
            'url-loader?name=[path][name].[ext]?[hash:8]',
            {
                loader: 'img-loader',
                options: {
                    // 根据环境判断是否启用资源压缩
                    enabled: process.env.NODE_ENV === 'production', 
                    gifsicle: {
                        interlaced: false // 替换使用渐进式渲染
                    },
                    mozjpeg: {
                        progressive: true, // 创建基准jpeg文件
                    },
                    optipng: {
                        optimizationLevel: 4, // 优化级别,0-7,值越大,压缩越多
                    }, 
                    pngquant: {
                        quality: '75-90', // 压缩质量,0-100,值越高,质量越高
                        speed: 3 // 执行速度,0-10,速度过高质量受损,不建议过高
                    },
                    svgo: {
                        plugins: [
                            { removeTitle: true }, // 去除标题信息
                            { convertPathData: false } // 转换路径为更短的相对或决定路径
                        ]
                    }
                }
            }
        ]
    }

以上为常见使用配置,更多详细配置信息请查看对应说明 imagemin文档 ,特别注意的是上面使用了 process.env.NODE_ENV 当前环境变量,只有在生产环境启用图片压缩,因为压缩过程比较比较耗时,可能会降低开发、调试效率。

数据资源

对于数据类型文件资源,webpack内置支持加载解析 .json 文件,而其他类型则需要安装配置相应加载器,如 .xml 文件,需要安装并配置 xml-loader

资源管理的思考

在传统或稍早一点的应用中,我们通常会将所有的图片,字体等资源放在一个基础目录下,如 assets/images ,但是对于那些在多项目间重复的插件代码或资源来说,每一次迁移,我们都得在一大堆图片,字体资源里寻找出我们需要迁移的资源,这对代码可重用和其独立性有一定限制,而且与现在提倡的组件化开发模式也不相符。

webpack对于资源的处理方式给组件化开发提供了很大便利,使得我们以组件为单位,可以在某一组件目录下存放所有相关的js,css,图片,字体等资源文件;组件的迁移公用成本很低。不过组件化开发并不是说不需要资源目录了,一些公用的资源依然放在项目的基础目录下。

说明

终于可以松口气,对于webpack管理CSS、图片、字体、数据资源的实践基本总结完成,其实感觉要介绍的还有很多,但是要尽量保证文章思路清晰,语句流畅,而且篇幅不能太长,水平有限,花费较多时间经历,希望能对读者有所帮助,后续篇章也会继续穿插介绍,力争本系列能较完整、较清晰地描述如何使用webpack开发SPA应用。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms of the Intelligent Web

Algorithms of the Intelligent Web

Haralambos Marmanis、Dmitry Babenko / Manning Publications / 2009-7-8 / GBP 28.99

Web 2.0 applications provide a rich user experience, but the parts you can't see are just as important-and impressive. They use powerful techniques to process information intelligently and offer featu......一起来看看 《Algorithms of the Intelligent Web》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具