25 webpack中处理样式

样式处理

Webpack中需要配置相应Loade对Css代码转换成Webpack能够识别的JavaScript代码。对Css模块转换最常用的两个Loader分别是Style-loader,Css-loader,其基础配置如下:

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

如果项目使用了Css的预编译语言,比如Less,则需额外配置转换预编译语言的Less-loader:

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

如果要在项目中使用下一代Css语法,则需配置Postcss-loader:

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

以上示例涵盖了样式处理的大部分场景,对于不同的场景需要合理搭配相关Loader的使用,才能保证Webpack正确处理样式。

Css modules

JavaScript模块化已经非常成熟了,但是Css的模块化发展相对于JavaScript发展是非常缓慢的。在使用Css的时候,我们经常遇到如下问题:

全局污染

使用各种各样的选择器来设置样式,方便我们重写样式。缺点是样式是全局生效的,在我们项目逐渐变大的时候,我们需要写的Css代码也越来越多,我们的各种class名称就会写很多,这个时候我们就有可能错误的覆盖了之前定义好的样式,导致预期效果没有出现。

命名混乱
越来越多的命名让我们无法统一一个标准命名。多人开发更是更加复杂,每个人都有自己的命名风格,短时间是很难统一的。

依赖管理不彻底

拿单页面应用来说,我们在引入一个组件的时候,只需要引入它所需要的Css样式就行了。现在的做法是除了引入组件所需的js还要引入所需的Css,目前Css相关的预编译语言(Sass,Less)很难实现编译出每个组件的单独Css文件,引入其他模块的Css,就造成了浪费,而且增加了代码量。

无法共享变量

复杂的组件要使用JavaScript和Css来共同处理样式,就会造成有些变量在js和css中代码 冗余, 预编译语言不能提供javaScript和Css共享变量的能力

代码压缩不彻底

为了性能,我们在打包的时候不仅要抽取公共模块和压缩JavaScript,甚至要压缩Html和Css,对于Css,如果非常长的类名则无法压缩。

为了性能,我们在打包的时候不仅要抽取公共模块和压缩JavaScript,甚至要压缩Html和Css,对于Css,如果非常长的类名则无法压缩。

为了解决上述的问题,也出现过很多解决方案,比如使用css-in-js,这种方式把完全摒弃了Css,全部通过JavaScript来书写样式。完全把css封装在JavaScript中,可谓是十分的过激。在处理:hover等其他伪类选择器操作的时候,将十分复杂。

Css modules就很好解决了上述的问题,Css modules像JavaScript模块那样把Css作为一个单独的模块,做到与其他模块进行隔离,实现了Css的局部作用域,让我们还是可以写Css样式,然后通过模块按需引入,在处理:hover需求上面和之前的Css是一样的,因为它们始终都是Css。

使用Css Modules

Css-loader已经集成了Css modules,我们只需简单的配置就可以启用Css modules:

module.exports = {
    
    
    module: {
    
    
        rules: [
            {
    
    
                test: /.css$/,
                use: [
                    'style-loader',
                    {
    
    
                        loader: 'css-loader',
                        options: {
    
    
                            modules: true,
                        }
                    }
                ]
            }
        ]
    }
}

示例中index.css内容如下:

.root{
    
    
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-color: black;
}

在index.js中引入该css文件,然后进行使用:

import css from './index.css';
const oDiv = document.getElementById('root');
oDiv.classList.add(css.root);

就像引入JavaScript模块一样,使用import/require语句引入相应的css模块,之后使用该Css模块的属性进行class属性赋值。

优化class名称

Css modules默认生成的class名称是杂乱无章的,例如:class=”_3X0Aom5jxsrMjeo9F0A3Gr”,这样的类名缺少语义。LocalIdentName选项可以生成Css module的标识符用于配置每一个模块中生成的class名称,用于格式化生成class名称:

module.exports = {
    
    
    module: {
    
    
        rules: [
            {
    
    
                test: /.css$/,
                use: [
                    'style-loader',
                    {
    
    
                        loader: 'css-loader',
                        options: {
    
    
                            modules: {
    
    
                                localIdentName: "[path][name]-[local]-[hash:5]"
                            },
                        }
                    }
                ]
            }
        ]
    }
}

通过配置LocalIdentName,最后得出的class名称类似:class=“src-index-root-9b3a9”,使其更加语义化。

Css modules使用技巧

  1. 不要使用选择器,只使用class名来定义样式(只会转化calss名相关的样式,比如id选择器的样式定义,会原封不动的呈现出来)
  2. 建议不使用多个class,只使用一个class把样式定义好。(虽然是建议,但是我们这种 flag ? calss1 : (class2,class3))多个class也是可以转化的。
  3. 使用composes组合所有的样式,这个有点像Css中的预编译语言,比如Less中的相关功能,但是还是有一定差别的
  4. 不嵌套,不建议在class中嵌套其他class。

