【问题标题】:'window is not defined' error when using style-loader with webpack将样式加载器与 webpack 一起使用时出现“未定义窗口”错误
【发布时间】:2019-05-25 21:14:59
【问题描述】:

构建服务器端 React 应用程序并在使用 Webpack 时遇到 Style-Loader 问题。

我使用的是“^0.23.1”版本,在运行脚本以捆绑和构建时,Style-Loader 出现问题。

问题是window is not defined

webpack:///./node_modules/style-loader/lib/addStyles.js?:23
    return window && document && document.all && !window.atob;

有人遇到过这个问题吗?在查看了 Stack 和 Github 的 style-loader 问题后,我没有找到任何解决方案。

这是我的 webpack 文件:

const path = require('path');
const webpack = require('webpack');

module.exports = {
  // webpack to use node
  target: 'node',
  entry: './src/index.js',
  output: {
    filename: 'client-build.js',
    path: path.resolve(__dirname, 'build/public'),
    publicPath: '/build/public'
  },
  module: {
    rules: [
      {
        test: /\.js$|\.jsx$/,
        loader: 'babel-loader',
        exclude: '/node_modules/',
        options: {
          presets: [
            '@babel/preset-react'
          ]
        }
      },
      {
        test: /\.(s*)css$/,
        loader: ['style-loader', 'css-loader', 'sass-loader']
      },
      {
        test: /\.jpeg$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$|\.wav$|\.mp3$|\.jpg$|\.pdf$/,
        loader: 'file-loader',
        query: {
          name: 'assets/img/[name].[ext]'
        },
      },
    ]
  },
  plugins: [
    new webpack.ProvidePlugin({
      "React": "react",
    }),
  ],
}

如果你还有什么需要看的,我可以发帖。

【问题讨论】:

  • 您需要使用专为 SSR 设计的加载程序。 github.com/kriasoft/isomorphic-style-loader 是一个选项,但距离上次提交已经有一段时间了。还有github.com/noderaider/universal-style-loadergithub.com/creeperyang/iso-morphic-style-loader。祝你好运!
  • 我也遇到了这个问题,并且正在遵循本指南,对于帖子的作者来说它似乎工作正常:medium.com/@binyamin/…
  • 好的,我想通了。正如@ChristianScott 所说,style-loader 不适用于服务器端渲染。 @T。 Evans,如果 SSR 不是您的意图,请注意配置文件中的 target: node 设置。将此更改为 target: web 以针对客户端捆绑,而不是服务器端。这将阻止 style-loader 将其代码放入您使用 Node.js 运行的服务器端文件中。另外,在我的例子中,即使我有web 作为我的目标,我也有entry: { server: './src/index.js' },其中server 属性应该是main。当我改变它时,一切正常。
  • 感谢跟进@MegaMatt!
  • 最后一点。在发布我之前的评论后,我了解到entry 对象内的属性名称不必是特定值。 servermain 是用于我正在关注的演练的字符串,但它可以是您想要的任何东西。请注意,您选择的值可以使用特殊的[name] 语法在配置文件的其他地方引用。同样,在我的例子中,我有另一个 webpack 配置文件已经使用了一个名为 server 的条目,所以有第二个会导致某种冲突。因此,将其更改为 main(或 server 以外的任何内容)修复了我。

标签: reactjs webpack webpack-style-loader


【解决方案1】:

style-loader 尝试将样式注入网站的head (window / document),这在您的服务器上渲染/执行时将不存在。

你需要从你的服务器配置中移除这个加载器并用其他东西替换它(例如ExtractTextPluginMiniCSSExtractplugin,取决于你的 webpack 版本)

【讨论】:

  • 谢谢,你节省了我的时间
【解决方案2】:

我认为您的问题是,在节点服务器上运行 js 代码时没有 window 对象。这也是有道理的,因为您的服务器没有呈现代码的窗口。您可以使用 global 对象作为全局引用,请在此处查看相关帖子:Does node.js have equivalent to window object in browser

【讨论】:

  • 是的,你是对的。问题是 OP 的配置通过为 target 属性提供的 node 值来定位服务器端捆绑。 style-loader 加载器不是为服务器端捆绑而设计的,如果它被包含在这里,由于style-loader 引用了window。正如您所说,window 不是服务器上的东西,仅在客户端代码中。所以有2个选择:(1)将target更改为web(而不是node),或者(2)删除样式加载器并使用为服务器端渲染设计的加载器。
