javascript - 盖茨比/反应导航器
问题描述
您好,我目前遇到一个问题,我想在我的反应组件中使用 JS 显示一个弹出窗口,但是在构建我的 gatsby 时遇到错误。WebpackError:ReferenceError:未定义导航器。这是我将在我的 React 组件上使用的 JS 代码。
JS
var isMobile = {
Android: function () {
return navigator.userAgent.match(/Android/i)
},
iOS: function () {
return navigator.userAgent.match(/iPhone|iPad|iPod/i)
},
Windows: function () {
return navigator.userAgent.match(/IEMobile/i)
},
any: function () {
return isMobile.Android() || isMobile.iOS() || isMobile.Windows()
},
}
if (!isMobile.any()) {
$('body').addClass('is-not-ios')
$('.show-ios, .show-android').addClass('disabled')
$('.show-no-device').removeClass('disabled')
}
if (isMobile.Android()) {
$('body').addClass('is-not-ios')
$('head').append('<meta name="theme-color" content="#FFFFFF"> />')
$('.show-android').removeClass('disabled')
$(
'.show-ios, .show-no-device, .simulate-android, .simulate-iphones'
).addClass('disabled')
}
if (isMobile.iOS()) {
$('body').addClass('is-ios')
$('.show-ios').removeClass('disabled')
$(
'.show-android, .show-no-device, .simulate-android, .simulate-iphones'
).addClass('disabled')
}
if (pwaEnabled === true) {
//Setting Timeout Before Prompt Shows Again if Dismissed
var now = new Date()
var start = new Date(now.getFullYear(), 0, 0)
var diff = now - start
var oneDay = 1000 * 60 * 60 * 24
var day = Math.floor(diff / oneDay)
var dismissDate = localStorage.getItem('Appkit-PWA-Timeout-Value')
if (day - dismissDate > pwaRemind) {
localStorage.removeItem('Appkit-PWA-Prompt')
}
//Dismiss Prompt Button
$('.pwa-dismiss').on('click', function () {
console.log('User Closed Add to Home / PWA Prompt')
localStorage.setItem('Appkit-PWA-Prompt', 'install-rejected')
$('body')
.find('#menu-install-pwa-android, #menu-install-pwa-ios, .menu-hider')
.removeClass('menu-active')
localStorage.setItem('Appkit-PWA-Timeout-Value', day)
})
//Detecting Mobile Operating Systems
var isMobile = {
Android: function () {
return navigator.userAgent.match(/Android/i)
},
iOS: function () {
return navigator.userAgent.match(/iPhone|iPad|iPod/i)
},
any: function () {
return isMobile.Android() || isMobile.iOS() || isMobile.Windows()
},
}
var isInWebAppiOS = window.navigator.standalone == true
var isInWebAppChrome = window.matchMedia('(display-mode: standalone)').matches
//Trigger Install Prompt for Android
if (isMobile.Android()) {
function showInstallPrompt() {
if ($('#menu-install-pwa-android, .add-to-home').length) {
if (localStorage.getItem('Appkit-PWA-Prompt') != 'install-rejected') {
setTimeout(function () {
$('.add-to-home').addClass(
'add-to-home-visible add-to-home-android'
)
$('#menu-install-pwa-android, .menu-hider').addClass('menu-active')
}, 4500)
console.log('Triggering PWA Window for Android')
} else {
console.log(
'PWA Install Rejected. Will Show Again in ' +
(dismissDate - day + pwaRemind) +
' Days'
)
}
} else {
console.log(
'The div #menu-install-pwa-android was not found. Please add this div to show the install window'
)
}
}
let deferredPrompt
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault()
deferredPrompt = e
showInstallPrompt()
})
$('.pwa-install').on('click', function (e) {
deferredPrompt.prompt()
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
//console.log('User accepted the A2HS prompt');
} else {
//console.log('User dismissed the A2HS prompt');
}
deferredPrompt = null
})
})
window.addEventListener('appinstalled', (evt) => {
$('#menu-install-pwa-android, .menu-hider').removeClass('menu-active')
})
}
//Trigger Install Guide iOS
if (isMobile.iOS()) {
if (!isInWebAppiOS) {
if ($('#menu-install-pwa-ios, .add-to-home').length) {
if (localStorage.getItem('Appkit-PWA-Prompt') != 'install-rejected') {
console.log('Triggering PWA Window for iOS')
setTimeout(function () {
$('.add-to-home').addClass('add-to-home-visible add-to-home-ios')
$('#menu-install-pwa-ios, .menu-hider').addClass('menu-active')
}, 4500)
} else {
console.log(
'PWA Install Rejected. Will Show Again in ' +
(dismissDate - day + pwaRemind) +
' Days'
)
}
} else {
console.log(
'The div #menu-install-pwa-ios was not found. Please add this div to show the install window'
)
}
}
}
}
const loadScript = () => window.addEventListener('load', () => isMobile())
export default loadScript
这是我将使用的组件loadScript
const MasterIndexPage = () => {
useEffect(() => {
loadScript()
}, [])
<div
id="menu-video"
className="menu menu-box-bottom rounded-m"
data-menu-height="410"
data-menu-effect="menu-over"
>
<div class="responsive-iframe max-iframe">
<iframe
src="https://www.youtube.com/embed/qCSBMbUa9jg"
frameborder="0"
allowfullscreen
></iframe>
</div>
<div className="menu-title">
<p className="color-highlight">Learn</p>
<h1>How to install</h1>
<a href="#" className="close-menu">
<i className="fa fa-times-circle"></i>
</a>
</div>
<div className="content mt-n2">
<p>Install Sparkle</p>
<a
href="#"
className="close-menu btn btn-full btn-m shadow-l rounded-s text-uppercase font-600 bg-green-dark mt-n2"
>
Done
</a>
</div>
</div>
}
谁能帮我解决问题?这是我只使用完整 JS 时的预期输出。
每当我加载页面时都会提示。
解决方案
构建 gatsby 时遇到错误
总结和简化,gatsby develop
由客户端(浏览器)使用 web 套接字直接解释(这就是你有即时刷新的原因)并且有一个window
ornavigator
对象,而gatsby build
由节点服务器处理,显然没有window
,document
或其他全局对象(如navigator
)因为它们甚至还没有定义。
这是 Gatsby 中一个非常常见且直接的问题,可以通过添加以下条件轻松绕过:
typeof window !== `undefined`
如果定义了窗口对象,则意味着您不在构建时进程中,因此您可以访问它。
useEffect(() => {
if(typeof window !== 'undefined') loadScript()
}, [])
或者通过在每个 navigator 语句中添加它:
Android: function () {
return typeof window !== 'undefined' && navigator.userAgent.match(/Android/i)
},
iOS: function () {
return typeof window !== 'undefined' &&
navigator.userAgent.match(/iPhone|iPad|iPod/i)
},
Windows: function () {
return typeof window !== 'undefined' &&
navigator.userAgent.match(/IEMobile/i)
},
any: function () {
return typeof window !== 'undefined' && (isMobile.Android() || isMobile.iOS() || isMobile.Windows())
},
}
当然,根据需要调整它。
此解决方法 ( typeof window !== 'undefined'
) 适用于所有对window
、document
、navigator
和其他在 SSR 中不可用的全局对象的所有引用。
超出了问题的范围。极不建议避免像您正在做的那样(使用 jQuery)将 DOM 指向:
$('body').addClass('is-not-ios')
真的,不要这样做。
使用 React,您正在创建和操作虚拟 DOM (vDOM),以避免像操作真实 DOM 那样的高性能操作。使用 React,您可以使用钩子 ( useRef
) 或其他变通方法来指向 React 范围和环境中的那些元素。
你的方法会导致水合问题,因为你正在执行 React 范围之外的操作。这意味着您可能会遇到某些元素的渲染问题,尤其是在前后移动或触发某些显示动作时。
你知道在这类问题上我最好的方法是什么吗?
是的,避免使用 jQuery。
您可以通过创建一个useState
根据您userAgent
添加类的逻辑更改其值的钩子来实现相同的行为。
例如:
const MasterIndexPage = props =>{
const [userAgent, setUserAgent]=useState("");
const detectUserAgent = ()=>{
if(navigator.userAgent.match(/Android/i)) setUserAgent("isAndroid");
// and so on for the rest
}
useEffect(() => {
detectUserAgent()
}, [])
return <main className={`${userAgent === "isAndroid" ? "someClassName" : ``}`}>
<div
id="menu-video"
className="menu menu-box-bottom rounded-m"
data-menu-height="410"
data-menu-effect="menu-over"
>
<div class="responsive-iframe max-iframe">
<iframe
src="https://www.youtube.com/embed/qCSBMbUa9jg"
frameborder="0"
allowfullscreen
></iframe>
</div>
<div className="menu-title">
<p className="color-highlight">Learn</p>
<h1>How to install</h1>
<a href="#" className="close-menu">
<i className="fa fa-times-circle"></i>
</a>
</div>
<div className="content mt-n2">
<p>Install Sparkle</p>
<a
href="#"
className="close-menu btn btn-full btn-m shadow-l rounded-s text-uppercase font-600 bg-green-dark mt-n2"
>
Done
</a>
</div>
</div>
<main>
}
为了避免超出实际的答案,我只添加了 Android 解决方法,但相同的逻辑适用于其余部分。您甚至可以在 中添加一个函数调用className
来返回userAgent
验证以避免像className={someFunctionThatReturnsTheClassname()}
. 当然,即使在获取和返回userAgent
值的自定义挂钩中,您也可以将所有这些逻辑扩展和隔离到单独的函数中。
推荐阅读
- utf-8 - raku:支持 Raku 2020.10 中的 utf8-c8 问题
- javascript - 如何检查按钮是否被按下javaScript
- javascript - 如何在javascript中使用正则表达式替换字符串?
- python - 从python中的不同文件夹导入文件
- iterm2 - iterm2中暗模式和亮模式切换的快捷键?
- java - 如何解释 jeprof 输出
- python - 总结邻居数
- node.js - Angular CLI 不创建项目并且需要 npm@6
- javascript - Webp 不显示在服务器上
- gitlab - gitlab cloud CI:如何增加共享运行器的内存