[React-章节3]如何为react导入css和img?

接着上一篇 《[备忘录] 记录一下学习react中遇到的知识点》,这次我要将css和img引入到我的站点,下面看看具体怎么做吧。

CSS

我们写网站,一般会压缩成一个或者多个大css文件放在<head>里,这样就可以控制所有html标签的样式了。

而react是组件化开发模式,通过对站点拆解成多个component。每个component高内聚,包含了对应的jsx控制dom,同时也可以引入相关的css文件,这样组件可以整体保存以便在各处复用。

webpack可以实现这样的目标,它将js文件和css文件都视为模块,js文件引入css文件就和引入一个js文件一样简单。webpack会帮我们把jsx和css都转化成js操作,统一生成到bundle.js中,动态的画出dom树,并将css样式填充到head中。

下面3个链接做了一些相关的介绍,前两个比较基础,最后一个比较深入:

webpack学习笔记

加载css

css-loader分析 阮一峰作品,解释了css-loader的各种用法,增强理解

我继续在上一篇的代码基础上做试验,首先为TodoItem这个组件编写一个TodoItem.css文件:

我定义了一个class,为其设置了背景颜色,那么现在我在TodoItem.es6中引入这个css文件以便渲染:

可以看到,我为div指定了{style.TodoItemViewDiv}这个className,并且在文件的头部引入了这个css文件。现在打开浏览器,并不会看到效果,因为我们并没有告诉webpack该怎么处理这个引入的css文件。

现在安装2个插件,上面的2个博客有一些介绍,这里不赘述。

最后编辑webpack.config.js,配置如下:

添加了一个loader: “style!css?modules”项,要从右往左阅读,css?modules意思是调用css-loader插件,modules指示它应按模块重写css名称避免跨模块冲突。其产出的结果继续传递给style-loader生成style标签,最终实现css的引入。

现在访问页面,可以看到3个TodoItem都变成了灰色背景。我看了一下html发现div加上了class=”TodoItemViewDiv”,并且在head里多了一个style标签,里面写着TodoItem.css中的内容,整体效果如下:

查看网页源码,可以看到编译后的效果:

可见,通过import方式导入css,通过className指向style中的css项,webpack替我们重命名了相关的class,从而避免了跨component的冲突,简直太棒了。

提取css

在此之前,webpack将所有的css都转化成js操作编译到了bundle.js中。如果我们写了很多component,那么意味着css总体积很大,那么放到bundle.js中就不太合适了,所以很简单的想法就是把css从bundle.js中提取出来,直接当做一个css文件链入进来。

这里要用到一个新的插件,安装方法:npm install extract-text-webpack-plugin –save-dev。它的作用是将css-loader(编译单个css文件),style-loader(将css放到style标签)的最终产出物全部提取出来,放到一个独立的.css文件中存储。

为了使用它,我们只需要改一下webpack.config.js文件,引入并配置它:

这里引入了extract-text-webpack-plugin插件,然后注释掉了原先的css相关的loader,将最终产物交给了ExtractTextPlugin从而生成磁盘上的css文件。同时,还需要注册这个插件给webpack,在plugins中可以看到对应配置,意思是将所有css提取到bundle.css文件中。

搞定这些之后,我们重启服务器发现原先的css样式不见了!原来这个插件只负责提取css但并不会修改bundle.js去创建对应的<link>标签。我们可以先自己在index.html的<head>里主动引入一下,看一下bundle.css是否生效:

果不其然,样式生效了!

自动外链引入css

既然extract-text-webpack-plugin插件无法通过link自动引入,那么只能搬出救兵插件了:HtmlWebpackPlugin。

安装方法:npm install html-webpack-plugin@2 –save-dev

这个插件支持几个功能,主要是帮我们生成index.html并引入我们的之前提取的css和bundls.js,同时它也能为我们的css和js资源进行唯一命名,用于实现浏览器缓存淘汰。

基础用法参考:英文点这里,中文点这里

我引入了这个插件,通过配置令其生成一个首页new_index.html,避免覆盖我原先手写的index.html。同时我指定了css和js文件的输出名称包含了所属chunk的[name](当前我还没学到chunk是什么,目前默认一个项目就一个chunk,也就是一套bundle.css和bundle.js)和文件哈希值[hash:8],可以认为是文件哈希的前8个字符。

