Webpack dev服务器抛出错误 - 拒绝执行脚本,因为它的MIME类型(' text / html')不可执行

时间:2017-12-27 17:15:42

标签: javascript node.js webpack webpack-dev-server facebook-authentication

我使用Webpack bundler和Webpack dev服务器进行本地开发。前端位于React.js + Redux中,后端位于Node.js和koajs中。

在后端,我使用passportjs库进行用户身份验证,使用其他库koa-passportpassport-facebookpassport-google-auth通过Facebook或Google进行身份验证。基本上,我实施了koa-passport-example

如果我的应用程序想要将用户重定向到Facebook或Google登录页面,Webpack dev服务器会抛出错误:

GET http://localhost:8090/auth/bundle.js net::ERR_ABORTED

Refused to execute script from 'http://localhost:8090/auth/bundle.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.

如果我通过Webpack生成bundle并将其托管在Node.js服务器上,我就不会收到此错误。我需要找出如何设置Webpack开发服务器来摆脱这个错误信息。

的package.json

    "scripts": {
    "debug": "./node_modules/nodemon/bin/nodemon.js --inspect ./script/server.js",
    "webpack": "npm run serve | npm run dev",
    "start": "node ./script/server.js",
    "serve": "./node_modules/.bin/http-server -p 8080",
    "dev": "webpack-dev-server -d --progress --colors --port 8090 --hot --inline",
  },
        "dependencies": {
        "@koa/cors": "^2.2.1",
        "actions": "^1.3.0",
        "aws-s3-form": "^0.3.5",
        "aws-sdk": "^2.165.0",
        "axios": "^0.16.2",
        "bootstrap": "^3.3.7",
        "bootstrap-timepicker": "github:janzenz/bootstrap-timepicker#feature/compatibility-es6",
        "d3-ease": "^1.0.3",
        "d3-selection": "^1.1.0",
        "d3-shape": "^1.2.0",
        "d3-transition": "^1.1.0",
        "font-awesome": "^4.7.0",
        "http-server": "^0.10.0",
        "immutable": "^3.8.2",
        "jquery": "^3.2.1",
        "jquery-ui": "^1.12.1",
        "jquery.panzoom": "^3.2.2",
        "jsonwebtoken": "^8.1.0",
        "juration": "^0.1.0",
        "knex": "^0.14.2",
        "koa": "^2.3.0",
        "koa-body": "^2.5.0",
        "koa-bodyparser": "^4.2.0",
        "koa-logger": "^3.1.0",
        "koa-passport": "^4.0.1",
        "koa-ratelimit": "^4.0.0",
        "koa-router": "^7.2.1",
        "koa-send": "^4.1.1",
        "koa-session": "^5.5.1",
        "koa-static": "^4.0.2",
        "moment": "^2.18.1",
        "objection": "^0.9.2",
        "oembed-auto": "0.0.3",
        "passport": "^0.4.0",
        "passport-facebook": "^2.1.1",
        "passport-google-oauth": "^1.0.0",
        "passport-jwt": "^3.0.1",
        "pg": "^7.4.0",
        "probe-image-size": "^3.1.0",
        "puppeteer": "^0.12.0",
        "react": "^15.6.1",
        "react-dom": "^15.6.1",
        "react-dropzone": "^4.2.1",
        "react-facebook-login": "^3.6.2",
        "react-google-login": "^3.0.2",
        "react-modal": "^3.1.2",
        "react-redux": "^5.0.6",
        "react-router": "^4.2.0",
        "react-router-dom": "^4.2.2",
        "react-router-redux": "^4.0.8",
        "react-share": "^1.17.0",
        "react-transition-group": "^1.2.1",
        "react-twitter-widgets": "^1.7.1",
        "redux": "^3.7.2",
        "redux-thunk": "^2.2.0",
        "request": "^2.83.0",
        "request-promise-native": "^1.0.5",
        "select2": "^4.0.4",
        "select2-bootstrap-theme": "0.1.0-beta.10",
        "shave": "^2.1.3",
        "sqlite3": "^3.1.13",
        "sugar-date": "^2.0.4",
        "svg-url-loader": "^2.3.0",
        "twitter": "^1.7.1",
        "twitter-widgets": "^1.0.0",
        "unfluff": "^1.1.0"
      },
      "devDependencies": {
        "autoprefixer": "^7.1.4",
        "babel": "^6.23.0",
        "babel-core": "^6.26.0",
        "babel-loader": "^7.1.2",
        "babel-preset-es2015": "^6.24.1",
        "babel-preset-react": "^6.24.1",
        "css-loader": "^0.28.7",
        "duplicate-package-checker-webpack-plugin": "^2.0.2",
        "eslint": "^4.7.2",
        "eslint-config-airbnb": "^15.1.0",
        "eslint-plugin-import": "^2.7.0",
        "eslint-plugin-jsx-a11y": "^5.1.1",
        "eslint-plugin-react": "^7.4.0",
        "favicons-webpack-plugin": "0.0.7",
        "file-loader": "^0.11.2",
        "friendly-errors-webpack-plugin": "^1.6.1",
        "html-webpack-plugin": "^2.30.1",
        "less": "^2.7.2",
        "less-loader": "^4.0.5",
        "node-sass": "^4.5.3",
        "nodemon": "^1.12.1",
        "npm-install-webpack-plugin": "^4.0.5",
        "postcss": "^6.0.11",
        "postcss-loader": "^2.0.6",
        "sass-loader": "^6.0.6",
        "style-loader": "^0.18.2",
        "url-loader": "^0.5.9",
        "webpack": "^3.6.0",
        "webpack-dev-server": "^2.9.1",
        "webpack-merge": "^4.1.0",
        "webpack-notifier": "^1.5.0"
      }

