Vue-cli: Allow multiple entries when using build with the target flag.

Created on 1 May 2019  路  12Comments  路  Source: vuejs/vue-cli

What problem does this feature solve?

It would allow users to build multiple libraries at once, ideally this would be usable with the --watch flag.

I have posted a detailed issue on the forum describing my use case for those interested, but after looking at the code I can see that this will most likely require updates to the internal structure of the Vue cli.

This is primarily aimed at people building modular component libraries on top of Vue (particularly ones that can be loaded from a CDN which is my use case).

For additional background I am trying to pre-compile my components (using --target lib) and then load them over http using the technique outlined here but that is outside of the context of what I am actually asking for.

What does the proposed API look like?

If no path is provided when using build with the --target command fallback to the entries config (particularly the one in vue.config.js) ie:

    config.entryPoints.delete("app");
    // Add entry points
    config
      .entry("component-one")
      .add("./src/components/ComponentOne.vue")
      .end()
      .entry("component-two")
      .add("./src/components/ComponentTwo.vue")
      .end();
feature request cli-service build

Most helpful comment

Another use case for this is when you embed Vue into an existing non-SPA application. To modernize pages separately you'll need to build a Vue library for each one of them, so multiple entries is a necessity to avoid (1) invoking vue-cli-service multiple times during production build (2) supporting dev workflow when vue-cli-service should run in the background and watch for changes.

All 12 comments

How would the --name option for all of these library be set individually?

@LinusBorg so currently if I run vue-cli-service build with the above entry config it will build component-one.js and component-two.js I am guessing this comes from the entry name? So that is how I would see that working, but setting the name individually could still work as you are going to have to add the path to each component manually anyway...

Originally I was messing around with being able to glob a directory i.e components/**/*.vue and then base the file name off the component file name, but then I realized this didn't really work as I actually only wanted to build a small subset of all my components as libraries as lots of the components were just smaller pieces of the components that became libraries, hence the needing to set up each one manually.

I'm not talking about the filename. I'm talking about what webpack calls libraryTarget, and Vue CLI calls --name in the build command options. It's the name of the global variable that each component is available as when imported from a CDN as a UMD bundle.

Oh I see, sorry!

Ahhh either the entry name or possibly an additional resolver like name() that falls back to the entry name? I am not entirely sure whatever fits in best with the "Vue" way of doing things.

@LinusBorg, if this is not in the scope of the vue-cli is this the kind of functionality that could be achieved via webpack-chain? I have been kicking around with some ideas in my head but I think this functionality occurs at too lower level to be able to _inject_ something to achieve this?

Is this the kind of change that should be opened as a RFC in https://github.com/vuejs/rfcs?

I am not sure if the change is significant enough to warrant an RFC.

This is primarily aimed at people building modular component libraries on top of Vue

馃檵鈥嶁檪 hi, hello, yes. this is me. this is exactly what I'm trying to do.

Currently I'm just doing vue-cli-service build --target lib --name my-lib-name ./src/index.js (where index.js has a list of imports and named exports for each component). This results in a single output file... but consumers of the library would still be importing ALL components unless they have tree-shaking turned on.

The problem we are running in to now is that some consumers are doing SSR... and not all components are SSR-friendly (they relay on browser APIs like localStorage, etc). But because everything gets rolled in to a single file... even if they import an SSR-friend component, an error still happens because it's trying to evaluate the entire file (this is specific to non-production / local dev mode, where tree-shaking is never enabled).

So now we are looking to create an individual export file per component. But just realized that the vue-cli-service does not allow for multiple inputs nor multiple outputs (I tried doing vue-cli-service build --target lib ./src/**/*.vue which did not give an error.... but did produce a rather bizarre file with not my components haha).

I did come across this issue which is somewhat related: https://github.com/vuejs/vue-cli/issues/2744, which is not ideal but does get us a closer to what we need.

