• 【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;
        })
    }
    View Code

    引入了两个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。最后打包输出整个文件。可以说对于新手还是有点复杂,工具带来了便利性,自然也带来了学习的成本。诸多选择和诸多配置的最后,我们要找到一个适合我们自己的配置,并了解各个模块的机制才能面对不同需求的不同搭配。

     参考:

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

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

     https://survivejs.com/webpack/styling/separating-css/
     系列:
  • 相关阅读:
    Vue 创建项目
    Vue组件之间的传参
    Vue自定义组件
    Python开发之路
    爬虫
    手撸系列
    Django从入门到不会放弃
    前端
    day29 TCP的三次握手 TCP的四次挥手 基于TCP的socket
    day28 客户端服务端架构介绍
  • 原文地址:https://www.cnblogs.com/stoneniqiu/p/6534538.html
Copyright © 2020-2023  润新知