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

前言

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

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

webpack与CSS

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

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

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

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

固然为了方便使用引用路径,还能够配置路径片断别名:node

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

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

  • 使用

配置好之后,假如咱们在styles目录下建立了一个index.css文件,如今能够在JavaScript文件中直接引入该CSS: import 'styles/index.css';require('styles/index.css');git

css内容以下:github

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

页面展现如图:web

style

内联样式

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

{
        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接受该输入继续处理。json

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开发者们固然考虑了这样的需求。

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: 指定输出文件的文件名,有几个可用内置变量:
  • 实例

在配置时能够指定参数: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应用。

原创文章,转载请注明: 转载自 熊建刚的博客

本文连接地址: webpack与SPA实践之管理CSS等资源

相关文章
相关标签/搜索