webpack 样式加载的实现原理

加载css需要用到css-loader和style-loader css-loader将@import 和 url 处理成正规的ES6 import ,如果@import指向的是一个外部资源,css-loader会跳过,而只会对内部资源做处理。css-loader处理之后,style-loader会将输出的css注入到打包文件中。css默认是inline模式,且实现了HMR接口。但inline不太适用于生产环境(全部输出在页面上)。还需要用extracttextplugin生成一个单独的css文件,但先一步一步来。

一,样式打包

1.安装css-loader,style-loader

npm install css-loader style-loader --save-dev

2.修改webpack.config.js增加一个一级子节点

module:{
  rules:[{
  test:/\.css$/,
  use: ['style-loader', 'css-loader'],
  }]
 },

test的正则会匹配.css的文件。use中的执行顺序是从右到左。loader的执行是连续的,就像管道一样,先到css-loader再到style-loader。loaders: ['style-loader', 'css-loader'] 可以理解为:styleloader(cssloader(input)) 。

3.添加样式

app/mian.css

body {
 background: cornsilk;
}

然后在index.js中引入

import './main.css';

再运行npm start,在http://localhost:8080/中打开

这时候页面出现了背景色,而且发现样式写入了header中,这个时候你改变颜色,界面也会无刷新的更新,这正是上一节HMR的效果。

样式也是通过webpackHotUpdate方法进行更新。

二、加载less

再看一下如何加载less,先安装less-loader

npm install less less-loader --save-dev

再修改配置文件:

module:{
  rules:[{
   test: /\.less$/,
      use: ['style-loader', 'css-loader', 'less-loader'],
  }]
 },

然后建立一个less文件。less.less

@base: #f938ab;

.box-shadow(@style, @c) when (iscolor(@c)) {
 -webkit-box-shadow: @style @c;
 box-shadow:   @style @c;
}
.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
 .box-shadow(@style, rgba(0, 0, 0, @alpha));
}
.box {
 color: saturate(@base, 5%);
 border-color: lighten(@base, 30%);
 div { .box-shadow(0 0 5px, 30%) }
}

body {
 background: cornsilk;
}

修改index.js

import './less.less';
 import component from './component';

var ele=document.createElement("div");
ele.innerHTML="this is an box";
ele.className="box";
document.body.appendChild(ele);

let demoComponent=component();
document.body.appendChild(demoComponent);

得到效果:

可以看见编译成功,要注意的是,再使用less的时候import只能是less文件,这个时候再import main.css会报错。这一节对less就做一个简单的演示,其他样式预处理器同理,下面的内容还是继续基于css。

三、理解css作用域和css 模块

一般来说css的作用域都是全局的,我们常在母版页里面添加了多个样式文件,后面的样式文件会覆盖前面的样式文件,常常给我们的调试带来麻烦。而CSS Modules通过import引入了本地作用域。这样能够避免命名空间冲突。webpack的css-loader是支持CSS Modules的,怎么理解呢,先看几个例子。我们先在配置中开启(先关掉HMR):

