#javascript #node.js #reactjs
#javascript #node.js #reactjs
Вопрос:
Я создаю универсальное приложение React с возможностями входа в Google. К сожалению, в Google нет универсальной библиотеки Google API ( gapi
), которую я мог бы использовать как на клиенте, так и на сервере.
Что я действительно пытаюсь здесь сделать, так это запустить мой серверный код и заставить Babel автоматически перекомпилировать мой код при внесении изменений и автоматически перезапустить сервер (как это делает nodemon, но с шагом компиляции). Я нашел пакет npm babel-watch
, который будет делать именно это, однако он не интегрируется с webpack.
В моем шаблоне index.html файл, у меня есть следующий код в HTML <head>
:
index.html
<script type="text/javascript">
window.gapiPromise = new Promise(resolve => window.gapiLoadedCallback = () => resolve(gapi))
</script>
<script src="https://apis.google.com/js/platform:auth2.js?onload=gapiLoadedCallback" async defer></script>
Приведенное выше создает новое Promise
значение, которое разрешается значением gapi
when platform.js
загружается. Однако, поскольку это загружается только на клиенте gapi
и gapiPromise
не существует на сервере.
У меня также есть вход в Google в компоненте React, который использует глобальную gapiPromise
переменную для рендеринга кнопки, когда gapi
она готова к использованию:
GoogleSignIn.jsx
import React from 'react'
// should only run on client
if (gapiPromise !== false) {
gapiPromise.then(gapi => {
gapi.auth2.init({
client_id: '[removed]'
})
})
}
class GoogleSignIn extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
// should only run on client
if (gapiPromise !== false) {
gapiPromise.then(gapi => gapi.signin2.render('g-signin2', {
'scope': 'email',
'width': 160,
'height': 50,
'theme': 'light',
'onsuccess': this.props.onSuccess,
'onfailure': this.props.onFailure
}))
}
}
render() {
return (
<div className="google-sign-in">
<div id="g-signin2"></div>
</div>
)
}
}
export default GoogleSignIn
Это отлично работает на клиенте, но когда я пытаюсь отобразить его на сервере, узел жалуется, что:
/Users/jreznik/Sites/my-app/dist/server.js:3568
if (gapiPromise !== false) {
^
ReferenceError: gapiPromise is not defined
at Object.defineProperty.value (/Users/jreznik/Sites/my-app/dist/server.js:3568:6)
at __webpack_require__ (/Users/jreznik/Sites/my-app/dist/server.js:20:30)
at Object.defineProperty.value (/Users/jreznik/Sites/my-app/dist/server.js:3408:22)
at __webpack_require__ (/Users/jreznik/Sites/my-app/dist/server.js:20:30)
at Object.defineProperty.value (/Users/jreznik/Sites/my-app/dist/server.js:3354:24)
at __webpack_require__ (/Users/jreznik/Sites/my-app/dist/server.js:20:30)
at Object.defineProperty.value (/Users/jreznik/Sites/my-app/dist/server.js:194:21)
at __webpack_require__ (/Users/jreznik/Sites/my-app/dist/server.js:20:30)
at Object.<anonymous> (/Users/jreznik/Sites/my-app/dist/server.js:59:16)
at Object.<anonymous> (/Users/jreznik/Sites/my-app/dist/server.js:131:31)
Я попытался gapiPromise
добавить в этот файл префикс global.
(т.Е. global.gapiPromise
), а затем определить global.gapiPromise = false
в файле ввода моего сервера ( server.js
), но затем узел жалуется:
/Users/jreznik/Sites/my-app/dist/server.js:3569
global.gapiPromise.then(function (gapi) {
^
TypeError: Cannot read property 'then' of undefined
Наконец, я смог заставить его работать, используя webpack DefinePlugin
:
webpack.server.config.js
...
plugins: [
new webpack.DefinePlugin({
'window': {},
'gapiPromise': false
})
]
...
Но если я это сделаю, я не смогу использовать babel-watch
пакет npm для автоматической перекомпиляции и перезапуска сервера.
Как я могу заставить узел перестать жаловаться на эти неопределенные глобальные переменные?
Ответ №1:
Это клиентская библиотека, что означает, что она предназначена только для использования с DOM из браузера. На самом деле это эквивалентно window.gapiPromise
. И window
объект представляет собой открытое окно в браузере.
Комментарии:
1. Спасибо за ваш ответ. Я понимаю это, однако я не собираюсь выполнять какой-либо код с
gapi
сервера, а просто визуализирую компонент на стороне сервера (который должен только визуализироваться<div className="google-sign-in"> <div id="g-signin2"></div> </div>
), а затем клиент будет использоватьgapi
для визуализации полной кнопки.2. Вы не можете использовать его на стороне сервера, потому что это клиентская библиотека .
Ответ №2:
Хорошо, после нескольких часов возни с этим, я нашел решение через час после публикации этого вопроса.
Из моего файла GoogleSignIn.jsx я удалил код…
// should only run on client
if (gapiPromise !== false) {
gapiPromise.then(gapi => {
gapi.auth2.init({
client_id: '[removed]'
})
})
}
… и в шаблон:
<script type="text/javascript">
window.gapiPromise = new Promise(resolve => window.gapiLoadedCallback = () => {
gapi.auth2.init({
client_id: '[removed]'
})
resolve(gapi)
})
</script>
<script src="https://apis.google.com/js/platform:auth2.js?onload=gapiLoadedCallback" async defer></script>
Не совсем уверен, почему Node не жалуется на дополнительные gapiPromise
s в componentDidMount()
методе, но это не так. Также я изменил if-оператор для проверки gapiPromise
на:
if (typeof gapiPromise !== 'undefined') {