无法导入CSS / SCSS模块。 TypeScript说“找不到模块”

Can't import CSS/SCSS modules. TypeScript says “Cannot Find Module”

我正在尝试从CSS模块导入主题,但是TypeScript给我一个"找不到模块"错误,并且该主题未在运行时应用。 我认为我的Webpack配置有问题,但是我不确定问题出在哪里。

我正在使用以下工具:

1
2
3
4
5
6
7
8
9
10
11
"typescript":"^2.0.3"
"webpack":"2.1.0-beta.25"
"webpack-dev-server":"^2.1.0-beta.9"
"react":"^15.4.0-rc.4"
"react-toolbox":"^1.2.3"
"node-sass":"^3.10.1"
"style-loader":"^0.13.1"
"css-loader":"^0.25.0"
"sass-loader":"^4.0.2"
"sass-lint":"^1.9.1"
"sasslint-webpack-plugin":"^1.0.4"

这是我的webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
var path = require('path');
var webpack = require('webpack');
var sassLintPlugin = require('sasslint-webpack-plugin');

module.exports = {
  entry: [
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/dev-server',
    './src/index.tsx',
  ],
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'http://localhost:8080/',
    filename: 'dist/bundle.js',
  },
  devtool: 'source-map',
  resolve: {
    extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'],
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'source-map-loader',
      exclude: /node_modules/,
      enforce: 'pre',
    }, {
      test: /\.tsx?$/,
      loader: 'tslint-loader',
      exclude: /node_modules/,
      enforce: 'pre',
    }, {
      test: /\.tsx?$/,
      loaders: [
        'react-hot-loader/webpack',
        'awesome-typescript-loader',
      ],
      exclude: /node_modules/,
    }, {
      test: /\.scss$/,
      loaders: ['style', 'css', 'sass']
    }, {
      test: /\.css$/,
      loaders: ['style', 'css']
    }],
  },
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  },
  plugins: [
    new sassLintPlugin({
      glob: 'src/**/*.s?(a|c)ss',
      ignoreFiles: ['src/normalize.scss'],
      failOnWarning: false, // Do it.
    }),
    new webpack.HotModuleReplacementPlugin(),
  ],
  devServer: {
    contentBase: './'
  },
};

和我要导入的App.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import * as React from 'react';

import { AppBar } from 'react-toolbox';
import appBarTheme from 'react-toolbox/components/app_bar/theme.scss'
// local ./theme.scss stylesheets aren't found either

interface IAppStateProps {
  // No props yet
}

interface IAppDispatchProps {
  // No state yet
}

class App extends React.Component<IAppStateProps & IAppDispatchProps, any> {

  constructor(props: IAppStateProps & IAppDispatchProps) {
    super(props);
  }

  public render() {
    return (

       
          <AppBar title='My App Bar' theme={appBarTheme}>
          </AppBar>
       

    );
  }
}

export default App;

启用类型安全样式表模块导入还需要什么?


TypeScript不知道.ts.tsx以外的文件,因此如果导入文件的后缀未知,它将引发错误。

如果您有一个webpack配置允许您导入其他类型的文件,则必须告诉TypeScript编译器这些文件存在。为此,添加一个声明文件,在其中声明具有合适名称的模块。

要声明的模块的内容取决于用于文件类型的webpack加载器。在通过sass-loader css-loader style-loader通过管道传输*.scss文件的Webpack配置中,导入的模块中将没有内容,并且正确的模块声明应如下所示:

1
2
// declaration.d.ts
declare module '*.scss';

如果为css-modules配置了加载程序,则只需扩展声明,如下所示:

1
2
3
4
5
// declaration.d.ts
declare module '*.scss' {
    const content: {[className: string]: string};
    export default content;
}


这是一个对我有用的完整配置(如果有人遇到相同的问题,我花了一个小时痛苦地反复试验):

TypeScript + WebPack + Sass

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
module.exports = {
  //mode:"production",
    mode:"development", devtool:"inline-source-map",

    entry: ["./src/app.tsx"/*main*/ ],
    output: {
        filename:"./bundle.js"  // in /dist
    },
    resolve: {
        // Add `.ts` and `.tsx` as a resolvable extension.
        extensions: [".ts",".tsx",".js",".css",".scss"]
    },
    module: {
        rules: [

            { test: /\.tsx?$/, loader:"ts-loader" },

            { test: /\.scss$/, use: [
                { loader:"style-loader" },  // to inject the result into the DOM as a style block
                { loader:"css-modules-typescript-loader"},  // to generate a .d.ts module next to the .scss file (also requires a declaration.d.ts with"declare modules '*.scss';" in it to tell TypeScript that"import styles from './styles.scss';" means to load the module"./styles.scss.d.td")
                { loader:"css-loader", options: { modules: true } },  // to convert the resulting CSS to Javascript to be bundled (modules:true to rename CSS classes in output to cryptic identifiers, except if wrapped in a :global(...) pseudo class)
                { loader:"sass-loader" },  // to convert SASS to CSS
                // NOTE: The first build after adding/removing/renaming CSS classes fails, since the newly generated .d.ts typescript module is picked up only later
            ] },

        ]
    }
};

还要在项目中放入declarations.d.ts

1
2
// We need to tell TypeScript that when we write"import styles from './styles.scss' we mean to load a module (to look for a './styles.scss.d.ts').
declare module '*.scss';

package.json的dev-dependencies中,您将需要所有这些:

1
2
3
4
5
6
7
8
9
10
11
12
 "devDependencies": {
   "@types/node-sass":"^4.11.0",
   "node-sass":"^4.12.0",
   "css-loader":"^1.0.0",
   "css-modules-typescript-loader":"^2.0.1",
   "sass-loader":"^7.1.0",
   "style-loader":"^0.23.1",
   "ts-loader":"^5.3.3",
   "typescript":"^3.4.4",
   "webpack":"^4.30.0",
   "webpack-cli":"^3.3.0"
  }

然后,您应该在包含定义的CSS类的mystyle.scss旁边获得一个mystyle.d.ts,您可以将其作为Typescript模块导入并像这样使用:

1
2
3
import * as styles from './mystyles.scss';

const foo = FOO;

CSS将被自动加载(作为style元素注入到DOM中),并包含隐秘标识符而不是.scss中的CSS类,以隔离页面中的样式(除非您使用:global(.a-global-class) { ... })。

请注意,无论何时添加CSS类或删除它们或重命名它们,第一次编译都会失败,因为导入的mystyles.d.ts是旧版本,而不是编译期间刚刚生成的新版本。只是再次编译。

请享用。


如果使用tsconfig.json路径设置,请注意

请注意,如果将这些解决方案与tsconfig路径结合使用以缩短导入时间,则将需要其他配置。

如果碰巧使用如下路径tsconfig:

1
2
3
4
5
6
7
{
 "compilerOptions": {
   "paths": {
     "style/*": ["src/style/*" ],
    }
  }
}

因此,您可以执行以下操作:

1
import { header } from 'style/ui.scss';

然后,您还需要在webpack上添加一个模块来解析配置,例如:

1
2
3
4
5
6
7
8
9
module.exports = {
  ...
  resolve: {
    ...
    alias: {
      style: path.resolve(__dirname, 'src', 'style')
    }
  }
}

确保根据您的设置设置了路径。

由于它认为新的导入路径实际上是一个模块,因此webpack知道了要查找的位置,因此它默认为node_modules目录。使用此配置,它知道在哪里寻找并找到它,并且构建成功。