与全局样式共存

前端项目有时候避免不了引入一些全局样式,因为这些样式或许是针对Html标签的,或许是针对特定浏览器的。当我们使用Css modules的时候,此时就需要全局样式和模块样式共存。

很简单,那就是在使用相关Loader解析Css文件的时候,编写两份配置规则,利用Loader中的include和exclude来判断那个文件夹的位置下需要使用Css modules,哪里的不需要。如下配置所示:

module.exports = {
    
    
    module: {
    
    
        rules: [
            {
    
    
                test: /.css$/,
                include: path.resolve(__dirname, 'src'),
                use: [
                    'style-loader',
                    {
    
    
                        loader: 'css-loader',
                        options: {
    
    
                            modules: {
    
    
                                localIdentName: "[path][name]-[local]-[hash:5]"
                            },
                        }
                    }
                ]
            },
            {
    
    
                test: /.css$/,
                include: path.resolve(__dirname, 'public/css'),
                use: [
                    'style-loader',
                    {
    
    
                        loader: 'css-loader',
                    }
                ]
            }
        ]
    }
}

覆盖局部样式

特殊场景下,我们需要覆盖掉局部样式,采用Css modules之后,最后会生成混淆过后的class名称,我们无法预测名称,所以直接使用class名称来进行覆盖是不现实的,这里最好的实践是,给元素自定义一个属性比如 data-name=‘render’,

那么我们在外部的Css中可以使用如下方式,来实现局部样式的覆盖:

[data-name='render'] {
    
    
/* override-style */
}

更多Css modules使用请前往https://www.webpackjs.com/loaders/css-loader/#modules进行查阅。

Postcss

Babel用于转换那些现在浏览器还不能完全兼容的JavaScript高级特性,Postcss就相当于是Css中的Babel,用于来处理css特性相关的问题的。同Babel一样,Postcss可以通过插件机制灵活地扩展其支持的特性,这一点Css相关的预编译语言是完全做不到的,因为它们的语法是固定的。

Css有些特性现在浏览器也是无法实现的,需要添加各种浏览器前缀实现。使用Postcss就能够自动对那些需要兼容的Css样式进行浏览器前缀的添加。

module.exports = {
    
    
    module: {
    
    
        rules: [
            {
    
    
                test: /.css$/,
                include: path.resolve(__dirname, 'src'),
                use: [
                    'style-loader',
                    {
    
    
                        loader: 'postcss-loader',
                        options: {
    
    
                            postcssOptions: {
    
    
                                plugins: [
                                    'postcss-cssnext'
                                ],
                            },
                        },
                    },
                ]
            }
        ]
    }
}

Postcss-loader用于解析Css,插件Postcss-cssnext实现了浏览器前缀的添加和下一代css语法的解析。

对Css的兼容处理跟使用Babel一样也需要指明项目的目标环境。其目标环境的配置方式也有多种。在这里推荐在package.json中配置browserlist选项或者在.browserslistrc文件中进行配置,如此一来,目标环境的配置可供多个工具进行使用:

{
    
    
    "browserslist": [
        "last 100 versions"
    ]
}

更多Postcss的使用请前往https://www.postcss.com.cn/进行查阅。

Mini-css-extract-plugin

通过Loader处理后的Css代码是依附在输出文件中的,只有加载完相关的JavaScript文件,样式才会生效。导致了请求的时候,加载的JavaScript文件比较大,页面渲染较慢。

使用Mini-css-extract-plugin插件可以使得Css从JavaScript中独立出来,然后利用浏览及的加载机制实现JavaScript和Css的并行加载:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    
    
    module: {
    
    
        rules: [
            {
    
    
                test: /\.css$/,
                use: [
                    {
    
    
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
    
    
                        loader: 'css-loader',
                        options: {
    
    
                            modules: {
    
    
                                localIdentName: "[path][name]-[local]-[hash:5]"
                            },
                        }
                    },
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
    
    
            filename: '[name].css',
            chunkFilename: '[id].css',
        }),
    ]
}

现在就把Css单独抽离成一个css文件了,不在依赖JavaScript进行加载。

更多该插件的使用请前往https://www.npmjs.com/package/mini-css-extract-plugin进行查阅。

本章节提供案例源码下载:https://gitee.com/mvc_ydb/webpack/blob/master/handleStyle.zip

猜你喜欢

转载自blog.csdn.net/sinat_41212418/article/details/121864061
25