编辑之后的webpack.config.js是这样的:

重新运行webpack-dev-server,访问localhost:8080/new_index.html,可以正常访问。在浏览器中查看源代码,可以看到生成的new_index.html符合我们的预期,它自动引入了bundle.js和bundle.css,并且文件名都唯一编码了:

html-webpack-plugin还支持将index.html进行模板化配置,可以按自己的意愿订制首页,例如增加一些head里的meta标签,这里不深入。

引入图片

首先,webpack里一切都是模块,比如之前看到的css和js,其实img也被视为了模块。

这里我想为TodoItem这个组件加一个背景图片,所以我需要修改TodoItem.css添加一个background-img样式。

为了让webpack能够正确的识别图片并将其视为模块,需要为webpack安装2个插件:npm install file-loader url-loader –save-dev,插件的具体用途可以参考:只看url-loader部分。它主要做了2个事情,一个是将图片视为模块,另外是对图片进行可选的内联处理

然后,需要修改webpack.config.js,令插件url-loader识别并作用到图片文件,配置如下:

现在,我放置了一个a.jpg图片在项目目录下,编辑TodoItem.css添加一个backgound-image样式引用这个图片:

访问localhost:8080/a_index.html,可以看到背景图出现了。在浏览器中打开css文件,可以看到图片被内联处理了:

但是这样编写css直接引入./a.jpg,只体现了插件对图片的内联预处理,并没有体现出模块的感觉。下面,我为TodoHeader组件添加一个<img>标签,通过模块化方式引入a.jpg并赋值到<img>的src属性,最终在页面顶部可以看到一个图片。

通过Import引入图片就和引入模块一样,在webpack里一切皆模块。最终我在jsx里带入了img,重新访问页面,可以看到渲染后的结果像这样,url-loader同样作用于此种情况,完成了data编码:

学单页应用之前,先学会多页应用

基于html-webpack-plugin,我们就可以实现一个多页面的站点了,怎么做呢?

首先,我们之前的webpack.config.js只配置了一个entry入口js,叫做app.es6。它包含了整个站点的所有功能,最终会打包成bundle.js从而生成整个index页面。

在webpack的概念里,每个entry也称作一个chunk,可以理解为打包后的一个单元,之前我们都是整个站点1个单元,是这样配置的:

这样最终只会生成1个bundle.js,1个bundle.css,1个index.html。其中[name]就是chunk的名字,这里我们用数组的方式[“./app.es6”]配置的entry,所以默认名字叫做main。下面,我们改一下entry的定义,配置2个entry入口,但是它们都使用同一套代码app.es6,这次我们用字典的形式配置,从而可以指定chunk的名字叫做a和b:

现在,我们可以认为webpack中存在了2个chunk,一个叫做a,一个叫做b,也就是2个输出单元。接下来,我们需要继续使用之前的html-webpack-plugin定义输出2个html,并且2个html分别基于a和b这2个单元进行打包。相关的基础知识可以在这里补充一下:只看资源的编译和输出部分即可。

这里,我配置生成a_index.html包含了chunk a(也就是entry a),b_index.html包含了chunk_b(也就是entry b),配置如下:

重启webpack-dev-server,访问localhost:8080/a_index.html可以看到:

再访问localhost:8080/b_index.html可以看到:

可见,a_index.html引入了bundle-a-d6fa2d54.css和bundle-a-d6fa2d54.js,而b_index.html引入了bundle-b-d6fa2d54.css和bundle-b-d6fa2d54.js,一个多页面应用就这样实现了!

查看完整项目代码

点击这里,下载我的完整试验代码。由于package.json里包含了完整的依赖包列表,所以你只要执行一下npm update即可完成环境搭建,接着运行webpack-dev-server,即可访问localhost:8080/a_index.html和b_index.html了。

通过本篇博客,掌握了css提取,img引入,多页面应用的编写。并且理解了webpack的模块化含义,熟悉了几个常用的必备插件,为后续单页应用的学习铺平道路。

在下一篇中,我将深入chunk,看看如何将项目的公共模块单独提取为独立chunk(js文件),更加合理的组织项目代码。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~