о
Сначала вставьте адрес, если он вам нравится, вы можете сначала пометить волну 😳
Адрес онлайн-просмотра:http://118.24.21.99:5000/(немного больше времени загрузки)
Адрес репозитория GitHub:douban-movie-react
(Некоторые статические ресурсы на странице фильма не могут быть загружены в онлайн-предварительном просмотре. Это должно быть проблемой с конфигурацией Nginx. Чтобы получить наилучшие впечатления, вы можете клонировать его локально. Метод работы см. в документации GitHub)
Скорость загрузки nginx была значительно улучшена после включения gzip. . .
ПК-версия суперимитационного фильма Douban на основе React реализует домашнюю страницу, страницу фильма, страницу персонажа, рейтинг, страницу короткого обзора, страницу длинного обзора, страницу новостей и билетов в кино, страницу классификации, страницу рейтинга, страницу поиска, 404 страницы. .
стек технологий
- react
- redux
- redux-thunk
- react-router
- react-slick
- antd
- scss
- css-modules
store vs. state
Этот проект имеет большой (особый) Недостаток в том, что все данные, запрашиваемые к API, хранятся в хранилище redux, и каждый компонент отправляет запрос на соответствующий контент (я обнаружил, что он не очень хорошо написан в середине написания, но мне было лень изменить его позже и сбежал), что напрямую привело к необходимости Увеличить объем написанного примера кода. На самом деле эти данные должны быть помещены в состояние реакции каждого компонента или инициированы компонентом более высокого порядка для выполнения нескольких запросов, а затем переданы данным каждому компоненту марионетки. в магазине.Вместо того, чтобы сдать его в состояние, есть следующие принципы:
Do other parts of the application care about this data?
Нет, это просто какое-то фиксированное отображение данных, и между данными не будет взаимодействия.
Do you need to be able to create further derived data based on this original data?
Нет, просто какое-то фиксированное отображение данных.
Is the same data being used to drive multiple components?
Нет, каждый API, предоставляемый Douban, перекрывает множество данных, например, чтобы получить данные комментариев к фильму, возвращаемое значение будет содержать не только массив комментариев, но и данные темы фильма. для хранения данных фильма, где находится рецензия, для отображения на странице рецензии.
Is there value to you in being able to restore this state to a given point in time (ie, time travel debugging)?
Нет, нет никаких операций типа отмены, повтора.
Do you want to cache the data (ie, use what's in state if it's already there instead of re-requesting it)?
Если нужно класть данные в redux store, то это еле квалифицированно, мы можем кешировать данные просматриваемых страниц фильма (ведь информацию о фильме практически невозможно изменить во время просмотра)
Слишком много стандартного кода
Содержимое каждого компонента на странице получается путем запроса к бэкенду.Если каждый компонент будет отправлять запросы, создавать различные действия, писать редукторы и т. д., объем примера кода слишком велик.Каждый компонент представляет собой простой компонент отображения , и логика схожая, поэтому в проекте абстрагируется повторяющийся код.
Вот такая структура магазина (хочу сам разругаться):
Различные части каждого компонента презентацииpageName
, moduleName
(для организации редукционного магазина),API
(разные компоненты презентации запрашивают разные URI),view
(компоненты марионетки, для отображения которых используется каждый компонент),param
(Многие URI должны быть объединены через параметры в маршруте, такие как запрос/subject/26430636
Соответствующий API/subject/:id
). Итак, каждый раз, когда вы создаете новый компонент, просто делайте это:
export default viewGenerator(
{
pageName,
moduleName,
API: API_CELEBRITY,
view: Celebrity
}
)
// 再传入路由中的query对和对应的值即可
<Celebrity id={id}
params={{
id: this.props.match.params.id,
}}
/>
Принцип также прост, используяpageName
, moduleName
пройти черезactionCreator
Создайте соответствующую функцию, которая инициирует запрос, и соответствующее состояние в хранилище избыточности какmapStateToProps
а такжеmapDispatchToProps
Чтобы подключить HOC, этот HOC действует только как декоратор для выполнения той же логики запроса данных этих компонентов дисплея - в соответствии с входящими реквизитами.fetchByParam(params)
(API каррирован, потому что он не изменится, поэтому нужно только предоставитьparams
), этот HOC будетconstructor
вызовет функцию входящего запроса, а затем визуализирует компонент марионетки для обертывания, отделит логику данных от интерфейса, можно просмотреть конкретный кодutils/fetchGenerator
.
Пока что каждый раз, когда вы генерируете новый компонент, вам нужно только написать компонент puppet, соответствующий этому компоненту.Процесс генерации компонентов выглядит следующим образомviewGenerator -> viewDecorator -> (木偶)view
,viewGenerator
Отвечает за генерацию соответствующей функции запроса и состояния,viewDecorator
Отвечает за применение и выполнение логики данных и передачу данных вview
и визуализироватьview
.
Аналогичная операция требуется в редукторе:
let contentReducer = reducerGenerator(
{
pageName,
moduleName
}
)
Можно сгенерировать соответствующий редюсер, а также можно передать пользовательский редюсер, который перезапишет тот же самый.action.type
Например, когда страница тега загружает больше данных, новый редуктор должен объединить вновь запрошенные данные, а не заменить их.
[SUCCESS_ACTION]: (state, action) => {
let payload = action.doesPushBack ?
pushPayload(state.payload, action.payload) :
action.payload
return {
...state,
isLoading: false,
payload
}
}
тайник
После того, как некоторые страницы действительно загружаются один раз, нет необходимости повторно запрашивать (например, страницы фильмов), которые можно было бы использовать здесь.redux-presist
Но попробуйте написать более простое и грубое (рудиментарное) промежуточное ПО, которое будет кэшировать данные, запрошенные URI напрямую.Если вы хотите кэшировать возвращаемое значение URI, вы можете напрямую определить его в действии успешного запроса.cacheKey
а такжеcacheValue
, и добавьте его перед отправкой запросаisCached
Если он закеширован, повторно отправлять запрос не нужно, сразу переходите кcaches
Переходим к получению кеша, где все кеши хранятся в памяти вместо localStorage, весь код промежуточного ПО выглядит следующим образом:
const caches = {}
const hasCachedKey = (cacheKey) => {
return Object.keys(caches).indexOf(cacheKey) >= 0
}
const cacheMiddleware = store => next => action => {
if (typeof action.cacheKey === 'undefined' ||
typeof action.cacheValue === 'undefined') {
next(action)
return
}
if (!hasCachedKey(action.cacheKey)) {
caches[action.cacheKey] = action.cacheValue
}
next(action)
}
const getCache = (cacheKey) => {
if (hasCachedKey) {
return caches[cacheKey]
}
}
const isCached = (cacheKey, cacheDetector = hasCachedKey) => {
return hasCachedKey(cacheKey)
}
export { cacheMiddleware, isCached, getCache }
разное
СДЕЛАТЬ:
- Загрузка первого экрана слишком медленная (полных 500k...), что не имеет большого значения для разделения кода по маршруту, в основном из-за того, что различные сторонние библиотеки слишком велики, хотя они были загружены по требованию. , их еще нужно оптимизировать.
- рендеринг на стороне сервера
- Адаптировать к мобильным устройствам
предварительный просмотр
Дома
Две карусели + пользовательский список, который использует карусельreact-slick
, индикатор номера страницыreact-slick
Не предусмотрено, определите количество страниц в состоянии родительского компонента, будетarrow-prev
а такжеarrow-next
OnClick можно использовать для установки номера страницы. Предварительный просмотр при наведении мыши аналогичен модальному, и позиция может выходить за рамки родительского компонента.createProtal
для достижения, добавлено вdocument.body
на, черезgetBoundingBox
Пусть родительский компонент передает ему позицию для рендеринга.
страница фильма
Страница людей
Таблица лидеров
краткая страница обзора
Содержимое страницы короткого комментария направляется через запрос синтаксического анализа.start
а такжеcount
Чтобы получить данные, инкапсулируйте компонент пагинации, чтобы изменить запрос маршрута для переключения верхней и нижней страниц.
страница длинного отзыва
То же, что и на странице коротких комментариев, но немного больше, чтобы добиться того же эффекта, что и у Douban после расширения длинного содержимого и закрытия исправления столбца внизу, используйтеgetBoundingBox
Чтобы судить, следует ли зафиксировать развернутый в данный момент длинный комментарий и закрыть столбец ——!this.props.isFold && contentRect.top <= innerHeight && contentRect.bottom >= innerHeight - barRectHeight
,можно так судить.Надо заметить,что при прямом нажатии на развернуть тоже требуется тест,потому что судить надо не только при прокрутке.
Страница видео и билетов
Страница категории
Классификация кино и телевидения жестко закодирована, и щелчок вызовет изменение маршрутизации, а затем изменение параметра будет инициировано повторно. Разница между запросом, выдаваемым кнопкой «Загрузить еще», и инициализированным запросом заключается в том, что начало в URI отличается.Например, инициализированный запрос/movie/search?tag=电影&count=20
, первый щелчок, чтобы загрузить больше,/movie/search?tag=电影&start=20
. В этом случае нужно только повторно отправить запрос и добавить данные из второго запроса, отличие от первого в том, что при клике, т.е.ACTION.START
должно быть завершено в соответствующем состоянииcount += 20
(в противном случае, если вы щелкнете два раза подряд, будут выданы два идентичных запроса), а затем настройте редуктор для объединения запрошенных данных, содержащих массив фильмов, с задней частью исходных данных.
страница поиска
Будьте ленивы и используйте поле ввода ant напрямую, но не забудьте указать поле вводаonChange
Добавьте функцию устранения дребезга.
404
3, 2, 1, вернуться на главную страницу
API
API в проектеисточник
Суммировать
Опубликуйте адрес еще раз, можете поставить звездочку, если хотите 😳
Адрес онлайн-просмотра:http://118.24.21.99:5000/
Адрес репозитория GitHub:douban-movie-react
Я пишу его несколько месяцев, но он все еще кажется очень неряшливым.Я надеюсь, что вы можете дать более ценные мнения, и добро пожаловать, чтобы оставить сообщение для обсуждения.