Next.js: Запрос функции: поддержка Basepath

Созданный на 21 авг. 2018  ·  73Комментарии  ·  Источник: vercel/next.js

Запрос функции

Связан ли ваш запрос функции с проблемой? Пожалуйста, опишите.

Несколько зон - отличная функция, которая позволяет запускать несколько приложений next.js в одном домене, но не позволяет определять базовый путь, который будет приниматься всеми частями next.js. Поскольку сейчас мы не можем создавать приложения с пространством имен, невозможно иметь одинаковые имена для страниц в разных приложениях.

Опишите решение, которое вы хотите

Я хочу иметь возможность настроить basepath в файле next.config.js. Благодаря этой конфигурации все части next.js (маршрутизатор, ссылка, статические ресурсы и т. Д.) Будут знать базовый путь и автоматически сгенерировать и сопоставить с правильными путями.

Опишите альтернативы, которые вы рассмотрели

Одна альтернатива - вложить все желаемые страницы в папку, соответствующую базовому пути. Это решает только одну небольшую проблему с маршрутизацией и довольно неприятно, потому что большинство моих базовых путей не являются одноуровневыми.
Вторая альтернатива - настроить прокси таким образом, чтобы базовый путь автоматически удалялся до того, как запрос поступает в приложение next.js, а также реализовать настраиваемый компонент Link, который автоматически добавляет базовый путь ко всем ссылкам. Я просто не хочу поддерживать кастомный форк next.js. На мой взгляд, это не имеет смысла.

Дополнительный контекст

Решение assetPrefix позволяет нам определять разные префиксы для каждого приложения. Но насколько я знаю, это работает только с разными хостами.

пример с зонами

module.exports = {
  assetPrefix: NOW_URL ? `https://${alias}` : 'http://localhost:4000'
}

Если я добавлю к нему базовый путь, все не удастся

module.exports = {
  assetPrefix: NOW_URL ? `https://${alias}/account` : 'http://localhost:4000/account'
}

screen shot 2018-08-21 at 10 47 08

На мой взгляд, мы должны разделить его на 2 переменные:

module.exports = {
  assetPrefix: NOW_URL ? `https://${alias}` : 'http://localhost:4000',
  basepath: '/account'
}

Связанные вопросы

story needs investigation

Самый полезный комментарий

Любое обновление по этому поводу ... в прошлом году примерно в это время я столкнулся с этой проблемой. Теперь, год спустя, я работаю над новым приложением и должен делать те же обходные пути, что и в прошлом году ... немного тревожно для «готовой к производству» реакции fw. Базовые пути должны быть ванильной функцией.

Я не уверен, чего вы ожидаете, публикуя это.

Моя команда (5 человек) работает над Next.js на постоянной основе, и мы работаем над многими функциями одновременно. В прошлом году мы работали над этим:

Эффективно делает приложения Next.js (новые и существующие) значительно меньше, быстрее и более масштабируемыми.

Если вы хотите проголосовать за функцию, вы можете это сделать. используйте функцию 👍 на начальной нити.

Я определенно согласен с тем, что basePath должна быть встроенной функцией. Это уже в дорожной карте, и я даже написал первоначальный PR, который вы могли увидеть, прочитав в ветке.

Вот PR: https://github.com/zeit/next.js/pull/9872

Не стесняйтесь обращаться по адресу [email protected], если вы хотите внести финансовый вклад в реализацию этой функции.

Все 73 Комментарий

cc @jxnblk

Копия @alexindigo @DullReferenceException

Хотел бы получить ваш отзыв

Поигравшись с кодом, я понял, что было бы намного проще разделить assetPrefix на несколько частей:

module.exports = {
  host: NOW_URL ? `https://${alias}` : 'http://localhost:3000',
  basePath: '/account',
}

Мы все еще можем сохранить внутреннюю переменную assetPrefix , но пользователь должен более точно определить, что ему нужно.

Для части актива действительно нормально предоставить эти две переменные вместе.
Для маршрутизации и т.д. они нужны нам отдельно.
Или, может быть, мы даже можем предоставить его вместе в файле конфигурации, а затем разделить его в кодовой базе next.js. Боюсь, что в данном случае assetPrefix - неправильное имя.

В качестве побочного эффекта это также приводит к меньшему количеству изменений кода.
Это довольно очевидно, если сравнить эти два PR:
https://github.com/panter/next.js/pull/2 (разделить)
https://github.com/panter/next.js/pull/1 (пройти оба)

На мой взгляд, они должны быть отдельными, причина в том, что это не ломается и более гибко хранить assetPrefix и иметь basePath отдельно.

Тогда assetPrefix правильное имя? Обе переменные на самом деле являются префиксом, верно?

assetPrefix предназначен для ресурсов, например, для пакетов страниц. basePath будет для маршрутизатора.

Как это должно работать:

  • если assetPrefix определено, используйте assetPrefix для загрузки пакетов, не трогайте маршрутизатор (текущее поведение)
  • если указаны assetPrefix и basePath , используйте assetPrefix для загрузки пакетов, добавьте basePath в маршрутизатор
  • если assetPrefix не определено, а basePath равно, используйте basePath для загрузки пакетов и добавьте basePath в маршрутизатор
  • если ни assetPrefix ни basePath не определены, мы не делаем ничего другого (текущее поведение, когда assetPrefix не указано)

Копия @alexindigo @DullReferenceException @ 3rd-Eden

Не могли бы вы дать отзыв о предложении выше: https://github.com/zeit/next.js/issues/4998#issuecomment -414978297

@tomaswitek Не уверен, что именно не сработало для вас с текущим assetPrefix , это префикс ресурса, который мы используем в производстве: "assetPrefix":"https://static.trulia-cdn.com/javascript" и он работает должным образом.

И вообще, мы используем несколько зон (мы называем их островами) в одном домене, и нам никогда не приходило в голову «basePathing» каждого острова, поскольку это усложнило бы взаимодействие между островами. Позвольте мне подробнее остановиться на этом:

Итак, у нас есть два острова A и B , и основная идея с ними - прозрачность для пользователей, которые перемещаются с острова на остров в рамках своего опыта работы с одним веб-сайтом. Значит, между островами должно быть сообщение. Тогда есть проблема развертывания и проблема приложения.

  1. Проблема развертывания и проблема приложения - приложение не знает, где его можно развернуть, оно просто знает, как обрабатывать входящие HTTP-запросы - оно установило маршруты, на которые может отвечать.
    Когда он где-то развернут - это могут быть разные домены, разные порты и да, теоретически это может быть другой basePath, который будет прозрачным для приложения через прокси или другие средства.

  2. Перекрестные связи между островами - чтобы сохранить дух островов как отдельных развертываемых объектов, не должно быть никаких внутренних утечек знаний о реализации между разными островами.
    Таким образом, лучший способ для островов ссылаться на страницы друг друга - это экспортировать доступные маршруты для других островов (_и в мире nextjs, похоже, предпочтительнее использовать пользовательские компоненты типа <IslandALink> путь_).
    Пока все просто - предполагается, что все острова используют один и тот же домен и имеют свой набор абсолютных путей ( /path1 , path2 и т. Д.). Таким образом, второй остров импортирует этот список путей и полагается на его стабильность. В то же время для каждого острова довольно минимальное требование, чтобы их пути были обратно совместимы (что в любом случае хорошо в сети) :)

Когда мы добавляем специфический для развертывания basePath, мы автоматически увеличиваем сложность всей системы - должен ли каждый остров знать (и, возможно, диктовать) свой собственный basePath развертывания? Тогда чем это отличается от того, как это работает сейчас? Или остров А должен быть независимым от пути развертывания? Тогда как остров B найдет развернутый остров A, если он знает только то, что остров A знает о себе? Или вам нужно будет предоставить basePath для всех развернутых островов для всех остальных островов? А с современным подходом к развертыванию, это означает повторное развертывание всех островов, когда вам нужно добавить новый.

Или как вы представляли себе эту часть истории?

Спасибо.

^ он был написан до утреннего кофе, поэтому, пожалуйста, дайте мне знать, если вам нужно более связное объяснение каких-либо его частей. :)

Прежде всего, спасибо, ребята, что вы нашли время изучить мою проблему.

@timneutkens Да assetPrefix имеет приоритет над basePath , это именно то, что мы обсуждали в начале. После того, как я увидел, сколько файлов мне нужно изменить, я подумал, что второй способ будет чище. Но я вернусь к первому решению. Давайте держать его полностью отдельно, никаких проблем. Я просто громко думал.

@alexindigo Спасибо за подробный ответ. Позвольте мне попытаться ответить на ваши вопросы 😏

Не уверен, что именно у вас не сработало с текущим assetPrefix

У меня тут две проблемы:

  1. Я не могу работать с несколькими доменами или поддоменами в текущем проекте. (Ограничения домена и отсутствие подстановочного SSL-сертификата)
  2. Текущая реализация assetPrefix в одном домене требует дополнительных настроек маршрутизации прокси, статических файлов и т. Д. Мы могли бы уменьшить эти настройки, введя basePath . Это ничего не тормозит и не увеличивает сложность, потому что вам не нужно предоставлять basePath как уже упоминалось в @timneutkens .

приложение не знает, где его можно развернуть

У нас, конечно же, одна цель! Мы динамически определяем assetPrefixes в имеющемся у нас решении. Он предоставляется через заголовки запроса через прокси.

Тогда чем это отличается от того, как это работает сейчас?

Маршрутизатор будет знать contextPath и уменьшит объем настраиваемого кода.

должен ли каждый остров знать (и, возможно, диктовать) свой базовый путь развертывания? Или остров А должен быть независимым от пути развертывания?

Так не должно быть. Здесь у разработчика должна быть свобода. Должна быть возможность предоставлять basePath динамически так же, как assetPrefix.

Тогда как остров B найдет развернутый остров A, если он знает только то, что остров A знает о себе? Или вам нужно будет предоставить basePath для всех развернутых островов для всех остальных островов? А с современным подходом к развертыванию, это означает повторное развертывание всех островов, когда вам нужно добавить новый.

Возможно, вы также могли бы добавить basePath в экспорт маршрутов. Я не знаю. Я не говорю, что переменная basePath важна для каждого варианта использования. Похоже, это не лучшее решение для вас. Но это совершенно нормально. Дело в том, что вы все еще можете использовать только assetPrefix и для ваших островов ничего не изменится. Похоже, у вас все равно есть собственная маршрутизация. Перекрестные связи между зонами даже не важны для нашего проекта, наши зоны действительно независимы и изолированы друг от друга.

А с современным подходом к развертыванию, это означает повторное развертывание всех островов, когда вам нужно добавить новый.

Я не вижу причины, почему. Я даже могу представить, что у некоторых зон есть basePaths, а у некоторых нет. И, возможно, некоторые приложения будут использовать конфигурацию basePath даже без настройки нескольких зон.

@alexindigo, не могли бы вы предоставить нам два реальных URL-адреса острова, которые отображаются в next.js, чтобы я мог увидеть его в действии? Я попытался найти его, но не смог найти страницу в вашем домене с запросами _next 😄
Все ли ваши острова имеют одинаковую конфигурацию?
"assetPrefix":"https://static.trulia-cdn.com/javascript"

@tomaswitek

Я не могу работать с несколькими доменами или поддоменами в текущем проекте. (Ограничения домена и отсутствие подстановочного SSL-сертификата)

О, значит, вы не используете CDN в классическом смысле, а полагаетесь на ресурсы, извлекаемые напрямую из каждого приложения? Понимаю.

Текущая реализация assetPrefix в одном домене требует дополнительных настроек маршрутизации прокси, статических файлов и т. Д. Мы могли бы уменьшить эти настройки, введя basePath. Это ничего не тормозит и не увеличивает сложность, потому что вам не нужно указывать basePath, как уже упоминалось в

