vuejs / vue-cli

🛠️ webpack-based tooling for Vue.js Development
https://cli.vuejs.org/
MIT License
29.76k stars 6.33k forks source link

"Avoid modifying webpack output.publicPath directly", but some usecases might need it. E.g. serving static from different domain? #6661

Open ivictbor opened 3 years ago

ivictbor commented 3 years ago

Version

@vue/cli-service 3.5.0

Environment info

 System:
    OS: Linux 4.19 Ubuntu 20.04.2 LTS (Focal Fossa)
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  Binaries:
    Node: 12.22.1 - /usr/bin/node
    Yarn: 1.17.3 - /mnt/c/Program Files (x86)/Yarn/bin/yarn
    npm: 7.11.1 - /usr/bin/npm
  Browsers:
    Chrome: Not Found
    Firefox: Not Found
  npmPackages:
    @fortawesome/vue-fontawesome: ^0.1.8 => 0.1.10 
    @vue/babel-helper-vue-jsx-merge-props:  1.2.1 
    @vue/babel-plugin-transform-vue-jsx:  1.2.1 
    @vue/babel-preset-app:  3.12.1 
    @vue/babel-preset-jsx:  1.2.4 
    @vue/babel-sugar-composition-api-inject-h:  1.2.1 
    @vue/babel-sugar-composition-api-render-instance:  1.2.4 
    @vue/babel-sugar-functional-vue:  1.2.2 
    @vue/babel-sugar-inject-h:  1.2.2 
    @vue/babel-sugar-v-model:  1.2.3 
    @vue/babel-sugar-v-on:  1.2.3 
    @vue/cli-overlay:  3.12.1 
    @vue/cli-plugin-babel: ^3.5.0 => 3.12.1 
    @vue/cli-plugin-eslint: ^3.5.0 => 3.12.1 
    @vue/cli-plugin-pwa: ^4.5.10 => 4.5.13 
    @vue/cli-service: ^3.5.0 => 3.12.1 
    @vue/cli-shared-utils:  3.12.1 (4.5.13)
    @vue/component-compiler-utils:  3.2.0 
    @vue/eslint-config-airbnb: ^4.0.0 => 4.0.1 
    @vue/eslint-config-prettier: ^4.0.1 => 4.0.1 
    @vue/eslint-config-standard: ^4.0.0 => 4.0.0 
    @vue/preload-webpack-plugin:  1.1.2 
    @vue/web-component-wrapper:  1.3.0 
    emoji-mart-vue-fast: ^8.0.3 => 8.0.3 
    eslint-plugin-vue: ^5.0.0 => 5.2.3 (4.7.1)
    vue: ^2.6.6 => 2.6.12 
    vue-affix: ^0.5.2 => 0.5.2 
    vue-avatar-editor-improved: ^1.0.4 => 1.0.7 
    vue-blurhash: ^0.1.4 => 0.1.4 
    vue-carousel: ^0.18.0 => 0.18.0 
    vue-cli-plugin-webpack-bundle-analyzer: ^2.0.0 => 2.0.0 
    vue-cli-webpack:  1.0.0 
    vue-click-outside: ^1.0.7 => 1.1.0 
    vue-ctk-date-time-picker: ^2.4.0 => 2.5.0 
    vue-custom-scrollbar: ^1.1.0 => 1.4.0 
    vue-debounce: ^2.3.0 => 2.6.0 
    vue-eslint-parser:  5.0.0 (2.0.3)
    vue-hot-reload-api:  2.3.4 
    vue-loader:  15.9.7 
    vue-observe-visibility:  0.4.6 
    vue-resize:  0.4.5 
    vue-router: ^3.0.1 => 3.5.1 
    vue-slicksort: ^1.1.3 => 1.2.0 
    vue-smooth-dnd: ^0.8.1 => 0.8.1 
    vue-style-loader:  4.1.3 
    vue-template-compiler: ^2.5.21 => 2.6.12 
    vue-template-es2015-compiler:  1.9.1 
    vue-virtual-scroller: ^1.0.0-rc.2 => 1.0.10 
    vuejs-datepicker: ^1.6.2 => 1.6.2 
    vuex: ^3.0.1 => 3.6.2 
  npmGlobalPackages:
    @vue/cli: Not Found

Steps to reproduce

Define output.publicPath in vue.config.js

What is expected?

