Javascript 使用React动态加载样式表

Javascript 使用React动态加载样式表,javascript,stylesheet,reactjs,isomorphic-javascript,Javascript,Stylesheet,Reactjs,Isomorphic Javascript,我正在建立一个CMS系统来管理营销登录页。在“编辑登录页”视图中,我希望能够为用户正在编辑的任何登录页加载关联的样式表。我怎么能用React做这样的事 我的应用程序是完全反应,同构,运行在。我对相关页面的基本组件继承权如下所示: App.jsx (has `<head>` tag) └── Layout.jsx (dictates page structure, sidebars, etc.) └── EditLandingPage.jsx (shows the landin

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

我的应用程序是完全反应,同构,运行在。我对相关页面的基本组件继承权如下所示:

App.jsx (has `<head>` tag)
└── Layout.jsx (dictates page structure, sidebars, etc.)
    └── EditLandingPage.jsx (shows the landing page in edit mode)
App.jsx(有``标记)
└── Layout.jsx(指定页面结构、侧栏等)
└── EditLandingPage.jsx(在编辑模式下显示登录页)
登录页的数据(包括要加载的样式表的路径)在
EditLandingPage
ComponentDidMount
中异步获取

如果您需要任何其他信息,请告诉我。我很想弄明白


好处:我还想在离开页面时卸载样式表,我假设我可以在
ComponentWillUnmount
,对吗?

这是prime mixin Territory。首先,我们将定义一个帮助器来管理样式表

我们需要一个加载样式表并返回成功承诺的函数。样式表实际上是相当疯狂的检测加载

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; });
}
同样,未经测试,如果有任何问题,请更新此

现在我们有了组件

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
  }
});
React.createClass({
getInitialState:函数(){
返回{foo:false};
},
componentDidMount:function(){
loadStyleSheet('foo','/css/views/foo.css');
},
render:function(){
如果(!this.state.foo){
返回
}
//返回取决于样式的内容
}
});

剩下的唯一要做的事情是在尝试加载样式表之前检查样式表是否已经存在。希望这至少能让您走上正确的道路。

只需使用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>
        )
    }
};
import*as React from'React';
导出默认类主页面扩展React.Component{
建造师(道具){
超级(道具);
this.state={stylePath:'style1.css'};
}
把手按钮点击(){
this.setState({stylePath:'style2.css'});
}
render(){
返回(
单击以更新样式表
)
}
};
此外,我还将其实现为react组件。您可以通过npm install react动态样式加载器进行安装

检查我的github存储库以检查:

我认为Burakhan的答案是正确的,但是在body标记中加载
是很奇怪的。这就是为什么我认为应该将其修改为以下[我使用React挂钩]:

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>
  );
};
import*as React from'React';
导出默认主页=(道具)=>{
const[stylePath,setStylePath]=useState(“style1.css”);
const handleButtonClick=()=>{
setStylePath({stylePath:'style2.css'});
}
useffect(()=>{
var head=document.head;
var link=document.createElement(“链接”);
link.type=“text/css”;
link.rel=“样式表”;
link.href=stylePath;
头.子(链接);
return()=>{head.removeChild(link);}
},[stylePath]);
返回(
单击以更新样式表
);
};

我就是这样动态添加样式的:

import React, { Component } from "react";

class MyComponent extends Component {
    componentDidMount() {
        const cssUrl = "/public/assets/css/style.css";
        this.addStyle(cssUrl);
    }

    addStyle = url => {
        const style = document.createElement("link");
        style.href = url;
        style.rel = "stylesheet";
        style.async = true;

        document.head.appendChild(style);
    };

    render() {
        return <div> textInComponent </div>;
    }
}

export default MyComponent;
import React,{Component}来自“React”;
类MyComponent扩展组件{
componentDidMount(){
const cssUrl=“/public/assets/css/style.css”;
此.addStyle(cssUrl);
}
addStyle=url=>{
const style=document.createElement(“链接”);
style.href=url;
style.rel=“样式表”;
style.async=true;
document.head.appendChild(样式);
};
render(){
返回textInComponent;
}
}
导出默认MyComponent;

除了为样式表创建元素,您还可以尝试根据某些条件导入css。ECMAScript提供了一个支持动态模块导入的方案,其工作原理如下:

if (condition) {
  import('your css path here').then((condition) => {});
}

我使用react头盔,在渲染功能中

{inject ? 
    <Helmet>
        <link rel="stylesheet" href="css/style.css" />
    </Helmet> : null}
{inject?
:null}

哇,太棒了!我一定会试一试,让你知道结果如何。谢谢重点测试firefox,因为它是所有浏览器中支持最差的。这很有帮助,但也有一些输入错误(bug),我不明白为什么不根据道具或状态更改项目中链接的href属性…我不想加载整个样式表,尤其是因为我使用Rails,所以我根据你的答案做了一些黑客操作,但是有条件地添加了一个样式标签,这对于我所需要的非常有效。我正在使用react create应用程序,所以我不得不将css移到公用文件夹中。@cabaji99链接元素的href是什么样子的?你做过
“%PUBLIC\u URL%/stylesheet\u name.css”
还是什么?这对我来说也很好,请记住,如果您想从Javascript代码中动态访问
public
文件夹中的样式表,您应该使用
public\u URL
环境变量,如下所示:
@marcprins您可以尝试渲染到
元素中。我认为您需要
返回()=>{head.removeChild(link);}
就在
head.appendChild
节点下进行清理,否则当样式路径发生变化时,您只需继续添加节点。效果就像一个符咒:)
{inject ? 
    <Helmet>
        <link rel="stylesheet" href="css/style.css" />
    </Helmet> : null}