Между прочим, это не было «нет, не добавляйте эту функцию» :) Это было больше похоже на «Возможно, мы можем подумать об этом подходе более целостно» :)

Так не должно быть. Здесь у разработчика должна быть свобода. Должна быть возможность предоставлять basePath динамически так же, как assetPrefix.

Да. Это работает только тогда, когда между островами нет связи. И похоже, что это ваш вариант использования. В то же время мне трудно понять, что делает их островами, а не просто кучей отдельных приложений, если они на 100% независимы? :)

Возможно, вы также могли бы добавить basePath в экспорт маршрутов.

Я не понимаю, как это можно сделать (легко), поскольку экспорт маршрутов происходит во время сборки, а basePath определяется во время развертывания, и может быть более одного развертывания одного и того же артефакта кода (stage, preprod, prod, тестирование env и т. д.).


Все ли ваши острова имеют одинаковую конфигурацию?
"assetPrefix": " https://static.trulia-cdn.com/javascript "

Да, все острова делятся своими активами, поскольку next выполняет хеширование контента, это не только не проблема, но и очень выгодно. (Мы извлекаем собранные активы из каждого артефакта и публикуем в CDN во время развертывания).

Таким образом, у нас есть только «обычные html-запросы» к нашим серверам приложений, поэтому я не увижу никаких путей «_next» на trulia.com.

Что касается примеров островов:

Наш свежий, совершенно новый остров - страница районов - https://www.trulia.com/n/ca/san-francisco/pacific-heights/81571 (и вы можете найти больше их здесь: http://www.trulia. com / окрестности)
Этот остров отвечает за все пути /n/* .

И еще один остров - это наша страница входа - https://login.trulia.com/login - это выглядит как другой домен, но на самом деле это не так, это выглядит так по разным причинам, но технически это то же развертывание. :)
И этот остров обрабатывает URL-адреса типа /login , /signup .

Дайте мне знать, если у вас возникнут вопросы.

@alexindigo большое спасибо за ваши примеры.
У меня есть несколько вопросов после анализа примеров 😄

Вы по-прежнему выполняете серверный рендеринг для каждого острова, но вы пытаетесь извлечь как можно больше ресурсов в общий CDN, верно?

Не могли бы вы подробнее описать, что именно происходит при вызове https://www.trulia.com/n/ca/san-francisco/pacific-heights/81571 ? Ваш прокси знает, что /n обозначает обзор окрестностей, и перенаправляет его на нужный остров? Это как-то влияет на запрос до того, как он прибудет на остров?

Используете ли вы встроенную маршрутизацию из следующего внутри острова или у вас есть индивидуальное решение?
Я хотел проверить маршрут на вашем острове. К сожалению, Neighborhood overview имеет более или менее простую модальную навигацию без изменения URL-адреса. В Login, похоже, есть полностью индивидуальное решение.

Надеюсь, я отвечу на все ваши вопросы в этом комментарии 😏

Между прочим, это не было «нет, не добавляйте эту функцию» :) Это было больше похоже на «Возможно, мы можем подумать об этом подходе более целостно» :)

Конечно, было бы здорово найти решение, в котором мне не нужно трогать next.js 😏

Да. Это работает только тогда, когда между островами нет связи. И похоже, что это ваш вариант использования. В то же время мне трудно понять, что делает их островами, а не просто кучей отдельных приложений, если они на 100% независимы? :)

Я никогда не писал и не говорил, что ищу «островное» решение. У меня только что был чат с @timneutkens, где я описал свою проблему, и Тим ответил, что в основном next.js не поддерживает базовые пути. И немного погуглив, я понял, что не единственный, кто его ищет. Так что я подумал, что могу внести небольшой вклад. После этого Тим связался с вами, чтобы оставить отзыв, и я очень благодарен за ваш отзыв.

Я не понимаю, как это можно сделать (легко), поскольку экспорт маршрутов происходит во время сборки, а basePath определяется во время развертывания, и может быть более одного развертывания одного и того же артефакта кода (stage, preprod, prod, тестирование env и т. д.).

Что ж, если вы хотите экспортировать маршруты во время сборки и сделать их доступными для других островов, то, вероятно, единственный простой способ - это жестко закодировать basePath в конфигурации. Я понимаю вашу точку зрения. С другой стороны, действительно ли это такая проблема? Вы по-прежнему можете развернуть приложение в разных доменах и портах, и вы можете использовать один и тот же basePath для каждого env.

Доброе утро @tomaswitek :)

По моему опыту работы с функциональностью basePath, она очень обманчива по своей сложности, и обычно лучше реализовывать такие вещи, не торопясь с одной конкретной проблемой,
но глядя на это под разными углами. Подобно тому, как вы подошли бы к глубокому слиянию - наметьте несколько вариантов использования и посмотрите, как (и если) все они подпадают под один зонтик. Поскольку наличие несовместимых функций между (даже основными) версиями фреймворка очень раздражает :)

Вы по-прежнему можете развернуть приложение в разных доменах и портах, и вы можете использовать один и тот же basePath для каждого env.

Похоже, вы были бы в порядке с решением, в котором этот "basePath" является частью вашего кода маршрутизации, чем-то, что вы упомянули в начале - например, подпапкой внутри каталога pages (кстати, такой подход будет сигналом для выбранных разработчиков basePath очень хорошо). Но единственное, что вас остановило, это то, что внутренний путь nextjs для ресурсов _next не настраивается.

И это звучит как более узкая проблема, которую мы можем решить с менее долгосрочными побочными эффектами.

И это может продвинуть нас еще дальше, например, если мы сможем настроить assetPath для каждого актива (например, с какой-то картой next.config) - это позволит нам разделять ресурсы между приложениями, что улучшит производительность и другие вещи.

И для этой функции есть открытый пиар. ;) / cc @timneutkens звучит так, будто пора вернуться к этому щенку. :)

Если вы не собираетесь добавлять это в ближайшее время, можем ли мы добавить в файл readme пример server.js на основе express, который делает это и работает? Я пробовал несколько, которые постоянно возникали в этих проблемах, но не смог заставить их работать. Благодарю.

Привет @ccarse, у меня есть рабочий форк, который мы уже используем в производстве: https://github.com/panter/next.js/pull/2
Я также готов потратить время, чтобы открыть PR для этой функции.
@timneutkens @alexindigo есть ли другой способ решить эту проблему?
Если нам не нужна конфигурация basePath , не могли бы вы дать нам минимальный пример, используя assetPath ?

Моя компания тоже сталкивается с этим.

Мы постепенно принимаем устаревшее приложение, раздел за разделом, и заменяем его Next.js.

В качестве упрощенного примера:

| URL | Приложение |
| --- | --- |
| example.com | наследие |
| example.com/shop | следующий |
| example.com/search | наследие |
| example.com/members | следующий |

Это означает, что мы хотим, чтобы в каждом приложении Next.js все было префиксом ... Страницы, маршруты, ресурсы и т. Д.

Также стоит отметить, что мы не используем Now, поэтому мы не можем воспользоваться маршрутизацией now.json . У нас есть собственный балансировщик нагрузки, который находится перед всем доменом и затем маршрутизирует трафик на основе подпути.

Мы также используем настраиваемый сервер (hapi), поэтому было бы неплохо, если бы мы могли использовать все, что здесь создается, и на настраиваемом сервере.

Может быть, есть какая-то комбинация настроек now.config.json или какое-то использование микропрокси, которое мы можем использовать для выполнения того же самого, но мы еще не выяснили правильную комбинацию.

Мы сталкиваемся, я думаю, с той же проблемой с несколькими статически экспортируемыми приложениями Next.js, размещенными на Now v2.

| URL | Приложение |
| - | - |
| example.com | следующий |
| example.com/dashboard | следующий |

Как и ожидалось, корневое приложение работает нормально. Однако во втором случае дела идут наперекосяк. В настоящее время мы упаковываем next/link который в сочетании с assetPrefix решает большую часть проблемы:

export default ({ children, href, ...rest }) => (
      <Link href={process.env.NODE_ENV === "production" ? `/dashboard${href}` : href} {...rest}>
        {children}
      </Link>
);

Однако это нарушает prefetch потому что затем пытается искать файлы .js по неправильному URL:

Наш текущий обходной путь - отключить prefetch , что не идеально.

Какой статус по этому поводу?

Также ищите обновления по этому поводу.

@timneutkens Я готов потратить время, чтобы открыть для него PR, если сообщество будет заинтересовано. Мы уже используем решение (https://github.com/panter/next.js/pull/1) в производстве, и мы очень им довольны.

Нам также нужно решение для этого

Вскоре мы представим новый API, который сделает это предложение устаревшим.

Также на это повлияло. Необходимо запустить следующий проект в пути к подкаталогу. С нетерпением жду официальной функции. Есть ли расчетное время прибытия?

API

Так как дела? : D

Пожалуйста, не спамите тему и используйте функцию GitHub 👍 для решения самой проблемы.

@timneutkens Можете ли вы предоставить дополнительную информацию? Какой API сделает это устаревшим? Что вы считаете «скоро»? Спасибо.

Возможно, это не совсем относится к мульти-зонам, но может помочь ...

Я решил нечто подобное, создав собственный сервер и используя промежуточное ПО прокси.

например: @Zertz
Обратите внимание: вам все еще нужно решить проблему со связью. Опять же, я решил это, создав компонент ссылки и передав префикс в приложение через конфигурацию, и если префикс существует, используйте его или ничего не используйте, то же самое для статических изображений.

const proxy = require('http-proxy-middleware');

app.setAssetPrefix('/dashboard');

  // Express custom server
  // Proxy so it works with prefix and without...
  // So if asset prefix is set then it still works
  const server = express();
  server.use(
    proxy('/dashboard', {
      target: 'http://localhost:3000', 
      changeOrigin: true,
      pathRewrite: {
        [`^/dashboard`]: '',
      },
    }),
  );

Предложение, о котором я упоминал, - № 7329.

Предложение, о котором я упоминал, - № 7329.

@timneutkens
Не могли бы вы предоставить более подробную информацию о том, как предлагаемый перехватчик решит наши проблемы с базовым путем?
А как насчет перенаправления маршрутизатора типа Router.push('/about') , это тоже будет заменено на перехватчик?

Спасибо за уделенное время 😏

API-интерфейс маршрутизатора также изменится, так как для него потребуется компонент для связи. На этом этапе вы можете использовать относительные пути для самого URL-адреса.

Есть ли новости о том, когда мы сможем получить решение или, по крайней мере, обходной путь для этого?

Используйте 👍 в первом выпуске вместо публикации каких-либо обновлений.

@ MMT-LD Ваше решение мне подходит, но теперь при каждом щелчке по ссылке или при нажатии на маршрутизатор страница перезагружается ☹️

Я попробовал решение @Zertz , и оно отлично сработало!
Также я мог исправить проблему prefetch , скопировав выходные файлы по предварительно выбранным путям.
https://github.com/fand/MDMT/blob/master/scripts/copy-preload.js

... это подвох, но все равно работает it's

@nicholasbraun

Теперь при каждом щелчке по ссылке или при нажатии на маршрутизатор страница перезагружается ☹️

У меня была эта проблема, но она была исправлена ​​с использованием параметра as в ссылке, поэтому ссылка указывает на внутренний файл, но as относится к пути
например:
<Link href={"/${item.link}"} as={"./${item.link}"}>

@nicholasbraun

Ваше решение вроде работает для меня, но теперь при каждом щелчке по ссылке или событии нажатия маршрутизатора страница перезагружается ☹️

Это то, что я имел в виду. Это по памяти .... но я уверен, что вы не можете добраться до того, что вам нужно, из приведенного ниже.

// WithConfig component
import getConfig from 'next/config';

const { publicRuntimeConfig } = getConfig();

const WithConfig = ({ children }) =>
  children && children({ config: publicRuntimeConfig });

export default WithConfig;
// Extended Link component

 import React from 'react';
import PropTypes from 'prop-types';
import Link from 'next/link';
import { WithConfig } from '../WithConfig';
/* 
    <Link> component has two main props:
    href: the path inside pages directory + query string. e.g. /page/querystring?id=1
    as: the path that will be rendered in the browser URL bar. e.g. /page/querystring/1

*/

const NextLink = ({
  browserHref,
  pagesHref,
  whatever,
}) => {
  return (
    <WithConfig>
      {({ config: { pathPrefix } = {} }) => (
        <Link
          as={pathPrefix ? `${pathPrefix}${browserHref}` : browserHref}
          href={pagesHref}
          passHref
        >
          <a>{whatever}</a> // this bit is up to you - children or whatever
        </Link>
      )}
    </WithConfig>
  );
};

NextLink.propTypes = {
  browserHref: PropTypes.string.isRequired,
  pagesHref: PropTypes.string,
};

NextLink.defaultProps = {
  pagesHref: undefined,
};

export default NextLink;

Применение:

import NextLink from '../NextLink'

<NextLink browserHref={`/page/1`} pagesHref={`/page?querystring=1`} whatever='I'm the link' />

Удачи: смайлик:

Поскольку useLink RFC сейчас отклонен (# 7329) и поддержка basePath нам очень поможет, рад ли проект Next.js принять PR, реализующие его? Я готов это сделать.

Глядя на эту реализацию @tomaswitek , кажется, что она движется в правильном направлении, а главное - basePath . Есть ли другие неочевидные вещи, которые затруднят поддержку basePath ?

В целом, я думаю, что дизайн понятен, всего одна переменная конфигурации:

module.exports = {
  basePath: '/demo'
}

Здесь четко определены взаимодействия с assetPrefix : https://github.com/zeit/next.js/issues/4998#issuecomment -414978297.


ОБНОВЛЕНИЕ : я также думал, можно ли реализовать это, создав собственный маршрутизатор и каким-то образом поменяв местами стандартный, но это не представляется возможным, Next.js жестко кодирует свой маршрутизатор, см., Например, здесь . Я также скептически отношусь к тому, что "только" замены роутера будет достаточно; эта функция, вероятно, должна поддерживаться Next.js в целом.

Эта проблема существует с 2017 года, есть ли решение? Или официальный ответ на наш запрос basePath?

Итак, после попытки сделать эту работу с комбинацией assetPrefix и пользовательского компонента <Link> как предложено, например, в https://github.com/zeit/next.js/issues/4998#issuecomment -464345554 или https://github.com/zeit/next.js/issues/4998#issuecomment -521189412, к сожалению, я не верю, что это возможно.

Определение assetPrefix было относительно простым, примерно так в next.config.js :

const assetPrefix = process.env.DEPLOYMENT_BUILD ? '/subdir' : '';

module.exports = {
  assetPrefix,
  env: {
    ASSET_PREFIX: assetPrefix,
  },
}

Следующий шаг - настраиваемый компонент Link . Первая идея, приведенная, например, в https://github.com/zeit/next.js/issues/4998#issuecomment -464345554, заключается в добавлении префикса href следующим образом (упрощенно):

export default ({ children, href, ...rest }) => (
  <Link href={`${process.env.ASSET_PREFIX}${href}`} {...rest}>
    {children}
  </Link>
);

Как сообщают другие в этом потоке, это прерывает предварительную выборку, поскольку запросы внезапно поступают в / subdir /_next/static/.../pages/ subdir /example.js - другого «subdir» там быть не должно. Но с нашим пользовательским компонентом Link мы устанавливаем href равным /subdir/example , поэтому неудивительно, что Next.js запрашивает пакет страницы pages/subdir/example.js .

Итак, проблемная предварительная выборка не звучит как конец света (хотя UX довольно уродлив), но в нашем приложении все становится еще хуже, поскольку мы используем динамическую маршрутизацию Next.js 9. Для этого нам нужно правильно установить as чтобы эволюция пользовательского компонента Link выглядела следующим образом:

export default ({ children, href, as, ...rest }) => (
  <Link 
    href={`${process.env.ASSET_PREFIX}${href}`}
    as={`${process.env.ASSET_PREFIX}${as}`}
    {...rest}
  >
    {children}
  </Link>
);

Использование:

<CustomLink href='/post/[id]' as='/post/1'>...</CustomLink>

который преобразуется в:

<Link href='/subdir/post/[id]' as='/subdir/post/1'>...</Link>

и это вообще не сработало для меня при развертывании на Now - попытка перейти к https://deployment-id.now.sh/subdir/post/1 привела к 404. Я не совсем уверен, почему, возможно, это также проблема с @now/next builder ( ОБНОВЛЕНИЕ : это из-за https://github.com/zeit/next.js/pull/8426#issuecomment-522801831), но в конечном итоге мы путаем маршрутизатор Next.js, когда просим /subdir/post/[id] component, если на диске нет такого файла.

В этом потоке есть еще один пример, https://github.com/zeit/next.js/issues/4998#issuecomment -521189412, с префиксом только как ,

export default ({ children, href, as, ...rest }) => (
  <Link href={href} as={`${process.env.ASSET_PREFIX}${as}`} {...rest}>
    {children}
  </Link>
);

но это вызовет эту ошибку в браузере:

Ваше значение <Link> as несовместимо со значением href . Это неверно.

Об этой проблеме сообщается на странице https://github.com/zeit/next.js/issues/7488.

После всего этого я не думаю, что есть решение, пока не будет поддерживаться что-то вроде basePath , с чем я был бы рад помочь.

@borekb Я тоже готов помочь, о чем уже говорил несколько раз. Все обходные пути, которые я видел до сих пор, решают только часть проблемы. Сейчас мы используем в продакшене форк next.js, который реализует basePath.

Вскоре мы представим новый API, который сделает это предложение устаревшим.

@tim Это все еще так или новое предложение по API закрыто? https://github.com/zeit/next.js/issues/7329

Кстати. завтра будет ровно год я открыл этот номер 🎉

Одна относительно дикая идея - разместить страницы в чем-то вроде src/pages а затем создать символическую ссылку на них в нужном месте. Например:

  • Чтобы развернуть на myapp.example.com , я бы сделал символическую ссылку src/pages на pages
  • Чтобы развернуть на example.com/myapp , я бы сделал символическую ссылку src/pages на pages/myapp

В сочетании с пользовательским компонентом <Link> и assetPrefix он _could_ работать, но я не достаточно храбр, чтобы попробовать его 😄.

Есть какие-нибудь обновления с этим?

Есть ли прогресс в поддержке basePath ? :)

@nicholasbraun

Теперь при каждом щелчке по ссылке или событии отправки маршрутизатора страница перезагружается. Frowning_face

У меня была эта проблема, но она была исправлена ​​с использованием параметра as в ссылке, поэтому ссылка указывает на внутренний файл, но as относится к пути
например:
<Link href={"/${item.link}"} as={"./${item.link}"}>

Ты спас мне день! :)))

я делаю то же самое с Router.push(`/route`, `${process.env.BASE_PATH}route`);

@nicholasbraun

Теперь при каждом щелчке по ссылке или при нажатии на маршрутизатор страница перезагружается ☹️

У меня была эта проблема, но она была исправлена ​​с использованием параметра as в ссылке, поэтому ссылка указывает на внутренний файл, но as относится к пути
например:
<Link href={"/${item.link}"} as={"./${item.link}"}>

Это решение не работает с маршрутизацией на основе следующих 9 файлов. /route/[id] , ${process.env.BASE_PATH}/route${id} выдает эту ошибку

Этот комментарий очень хорошо объясняет проблему.

Хотя я видел, как некоторые люди обсуждали, как решения здесь нарушают предварительную загрузку. Для нас есть еще один более важный вопрос.

В next9 использование assetPrefix в вашем href заставляет next _always_ выполнять маршрут к серверу. Я создал репозиторий репродукций в этом выпуске, который демонстрирует, как это происходит.

Это по существу нарушает наш кеш Apollo Client, поскольку он воссоздается на каждом маршруте.

Я думаю, что реализация сравнивает базовую страницу href без assetPrefix со следующими маршрутами href (который включает assetPrefix), что приводит к глубокому маршруту.

например, если вы находитесь на href /prefix/page (основная страница - это всего лишь /page ), а ваш следующий маршрут href - /prefix/page/[id] (потому что без префикса это будет 404) это совершенно другой маршрут, и неглубокий маршрут невозможен.

Быстрый поиск обходных путей с помощью экспресс-маршрутов

При использовании компонент с реквизитами href то есть basePath, предварительная выборка не работает.
PLZ поддерживает basePath и prefetch, будет круто

Я действительно мог бы это использовать. Я запускаю несколько приложений из одного источника сервера, и каждое из них разделено на свои собственные web/appX/{next project files} . Было бы здорово иметь больший контроль над basePath. На данный момент я нашел обходной путь, но это не очень красиво.

статический экспорт также требует basePath 😊

похоже работа успешна 👏

{
  experimental:{
    basePath: '/some/dir',
  }
}

