首頁  >  文章  >  web前端  >  在webpack與SPA中如何管理CSS等資源

在webpack與SPA中如何管理CSS等資源

亚连
亚连原創
2018-06-20 11:37:341472瀏覽

本篇文章主要介紹了webpack與SPA實踐之管理CSS等資源的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧

上一篇介紹如何使用webpack搭建一個穩定的支援本地服務、自動刷新、模組熱替換、使用ES6編寫JavaScript的開發環境,本篇主要介紹webpack如何處理HTML應用三大元素的另一個元素– CSS及其他諸如圖片、字體檔案或資料設定檔等資源。

前言

在學習使用webpack時,我們需要明白無論它怎麼設計,它的工作原理、流程是什麼,最根本的它處理的還是HTML文件中的HTML標籤、JavaScript、CSS、圖片等資源,而且最終的處理結果依然必須是一個HTML文檔,包括DOM、JavaScript、CSS,而CSS在文檔中的存在方式,有三種:行內樣式,內聯樣式,外鏈樣式,行內樣式使用方式早已不推薦,所以webpack處理CSS方式也就兩種:

  1. 內聯樣式: 以c9ccee2e6ea535a969eb3f532ad9fe89標籤方式在HTML文檔中嵌入樣式;

  2. 外鏈樣式: 打包產生CSS文件,透過2cdf5bf648cf2f33323966d7f58a7f3f標籤引入樣式;

##webpack與CSS

我們知道,webpack本質是只能處理JavaScript的,而對於其他資源,需要使用載入器和插件將其處理成JavaScript模組,然後進行模組依賴管理。 webpack提供style-loader和css-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處理CSS的方式有兩種,第一種是以內聯方式在頁面93f0f5c25f18dab9d176bd4f6de5d30e標籤內動態插入c9ccee2e6ea535a969eb3f532ad9fe89內聯樣式,這種方式也是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有兩個常用參數:

  1. modules: {boolean}指定是否使用CSS模組(如:local和:global設定局部或全域樣式規則),預設是false,開啟設定如css-loader?modules;

  2. 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在頁面93f0f5c25f18dab9d176bd4f6de5d30e標籤內插入一個c9ccee2e6ea535a969eb3f532ad9fe89標籤,該標籤內樣式規則即此輸入內容,如下實例:

外链样式

当然,我们并不总是希望所有样式都以内联方式存在页面中,很多时候我们也希望通过外链方式使用样式表,特别是样式规则较多的时候,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相关内容,其中:

  1. output.path是webpack处理文件后输出的路径,对于CSS文件输出依然适用,即CSS文件也将输出至该目录;

  2. output.publicPath是指浏览器访问资源时的相对基础路径,规则是: output.publicPath + output.filename;

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

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)。

参数

emitFile: 是否输出文件;

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;
  }

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

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

<img src={imgSrc} />
 ...
import imgSrc from &#39;path/xxx.png&#39;;

Url Loader

你可能会发现前面并没有安装file-loader,因为有更好用的加载器url-loader,url-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: [
       // &#39;url-loader?name=[path][name].[ext]?[hash:8]&#39;
       {
         loader: &#39;url-loader&#39;,
         query: {
           limit: 6000,
           name: &#39;[path][name].[ext]?[hash:8]&#39;
         }
       }
     ]
  }, {
     test: /\.(woff|woff2|eot|ttf|otf)$/,
     loaders: [{
       loader: &#39;url-loader&#39;,
       query: {
         limit: 10000,
         name: &#39;[path][name].[ext]?[hash:8]&#39;
       }
     }]
  }

资源优化

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

安装

npm install --save-dev img-loader

配置

  {
    test: /\.(jpe?g|png|gif|svg)$/i,
    loaders: [
      &#39;url-loader?name=[path][name].[ext]?[hash:8]&#39;,
      {
        loader: &#39;img-loader&#39;,
        options: {
          // 根据环境判断是否启用资源压缩
          enabled: process.env.NODE_ENV === &#39;production&#39;, 
          gifsicle: {
            interlaced: false // 替换使用渐进式渲染
          },
          mozjpeg: {
            progressive: true, // 创建基准jpeg文件
          },
          optipng: {
            optimizationLevel: 4, // 优化级别,0-7,值越大,压缩越多
          }, 
          pngquant: {
            quality: &#39;75-90&#39;, // 压缩质量,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應用。

上面是我整理給大家的,希望今後對大家有幫助。

相關文章:

在vue中使用axios實作檔案上傳

#在js中如何實作將陣列新增到物件中

使用gulp如何建立完整的專案流程

#在jQuery如何實作動態控制頁面元素

在canvas中如何實現軌跡回放功能

以上是在webpack與SPA中如何管理CSS等資源的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn