reactjs - Typescript ve Javascript arasındaki varsayılan export farkları Webpack'te


Başka bir dosyadan bir bileşen alan bir sayfaya sahip bir React uygulamam var.

// code from IconPage.tsx

import {AccountBalanceIcon} from './icons';

<AccountBalanceIcon />
// Code from ./icons.ts
export { default as AccountBalanceIcon } from "@material-ui/icons/AccountBalance";

Bu, dışa aktarılan dosya bir TypeScript (.ts) dosyası olduğunda sorunsuz çalışıyor, ancak dosyanın adını .js olarak değiştirdiğimde oldukça farklı bir davranış alıyorum.

// Code from ./icons.js
export { default as AccountBalanceIcon } from "@material-ui/icons/AccountBalance";

Bu, webpack'in derleme adımında hatalara yol açar

Module not found: Error: Can't resolve '@material-ui/icons/AccountBalance' in '.../src/pages'
Did you mean 'AccountBalance.js'?
BREAKING CHANGE: The request '@material-ui/icons/AccountBalance' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

Evet, package.json dosyamda "type": "module" kullanıyorum, bu yüzden kodu derlenmesi için .js uzantısı ekleyerek yeniden düzenledim.

// Code from ./icons.js
export { default as AccountBalanceIcon } from "@material-ui/icons/AccountBalance.js";

Bu yapıldıktan sonra, webpack derler, ama import bildirimini yanlış yaptığınızda tarayıcı konsolunda alışık olduğum React hatasını alıyorum.

react-dom.development.js:25058 Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

Check the render method of `IconPage`.
    at createFiberFromTypeAndProps (react-dom.development.js:25058:1)
    at createFiberFromElement (react-dom.development.js:25086:1)
    at createChild (react-dom.development.js:13446:1)

Dosyanın adını tekrar .ts olarak değiştirmek (ve hatta AccountBalance.js'i yerinde bırakmak), çalışma zamanı hatasını çözer.

Analiz

Farkın nedeni yapı kodunda gösterilmiştir.

Bir JavaScript kaynak dosyası olarak, webpack'in ürettiği sonuç kod aşağıdaki gibidir.

    __webpack_require__.d(__webpack_exports__, {
        "AccountBalanceIcon": () =>
            (_material_ui_icons_AccountBalance_js__WEBPACK_IMPORTED_MODULE_0__)
    });

Ve typescript çıktısı, şöyle görünüyor.

    __webpack_require__.d(__webpack_exports__, {
        "AccountBalanceIcon": () =>
           (_material_ui_icons_AccountBalance_js__WEBPACK_IMPORTED_MODULE_0__["default"])
    });

Not: TypeScript kaynağı, ithal edilen modülün .default özelliğine atıfta bulunurken, JavaScript kaynak kodu modülün kendisine atıfta bulunur.

tsc --showConfig

{
    "compilerOptions": {
        "target": "es2017",
        "jsx": "react",
        "experimentalDecorators": true,
        "module": "esnext",
        "moduleResolution": "node",
        "allowJs": true,
        "outDir": "./dist",
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true
    },
    "include": [
        "./src/**/*"
    ],
    "exclude": [
        "./test/**/*"
    ]
}

webpack.config.js

import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";

const pathResolve = (path) => new URL(path, import.meta.url).pathname;

export default {
  mode: process.env.NODE_ENV === "production" ? "production" : "development",
  devtool: "inline-source-map",
  entry: "./src/index.tsx",
  output: {
    path: pathResolve("dist"),
    filename: "bundle.js",
    publicPath: '/',
  },
  devServer: {
    historyApiFallback: true,
    hot: true,
    port: 3000,
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"]
  },
  module: {
    rules: [
      { test: /\.tsx?$/, exclude: /node_modules/, loader: "ts-loader"},
      { test: /\.js$/, use: ["source-map-loader"], enforce: "pre" },
      { test: /\.css$/, use: ['style-loader', 'css-loader']},
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
    ],
  },
  plugins:[
    new HtmlWebpackPlugin({
      template: pathResolve("./public/index.html"),
      filename: "index.html"
    }),
    new MiniCssExtractPlugin({filename: "app.css"}),
  ],
  ignoreWarnings: [/Failed to parse source map/],
};

Sanırım sorunum aslında bu yapılandırma dosyalarından biriyle ilgili.

Soru

JavaScript dosyasının, JS ve TS projelerinin bu export'u kullanabilmesi için Material-UI bileşenini temiz bir şekilde nasıl export etmesini sağlayabilirim?

Soru soruyorum çünkü bu örnekteki icons.ts dosyası aslında problemi göstermek için bir basitleştirmedir. İkonların kodu, paketlenip dönüştürülmüş JS dosyaları olarak dağıtılan daha büyük bir bileşen kütüphanesinin parçasıdır.

Cevap #1

bunun yerine:

export { default as AccountBalanceIcon } from "@material-ui/icons/AccountBalance";

denemek :

export { AccountBalance as AccountBalanceIcon } from "@material-ui/icons";

İlgili makaleler