К сожалению, это довольно плохое ограничение для нас :(

У нас все приложения работают за обратным прокси, поэтому пути должны иметь префикс (в примере ниже это префикс /auction-results )

Мы уже используем префикс assetPrefix - и это позволяет приложениям нормально работать для запросов на стороне сервера.
Например: mydomain.com/auction-results/ отлично работает, используя некоторую экспресс-маршрутизацию, например:

router.get(`/${appPrefix}/`, (req, res) => {
  nextApp.render(req, res, '/national', req.params);
});

Но когда мы пытаемся выполнить навигацию на стороне клиента через next/link , например:

Где /auction-results - это префикс приложения, а /national - это страница в ~pages/national

<Link href="/national" as="/auction-results/">
  <a>Goto National Page</a>
</Link>

Это ничего не делает (призрачный щелчок)

Ссылки на полное обновление страницы - далеко не идеальный вариант.

Если есть способ помочь с этим, я бы с удовольствием

Любое обновление по этому поводу ... в прошлом году примерно в это время я столкнулся с этой проблемой. Теперь, год спустя, я работаю над новым приложением и должен делать те же обходные пути, что и в прошлом году ... немного тревожно для «готовой к производству» реакции fw. Базовые пути должны быть ванильной функцией.

Любое обновление по этому поводу ... в прошлом году примерно в это время я столкнулся с этой проблемой. Теперь, год спустя, я работаю над новым приложением и должен делать те же обходные пути, что и в прошлом году ... немного тревожно для «готовой к производству» реакции fw. Базовые пути должны быть ванильной функцией.

Я не уверен, чего вы ожидаете, публикуя это.

Моя команда (5 человек) работает над Next.js на постоянной основе, и мы работаем над многими функциями одновременно. В прошлом году мы работали над этим:

Эффективно делает приложения Next.js (новые и существующие) значительно меньше, быстрее и более масштабируемыми.

Если вы хотите проголосовать за функцию, вы можете это сделать. используйте функцию 👍 на начальной нити.

Я определенно согласен с тем, что basePath должна быть встроенной функцией. Это уже в дорожной карте, и я даже написал первоначальный PR, который вы могли увидеть, прочитав в ветке.

Вот PR: https://github.com/zeit/next.js/pull/9872

Не стесняйтесь обращаться по адресу [email protected], если вы хотите внести финансовый вклад в реализацию этой функции.

Какой статус по этому поводу? мы действительно зависим от этого: /

Поддержка @Sletheren basePath сейчас является экспериментальной, используйте на свой страх и риск.

ср. https://github.com/zeit/next.js/pull/9872

Поддержка @Sletheren basePath сейчас экспериментальная, используйте на свой страх и риск.

ср. # 9872

@martpie Я это уже видел, но для. В моем случае basePath - это не один, это может быть несколько basePath, поскольку мы обслуживаем наше приложение по разным «URL-адресам» и установка basePath во время сборки не является вариантом (даже если у него есть для поддержки массива путей, а не одной строки)

@timneutkens Спасибо за обновление. Не могли бы вы дать еще одно обновление. Это для нас ключевая особенность, и нам нужно знать ...

  1. Будет ли это только для предприятий (ваше упоминание о контакте с отделом корпоративных продаж вызвало некоторое раздражение)?

  2. Вроде бы в дорожной карте, согласно PR, больше не удалятся; Можете ли вы указать, безопасно ли использовать эту функцию сейчас, не ожидая каких-либо сюрпризов в ближайшие месяцы, таких как урезанная версия с открытым исходным кодом и еще одна с полной поддержкой после того, как мы несколько недель обсуждали произвольные цены с некоторыми случайными продавцами?

Я понимаю, что вы, ребята, работаете над множеством функций, и у каждого свои приоритеты, но даже для небольших настроек необходимо проксировать Далее, запускать несколько экземпляров и выделять им выделенные basePath на каждую службу. Прежде чем мы начнем создавать несколько сервисов на Next, нам нужно знать, насколько вероятно, и скоро эта функция станет полностью открытой. В противном случае было бы слишком рискованно тратить время на Next.

Благодарим за понимание и ждем ваших отзывов.

FWIW, теперь он работает и для других проезжает мимо:

Поместите это в свой next.config.js :

module.exports = {
  experimental: {
    basePath: '/custom',
  },
}

Затем мне нужно было перезапустить сервер и правильно настроить промежуточное ПО веб-сервера:

Я ловлю все запросы по настраиваемому пути, например. app.use('/custom', (req, res...) => { ... а затем (что было важно) мне нужно проксировать URL-адрес системы, в которой работает Next (так что внутренний адрес вашей оркестровки контейнера и снова с соответствующим путем, если вы используете http-proxy => например, ... target: 'http://next:3000/custom ), а не только хост без настраиваемого пути. Если вы используете http-proxy-middleware вам это не нужно.

Вроде все нормально, я надеюсь, что для этой функции не потребуется лицензия EE. Если вашей команде нужна помощь, чтобы довести эту функцию до совершенства, пожалуйста, дайте нам знать, возможно, мы сможем помочь!

Изменить: просто попробовал это также в производственном режиме Next, и, похоже, он тоже работает.

@timneutkens Спасибо за обновление. Не могли бы вы дать еще одно обновление. Это для нас ключевая особенность, и нам нужно знать ...

  1. Будет ли это только для предприятий (ваше упоминание о контакте с отделом корпоративных продаж вызвало некоторое раздражение)?
  2. Вроде бы в дорожной карте, согласно PR, больше не удалятся; Можете ли вы указать, безопасно ли использовать эту функцию сейчас, не ожидая каких-либо сюрпризов в ближайшие месяцы, таких как урезанная версия с открытым исходным кодом и еще одна с полной поддержкой после того, как мы несколько недель обсуждали произвольные цены с некоторыми случайными продавцами?

Я понимаю, что вы, ребята, работаете над множеством функций, и у каждого свои приоритеты, но даже для небольших настроек необходимо проксировать Далее, запускать несколько экземпляров и выделять им выделенные basePath на каждую службу. Прежде чем мы начнем создавать несколько сервисов на Next, нам нужно знать, насколько вероятно, и скоро эта функция станет полностью открытой. В противном случае было бы слишком рискованно тратить время на Next.

Благодарим за понимание и ждем ваших отзывов.

@ pe-s Я думаю, вы неправильно поняли мой пост.

На данный момент не существует "корпоративной версии Next.js". Я имел в виду многочисленные случаи, когда внешние компании обращались за консультацией, чтобы создать такие функции, как эта, в более короткие сроки. Например, поддержка зон была построена в сотрудничестве с Trulia.

Эта функция все еще находится в разработке и находится в дорожной карте. Все функции, над которыми мы работаем, имеют открытый исходный код, как я уже сказал, корпоративной версии Next.js. У нас есть несколько приоритетов высокоэффективной работы над дорожной картой, поэтому я обратился по адресу [email protected], если вам понадобится эта функция как можно скорее / для обсуждения корпоративной поддержки Next.js.

@timneutkens tx за быстрый ответ и отлично! Тогда мы можем пойти ва-банк :)
Продолжайте в том же духе!

Поддержка Basepath в next@canary сейчас отсутствует, это больше не экспериментальная функция. Скоро это будет на стабильном канале.

Я уже довольно поздно, но рассматривали ли вы возможность использовать настоящий HTML <base> вместо того, чтобы обрабатывать это вручную?

Поддержка Basepath в next@canary сейчас отсутствует, это больше не экспериментальная функция. Скоро это будет на стабильном канале.

@timneutkens , спасибо за это дополнение. Вы знаете, когда будет официально выпущена неэкспериментальная поддержка basePath?

Кроме того, когда я устанавливаю basePath, ресурсы (расположенные в общей папке) обслуживаются по соответствующему URL-адресу, как и ожидалось. Но когда я ссылаюсь на них в своем коде, мне приходится вручную добавлять базовый путь к src, потому что в противном случае на них по-прежнему будут ссылаться из обычного пути. Это ожидаемое использование basePath? Я также пробовал использовать assetPrefix, но это не повлияло на мой код, что я мог сказать.

Пример :

  1. используя next v9.4.5-canary.24
  2. basePath установлен в /alerts в next.config.js:
const basePath = '/alerts';
module.exports = {
  basePath: basePath,
  env: {
    BASE_PATH: basePath,
  },
};
  1. актив, расположенный в public/images/example.png
  2. пример использования актива в компоненте реакции:
const ExampleImage = () => (
  <img src={`${process.env.BASE_PATH}/images/example.png`} />
);

В моих тестах он не обновляет URL-адреса ресурсов.

Установил последнюю канарейку:
npm install [email protected]

next.config.js

const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  basePath: isProd ? '/example' : ''
}

Все страницы и ссылки загружаются правильно:
http: // localhost : 3000 / пример / сообщений / предварительный рендеринг
http: // локальный : 3000 / пример / сообщений / ssg-ssr
http: // localhost : 3000 / пример / сообщений / предварительный рендеринг

Но изображения, значки и т. Д. Не отображаются:
http: // локальный : 3000 / favicon.ico 404
http: // localhost : 3000 / images / profile.jpg 404

Кто-нибудь это тестировал? Я также пробовал использовать assetPrefix, но это тоже не сработало.

Кроме того, я запутался, почему бы не использовать для этого встроенные функции браузера?
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

Спасибо, что изучили это со своей стороны
@timneutkens , следует ли нам повторно открыть эту проблему / создать новую проблему для этой ошибки?

Вы должны вручную добавлять префиксы к изображениям. Вы можете получить basePath, используя

const {basePath} = useRouter()

https://nextjs.org/docs/api-reference/next.config.js/cdn-support-with-asset-prefix

Next.js будет автоматически использовать ваш префикс в загружаемых скриптах, но это никак не повлияет на общую папку;

Теперь я понял, что есть несколько способов ссылки на файлы в / public. например, <img/> <link/> ...
Поэтому мы должны вручную указывать basePath для каждого?

Если бы был доступен компонент, как показано ниже, я думаю, это сэкономило бы время и уменьшило бы путаницу для многих людей?

<WithinBasePath>
  {/* automatically fixes the path with basePath */}
  <img src="/logo.png" />
</WithinBasePath>

Я действительно не думаю, что это уместно, но я имел в виду именно это.

// src/components/WithinBasePath/index.tsx

import React from "react"
import path from "path"
import { useRouter } from "next/router"
interface Props {}

const WithinBasePath: React.FC<Props> = (props) => {
  const { basePath } = useRouter()
  const children = [props.children].flatMap((c) => c) as React.ReactElement[]
  return (
    <>
      {children.map((child, key) => {
        let newChild = null

        switch (child.type) {
          case "img":
            newChild = React.createElement(child.type, {
              ...child.props,
              src: path.join(basePath, child.props.src),
              key,
            })
            break
          case "link":
            newChild = React.createElement(child.type, {
              ...child.props,
              href: path.join(basePath, child.props.href),
              key,
            })
            break
          default:
            newChild = React.createElement(child.type, {
              ...child.props,
              key,
            })
        }
        return newChild
      })}
    </>
  )
}
export default WithinBasePath

// pages/test.tsx

import React from "react"
import WithinBasePath from "@src/components/WithinBasePath"
interface Props {}

const test: React.FC<Props> = (props) => {
  return (
    <WithinBasePath>
      <img src="/123.jpg" />
      <link href="/abc.jpg" />
      <div>other element</div>
    </WithinBasePath>
  )
}
export default test

Для тех, кто пытается использовать const {basePath} = useRouter() который является хуком, для работы с классами и компонентами и получает эту ошибку:

Предупреждение о недопустимом вызове трубки

https://reactjs.org/warnings/invalid-hook-call-warning.html

Вы можете заставить его работать, используя:

import { withRouter, Router } from 'next/router'

class Example extends Component<{router: Router}, {router: Router}> {
  constructor(props) {
    super(props)
    this.state = {
      router: props.router
    }
  }
  render() {
    return (
      <Layout home>
        <Head><title>Example title</title></Head>
        <img src={`${this.state.router.basePath}/images/creators.jpg`} />
      </Layout>
    )
  }
}
export default withRouter(Example)

Если вы хотите использовать basePath с уценкой, похоже, вам нужно найти и заменить в строке:

const content = this.state.doc.content.replace('/docs', `${this.state.router.basePath}/docs`);
return (
<Layout>
  <Container docs={this.state.allDocs}>
    <h1>{this.state.doc.title}</h1>
    <div
      className={markdownStyles['markdown']}
      dangerouslySetInnerHTML={{ __html: content }}
    />
  </Container>
</Layout>
)

Вы должны вручную добавлять префиксы к изображениям. Вы можете получить basePath, используя

const {basePath} = useRouter()

Однако это решение не принимает во внимание изображения, импортированные в файл css или scss. У вас есть решение, как установить базовый путь при импорте актива из файла css или scss?
С помощью этого решения нам нужно будет гарантировать, что все изображения импортируются либо через тег img, либо через встроенный стиль, либо в тег стиля. Это не идеально, потому что это разделит ваши стили для реализации в нескольких местах.

@peetjvv Вот неоптимальное решение для использования ресурсов с префиксом basePaths в CSS. Создайте, импортируйте и добавьте компонент <CSSVariables> в _app.tsx , который внедряет глобальный встроенный элемент <style> содержащий переменные CSS, которые затем можно использовать во всех таблицах стилей.

Например, при открытии переменных <body> build и inject:

<style>
:root {
      --asset-url: url("${basePath}/img/asset.png");
}
</style>

Чтобы получить этот basePath, я использую подход @kmturley , используя withRouter .
Вот как может выглядеть этот компонент:

import { withRouter, Router } from "next/router";
import { Component } from "react";

export interface IProps {
  router: Router;
}

class CSSVariables extends Component<IProps> {
  render() {
    const basePath = this.props.router.basePath;
    const prefixedPath = (path) => `${basePath}${path}`;
    const cssString = (value) => `\"${value}\"`;
    const cssURL = (value) => `url(${value})`;
    const cssVariable = (key, value) => `--${key}: ${value};`;
    const cssVariables = (variables) => Object.entries(variables)
      .map((entry) => cssVariable(entry[0], entry[1]))
      .join("\n");
    const cssRootVariables = (variables) => `:root {
      ${cssVariables(variables)}
    }`;

    const variables = {
      "asset-url": cssURL(
        cssString(prefixedPath("/img/asset.png"))
      ),
    };

    return (
      <style
        dangerouslySetInnerHTML={{
          __html: cssRootVariables(variables),
        }}
      />
    );
  }
}

export default withRouter(CSSVariables);
Была ли эта страница полезной?
0 / 5 - 0 рейтинги