webpack.config.js

const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const path = require('path');
const WebpackNotifierPlugin = require('webpack-notifier');
const autoprefixer = require('autoprefixer');

const TARGET = process.env.npm_lifecycle_event;
console.log(`target event is ${TARGET}`);

let outputFileName = 'app';
outputFileName += TARGET === 'prod' ? '.min.js' : '.js';

const common = {
  entry: {
    app: './index.jsx',
  },
  module: {
    rules: [
      {
        test: /\.js[x]?$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader?presets[]=es2015&presets[]=react',
        },
      },
      {
        test: /\.scss$/,
        loaders: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.less$/,
        loaders: ['style-loader', 'css-loader', 'less-loader'],
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(eot|ttf|svg|gif|png|jpg|otf|woff|woff2)$/,
        loader: 'url-loader',
      },
    ],
  },
  plugins: [
    new webpack.ProvidePlugin({
      jQuery: 'jquery',
      $: 'jquery',
      jquery: 'jquery',
      'window.jQuery': 'jquery',
    }),
    new webpack.LoaderOptionsPlugin({
      options: {
        postcss: [
          autoprefixer({
            browsers: ['last 3 versions'],
          }),
        ],
      },
    }),
    new WebpackNotifierPlugin(),
  ],
};

if (TARGET === 'dev' || !TARGET) {
  module.exports = webpackMerge(common, {
    devtool: 'eval-source-map',
    output: {
      filename: 'bundle.js',
      sourceMapFilename: '[file].map',
    },
    devServer: {
      contentBase: path.resolve(__dirname), // New
      historyApiFallback: true,
    },
  });
}

login.jsx

...
    <a href="/auth/facebook" className="btn btn--secondary ut-font-decima">Login</a>
...

server.js

const Koa = require('koa');
const Router = require('koa-router');
const logger = require('koa-logger');
const cors = require('@koa/cors');
const bodyParser = require('koa-bodyparser');
const serve = require('koa-static');
const path = require('path');
const session = require('koa-session');

const app = new Koa();
// trust proxy
app.proxy = true;

const router = new Router();

// sessions
app.keys = ['your-session-secret'];
app.use(session({}, app));

app.use(logger());
app.use(cors());
app.use(bodyParser());

require('./controllers/auth');
const passport = require('koa-passport');

app.use(passport.initialize());
app.use(passport.session());

app.use(serve(path.join(process.env.PWD, '/dist')));

router
  .get('/auth/facebook', passport.authenticate('facebook'))
  .get(
    '/auth/facebook/callback',
    passport.authenticate('facebook', {
      successRedirect: '/podcast',
      failureRedirect: '/',
    }),
  );

app.use(router.routes()).use(router.allowedMethods());

// don't listen to this port if the app is required from a test script
if (!module.parent) {
  app.listen(process.env.PORT || 1337);
  console.log('app listen on port: 1337');
}

2 个答案:

答案 0 :(得分:10)

