Node js 服务器不在 React 应用程序中显示图像

Node js server doesn't display images in React app

我在前端有一个站点,因此决定向其添加后端(以获取数据并进行一般修改)。后端给前端提供数据,但是图片有问题,不知道为什么。 data.js提供了_id,URL,title,flavor,category,region,variety,description,但是没有图片(Failed to load resource:服务器响应状态为404(Not Found))。

不幸的是,我试图搜索答案,但没有找到适合我的答案。前端也是我自己配置​​的webpack5

这里是 link 回购:link to the repo

下面我为大家介绍所有重要信息:

节点 v16.13.2

我的文件结构:

mainFolder
 - backend
    -- data.js
    -- server.js
 - frontend
    -- config
      --- webpack.common.js
      --- webpack.config.js
      --- webpack.dev.js
      --- webpack.prod.js
    -- public
      --- folder
        ---- pic.jpg
    -- src
      --- (...)
        ---- frontend.js
    -- (...)
    -- package.json (in frontend folder)
 - package.json (in root folder)

package.json(在根文件夹中):

{
  "name": ...,
  "type": "module",
  "version": ...,
  "description": ...,
  "author": ...,
  "license": ...,
  "main": ...,
  "scripts": {
    "start": "nodemon --watch backend --exec node --experimental-modules backend/server.js"
  },
  "dependencies": {
    "express": "^4.17.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

package.json(在前端文件夹中):

{
  "name": ...,
  "proxy": "http://127.0.0.1:8000",
  "version": ...,
  "description": ...,
  "author": ...,
  "license": ...,
  "main": ...,
  "scripts": {
    "start": "webpack serve --config config/webpack.config.js --env env=dev",
    "build": "webpack --config config/webpack.config.js --env env=prod"
  },
  "devDependencies": {...},
  "dependencies": {...}
}

server.js:

import express from 'express'
import data from './data.js'

const app = express()
const port = process.env.PORT || 8000

app.get('/api/products', (req, res) => {
  res.send(data.products)
})

app.get('/', (req, res) => {
  res.send('server is ready')
})

app.listen(port, () => {
  console.log(`serve at http://localhost:${port}}`)
})

data.js:

const data = {
  products: [
    {
      _id: 1,
      url: 'url',
      title: 'title',
      flavor: 'flavor',
      category: 'category',
      image: '/images/folder/pic.jpg',
      region: 'region',
      variety: 'variety',
      description:'description',
    },
    {...},
  ],
}

export default data

frontend.js:

import { Link } from 'react-router-dom'
import { useEffect, useState } from 'react'
import axios from 'axios'

const Frontend = ({ match }) => {
  const [products, setProducts] = useState([])

  useEffect(() => {
    const fetchData = async () => {
      const { data } = await axios.get('/api/products')
      setProducts(data)
    }
    fetchData()
  }, [])

  const list = products.map((product) => (
    <li key={product._id}>
      <Link to={`${match.url}/${product.url}`}>
        <img
          src={product.image}
          alt={product.url}
        />
        <div>
          <h2>{product.title}</h2>
          <p>{product.flavor}</p>
          <button>View more</button>
        </div>
      </Link>
    </li>
  ))

  return (
    <div>
      <ul>{list}</ul>
    </div>
  )
}

export default Frontend

webpack.common.js:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: path.resolve(__dirname, '..', './src/index.js'),

  output: {
    path: path.resolve(__dirname, '..', './dist'),
    filename: '[name].[contenthash:6].js',
    clean: true,
    publicPath: '/',
  },

  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ['babel-loader'],
      },
      {
        test: /\.(?:ico|gif|png|jpg|jpeg|mp4)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.(woff(2)?|eot|ttf|otf|svg|pdf)$/,
        type: 'asset/inline',
      },
    ],
  },

  resolve: {
    extensions: ['*', '.js', '.jsx'],
  },

  plugins: [
    new HtmlWebpackPlugin({
      title: '...',
      template: path.resolve(__dirname, '..', './src/template/template.html'),
      favicon: path.resolve(__dirname, '..', './public/favicon.ico'),
      filename: 'index.html',
    }),
  ],
}

webpack.config.js:

const { merge } = require('webpack-merge')

const commonConfig = require('./webpack.common.js')

module.exports = ({ env }) => {
  const envConfig = require(`./webpack.${env}.js`)

  return merge(commonConfig, envConfig)
}

webpack.dev.js:

const path = require('path')
const Dotenv = require('dotenv-webpack')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')

module.exports = {
  mode: 'development',
  devtool: 'eval-source-map',

  devServer: {
    proxy: {
      '/api': 'http://localhost:8000',
    },
    historyApiFallback: true,
    contentBase: path.resolve(__dirname, '..', './dist'),
    open: true,
    hot: true,
    compress: true,
    port: 8080,
  },

  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: require.resolve('babel-loader'),
            options: {
              plugins: [require.resolve('react-refresh/babel')],
            },
          },
        ],
      },
      {
        test: /\.(s[ac]|c)ss$/i,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
      },
    ],
  },

  plugins: [
    new Dotenv({
      path: path.resolve(__dirname, '..', './.env.development'),
    }),
    new ReactRefreshWebpackPlugin(),
  ],
}

webpack.prod.js:

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const Dotenv = require('dotenv-webpack')

module.exports = {
  mode: 'production',
  devtool: 'source-map',

  output: {
    path: path.resolve(__dirname, '..', './dist'),
    filename: 'js/[name].[contenthash:6].js',
  },

  module: {
    rules: [
      {
        test: /\.(s[ac]|c)ss$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'styles/[name].[contenthash:6].css',
      chunkFilename: '[id].css',
    }),
    new Dotenv({
      path: path.resolve(__dirname, '..', './.env.production'),
    }),
  ],
  optimization: {
    minimize: true,
    minimizer: [new CssMinimizerPlugin(), '...'],
    runtimeChunk: {
      name: 'runtime',
    },
  },
}

我看到您没有在 Express 应用中使用静态文件。您应该将静态文件添加到您的 Express 应用程序,并在您的后端服务器上提供图片。

基本上可以看看这个document.

此外,如果您喜欢视频,可以观看此 video

首先,你的后台文件应该是这样的;

backend
  -server.js
  -data.js
  -public
    --images
       ---coffee
         ----pic.jpg

这里是 server.js 文件:

import express from 'express'
import data from './data.js'
import path from 'path'
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const app = express()
const port = process.env.PORT || 8000

app.get('/api/products', (req, res) => {
  res.send(data.products)
})

app.get('/', (req, res) => {
  res.send('server is ready')
})

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

app.use(express.static(path.join(__dirname,'public')))
app.use('/static',express.static('public'))

app.listen(port, () => {
  console.log(`serve at http://localhost:${port}}`)
})

// go to 'http://localhost:8000/images/coffee/pic.jpg' it will work