繁体   English   中英

同构样式加载器为什么引发TypeError:与CSS-Modules一起使用时,无法读取未定义的属性“ apply”

[英]Why does isomorphic-style-loader throw a TypeError: Cannot read property 'apply' of undefined when being used in unison with CSS-Modules

我目前正在尝试在服务器上呈现应用程序,该服务器适用于HTML和JS,但发现无法加载我的样式(.less | .scss)。 我进行了一些研究,并不确定(但不确定)基于其他遇到相同问题的Webpack配置中缺少isomorphic-style-loader 我对其进行了设置,至少是按照我的理解,但是现在发现在运行该应用程序时出现以下错误:

TypeError: Cannot read property 'apply' of undefined at WithStyles.componentWillMount

我对整个React / Express有点陌生,但是一直在尝试跟随教程和学习,如果有什么地方不合适,请原谅。 我希望看看是否有人可以解释导致此错误的确切原因,并为我提供一些解决该错误的方法的思路。 下面是一些示例代码,如果有任何帮助,它们类似于我遇到的代码。

(作为参考,我正在关注Tyler McGinnis React Router Server Rendering教程,并尝试对其进行扩展以添加样式-Link Here

预先感谢您提供任何可能导致此错误的解释。


webpack.config.babel.js

import path from 'path'
import webpack from 'webpack'
import nodeExternals from 'webpack-node-externals'

const paths = {
  browser: path.join(__dirname, './src/browser'),
  server: path.join(__dirname, './src/server'),
  build: path.resolve(__dirname, 'public')
}

let browserConfig = {
  entry: `${paths.browser}/index.js`,
  output: {
    path: paths.build,
    filename: 'bundle.js',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.s?(a|c)ss$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]___[hash:base64:5]',
              sourceMap: true
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      }, {
        test: /\.less$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]___[hash:base64:5]',
              sourceMap: true
            }
          },
          {
            loader: 'less-loader',
            options: {
              javascriptEnabled: true
            }
          },
          'postcss-loader'
        ]
      }, {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      __isBrowser__: true
    })
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.json', '.css', '.scss', '.sass', '.less']
  }
}

let serverConfig = {
  entry: `${paths.server}/index.js`,
  target: 'node',
  externals: [nodeExternals()],
  output: {
    path: __dirname,
    filename: 'server.js',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.s?(a|c)ss$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]___[hash:base64:5]',
              sourceMap: true
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      }, {
        test: /\.less$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]___[hash:base64:5]',
              sourceMap: true
            }
          },
          {
            loader: 'less-loader',
            options: {
              javascriptEnabled: true
            }
          },
          'postcss-loader'
        ]
      }, {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      __isBrowser__: false
    })
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.json', '.css', '.scss', '.sass', '.less']
  }
}

module.exports = [browserConfig, serverConfig]

server.js

import express from "express"
import cors from "cors"
import React from "react"
import bodyParser from 'body-parser'
import serialize from "serialize-javascript"
import { renderToString } from "react-dom/server"
import { StaticRouter, matchPath } from "react-router-dom"
import App from '../shared/App'
import routes from '../shared/routes'

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

app.use(cors())
app.use(bodyParser.json()) // support json encoded bodies
app.use(bodyParser.urlencoded({extended: true})) // support encoded bodies
app.use(express.static("public"))