进一步研究Webpack,我们应该清楚Webpack是什么以及它用于什么。 Webpack是前端工具,它将构建前端项目,并具有管理类似于gulp / grunt的任务的能力。它可以是服务静态内容的服务器。但它不是一个完整的后端服务器。您无法轻松构建后端API并管理复杂的路由。这包括登录功能等。而不是重新发明轮子,使用Webpack作为开发工具,轻松修改和查看网页设计的更新结果。如果您需要更多功能,请通过在监视模式下运行Webpack来集成Webpack,同时运行后端服务器并设置代理,以便Webpack将遵循后端服务器进行复杂路由。你可以使用任何后端技术,虽然Webpack是基于Common.js库构建的,因此将它集成到node.js和express中似乎是最简单的,因为它们是javascript生态系统的一部分。

如果我可以评论,无论如何,我正在阅读DevServer的webpack文档,我认为服务器正在使用不正确的MIME类型进行响应可能是因为它没有找到bundle.js脚本所在的位置期待它。我注意到控制台输出为“http://localhost:8090/auth/bundle.js”,并且在文档中dev服务器期望它在根目录中。我认为如果bundle.js真的在auth目录中,你可能需要通过publicPath选项告诉服务器它在哪里。

output: {
  filename: 'bundle.js',
  sourceMapFilename: '[file].map',
  path: path.resolve('build/js/),// moves the bundle.js out of the root
  publicPath: '/auth/' // it is recommended that the publicPath is declared in both output and devServer
  // publicPath links the path of bundle.js to this path in the html.
},
devServer: {
  contentBase: path.resolve(__dirname), // New
  historyApiFallback: true,
  publicPath: "/auth/" // Both publicPath options should be the same as what is in your html loading the scripts
},

据我了解webpack dev服务器,bundle.js不会写入光盘。它实际上是服务。

现在所有这一切都需要代理已经构建的node.js服务器或构建一个来处理你需要使用的api。 Webpack提供了一个开发中间件模块,用作基本node.js express服务器中的中间件。您可以看到中间件here的基础知识。您真正需要从文档开始的是通过npm webpack-dev-middleware和express

进行安装

npm install --save-dev webpack-dev-middleware express

然后在项目的根目录中创建一个新的服务器文件,如index.js,因为您已经有了server.js。现在创建您需要的基本服务器,只需要处理api调用所需的路由和包。

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');

const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);

// Tell express to use the webpack-dev-middleware and use the webpack.config.js
// configuration file as a base.
app.use(webpackDevMiddleware(compiler, {
  publicPath: config.output.publicPath
}));

// Serve the files on port 3000.
app.listen(3000, function () {
  console.log('Example app listening on port 3000!\n');
});

这是来自webpack网站,您需要自己进行api路由。您将像普通节点项目一样运行项目,它应该处理bundle.js请求。

让我们不要忘记koa koa-webpack-dev有一个插件。我没有亲自使用过koa,但如果你需要它,你可以看到如何使用它here

答案 1 :(得分:2)

我遇到了类似的问题,并认为如果有人遇到类似的问题,我会发布解决方案。基本上,我试图在动态子路由localhost:3000/route/dynamicRoute上刷新我的应用,但它抛出了类似的错误。我通过在Webpack配置的publicPath: '/'设置中添加output解决了我的问题。以下是我的webpack.config.js供参考。

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

const outputDirectory = 'dist';

module.exports = {
 entry: ['babel-polyfill', './src/client/index.js'],
 output: {
  path: path.join(__dirname, outputDirectory),
  filename: 'bundle.js',
  publicPath: '/'
 },
 module: {
  rules: [
   {
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
     loader: 'babel-loader'
    }
  },
  {
   test: /\.css$/,
   use: ['style-loader', 'css-loader']
  },
  {
   test: /\.(pdf|jpg|png|gif|svg|ico)$/,
    use: [
     {
      loader: 'url-loader'
     },
    ]
   }
 ]
},
devServer: {
  port: 3000,
  open: true,
  proxy: {
   '/api': 'http://localhost:8080'
  },
  historyApiFallback: true,
  contentBase: './public/index.html',
  hot: true
 },
 plugins: [
  new CleanWebpackPlugin([outputDirectory]),
  new HtmlWebpackPlugin({
   template: './public/index.html',
   favicon: './public/favicon.ico'
  })
 ]
};