Часть 2: Ajax, стили и маршрутизация
Последнее обновление: 09.05.2017
Предыдущий пост в этой серии
Текущие версии:
- Реакция: v15.4
- next.js: v2.3
Где мы остановились
В последнем посте я немного рассказал о некоторых из первых проблем, с которыми я столкнулся, когда работал над преобразованием моего create-response-app в next.js app, а также задокументированы некоторые способы либо устранить препятствия, либо понять различия, чтобы сделать этот начальный удар как можно меньшим и легким для устранения!
Вызовы AJAX
Это было чем-то, из-за чего я немного нервничал, потому что обработка запросов ajax в Redux уже казалась немного неуклюжей, и я не был уверен, что добавление еще одного уровня ко всему этому повлияет на мое здравомыслие. Я играл с redux-sagas, redux-thunk, rxjs и observables и ... ну, возможно, некоторые из них - это всего лишь я и мои личные проблемы с тем, как некоторые из этих вещей работают. При этом: я был приятно удивлен тем, насколько приятно было многое из этого реализовать! Честно говоря, я все еще в значительной степени держался подальше от Redux при реализации каких-либо моих вызовов ajax, но почти ни один из них за пределами входа в систему не влиял на глобальное состояние, поэтому ничего не нужно было проходить через уровни глубокого преобразования генераторов, преобразователей и прочего.
getInitialProps и состояние
Одна вещь, которую предлагает вам next.js, - это специальная статическая функция, которую вы можете объявить либо для компонентов класса, либо для функциональных компонентов: getInitialProps. Итак, если бы вы, например, объявили это в классе, вы бы сделали это так:
class Foo extends React.Component {
static getInitialProps() {
return { data: "Some Data" };
}
render() {
return <div>{this.props.data}</div>;
}
}
Или, если вы должны объявить это в функциональном компоненте, вы можете сделать что-то вроде этого:
const Foo = ({ data }) => <div>{data}</div>;
Foo.getInitialProps = () => ({ data: "Some Data" });
Это говорит React о том, что есть некоторые свойства по умолчанию, которые должны быть заполнены как часть инициализации этой страницы. Это могут быть данные, которые необходимо загрузить из какого-либо внешнего источника; может быть, сервер API или что-то в этом роде. Может быть, необходимо произвести какие-то вычисления или ... есть много причин, по которым вы можете начать с заполнения некоторых данных. Это особенно важно для next.js и того, как он работает с рендерингом на стороне сервера: он сообщает next.js, какие данные следует предварительно выбрать или заполнить для подготовки. приложение и сделайте это быстро. Он также позволяет использовать некоторые хитрости с предварительной выборкой ссылок!
Одна вещь, с которой я столкнулся, заключается в том, что некоторые из моих конечных точек требуют аутентификации, и это происходит из токена, заполненного через Redux. Проблема в том, что getInitialProps будет вызываться ДО загрузки токена, если страница обновляется, что означает, что он будет отправлять неаутентифицированные запросы. Это то, что вам необходимо учитывать при принятии решения, использовать ли getInitialProps или нет для предварительного заполнения данных.
Когда я столкнулся с этой конкретной проблемой, я вместо этого решил пойти по стандартному маршруту React и вместо этого заполнить данные через componentDidMount. Также обратите внимание, что getInitialProps можно использовать только на страницах, но не на компонентах!
Работа с async / await
Еще одна вещь, которую next.js предоставляет из коробки, - это поддержка async / await, что действительно приятно! Если вы привыкли выполнять обещания для вызовов ajax, вы можете вместо этого объявить свои функции как async и использовать несколько операторов await. Вот пример с обещанием, которое возвращает объект, у которого есть функция, возвращающая другое обещание:
function foo() {
SomeService
.makeRequest('url')
.then(res => {
res
.json()
.then(body => {
console.log('Data received:', body);
});
});
}
Вместо этого может быть записано как:
async function foo() {
const res = await SomeService.makeRequest('url');
const body = await res.json();
console.log(body);
};
Он очень хорошо работает как с axios, так и с fetch, поэтому вам не придется делать что-то особенное, чтобы заставить его работать, и вы можете пользоваться преимуществами. более простого кода. Приведенный выше пример действительно демонстрирует, насколько проще просто выполнить async / await прямо из коробки! Возможность избежать путаницы с вложенными обещаниями того стоит!
Что касается того, какая библиотека лучше подходит для получения данных из API, я снова буду использовать фразу «выбор дилера». У меня был самый большой опыт работы с axios, поэтому я предпочитаю его, но я знаю, что значительная часть сообщества React использует fetch для простых вещей, так что вы можете сделать и то, и другое, и они оба будут отлично работать!
CSS
В предыдущем воплощении приложения, которое я создавал, оно создавалось в основном в create-react-app, и я использовал внешние файлы CSS для обработки стилизации компонентов. Это было… хорошо, но определенно не идеально. Его было неудобно изменять, и стили полагались на имена классов, которые были глобальными. Итак, у меня была бы такая структура каталогов:
- login/
- Login.js
- Login.css
- store/
- Store.js
- Store.css
- StoreForm.js
- StoreForm.css
- StoreItem.js
- StoreItem.css
И т.д. Вы, вероятно, увидите, где это может сильно запутать, тем более что все, что применяется в StoreForm.css
, может на самом деле повлиять и на Store.css
! Вместо этого next.js полагается на стилизованный jsx, который позволяет вам встраивать CSS для компонента В этот компонент (также в JSX) и обеспечивает, чтобы встроенный CSS влиял ТОЛЬКО на этот компонент!
Итак, если login.js был простым компонентом:
const Login = () => (
<div className="Login">
Login Time
</div>
);
И login.css содержит что-то вроде этого:
.Login {
border: 2px solid red;
}
Вместо этого вы можете сделать это внутри login.js и быть уверенным, что это повлияет только на тот компонент, который находится там:
const Login = () => (
<div className="Login">
<style jsx>{`
.Login { border: 2px solid red; }
`}</style>
Login Time
</div>
);
Маршрутизация
По общему признанию, маршрутизация в next.js намного сложнее. Гораздо проще иметь возможность использовать что-то вроде response-router и указывать свои маршруты, вложенные маршруты и правила маршрутизации / динамические маршруты и т. Д., Но здесь вам придется сделать некоторые вещи немного по-другому. Прежде всего, вам нужно будет присвоить вашим js-файлам в каталоге pages / имена маршрутов, которые вы хотите просмотреть. Итак, как упоминалось ранее, если ваше приложение имеет index.js и about.js внутри pages /, ваше приложение будет иметь Доступны [domain.com] и [domain.com] / about. Точно так же, если вы хотите использовать подчеркивания или тире в своем пути, это тоже сработает! pages / some-page.js и pages / some_page.js будут отображаться на [domain.com] / some-page и [domain.com] / some_page соответственно!
Но что, если вы хотите сделать что-нибудь посложнее? Допустим, вам нужно что-то вроде [domain.com] / posts / [slug]? Вы не можете сделать это из коробки без конфигурации, но, к счастью, next.js предоставляет возможность использовать ваш собственный server.js для переопределения поведения по умолчанию next.js!
Переход на Express.js для сервера
Я начал с попытки следовать примеру server.js из README, чтобы получить очень неоднозначные результаты. Я мог вроде как заставить что-то работать, но это была значительная борьба, и я столкнулся с множеством проблем, когда маршрут работал, пока я не обновляю страницу, или не работал при обновлении, а работал только по ссылке, которая была очень неприятно пытаться решить. То, что я вместо этого сделал после того, как обнаружил несколько проблем с Github и сообщений StackOverflow, принял Express в качестве моего настраиваемого сервера, что сделало динамическую маршрутизацию примерно в миллион раз проще в работе и анализе! Вот пример настраиваемого файла server.js, который использует экспресс и реализует некоторую динамическую маршрутизацию. Достаточно сказать, что для того, чтобы это сработало, вам нужно добавить в проект express через NPM или Yarn, а затем создать server.js:
const express = require('express'); const { parse } = require('url'); const next = require('next');
const dev = process.env.NODE_ENV !== 'production'; const app = next({ dev }); const handle = app.getRequestHandler();
app.prepare().then(() => { const server = express();
// CUSTOM ROUTES GO HERE
server.get('/blog/:slug', (req, res) => { const mergedQuery = Object.assign({}, req.query, req.params); return app.render(req, res, '/blog', mergedQuery); });
// THIS IS THE DEFAULT ROUTE, DON'T EDIT THIS
server.get('*', (req, res) => { return handle(req, res); });
const port = process.env.PORT || 3000; server.listen(port, err => { if (err) throw err; console.log(`> Ready on port ${port}...`); }); });
Это даст вам параметр, который будет отправлен в ваш компонент blog.js внутри вашего каталога pages /, и предоставит вам настраиваемую маршрутизацию, которую вы хотите! Это также упрощает и упрощает определение пользовательских маршрутов. Однако это становится немного странным, когда вам нужно иметь дело со стороной клиента и ссылаться на эти динамические маршруты на своих страницах. Итак, предполагая, что у нас есть настройка маршрута выше / blog /: slug, ваши ссылки на определенные слаги должны быть структурированы с использованием компонента Link next / link с помощью следующего :
<Link href={`/blog?slug=${slug}`} as={`/blog/${slug}`} prefetch>
...
</Link>
as
- это то, что пользователь увидит в своем браузере, а href
- это то, что будет интерпретировать next.js, чтобы выяснить, как что-то должно маршрутизироваться. Оба эти шага необходимы для того, чтобы поведение ссылки и поведение маршрутизации работали одинаково независимо от того, откуда отображается страница! Надеюсь, это избавит вас от многих безумных поисков в Google!
Я знаю, что есть также специально предназначенные для этого пакеты next.js, но у меня не было возможности с ними повозиться (например, next-routes ). Наконец, вам нужно изменить файл package.json, включив в него все, чтобы next.js знал, как запускать server.js. файл:
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js",
}
Теперь вы можете продолжать запускать свой сервер разработки с помощью npm run dev или yarn dev, а также можете построить / запустить свой производственный сервер!
Заключение
Теперь у нас есть довольно прочная основа для нашего приложения next.js. Мы рассмотрели многое из самого необходимого для запуска и работы нашей среды разработки и настроили так, как нам нравится, но теперь нам нужно будет начать тестирование и подготовку к развертыванию. В следующем и последнем посте этой серии (возможно, заключительном? Определенно не финале «Final Fantasy», но, возможно, финале «Final Destination») мы поговорим о тестировании, развертывании и, наконец, о том, что нужно сделать, чтобы ваше приложение работало (и пусть он тоже работает).
Следующая публикация в этой серии
Посмотрите мои новые книги!
Привет всем! Если вам понравилось то, что вы здесь прочитали, и вы хотите узнать больше со мной, ознакомьтесь с моей новой книгой об использовании последней версии приложения Create React:
Это охватывает все, что вам нужно знать, чтобы научиться использовать Create React App v2, чтобы стать лучшим и продуктивным фронтенд-разработчиком, и по-настоящему погрузиться в детали Create React App, создавая новый проект React с нуля!
И, конечно же, моя книга Phoenix Web Development также доступна, если вы хотите узнать больше о веб-разработке на Elixir:
Я очень рад наконец представить этот проект миру! Он написан в том же стиле, что и другие мои руководства, где мы будем строить каркас полного проекта от начала до конца, даже охватывая некоторые из сложных тем, таких как загрузка файлов, вход в Twitter / Google OAuth и API!