app.get("*", (req, res, next) => {
  const activeRoute = routes.find((route) => matchPath(req.url, route)) || {}

  const promise = activeRoute.fetchInitialData
    ? activeRoute.fetchInitialData(req.path)
    : Promise.resolve()

  promise.then((data) => {
    const css = new Set()
    const context = { data, insertCss: (...styles) => styles.forEach(style => css.add(style._getCss())) }
    const markup = renderToString(
      <StaticRouter location={req.url} context={context}>
          <App />
      </StaticRouter>
    )

    res.send(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>React on the Server!</title>
          <script src="/bundle.js" defer></script>
          <script>window.__INITIAL_DATA__ = ${serialize(data)}</script>
        </head>
        <body>
          <div id="app">${markup}</div>
        </body>
      </html>
    `)
  }).catch(next)
})

app.listen(port, () => console.log(`Server is listening on port: ${port}`))

routes.js

import AboutMain from './components/About/AboutMain'

const routes =  [
  {
    path: '/about',
    component: AboutMain
  }
]

export default routes

browser.js

// Import the neccessary modules for use in file
import React from 'react' // Main React module
import { hydrate } from 'react-dom' // render alternative for server rendering
import App from '../shared/App'
import { BrowserRouter } from 'react-router-dom' // React Router component for client side routing
import '../shared/components/global.scss' // Only has general rules, which do get applied

hydrate(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('app')
)

App.js

import React, { Component } from 'react'
import routes from './routes'
import { Route, Link, Redirect, Switch } from 'react-router-dom'

class App extends Component {
  render() {
    return (
      <div>    
        <Switch>
          {routes.map(({ path, exact, component: Component, ...rest }) => (
            <Route key={path} path={path} exact={exact} render={(props) => (
              <Component {...props} {...rest} />
            )} />
          ))}
          <Route render={(props) => <NoMatch {...props} /> } />
        </Switch>
      </div>
    )
  }
}

export default App

AboutMain.js

// Importing Required Modules
import React, {Component, Fragment} from 'react' // Importing React, Component, Fragment from "react"
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './about.scss'

class AboutMain extends Component {
  state = {
    phrase: 'We Made It!'
  }

  render() {
    return (
      <Fragment>
        <header className={s.banner}>
          <h1 className={s.heading}>{this.state.phrase}</h1>
        </header>
      </Fragment>
    )
  }
}

export default withStyles(s)(AboutMain) <-- Error Seems to occur here, at least I think.

about.scss

.banner {
  margin: 0 auto;
  padding: 15px;
  border: 2px solid #000;
}

.heading {
  text-transform: uppercase;
  text-decoration: underline;
}

问题消失了,仅仅是因为您删除了isomorphic-style-loader 请不要接受你自己的答案这样。 这里的问题是您没有提供context所以insertCss.apply(_context, styles)将抱怨,因为_contextundefined 要解决此问题,请按照下列步骤操作:

  1. App的同一目录中创建ContextProvider组件

ContextProvider.js

import React from 'react';
import PropTypes from 'prop-types'
import App from './App'

class ContextProvider extends React.Component {
    static childContextTypes = {
      insertCss: PropTypes.func,
    }

    getChildContext() {
      return { ...this.props.context }
    }

    render () {
      return <App { ...this.props } />
    }
  }

  export default ContextProvider
  1. 包裹ContextProvider 两个browser.jsserver.js 记住声明context的两个文件。

browser.js(在其他应用中,这将是根的客户端代码,即client.jsindex.js

// Import the neccessary modules for use in file
/* import statements */


const context = {
    insertCss: (...styles) => {
      const removeCss = styles.map(x => x._insertCss());
      return () => {
        removeCss.forEach(f => f());
      };
    },
  }

hydrate(
  <BrowserRouter>
    //ContextProvider wraps around and returns App so we can do this
    <ContextProvider context={context} />
  </BrowserRouter>,
  document.getElementById('app')
)

server.js

//Additional code above

app.get("*", (req, res, next) => {
  const activeRoute = routes.find((route) => matchPath(req.url, route)) || {}

  const promise = activeRoute.fetchInitialData
    ? activeRoute.fetchInitialData(req.path)
    : Promise.resolve()

  promise.then((data) => {
    const css = new Set()
    const context = { insertCss: (...styles) => 
styles.forEach(style => css.add(style._getCss()))}

    const markup = renderToString(
      <StaticRouter location={req.url}>
        <ContextProvider context={context}> 
         <App />
        </ContextProvider>
      </StaticRouter>
    )

    res.send(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>React on the Server!</title>
          <script src="/bundle.js" defer></script>
          <script>window.__INITIAL_DATA__ = ${serialize(data)}</script>
          <style type="text/css">${[...css].join('')}</style>
        </head>
        <body>
          <div id="app">${markup}</div>
        </body>
      </html>
    `)
  }).catch(next)
})

app.listen(port, () => console.log(`Server is listening on port: ${port}`))

我在这里写了一篇文章对此进行了更详细的解释: https : //medium.com/@danielnmai/load-css-in-react-server-side-rendering-with-isomorphic-style-loader-848c8140a096

整夜检查代码并无休止地搜索Google。 我找到了一个修复程序,该问题是我的代码的主要问题在于webpack.config.babel.js设置中。

我将浏览器测试更改为sass || scss使用style-loader ,而不是isomorphic-style-loader 我还从我的应用程序中删除了所有isomorphic-style-loader逻辑(即withStylesinsertCss等)

我不确定这是否是正确的方法,但与此同时,它似乎可以解决我的问题,并且不会返回任何错误或网络问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM