使用React动态加载样式表

nee*_*zer 15 javascript stylesheet reactjs isomorphic-javascript

我正在建立一个CMS系统来管理营销登陆页面.在"编辑登录页面"视图中,我希望能够为用户正在编辑的任何登录页面加载相关的样式表.我怎么能用React做这样的事情?

我的应用程序是完全React,同构,在Koa上运行.我的相关页面的基本组件层次结构看起来像这样:

App.jsx (has `<head>` tag)
??? Layout.jsx (dictates page structure, sidebars, etc.)
    ??? EditLandingPage.jsx (shows the landing page in edit mode)
Run Code Online (Sandbox Code Playgroud)

着陆页的数据(包括要加载的样式表的路径)是异步提取EditLandingPage的ComponentDidMount.

如果您需要任何其他信息,请与我们联系.很想得到这个想法!

额外奖励:我还想在离开页面时卸载样式表,我认为我可以反过来回答任何问题ComponentWillUnmount,对吧?

bur*_*kan 23

只需使用react的状态更新要动态加载的样式表路径.

import * as React from 'react';

export default class MainPage extends React.Component{
    constructor(props){
        super(props);
        this.state = {stylePath: 'style1.css'};
    }

    handleButtonClick(){
        this.setState({stylePath: 'style2.css'});
    }

    render(){
        return (
            <div>
                <link rel="stylesheet" type="text/css" href={this.state.stylePath} />
                <button type="button" onClick={this.handleButtonClick.bind(this)}>Click to update stylesheet</button>
            </div>
        )
    }
};
Run Code Online (Sandbox Code Playgroud)

此外,我已将其实施为反应组件.您可以通过npm install react-dynamic-style-loader安装.
检查我的github存储库以检查:https:
//github.com/burakhanalkan/react-dynamic-style-loader

  • 这很适合我需要的东西.我使用react-create-app所以我不得不将css移动到公共文件夹. (3认同)
  • @MarcoPrins您可以尝试react-helmet渲染到`&lt;head&gt;`元素中 (2认同)

Bri*_*and 6

这是主要的混合体.首先,我们将定义一个帮助程序来管理样式表.

我们需要一个加载样式表的函数,并返回一个成功的承诺.样式表实际上是非常疯狂的检测负载......

function loadStyleSheet(url){
  var sheet = document.createElement('link');
  sheet.rel = 'stylesheet';
  sheet.href = url;
  sheet.type = 'text/css';
  document.head.appendChild(sheet);
  var _timer;

  // TODO: handle failure
  return new Promise(function(resolve){
    sheet.onload = resolve;
    sheet.addEventListener('load', resolve);
    sheet.onreadystatechange = function(){
      if (sheet.readyState === 'loaded' || sheet.readyState === 'complete') {
        resolve();
      }
    };

    _timer = setInterval(function(){
      try {
        for (var i=0; i<document.styleSheets.length; i++) {
          if (document.styleSheets[i].href === sheet.href) resolve();
        } catch(e) { /* the stylesheet wasn't loaded */ }
      }
    }, 250);
  })
  .then(function(){ clearInterval(_timer); return link; });
}
Run Code Online (Sandbox Code Playgroud)

好吧##!@ ...我本来希望只是坚持一下,但是没有.这是未经测试的,所以如果有任何错误请更新它 - 它是从几篇博客文章中编译的.

其余的相当直接:

  • 允许加载样式表
  • 在可用时更新状态(防止FOUC)
  • 卸载组件时卸载任何已加载的样式表
  • 处理所有异步善良
var mixin = {
  componentWillMount: function(){
    this._stylesheetPromises = [];
  },
  loadStyleSheet: function(name, url){
    this._stylesheetPromises.push(loadStyleSheet(url))
    .then(function(link){
      var update = {};
      update[name] = true;
      this.setState(update);
    }.bind(this));
  },
  componentWillUnmount: function(){
    this._stylesheetPromises.forEach(function(p){
      // we use the promises because unmount before the download finishes is possible
      p.then(function(link){
        // guard against it being otherwise removed
        if (link.parentNode) link.parentNode.removeChild(link);
      });
    });
  }
};
Run Code Online (Sandbox Code Playgroud)

再次,未经测试,如果有任何问题请更新.

现在我们有了这个组件.

React.createClass({
  getInitialState: function(){
    return {foo: false};
  },
  componentDidMount: function(){
    this.loadStyleSheet('foo', '/css/views/foo.css');
  },
  render: function(){
    if (!this.state.foo) {
      return <div />
    }

    // return conent that depends on styles
  }
});
Run Code Online (Sandbox Code Playgroud)

唯一剩下的待办事项是在尝试加载之前检查样式表是否已存在.希望这至少可以让你走上正确的道路.


Moh*_*gdy 6

我认为 Burakhan 的答案是正确的,但加载<Link href = "" />到 body 标签中很奇怪。这就是为什么我认为应该将其修改为以下内容 [我使用 React hooks]:

import * as React from 'react';
export default MainPage = (props) => {
  const [ stylePath, setStylePath ] = useState("style1.css");
    
  const handleButtonClick = () => {
    setStylePath({stylePath: 'style2.css'});
  }

  useEffect(() => {
    var head = document.head;
    var link = document.createElement("link");

    link.type = "text/css";
    link.rel = "stylesheet";
    link.href = stylePath;

    head.appendChild(link);

    return () => { head.removeChild(link); }

  }, [stylePath]);

  return (
    <div>
      <button type="button" onClick={handleButtonClick}>
        Click to update stylesheet
      </button>
    </div>
  );
};
Run Code Online (Sandbox Code Playgroud)

  • 我认为你需要一个 `return () =&gt; { head.removeChild(link); }` 就在 `head.appendChild` 节点下进行清理,否则当 stylePath 发生变化时,您将继续添加节点。 (5认同)