【解决方案3】:

如果我理解正确,我认为您正在尝试使用样式加载器来捆绑服务器端代码。如果是这种情况,请尝试这样做而不是这样做:

    loader: ['style-loader', 'css-loader', 'sass-loader']

试试这个:

    loader: ['css-loader/locals', 'sass-loader']

样式加载器不应该用于服务器端代码。所以我们提供一种空加载器而不是 css-loader 并删除样式加载器。我猜这应该可以解决问题。

【讨论】:

  • 这会引发错误Module not found: Can't resolve 'css-loader/locals'
  • 你应该只在服务器配置中使用它,将客户端作为加载器:['style-loader', 'css-loader', 'sass-loader']
  • 这在最近的版本中变成了{ loader: 'css-loader', options: { onlyLocals: true} }exportOnlyLocals(参见webpack.js.org/loaders/css-loader/#exportonlylocals)。但是,在我的情况下,导出的 js 文件链接源中的 scss 样式表,这对我来说似乎很奇怪,但可能是预期的输出?
  • @AmbroiseRabier OMG,这是 5 小时的研究,谢谢!!!
  • 这也是我的问题。在我的情况下,我将它切换到同构样式加载器,但后来意识到我根本不需要加载器,因此能够完全移除它并继续前进。
【解决方案4】:

我遇到了一个问题,我需要来自组件库的一些主题和样式,而组件库又使用了 webpack 和 style-loader。
我的项目是纯脚本,应该生成一些文件,因此没有浏览器。它根本无法编译,因为 style-loader(和其他一些库)试图在标签中注入样式。
我最终模拟了窗口和文档,以便可以编译导入的项目。

注意这适用于我只需要组件库的一小部分的情况,如果您在更复杂的项目中使用它,可能会出现一些奇怪的错误。但它可能会帮助某人找出类似的问题

在实际导入之前运行它
因为它是导致问题的实际导入,所以您需要在导入之前进行破解。

import * as Hack from './hack/StyleLoaderHack';
Hack.runHack();
...
import {X} from 'your library'

StyleLoaderHack.js

class HackStyle {
    position;

    constructor() {
        this.position = [];
    }
}

class HackElement {
    className;
    childNodes;
    style;

    constructor(tag) {
        this.className = tag;
        this.attributes = [];
        this.childNodes = [];
        this.style = new HackStyle();
    }

    appendChild = (child) => {
        let append;
        if (!(child instanceof HackElement)) {
            append = new HackElement(child);
        } else {
            append = child;
        }
        this.childNodes.push(append);
        return append;
    };

    insertBefore = (newChild, refChild) => {
        let insert;
        if (!(newChild instanceof HackElement)) {
            insert = new HackElement(newChild);
        } else {
            insert = child;
        }
        this.childNodes.push(insert);
    };

    setAttribute = (qualifiedName, value) => {
        // sketchy but works
        this.attributes.push(qualifiedName);
        this.attributes.push(value);
    };
}

class HackDocument {
    head;

    constructor() {
        this.head = new HackElement("head");
    }

    createElement = (tagName) => {
        const element = new HackElement(tagName);
        return element;
    };

    querySelector = (target) => {
        const node = new HackElement(target);
        return node;
    };

    querySelectorAll = (target) => {
        if (target === "[data-emotion-css]") {
            return [];
        }
        const node = new HackElement(target);
        return [node];
    };

    createTextNode = (data) => {
        return new HackElement(data);
    };
}


/**
 * Adds some function to global which is needed to load style-loader, emotion, create-emotion and react-table-hoc-fixed-columns.
 */
export const runHack = () => {
    global.window = {};

    const hackDocument = new HackDocument();
    global.document = hackDocument;
};

【讨论】:

    猜你喜欢
    • 2019-01-19
    • 2016-12-06
    • 2019-12-02
    • 2020-02-06
    • 2018-10-02
    • 1970-01-01
    • 2018-10-03
    • 2017-01-23
    • 1970-01-01
    相关资源
    最近更新 更多