I need a way to pass output.publicPath without this exception message. My usecase:

I serve one same Vue SPA on hundreds and thouthands of subdomains:

https://sub1.mydomain.com/ https://sub2.mydomain.com/ https://sub3.mydomain.com/ https://sub4.mydomain.com/

etc. Like workpsaces in Slack.

Every time when new user joins service and creates new workpace on new subdomain, it does not hit a cache on CDN because URL is always different https://sub123.mydomain.com/js/app.js, https://sub124.mydomain.com/js/app.js.

So CDN bypasses the request to slower upstream and takes redundant place in caches store by storing same files on different urls.

To solve this, only one simple thing which I need to do is:

 configureWebpack: {
    output: {
      publicPath: process.env.NODE_ENV === 'production'
        ? 'https://sub1.mydomain.com/'  // just to show idea - we serve files for all from one domain

(+add CORS settings on serving end) so now the cache on CDN will not be separated and all new users will hit one cache when they load JS/CSS/fonts.

If I use native top-level publicPath option, some Vue runtime code creates a redirect and breaks a route by adding domain there, but I don't want redirect, I really want index.html to be served from https://sub2.mydomain.com/, I only need to change URLs of assets, not route of the application.

I had to destroy validateWebpackConfig.js in my CI build pipeline to solve this issue quickly, and after this, applications works perfectly:

ADD package-lock.json .
ADD package.json .

RUN npm ci

# ugly fix to prevent Avoid modifying webpack output.publicPath directly.
RUN echo "module.exports = function validateWebpackConfig () {}" > 'node_modules/@vue/cli-service/lib/util/validateWebpackConfig.js'

But it would be better if we can disable this "smart mode" and allow some flexibility, at least if we would have an option like allowOutputPublicPath :

if (!singleConfig.allowOutputPublicPath &&  target === 'app' && singleConfig.output.publicPath !== options.publicPath) {
  // only then drop exception

Is there any better/faster ways to point all statics to be loaded from a different domain? This definitely not super popular case, but from time to time developers might need it.

What is actually happening?

Configuration Error: Avoid modifying webpack output.publicPath directly. Use the "publicPath" option instead

npdev453 commented 3 years ago

+1

Also, it could be useful for webpack5 output: { publicPath: 'auto' } feature

SanBen commented 3 years ago

+1

This is a must for any kind of embedding where a widget is not served from the same domain for instance. It messes with the routing otherwise.

hey-adi42 commented 3 years ago

背景: vueCli 3.30后,废弃baseURL ,统一使用publicPath,同时禁用了output.publicPath 问题:项目打包的静态资源JS+CSS,想配置CDN域名,和项目域名不同域名,怎么搞嘛。 之前是只配置publicPath即可。

html 我们是通过Nginx代理到的CDN域名 JS+CSS 直接配置CDN域名,没有代理的原因是想节省链路,直接享受CDN加速

"@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-eslint": "~4.5.0", "@vue/cli-plugin-router": "~4.5.0", "@vue/cli-plugin-typescript": "~4.5.0", "@vue/cli-plugin-unit-mocha": "~4.5.0", "@vue/cli-plugin-vuex": "~4.5.0", "@vue/cli-service": "~4.5.0",

@sodatea 豪群哥,方便帮忙指点一下吗~

catrius commented 2 years ago

I think I found a fix for this.

First, use vue cli's publicPath instead of webpack:

vue.config.js

module.exports = defineConfig({
  publicPath: WEB_CDN_DOMAIN || '/',
...

Then, change your router history from createWebHistory(process.env.BASE_URL) to createWebHistory('/')

Index.js

import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory('/'),
  routes: [...],
});

export default router;
vitechliu commented 1 year ago

I think I found a fix for this.

First, use vue cli's publicPath instead of webpack:

vue.config.js

module.exports = defineConfig({
  publicPath: WEB_CDN_DOMAIN || '/',
...

Then, change your router history from createWebHistory(process.env.BASE_URL) to createWebHistory('/')

Index.js

import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory('/'),
  routes: [...],
});

export default router;

This fix works for me, but somehow in my project the router var should be like this (changed from base: process.env.BASE_URL,)

const router = new VueRouter({
  mode: 'history',
  base: '/',
  routes
})
iDestin commented 1 year ago

if you just want set webpack.output.publicPath in vue-cli, you can use this plugin vuecli-publicpath-webpack-plugin