предисловие
В прошлом году я написал приложение для веб-музыки и опубликовал серию статей, чтобы представить процесс разработки.create-react-app
Проекты, построенные официальными лесами,react-scripts
да1.x
версия, в то время как версия реакции16.2.0
, в октябре прошлого годаcreate-react-app
уже опубликовано2.0
Версия,react
Обновлен до декабря прошлого года16.7.0
Итеративное обновление технологий в области переднего плана происходит слишком быстро, и люди часто жалуютсяпожалуйста, не обновляйте,я не могу учиться,я не могу закончить
Быть фронте, вы должны быть готовы учиться в любое время, в противном случае вы будете устранены ⊙﹏⊙∥| °
Пока вы занимаетесь разработкой, вы должны поддерживать активное обучение, будь то в области фронтенда или в области бэкенда, но интервал между изучением новых технологий во фронтенде больше, чем в бэкенде. задний конец. Как специалист по Java, я хорошо понимаю o(╯□╰)o
Введение обновления
create-react-app
И по сей день,create-react-app
Он был обновлен до версии 2.x, в основном для обновления многих инструментов, от которых он зависит.Эти инструменты выпустили новые версии с новыми функциями и улучшениями производительности, такими какbabel7,webpack4,babel7
а такжеwebpack4
Каковы конкретные обновления, оптимизация, которые каждый может пойти, чтобы увидеть информацию.
Дальшеcreate-react-app
Некоторые обновленные точки
- Добавлен препроцессор Sass, модульная поддержка CSS.
- Обновление до Babel7
- Обновление до веб-пакета4
- новыйpreset-env
Чтобы узнать больше об обновлениях, нажмитездесь
react16.3
Поскольку раньше я использовал реакцию 16.2, когда дело доходит до реакции 16.7, мне приходится начинать с версии 16.3.
16.3 добавлено несколько новыхфункция жизненного цикла,context API,createRef APIа такжеforwardRef API, две новые функции жизненного циклаgetDerivedStateFromProps
а такжеgetSnapshotBeforeUpdate
В основном для замены предыдущегоcomponentWillMount
, componentWillReceiveProps
а такжеcomponentWillUpdate
, цель состоит в том, чтобы поддержатьerror boundariesи предстоящийasync rendering mode(асинхронный рендеринг). когда используешьasync rendering modeКогда первоначальный рендеринг прерывается, прерванное поведение обработки ошибок может привести к утечкам памяти, и использованиеcomponentWillMount
, componentWillReceiveProps
а такжеcomponentWillUpdate
увеличивает вероятность таких проблем
В предыдущей версии было два способа получить DOM или компонент: первый — дать ссылку, указать имя, а затем использовать refs.name или ReactDOM.findDOMNode(name), чтобы получить его, а второй — использовать ref callback, чтобы дать ref функцию обратного вызова. В начале я использовал первый, а потом перешел на коллбек ref, который сейчас официально объявлен устаревшим, рекомендуется использовать коллбек ref, так как у первого есть нескольконедостаток, использовать обратный вызов ref немного проблематично, поэтому предоставляется официальная новая операцияcreateRef API
Как получить dom при использовании функционального компонента,forwardRef APIПозволяет использовать функциональные компоненты и передавать ref в подкомпоненты, так что вы можете легко получить дом в подкомпоненте
Для получения дополнительной информации нажмитездесь
react16.6
Мне до сих пор очень нравится обновление этой версии, официальное наконец-то поддерживает то же, что и vueCode Splittingохватывать
Использование в реакцииCode Splitting, беда в том, чтобы самому написать компонент ленивой загрузки, или просто использовать стороннюю библиотеку. Теперь официально добавленоReact.lazyа такжеSuspenseиспользуется для поддержкиCode Splitting
import React, {lazy, Suspense} from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function MyComponent() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
Примечание. React.lazy и Suspense в настоящее время не поддерживают рендеринг на стороне сервера, а рендеринг на стороне сервера официально рекомендуется.Loadable Components
В компоненте класса есть функция жизненного циклаshouldComponentUpdate
Используется, чтобы сообщить компоненту, следует ли отображать, наследоватьReact.component
, вы можете сами пересоздать этот метод, чтобы судить и решать, как рендерить, наследоватьReact.PureComponent
, который уже реализован по умолчаниюshouldComponentUpdate
, он поверхностно сравнивает свойства и состояние и отображает только в том случае, если они не равны и не могут быть переписаны вами самостоятельно.shouldComponentUpdate
. Для функциональных компонентов у него нет такой функции, новой в этой версииReact.memo, так что функциональная компонента имеет иReact.PureComponent
та же функция
Добавлено в 16.3context API, при использовании контекста вам нужно использоватьConsumerкак ниже
const ThemeContext = React.createContext('light');
...
class MyComponent extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{theme => /* 使用context */}
</ThemeContext.Consumer>
);
}
}
Теперь вы можете использовать более удобныйstatic contextType
const ThemeContext = React.createContext('light');
...
class MyComponent extends React.Component {
render() {
let value = this.context;
/* 使用context */
}
}
MyComponent.contextType = ThemeContext;
Для получения дополнительной информации нажмитездесь
Обновить
Это обновление основано наисходный код
Перед запуском внесите некоторые изменения в каталог компонентов, используйте обычное имя каталога для хранения соответствующих компонентов, создайте новый каталог представлений, переместите компоненты из каталога компонентов в каталог представлений, а затем переместите компоненты из общего каталога в каталог компонентов
Изменить настройку
Начните обновление сейчас, будетreact-scripts
обновитесь до2.1.3
,react
обновитесь до16.7.0
npm install --save --save-exact react-scripts@2.1.3
npm install react@16.7.0 react-dom@16.7.0
Подождите минутку
бегатьnpm run start
Было обнаружено, что было сообщено об ошибке, основанной наreact-scripts
Версия 1.x настроила скрипт,react-scripts
Конфигурация сильно изменилась в версии 2.x, в результате чего оригинальный пользовательский скрипт использовать нельзя. Кроме того, поиск способа изменить конфигурацию занимает слишком много времени.Если вы знакомы с конфигурацией веб-пакета, запустите встроенныйeject
Извлеките файл конфигурации или найдите третью сторонуcustomize-cra,в этом случае нужно подробнее узнать о способе настройки.Если автор его не поддержит,реакт-скрипты сильно обновятся,и он не сможет вовремя адаптироваться к новой версии.Вот выбираю насилие и извлеките файл конфигурации.
let's do it
бегатьnpm run eject
scripts
Каталог уже есть в проекте (скрипт, написанный кастомной конфигурацией ранее), удалите его, запустите снова, подождите некоторое время и добавьте после выполнения множество зависимостей в package.json, а также немного postcss, babel и конфигурации eslint
wait
пакет.jsonscripts
Скрипт не обновлен, обратитесь к другимnpm run eject
Послеscripts
, а затем измените его следующим образом
"scripts": {
"start": "npm run dev",
"dev": "node scripts/start.js",
"build": "node scripts/build.js"
}
После извлечения появляются зависимости, связанные с разработкойdependencies
, а затем поместите связанные с разработкой зависимости вdevDependencies
И удалить зависимости, связанные с шуткой
бегатьnpm run dev
Подскажите, добавить ли конфигурацию списка браузеров, введите Y и нажмите Enter, тогда появится следующая ошибка, и стиль страницы нарушен
Module not found: Can't resolve '@/api/config'
В настоящее время псевдонимы не настроены@
а такжеstylus
Откройте WebPack.config.js в каталоге config, чтобы найти конфигурацию.resolveпод узломalias, добавьте псевдоним
config/webpack.config.js
module.exports = function(webpackEnv) {
...
return {
...
resolve: {
...
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
'@': path.join(__dirname, '..', "src")
},
}
}
...
}
оalias
,использоватьalias
Он может сократить время упаковки вебпака, но он не дружественен к IDE или инструментам, и его нельзя перепрыгнуть, очень неудобно просматривать код. Если выдержите, настройте, если не выдержите, напишите относительный путь при импорте, используйте его здесь.alias
Для демонстрации окончательный исходный код не используетсяalias
Тогда есть стилус.Официал поддерживает только sass.Может быть, что многие используют sass.Вы должны поддерживать еще несколько.≡(▔﹏▔)≡
Прежде чем использовать CSS в оригинальном виде, есть серьезная проблема, то есть будет проблема конфликтов CSS.Решений таких проблем много, напримерstyled-compoents,styled-jsxа такжеcss modules, первые два просто альтернативные,css modulesОн не подрывает оригинальный CSS, но также поддерживает CSS-процессоры, не зависит от фреймворка и может использоваться не только в React, но и в Vue. Чтобы включить модули css в веб-пакете, просто дайтеcss-loader
Одинmodules
Варианта достаточно.В проекте иногда css-файлы будут использовать css-модули, а некоторые нет.Для этого требованияresct-scripts
это так хорошо подходит
config/webpack.config.js
...
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function(webpackEnv) {
...
return {
...
module: {
strictExportPresence: true,
rules: [
...,
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}),
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader'
),
sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
...
]
}
}
}
В приведенной выше конфигурацииgetStyleLoaders
Это функция, которая возвращает конфигурацию загрузчика стилей и возвращает различные конфигурации в соответствии с переданными параметрами. В правилах начните с.css
или.(scss|sass)
В конце используйте обычный загрузчик, с.moduels.css
или.module.(scss|sass)
Включите css-модули в конце. Если вам нужно использовать модули css, добавьте .module перед суффиксом после имени файла.Соглашение об именовании файлов стилей в реакции совпадает с именем файла компонента, а компоненты и стили размещаются в одном каталоге.Если есть представляет собой файл с именем RecommendList.js , тогда файл стиля называется рекомендацией-список.модуль.css, и при объединении он становится следующим
Как может быть такой длинный хвост
Как убрать этот длинный хвост не влияя на использование css модулей, используем конфигурацию webpackRule.oneOfа такжеRule.resourceQuery
существуетwebpack.config.js
Добавьте конфигурацию стилуса в
config/webpack.config.js
...
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const stylusRegex = /\.(styl|stylus)$/;
// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function(webpackEnv) {
...
return {
...
module: {
strictExportPresence: true,
rules: [
...,
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
{
test: stylusRegex,
oneOf: [
{
// Match *.styl?module
resourceQuery: /module/,
use: getStyleLoaders(
{
camelCase: true,
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'stylus-loader'
)
},
{
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'stylus-loader'
)
}
]
},
...
]
}
}
}
oneOfИспользуется для получения одного из первых правил сопоставления,resourceQueryсоответствоватьimport style from 'xxx.styl?module'
, поэтому вам нужно использовать модуль css и добавить его после?module
, вам не нужно напрямуюimport 'xxx.styl'
,camelCase: true
Это параметр конфигурации в css-loader, что означает включение горбового именования.Чтобы использовать css-модули, вам нужно получить имя скомпилированного стиля через атрибут объекта.Если имя стиля разделено тире, вам нужно использовать селектор атрибутов, такой как style['css-name '], после включения верблюжьего регистра имен вы можете style.cssName
На данный момент стиль страницы нормальный, но css модули не использовались, а потом все css нужно поменять на css модули, что является утомительным процессом, возьмем в качестве примера компонент Recommend
сначала импортировать стиль
import style from "./recommend.styl?module"
Затем получите стиль через объект стиля
class Recommend extends React.Component {
...
render() {
return (
<div className="music-recommend">
<Scroll refresh={this.state.refreshScroll}
onScroll={(e) => {
/* 检查懒加载组件是否出现在视图中,如果出现就加载组件 */
forceCheck();
}}>
<div>
<div className="slider-container">
<div className="swiper-wrapper">
{
this.state.sliderList.map(slider => {
return (
<div className="swiper-slide" key={slider.id}>
<div className="slider-nav" onClick={this.toLink(slider.linkUrl)}>
<img src={slider.picUrl} width="100%" height="100%" alt="推荐" />
</div>
</div>
);
})
}
</div>
<div className="swiper-pagination"></div>
</div>
<div className={style.albumContainer} style={this.state.loading === true ? { display: "none" } : {}}>
<h1 className={`${style.title} skin-recommend-title`}>最新专辑</h1>
<div className={style.albumList}>
{albums}
</div>
</div>
</div>
</Scroll>
...
</div>
);
}
}
Некоторые из них являются фиксированными примерами имен плагинов, а некоторые являются фиксированными примерами имен, используемых для переключения скинов.Они не могут использовать модули css, поэтому вам необходимо использовать их в настоящее время.:global()
, указывающий глобальные стили, css-loader не будет обрабатывать имена стилей, такие как
:global(.music-recommend)
width: 100%
height: 100%
:global(.slider-container)
height: 160px
position: relative
:global(.slider-nav)
display: block
width: 100%
height: 100%
:global(.swiper-pagination-bullet-active)
background-color: #DDDDDD
Из-за добавления eslint появилось следующее предупреждение
./src/components/recommend/Recommend.js
Line 131: The href attribute is required for an anchor to be keyboard accessible. Provide a valid, navigable address as the href value. If you cannot provide an href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md jsx-a11y/anchor-is-valid
./src/components/singer/SingerList.js
Line 153: The href attribute is required for an anchor to be keyboard accessible. Provide a valid, navigable address as the href value. If you cannot provide an href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md jsx-a11y/anchor-is-valid
Line 159: The href attribute is required for an anchor to be keyboard accessible. Provide a valid, navigable address as the href value. If you cannot provide an href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md jsx-a11y/anchor-is-valid
Это правило предусматривает, что тег a должен указывать допустимый href, просто замените тег a чем-то другим.
ref
Я уже говорил, что добавил react16.3createRef API, затем замените обратный вызов ref этим новым API. Возьмем компонент Альбом в качестве примера.
существуетconstructor
используется вReact.createRef()
инициализация
src/views/album/Album.js
class Album extends React.Component {
constructor(props) {
super(props);
// React 16.3 or higher
this.albumBgRef = React.createRef();
this.albumContainerRef = React.createRef();
this.albumFixedBgRef = React.createRef();
this.playButtonWrapperRef = React.createRef();
this.musicalNoteRef = React.createRef();
}
...
}
Используйте ref, чтобы указать инициализированное значение
render() {
...
return (
<CSSTransition in={this.state.show} timeout={300} classNames="translate">
<div className="music-album">
<Header title={album.name}></Header>
<div style={{ position: "relative" }}>
<div ref={this.albumBgRef} className={style.albumImg} style={imgStyle}>
<div className={style.filter}></div>
</div>
<div ref={this.albumFixedBgRef} className={style.albumImg + " " + style.fixed} style={imgStyle}>
<div className={style.filter}></div>
</div>
<div className={style.playWrapper} ref={this.playButtonWrapperRef}>
<div className={style.playButton} onClick={this.playAll}>
<i className="icon-play"></i>
<span>播放全部</span>
</div>
</div>
</div>
<div ref={this.albumContainerRef} className={style.albumContainer}>
<div className={style.albumScroll} style={this.state.loading === true ? { display: "none" } : {}}>
<Scroll refresh={this.state.refreshScroll} onScroll={this.scroll}>
<div className={`${style.albumWrapper} skin-detail-wrapper`}>
...
</div>
</Scroll>
</div>
<Loading title="正在加载..." show={this.state.loading} />
</div>
<MusicalNote ref={this.musicalNoteRef}/>
</div>
</CSSTransition>
);
}
пройти черезcurrent
свойство получает экземпляр dom или компонента,
scroll = ({ y }) => {
let albumBgDOM = this.albumBgRef.current;
let albumFixedBgDOM = this.albumFixedBgRef.current;
let playButtonWrapperDOM = this.playButtonWrapperRef.current;
if (y < 0) {
if (Math.abs(y) + 55 > albumBgDOM.offsetHeight) {
albumFixedBgDOM.style.display = "block";
} else {
albumFixedBgDOM.style.display = "none";
}
} else {
let transform = `scale(${1 + y * 0.004}, ${1 + y * 0.004})`;
albumBgDOM.style.webkitTransform = transform;
albumBgDOM.style.transform = transform;
playButtonWrapperDOM.style.marginTop = `${y}px`;
}
}
selectSong(song) {
return (e) => {
this.props.setSongs([song]);
this.props.changeCurrentSong(song);
this.musicalNoteRef.current.startAnimation({
x: e.nativeEvent.clientX,
y: e.nativeEvent.clientY
});
};
}
Когда ref используется в теге html, current — это ссылка на элемент dom, а когда ref используется в компоненте, current — это экземпляр после монтирования компонента. После того, как компонент смонтирован, текущий будет указывать на элемент dom или экземпляр компонента, после выгрузки компоненту будет присвоено значение null, а ссылка будет обновлена до обновления компонента.
Code Splitting
Разделение кода может уменьшить размер js-файлов, ускорить передачу файлов и загрузку по требованию.Теперь react официально предоставляетReact.lazy
а такжеSuspense
Чтобы поддержать разделение кода, нажмите, чтобы узнать о них подробнее.здесь
Раньше маршруты прописывались прямо в компонентах, но теперь маршруты дизассемблированы, и маршруты единообразно настраиваются в конфигурационном файле, что удобно для централизованного управления
Добавьте каталог маршрутизатора в каталог src, а затем создайте новый.router.js
import React, { lazy, Suspense } from "react"
let RecommendComponent = lazy(() => import("../views/recommend/Recommend"));
const Recommend = (props) => {
return (
<Suspense fallback={null}>
<RecommendComponent {...props} />
</Suspense>
)
}
let AlbumComponent = lazy(() => import("../containers/Album"));
const Album = (props) => {
return (
<Suspense fallback={null}>
<AlbumComponent {...props} />
</Suspense>
)
}
...
const router = [
{
path: "/recommend",
component: Recommend,
routes: [
{
path: "/recommend/:id",
component: Album
}
]
},
...
];
export default router
В использованииlazy
Необходимо использовать внешний слой компонента, обернутого методомSuspenseупаковать и указатьfallback
,fallback
Отрисовывается при загрузке ресурса, соответствующего компоненту, здесь ничего не отрисовывается, укажитеnull. В официальном примере вRouteИспользуется только один внешний слойSuspense,посмотреть здесь, здесь будут подмаршруты, если они используются в самом внешнем слоеSuspense, Рендеринг дочернего элемента при отложенной загрузке резервного маршрута заменит содержимое компонентов представления родительского узла, что приведет к потере содержимого родительского компонента страницы, после завершения рендеринга представления маршрута подсборки появится все содержимое середине процесса перепрошивки, поэтому лучше всего в каждом компоненте представления маршрутизации сSuspenseпакет. тебе следуетpropsВручную передайте его компоненту ленивой загрузки, чтобы вы могли получить реактивный маршрутизаторmatch,historyЖдать
Использование апелляцииSuspenseВ части есть дублирующийся код, давайте трансформируем его компонентами высокого порядка
const withSuspense = (Component) => {
return (props) => (
<Suspense fallback={null}>
<Component {...props} />
</Suspense>
);
}
const Recommend = withSuspense(lazy(() => import("../views/recommend/Recommend")));
const Album = withSuspense(lazy(() => import("../containers/Album")));
const router = [
{
path: "/recommend",
component: Recommend,
routes: [
{
path: "/recommend/:id",
component: Album
}
]
},
...
];
Далее используйте эти конфигурации
Сначала поместите маршрут первого уровня, поместите его вApp
компонент, нормальная работа такова<Route path="/recommend" component={Recommend} />
, с помощьюreact-router-config, не нужно писать вручную, просто позвонитеrenderRoutes
метод, вы можете передать в конфигурации маршрутизации
Примечание. Конфигурация маршрутизации должна использовать несколько фиксированных свойств, большинство иRouteКомпонентные реквизиты одинаковы
Установите react-router-config, где версия react-router ниже, а также используется более низкая версия react-router-config
npm install react-router-config@1.0.0-beta.4
src/views/App.js
import { renderRoutes } from "react-router-config"
import router from "../router"
class App extends React.Component {
...
render() {
return (
<Router>
...
<div className={style.musicView}>
{/*
Switch组件用来选择最近的一个路由,否则没有指定path的路由也会显示
Redirect重定向到列表页
*/}
<Switch>
<Redirect from="/" to="/recommend" exact />
{/* 渲染 Route */}
{ renderRoutes(router) }
</Switch>
</div>
</Router>
);
}
}
RedirectОн используется для перенаправления и должен быть размещен вверху, иначе он не сработает.renderRoutes
Компонент Route будет создан в соответствии с конфигурацией, аналогичной<Route path="/recommend" component={Recommend} />
Затем используйте конфигурацию подмаршрута в компоненте «Рекомендовать».
src/views/recommend/Recommend.js
import { renderRoutes } from "react-router-config"
class Recommend extends React.Component {
render() {
let { route } = this.props;
return (
<div className="music-recommend">
...
<Loading title="正在加载..." show={this.state.loading} />
{ renderRoutes(route.routes) }
</div>
);
}
}
передачаrenderRoutes
После этого конфигурация маршрутизации текущего уровня будет передана вroute
, то черезroute.routes
Получите конфигурацию подмаршрутизации и т. д. для фейдеров и сабвуферов.
Исходный код renderRoutesпосмотреть здесь
Существуют и другие маршруты компонентов, которые необходимо изменить, и все они могут использоваться таким образом.
предварительный просмотр
Адрес предварительного просмотра:dxx.github.io/mango-music
QR код:
исходный код
Я чувствую себя хорошо, дай мне звезду, спасибо ~