如何使用 Typescript、React 和 Webpack 导入 SCSS 或 CSS 模块

Posted

技术标签:

【中文标题】如何使用 Typescript、React 和 Webpack 导入 SCSS 或 CSS 模块【英文标题】:How to import SCSS or CSS modules with Typescript, React and Webpack 【发布时间】:2019-10-01 15:20:03 【问题描述】:

我想在我的项目中使用 .scss(使用 react 和 typescript 构建),所以我使用 typings-for-css-modules-loadercss-loadersass-loader

当我npm run dev 时,我收到此错误:

./src/Common.scss
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
ModuleBuildError: Module build failed (from ./node_modules/sass-loader/lib/loader.js):

.common 
^
      Invalid CSS after "e": expected 1 selector or at-rule, was "exports = module.ex"

my.scss 像这样:

.common 
  height: 100px

而我的 wepack.config 是:

module.exports = 
    mode: 'development',
    entry: [
        'webpack-dev-server/client',
        path.resolve(root, 'src/index.tsx')
    ],
    devtool: 'inline-source-map',
    devServer: 
        contentBase: './dist',
        publicPath: '/',
        port: 8080,
        historyApiFallback: true
    ,
    resolve: 
        extensions: [".ts", ".tsx", ".scss", ".js", ".json", "css"]
    ,
    plugins: [
        new CleanWebpackPlugin(['dist']),
        new BundleAnalyzerPlugin(),
        new MiniCssExtractPlugin(
            filename: devMode ? '[name].css' : '[name].[hash].css',
            chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
        )
    ],
    output: 
        filename: 'bundle.js',
        path: path.resolve(root, 'dist'),
        publicPath: '/'
    ,
    module: 
        rules: [
            
                test: /\.tsx?$/,
                use: 'ts-loader'
            ,
            
                test: /\.(js|jsx)$/,
                use: 'babel-loader',
                exclude: /node_modules/
            ,
            
                test: /\.(sa|sc|c)ss$/,
                use: [
                    loader: devMode ? MiniCssExtractPlugin.loader : 'style-loader'
                , 
                    loader: 'css-loader',
                    options: 
                        sourceMap: true,
                        modules: true,
                    
                , 
                    loader: 'sass-loader', options: 
                        sourceMap: true
                    
                ]
            ,
             test: /\.(c|sc)ss$/, loader: 'typings-for-css-modules-loader?modules&sass' ,
            
                test: /\.(jpg|png|gif)$/,
                use: [
                    loader: 'file-loader',
                    options: 
                        name (file) 
                            if (devMode === 'development') 
                              return '[path][name].[ext]'
                            

                            return '[hash].[ext]'
                        
                    
                ]
            
        ]
    ,
    optimization: 
        splitChunks: 
            chunks: 'all'
        
    
;

当我将css-loadersass-loader 一起使用或单独使用typings-for-css-modules-loader 时,此错误已修复。

但是我又遇到了这样的错误:

ERROR in /Users/hly/Bpeanut.github.io/src/app/HeaderBar.tsx
./src/app/HeaderBar.tsx
[tsl] ERROR in /Users/hly/Bpeanut.github.io/src/app/HeaderBar.tsx(40,39)
      TS2339: Property 'header' does not exist on type 'typeof import("/Users/hly/Bpeanut.github.io/src/app/HeaderBar.scss")'.

HeaderBar.tsx 这样(错误就在这里):

import * as styles from './HeaderBar.scss';

class default HeaderBar extends Component 
    render() 
        return(<div className=styles.header>123</div>)
                                        ^
    

HeaderBar.scss 喜欢:

.header 
    height: 100px;

HeaderBar.scss.d.ts:

export interface IHeaderBarScss 
  'header': string;


export const locals: IHeaderBarScss;

现在我找到了另一种解决方法。

使用const styles = require('./HeaderBar.scss'); 代替import * as styles from './HeaderBar.scss';

它工作。

谢谢。

【问题讨论】:

根据文档,您必须将 css-loader 替换为 typings-for-css-modules-loader。目前您同时使用这两种方法可能会导致错误。 【参考方案1】:

我不使用 typings-for-css-modules-loader 来处理我的带有 css 和 scss 的项目,并且使用这种配置它工作得很好:


    test: /\.s?css$/,
    use: [
        
            loader: MiniCssExtractPlugin.loader,
            options: 
                sourceMap: true
            
        ,
        
        loader: 'css-loader',
            options: 
                sourceMap: true
            
        ,
        
            loader: 'sass-loader',
            options: 
                sourceMap: true
            
        
    ]
,

这能解决你的错误吗?

【讨论】:

谢谢。我过去和你用过同样的方法。它可以修复这个错误。但还有一个暴露了。 @LoveY.H 什么是新的? 我写在webpack.config下。我的错。谢谢。 @LoveY.H 好的 :) 在这种情况下,请验证我的回答【参考方案2】:

将 sass-loader 与 css-loader 和 style-loader 链接起来,立即将所有样式应用到 DOM。

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

// webpack.config.js

module.exports = 
    ...
    module: 
        rules: [
            test: /\.scss$/,
            use: [
                "style-loader", // creates style nodes from JS strings
                "css-loader", // translates CSS into CommonJS
                "sass-loader" // compiles Sass to CSS, using Node Sass by default
            ]
        ]
    
;

欲了解更多信息,请访问sass loader

【讨论】:

【参考方案3】:

在我的 create-react-app 项目中寻找使用 CSS 模块的方法时,我偶然发现了这个问题。 因此,添加create-react-app 场景的答案。

SCSS 或 CSS 模块文件名应以 module.scssmodule.css 结尾,以便能够在 create-react-app 项目中使用。

以下示例显示了一个组件 QuestionForm.tsx 和一个 SCSS 模块 QuestionForm.module.scss 一起工作。

// QuestionForm.tsx

import React,  useState  from 'react';
import style from './QuestionForm.module.scss';

export type PersonalData = 
  name: string;
;

type QuestionFormProps = 
  personalData: PersonalData;
  onChange: (data: PersonalData) => void;
;

export const QuestionForm: React.FC<QuestionFormProps> = ( personalData, onChange ) => 
  const [name, setName] = useState<string>(personalData.name);

  const handleSubmit = (event: React.FormEvent) => 
    event.preventDefault();
    onChange( name );
  ;

  const handleNameChange = (event: React.ChangeEvent<htmlInputElement>) => 
    setName(event.target.value);
  ;

  return (
    <form className=style.container onSubmit=handleSubmit>
      <div className=style.titleWrapper>
        <h2 className=style.title>Questionnaire</h2>
      </div>
      <label>
        What is your name?
        <input type="text" minLength=2 maxLength=20 value=name onChange=handleNameChange required />
      </label>
      <div className=style.cta>
        <input className="buttonPrimary" type="submit" value="Submit" />
      </div>
    </form>
  );
;
// QuestionForm.module.scss

@mixin flex($direction: row, $align: center, $justify: center) 
  align-items: $align;
  display: flex;
  flex-direction: $direction;
  justify-content: $justify;
  width: 100%;


.container 
  @include flex(column, flex-end, space-evenly);

  border: 1px solid whitesmoke;
  border-radius: 5px;
  height: 230px;
  padding: 0 10px;
  width: 400px;


.cta 
  @include flex();


.titleWrapper 
  @include flex();


.title 
  font-size: 1.5rem;
  font-weight: bold;

【讨论】:

以上是关于如何使用 Typescript、React 和 Webpack 导入 SCSS 或 CSS 模块的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 GraphQL 构建 TypeScript+React 应用

如何使用 usestate 和 setstate 而不是 this.setState 使用 react 和 typescript?

如何使用 Typescript、React 和 Webpack 导入 SCSS 或 CSS 模块

React-native:如何在 React-native 中使用(和翻译)带有 jsx 的 typescript .tsx 文件?

如何修复已使用 react 和 typescript 声明的错误标识符?

使用 graphql 将道具传递给高阶组件时出现 React-apollo Typescript 错误