代码之家  ›  专栏  ›  技术社区  ›  serraosays

Gatsby/React SSR构建中JS对象的条件分解

  •  0
  • serraosays  · 技术社区  · 6 年前

    以下部分, AudioPlayer react-media-player 在Gatsby/React dev环境中工作得很好。但要把它打造成一个反应型SSR真是太糟糕了。

    音频播放器 window 要实例化的对象,它不可用于Node.js节点. 所以,我不得不使用Gatsby的自定义Webpack配置来检测媒体播放器,并将一个空加载器放入混音中。效果很好:

    exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
      if (stage === "build-html") {
        actions.setWebpackConfig({
          module: {
            rules: [
              {
                test: /react-media-player/,
                use: loaders.null(),
              },
            ],
          },
        })
      }
    }
    

    但是当你试图通过Webpack构建时,现在我得到了一个错误:

    WebpackError:TypeError:无法解构属性 CurrentTime 属于

    这是有道理的,因为我们刚刚取消了 ,其中包含 . 所以我如何确保Webpack不会试图对 controls other answers on S.O. regarding conditional destructuring 在ES6中,对我来说毫无意义,所以请慢慢解释):

    import React, { Component } from 'react'
    import { Media, Player, controls } from 'react-media-player'
    
    const { CurrentTime, SeekBar, Duration, Volume, PlayPause, MuteUnmute } = controls
    let panner = null
    
    class AudioPlayer extends Component {
    
      componentDidMount() {
        const audioContext = new (window.AudioContext || window.webkitAudioContext)()
        panner = audioContext.createPanner()
    
        panner.setPosition(0, 0, 1)
        panner.panningModel = 'equalpower'
        panner.connect(audioContext.destination)
    
        const source = audioContext.createMediaElementSource(this._player.instance)
        source.connect(panner)
        panner.connect(audioContext.destination)
      }
    
      _handlePannerChange = ({ target }) => {
        const x = +target.value
        const y = 0
        const z = 1 - Math.abs(x)
        panner.setPosition(x, y, z)
      }
    
      render() {
        return (
          <div>
            { typeof window !== 'undefined' && Media && 
              <Media>   
                <div>
                  <Player
                    ref={c => this._player = c}
                    src={this.props.src}
                    useAudioObject
                  />
                  <section className="media-controls">
                    <div className="media-title-box">
                      <PlayPause className="media-control media-control--play-pause"/>
                      <div className="media-title-content">
                        <div className="media-title">{ this.props.mediaTitle }</div>
                        <div className="media-subtitle">{ this.props.mediaSubtitle }</div>
                      </div>
                    </div>
                    <div className="media-controls-container">
                      <CurrentTime className="media-control media-control--current-time"/>
                      <SeekBar className="media-control media-control--volume-range"/>
                      <Duration className="media-control media-control--duration"/>
                    </div>
                    <div className="media-sound-controls">
                      <MuteUnmute className="media-control media-control--mute-unmute"/>
                      <Volume className="media-control media-control--volume"/>
                    </div>            
                  </section>
                </div>
              </Media>
            }
          </div>
        )
      }
    }
    
    export default AudioPlayer
    
    0 回复  |  直到 6 年前
        1
  •  0
  •   serraosays    5 年前

    对于未来的谷歌用户/那些感兴趣的人来说,这个问题的最终解决方案需要很多努力才能完成。我会走完所有的事情,希望这能帮助其他人:

    AudioPlayer 组件问题

    • 我想原因是 react-media-player Media , Player , CurrentTime SeekBar , Duration Volume react媒体播放器 PlayPause MuteUnmute .

    • 我的习惯 音频播放器 所需组件3 { typeof window !== 'undefined'} 检查以使我的生成工作并重构 panner audioContext 变量 componentDidMount() . 注意,第一个 window 检查查找 media react媒体播放器 ,作为 react媒体播放器 在呈现函数中从不被调用。

    import React, { Component } from 'react'
    import { Media, Player, controls } from 'react-media-player'
    import PlayPause from '../../components/AudioPlayerPlayPause'
    import MuteUnmute from '../../components/AudioPlayerMuteUnmute'
    
    const { CurrentTime, SeekBar, Duration, Volume } = controls
    let panner = null
    
    class AudioPlayer extends Component {
    
      componentDidMount() {
        const audioContext = new (window.AudioContext || window.webkitAudioContext)()
        panner = audioContext.createPanner()
    
        panner.setPosition(0, 0, 1)
        panner.panningModel = 'equalpower'
        panner.connect(audioContext.destination)
    
        const source = audioContext.createMediaElementSource(this._player.instance)
        source.connect(panner)
        panner.connect(audioContext.destination)
      }
    
      _handlePannerChange = ({ target }) => {
        const x = +target.value
        const y = 0
        const z = 1 - Math.abs(x)
        panner.setPosition(x, y, z)
      }
    
      render() {
        return (
          <div>
            { typeof window !== 'undefined' && Media && 
              <Media>   
                <div>
                  <Player
                    ref={c => this._player = c}
                    src={this.props.src}
                    useAudioObject
                  />
                  <section className="media-controls">
                    <div className="media-title-box">
                      { typeof window !== 'undefined' && PlayPause && 
                        <PlayPause className="media-control media-control--play-pause"/>
                      }
                      <div className="media-title-content">
                        <div className="media-title">{ this.props.mediaTitle }</div>
                        <div className="media-subtitle">{ this.props.mediaSubtitle }</div>
                      </div>
                    </div>
                    <div className="media-controls-container">
                      <CurrentTime className="media-control media-control--current-time"/>
                      <SeekBar className="media-control media-control--volume-range"/>
                      <Duration className="media-control media-control--duration"/>
                    </div>
                    <div className="media-sound-controls">
                      { typeof window !== 'undefined' && MuteUnmute && 
                        <MuteUnmute className="media-control media-control--mute-unmute"/>
                      }
                      <Volume className="media-control media-control--volume"/>
                    </div>            
                  </section>
                </div>
              </Media>
            }
          </div>
        )
      }
    }
    
    export default AudioPlayer
    

    播放暂停 静音

    • 这些组件来自 react-media-player 示例库中的子组件称为 scale scaleX <transition> 但最终搞砸了建筑。我基本上只是将它们重构成PlayPause和MuteUnmute组件本身,这就解决了我在那里遇到的问题。

    • withMediaProps() 包装这些模块导出使Webpack适合。我不得不使用Gatsby空加载器来解决这个问题,所以在构建Webpack时不会查找 .

    静音.js:

    import React, { Component } from 'react'
    import { withMediaProps } from 'react-media-player'
    import Transition from 'react-motion-ui-pack'
    
    class MuteUnmute extends Component {
      _handleMuteUnmute = () => {
        this.props.media.muteUnmute()
      }
    
      render() {
        const { media: { volume }, className } = this.props
        return (
          <svg width="36px" height="36px" viewBox="0 0 36 36" className={className} onClick={this._handleMuteUnmute}>
            <circle fill="#eaebec" cx="18" cy="18" r="18"/>
            <polygon fill="#3b3b3b" points="11,14.844 11,21.442 14.202,21.442 17.656,25 17.656,11 14.074,14.844"/>
            <Transition
              component="g"
              enter={{ scale: 1 }}
              leave={{ scale: 0 }}
            >
              { volume >= 0.5 &&
                <path key="first-bar" fill="#3b3b3b" d="M24.024,14.443c-0.607-1.028-1.441-1.807-2.236-2.326c-0.405-0.252-0.796-0.448-1.153-0.597c-0.362-0.139-0.682-0.245-0.954-0.305c-0.058-0.018-0.104-0.023-0.158-0.035v1.202c0.2,0.052,0.421,0.124,0.672,0.22c0.298,0.125,0.622,0.289,0.961,0.497c0.662,0.434,1.359,1.084,1.864,1.94c0.26,0.424,0.448,0.904,0.599,1.401c0.139,0.538,0.193,0.903,0.216,1.616c-0.017,0.421-0.075,1.029-0.216,1.506c-0.151,0.497-0.339,0.977-0.599,1.401c-0.505,0.856-1.202,1.507-1.864,1.94c-0.339,0.209-0.663,0.373-0.961,0.497c-0.268,0.102-0.489,0.174-0.672,0.221v1.201c0.054-0.012,0.1-0.018,0.158-0.035c0.272-0.06,0.592-0.166,0.954-0.305c0.358-0.149,0.748-0.346,1.153-0.597c0.795-0.519,1.629-1.298,2.236-2.326C24.639,20.534,24.994,19.273,25,18C24.994,16.727,24.639,15.466,24.024,14.443z"/>
              }
            </Transition>
            <Transition
              component="g"
              enter={{ scale: 1 }}
              leave={{ scale: 0 }}
            >
              { volume > 0 &&
                <path key="second-bar" fill="#3b3b3b" d="M21.733,18c0-1.518-0.91-2.819-2.211-3.402v6.804C20.824,20.818,21.733,19.518,21.733,18z"/>
              }
            </Transition>
            <Transition
              component="g"
              enter={{ scale: 1 }}
              leave={{ scale: 0 }}
            >
              { volume === 0 &&
                <polygon key="mute" fill="#3b3b3b" points="24.839,15.955 23.778,14.895 21.733,16.94 19.688,14.895 18.628,15.955 20.673,18 18.628,20.045 19.688,21.106 21.733,19.061 23.778,21.106 24.839,20.045 22.794,18 "/>
              }
            </Transition>
          </svg>
        )
      }
    }
    
    export default withMediaProps(MuteUnmute)
    
    

    import React, { Component } from 'react'
    import { withMediaProps } from 'react-media-player'
    import Transition from 'react-motion-ui-pack'
    
    class PlayPause extends Component {
      _handlePlayPause = () => {
        this.props.media.playPause()
      }
    
      render() {
        const { media: { isPlaying }, className } = this.props
        return (
          <svg
            role="button"
            width="36px"
            height="36px"
            viewBox="0 0 36 36"
            className={className}
            onClick={this._handlePlayPause}
          >
            <circle fill="#eaebec" cx="18" cy="18" r="18"/>
              <Transition
                component="g"
                enter={{ scaleX: 1 }}
                leave={{ scaleX: 0 }}
              >
                { isPlaying &&
                  <g key="pause" style={{ transformOrigin: '0% 50%' }}>
                      <rect x="12" y="11" fill="#3b3b3b" width="4" height="14"/>
                      <rect x="20" y="11" fill="#3b3b3b" width="4" height="14"/>
                  </g>
                }
              </Transition>
              <Transition
                component="g"
                enter={{ scaleX: 1 }}
                leave={{ scaleX: 0 }}
              >
                { !isPlaying &&
                  <polygon
                    key="play"
                    fill="#3b3b3b"
                    points="14,11 26,18 14,25"
                    style={{ transformOrigin: '100% 50%' }}
                  />
                }
              </Transition>
          </svg>
        )
      }
    }
    
    export default withMediaProps(PlayPause)
    
    

    盖茨比-node.js节点

    /**
     * Implement Gatsby's Node APIs in this file.
     *
     * See: https://www.gatsbyjs.org/docs/node-apis/
     */
    
    exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
      if (stage === "build-html") {
        actions.setWebpackConfig({
          module: {
            rules: [
              {
                test: /AudioPlayer|PlayPause|MuteUnmute/,
                use: loaders.null(),
              },
            ],
          },
        })
      }
    }
    

    包/构建问题

    *我用的是Node v10.12,需要碰撞 node-gyp 不管是什么原因。在梳理完网络之后,我能猜测的是我的全球 节点gyp 包与最新的节点不同步,所以它崩溃了。 npm install -g node-gyp 然后 node-gyp -v 应该屈服 3.8.0 . 那个版本让我的版本开始了。

    package.json

      "dependencies": {
        "gatsby": "^2.0.12",
        "gatsby-cli": "^2.4.3",
        "gatsby-link": "^2.0.2",
        "gatsby-plugin-manifest": "^2.0.4",
        "gatsby-plugin-react-helmet": "^3.0.0",
        "gatsby-plugin-sass": "^2.0.1",
        "node-sass": "^4.9.3",
        "prop-types": "^15.6.2",
        "react": "^16.4.2",
        "react-anchor-link-smooth-scroll": "^1.0.11",
        "react-dom": "^16.4.2",
        "react-helmet": "^5.2.0",
        "react-hot-loader": "^4.3.11",
        "react-media-player": "^0.7.1",
        "react-modal": "^3.5.1",
        "react-motion-ui-pack": "^0.10.3",
        "react-tabs": "^2.2.2",
        "react-transition-group": "^2.4.0",
        "react-waypoint": "^8.0.3"
      }