Anyways... that is just a very long-winded way of saying that I would love for vue-cli-service to support multiple and dynamic entry points 馃檹

I'm not talking about the filename. I'm talking about what webpack calls libraryTarget, and Vue CLI calls --name in the build command options.

@LinusBorg I think webpack calls this library (libraryTarget is the equivalent to --format in the Vue CLI).

But to answer your question, my recommendation would be to follow what webpack calls a multi-part library

module.exports = {
    // mode: "development || "production",
    entry: {
        alpha: "./alpha",
        beta: "./beta"
    },
    output: {
        path: path.join(__dirname, "dist"),
        filename: "MyLibrary.[name].js",
        library: ["MyLibrary", "[name]"],
        libraryTarget: "umd"
    }
}

In this example, --name would be "MyLibrary" and the "name" would come from the entry name.

Finally getting a chance to dig back into this, looks like #1065 is strongly related to this.

Another use case for this is when you embed Vue into an existing non-SPA application. To modernize pages separately you'll need to build a Vue library for each one of them, so multiple entries is a necessity to avoid (1) invoking vue-cli-service multiple times during production build (2) supporting dev workflow when vue-cli-service should run in the background and watch for changes.

We are using the exact use case that @xfyre talks about. Our site is running on a Java CMS that incorporates Vue components in its templates that we bundle up as libraries.

To achieve that we're using a manual Webpack build similar to the approach @morficus suggests - using the object syntax for the entry point config key. It would be helpful to be able to use Vue CLI instead of a manual Webpack build.

I spotted someone attempting a solution using Vue CLI on the Vue forums. They tapped the Webpack config to add multiple entry points. However, it didn't appear to work for them. I haven't attempted this approach yet.

The following option works(tested around July 2020 with @vue/cli 4.4.6 ; [email protected] ):
Create simpe vue.js project with the following structure
image

ComponentA.vue and ComponentB.vue could be any of your choice.

ComponentA.js

import Vue from "vue";
import ComponentA from "./components/ComponentA.vue"

Vue.config.productionTip = false;

new Vue({
  render: h => h(ComponentA)
}).$mount("#componentA");

ComponentB.js is just like ComponentA.js with A replaced by B.

And !!! vue.config.js:

module.exports = {
    filenameHashing: false,
    pages: {
        componentA: {
            entry: 'src/componentA.js',
            template: 'public/index.html',
            filename: 'indexA.html',
            title: 'Component A',
            chunks: ['chunk-vendors', 'chunk-common', 'componentA']
        },
        componentB: {
            entry: 'src/componentB.js',
            template: 'public/index.html',
            filename: 'indexB.html',
            title: 'Component B',
            chunks: ['chunk-vendors', 'chunk-common', 'componentB']
        },
    },
}

see vuejs cli -> pages

After the build we get:
image

Note that indexA.html and indexB.html are based on the index.html from the public folder - the *.js includes will be set correctly, but the mounting point <div id=app></div> will not correspond the one given in the Component*.js file. At the time of writing this post I do not know where to set that. Anyway in the common case one would need the *.js components only and will attach them to an existing web page. It is also quite easy to correct manually the mounting point in index*.html if needed, but there should be an option to set it properly beforehand.

I also agree with all the posts above that building multiple Vue components for embedding in existing web applications (no matter SPA or other) is not easy or user friendly. The example above shows that it is possible without any further programming or changing the functionality in vue.js. There should only be some command of a kind:
npm run buildComponentsForEmbeddingInExistingWebApplication <component1> ... <componentN> (I am sure there is a better name :)),
which will generate single js files per component ready to be included in existing web pages.

Maybe this is because the "hardcore" vue.js developers never work outside vue.js and show the embedding option only in the documentation test examples - only with manual hard-coded includes, but they do not bother to improve the vue.js tools in order to streamline such tasks. It is only one command using ready functionality - please add it if you read this. Thanks in advance!

Best regards.
HTH

Was this page helpful?
0 / 5 - 0 ratings