module:{
  rules:[{
  test:/\.css$/,
  use: ['style-loader', {
  loader: 'css-loader',
   options: {
   modules: true,//让css-loader支持Css Modules。
  },
  },],

然后定义一个新的样式(main.css):

body {
 background: cornsilk;
}
.redButton {
 background: red;color:yellow;
}

给component加一个样式,先引入main.css。

import styles from './main.css';
export default function () {
 var element = document.createElement('h1');
  element.className=styles.redButton;
  element.innerHTML = 'Hello webpack';
 return element;
}

这个时候我们看到界面已经变化了。

再看右边生成的样式,我们的样式名称已经发生了改变。回顾整个过程相当于main.css中的每一个类名成了一个模块,在js中可以像获取模块一样的获取。但是你可能想,为毛我不能直接给元素赋值,干嘛要import呢。这是个好问题,我们再新增一个样式

不同样式文件的同名类

other.css

.redButton {
 background:rebeccapurple;color:snow;
}

它也有一个.redbutton的类(但效果是紫色的),然后在index.js中创建一个div元素并给它添加redbutton样式。

import './main.css';
import styles from './other.css';
import component from './component';

var ele=document.createElement("div");
ele.innerHTML="this is an other button";
ele.className=styles.redButton;
document.body.appendChild(ele);

let demoComponent=component();
document.body.appendChild(demoComponent);

再看效果

上面这个图说明了两问题,一个是我们在index.js中引入了2个样式文件,在index页面就输出了两个style,这让人有点不爽,但我们后面再解决。另外一个就是虽然两个样式文件中都有redButton这个类,但是这两者还是保持独立的。这样就避免了命名空间的相互干扰。如果你这个时候直接赋值

element.className="redButton";

这样是获取不到样式的。直接对元素的样式默认是全局的。

全局样式

如果想让某个样式是全局的。可以通过:global来包住。

other.css

:global(.redButton) {
 background:rebeccapurple;color:snow;
 border: 1px solid red;
}

main.css

:global(.redButton) {
 background: red;color:yellow;
}

这个时候redbutton这两个样式就会合并。需要直接通过样式名来获取。

element.className="redButton";

组合样式

我们再修改other.css,创建一个shadowButton 样式,内部通过composes组合redbutton类。

.redButton {
 background:rebeccapurple;color:snow;
 border: 1px solid red;
}

.shadowButton{
 composes:redButton;
 box-shadow: 0 0 15px black;
}

修改index.js:

var ele=document.createElement("div");
ele.innerHTML="this is an shadowButton button";
console.log(styles);
ele.className=styles.shadowButton;
document.body.appendChild(ele);

看一下是什么效果:

日志打印出来的是styles对象,它包含了两个类名。可以看见shadowButton是由两个类名组合而成的。div的class和下面的对应。

四、输出样式文件

css嵌在页面里面不是我们想要的,我们希望能够分离,公共的部分能够分开。extracttextplugin 可以将多个css合成一个文件,但是它不支持HMR(直接注释掉hotOnly:true)。用在生产环境挺好的

npm install extract-text-webpack-plugin --save-dev

先安装extracttextplugin这个插件,然后再webpack.config.js中进行配置:

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractTxtplugin = new ExtractTextPlugin({
 filename: '[name].[contenthash:8].css',
});

const commonConfig={
 entry: {
 app: PATHS.app,
 },
 output: {
 path: PATHS.build,
 filename: '[name].js',
 },
 module:{
  rules:[{
   test:/\.css$/,
   use:extractTxtplugin.extract({
   use:'css-loader',
   fallback: 'style-loader',
   })
  }]},
 plugins: [
 new HtmlWebpackPlugin({
  title: 'Webpack demo',
 }),
 extractTxtplugin
 ],
}

一开始看到这个配置,让人有点懵。首先看fileName,表示最后输出的文件按照这个格式'[name].[contenthash:8].css',name默认是对应的文件夹名称(这里是app),contenthash会返回特定内容的hash值,而:8表示取前8位。当然你也可以按照其他的格式写,比如直接命名:

new ExtractTextPlugin('style.css')

而ExtractTextPlugin.extract本身是一个loader。fallback:'style-loader'的意思但有css没有被提取(外部的css)的时候就用style-loader来处理。注意到现在我们的index.js如下:

import './main.css';
import styles from './other.css';
import component from './component';

var ele=document.createElement("div");
ele.innerHTML="this is an box";
ele.className=styles.shadowButton;
document.body.appendChild(ele);

let demoComponent=component();
document.body.appendChild(demoComponent);

//HMR 接口
if(module.hot){
 module.hot.accept('./component',()=>{
  const nextComponent=component();
  document.body.replaceChild(nextComponent,demoComponent);
  demoComponent=nextComponent;
 })
}

引入了两个css文件。

这个时候我们执行 npm run build

再看文件夹得到一个样式文件。(如果不想看到日志可以直接npm build)

但是我们在第三部分使用了CSS Modules,发现other.css的样式没有打包进来。所以,我们的webpack.config.js还要修改:

module:{
  rules:[{
   test:/\.css$/,
   use:extractTxtplugin.extract({
   use:[ {
   loader: 'css-loader',
   options: {
   modules: true,
  },
  }],
   fallback: 'style-loader',
   })
  }]},

再次build。

发现两个样式打包成了一个文件。只要内容发生了变化,样式的名称就会变化。更多配置可以移步https://www.npmjs.com/package/extract-text-webpack-plugin

小结:这一篇讲的内容有点多了,从基本的样式打包,到less,然后认识CSS Modules。最后打包输出整个文件。可以说对于新手还是有点复杂,工具带来了便利性,自然也带来了学习的成本。诸多选择和诸多配置的最后,我们要找到一个适合我们自己的配置,并了解各个模块的机制才能面对不同需求的不同搭配。本节原码:http://xz.jb51.net:81/201806/yuanma/webpack-ch4_jb51.rar

参考:

https://www.npmjs.com/package/css-loader#local-scope

https://survivejs.com/webpack/styling/loading/

https://survivejs.com/webpack/styling/separating-css/

系列:

【webpack】-- 入门与解析

【webpack】-- 自动刷新与解析

【webpack】-- 模块热替换

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

(0)

相关推荐

  • webpack处理 css\less\sass 样式的方法

    本文介绍了webpack处理 css\less\sass 样式的方法,分享给大家,具体如下: (一)处理普通的.css 文件,需要安装 css-loader,style-loader .less 文件,需要安装 less-loader .sass 文件,需安装  less-loader npm install --save-dev css-loader npm install --save-dev css-loader npm install less-loader --save-dev (二)

  • 浅谈webpack对样式的处理

    本文介绍了webpack对样式的处理,分享给大家,具体如下: 我们可以在js中引入样式文件 require('myStyle.css') 这时我们便需要引入相应的webpack loader来帮助我们解析这段代码. css-loader搭配style-loader 首先,我们可以引入css-loader和style-loader来处理css的解析,其中,css-loader是用来解析css文件,style-loader是用来将css文件嵌入到js文件里 var path = require('p

  • 详解windows下vue-cli及webpack 构建网站(二)导入bootstrap样式

    1.先安装好vue-cli,如果还没有安装好的可以参考:<windows下vue-cli及webpack 构建网站(一)环境安装> 2.安装好之后Vue的欢迎界面,我们要做个例子导入bootstrap的样式,就先去复制一份bootstrap的导航样式过来替换掉Vue的欢迎页面吧,打开Src文件夹下面的components组件目录,然后打开 Hello.vue文件,把 <h1>{{ msg }}</h1> <h2>Essential Links</h2&

  • webpack 样式加载的实现原理

    加载css需要用到css-loader和style-loader css-loader将@import 和 url 处理成正规的ES6 import ,如果@import指向的是一个外部资源,css-loader会跳过,而只会对内部资源做处理.css-loader处理之后,style-loader会将输出的css注入到打包文件中.css默认是inline模式,且实现了HMR接口.但inline不太适用于生产环境(全部输出在页面上).还需要用extracttextplugin生成一个单独的css文

  • jQuery滚动加载图片实现原理

    本文主要通过以下几方面来说明懒加载技术的原理,个人前端小菜,有错误请多多指出 一.什么是图片滚动加载? 通俗的讲就是:当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来.这就是图片懒加载. 二.为什要使用这个技术? 比如一个页面中有很多图片,如淘宝.京东首页等等,如果一上来就发送这么多请求,页面加载就会很漫长,如果js文件都放在了文档的底部,恰巧页面的

  • 解决vue 单文件组件中样式加载问题

    在写单文件组件时,一般都是把标签.脚本.样式写到一起,这样写个人感觉有点不够简洁,所以就想着把样式分离出去. 采用import加载样式 在局部作用域(scoped)采用@import加载进来的样式文件,想法是美好的.以为这样加载进来的样式文件也只对当前组件有效:可现实是残酷的,这样加载进来的样式无法限制其作用域. <style scoped> @import "样式文件"; </style> 解决方案 采用 src属性加载样式. <style src=&q

  • Vue简易版无限加载组件实现原理与示例代码

    目录 背景 实现功能 Props 使用 组件实现 scroll 事件 $emit 发射事件和 props 回调函数的区别 总结 背景 遇到的两个问题:scroll 事件不触发.如何将 loading 状态放在无限加载组件中进行管理. 无限加载组件在展示列表页数据时比较常见.特别是在 H5 列表页中,数据比较多,需要做分页,无限加载组件就是一个非常好的选择. 当列表页数据比较多时,一次性从服务端拿到所有的数据会比较耗时,长时间不展示列表数据,比较影响用户体验.所以对于一般的长列表数据,都会做分页.

  • Laravel中Facade的加载过程与原理详解

    前言 本文主要给大家介绍了关于Laravel中Facade加载过程与原理的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 简介 Facades(读音:/fəˈsäd/ )为应用程序的 服务容器 中可用的类提供了一个「静态」接口.你不必 use 一大串的命名空间,也不用实例化对象,就能访问对象的具体方法. use Config; class Test { public function index() { return Config::get('app.name');

  • require加载器实现原理的深入理解

    前言 我们常说node并不是一门新的编程语言,他只是javascript的运行时,运行时你可以简单地理解为运行javascript的环境.在大多数情况下我们会在浏览器中去运行javascript,有了node的出现,我们可以在node中去运行javascript,这意味着哪里安装了node或者浏览器,我们就可以在哪里运行javascript. 1.node模块化的实现 node中是自带模块化机制的,每个文件就是一个单独的模块,并且它遵循的是CommonJS规范,也就是使用require的方式导入

  • webpack css加载和图片加载的方法示例

    css加载器 在webpack中,所有的资源(js文件.css文件.模板文件,图片文件等等)都被看成是一个模块,因此多有的资源都是可以被加载的. 加载这些资源我们要在module属性定义这些加载配置(指定加载器). 在module属性中,通过定义loaders定义加载器,其属性值是一个数组,每一个成员代表一个配置. 通过test定义匹配的规则(正则表达式) 通过loader定义加载器,可以通过!级联多个加载器 加载css是要两个加载器,一个是style-loader,一个是css-loader样

  • 详解webpack模块加载器兼打包工具

     什么是 webpack? webpack是近期最火的一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX).coffee.样式(含less/sass).图片等都作为模块来使用和处理. 我们可以直接使用 require(XXX) 的形式来引入各模块,即使它们可能需要经过编译(比如JSX和sass),但我们无须在上面花费太多心思,因为 webpack 有着各种健全的加载器(loader)在默默处理这些事情,这块我们后续会提到. 你可以不打算将其用在你的项目上,但没有理由不去掌握它,因为以近

  • 详解webpack异步加载业务模块

    虽然把我们用到的JS文件全部打包一个可以节省请求数,但如果打包后的JS文件过大,那么也容易出现白屏现象,许多操作失灵.而且一些区域是点到才出现,那么相关的JS其实可以剥离出这个大JS文件外.这就涉及到异步加载了.异步加载是SPA的重要构建方式之一. 我们沿着上一篇的目录,这次学习webpack的require.ensure API.此文件也叫做ensure.html,涉及到avalon, jquery,还有两个业务代码ensure.js与ensure_a.js. 先看我们的页面: <!DOCTY

  • Webpack实战加载SVG的方法

    SVG 作为矢量图的一种标准格式,已经得到了各大浏览器的支持,它也成为了 Web 中矢量图的代名词. 在网页中采用 SVG 代替位图有如下好处: SVG 相对于位图更清晰,在任意缩放的情况下后不会破坏图形的清晰度,SVG 能方便地解决高分辨率屏幕下图像显示不清楚的问题. 在图形线条比较简单的情况下,SVG 文件的大小要小于位图,在扁平化 UI 流行的今天,多数情况下 SVG 会更小. 图形相同的 SVG 比对应的高清图有更好的渲染性能. SVG 采用和 HTML 一致的 XML 语法描述,灵活性

随机推荐