7.7 KiB

Add a Vue Project

The code for this example is available on Github:

{% github-repository url="https://github.com/nrwl/nx-recipes/tree/main/vue" /%}

Supported Features

We'll be using an Nx plugin for Vue called @nx/vite. Although we are using @nx/vite, not all dependencies will be able to be automatically updated. So we'll have to update any framework dependencies as needed.

{% pill url="/core-features/run-tasks" %} Run Tasks{% /pill %} {% pill url="/core-features/cache-task-results" %} Cache Task Results{% /pill %} {% pill url="/core-features/remote-cache" %} Share Your Cache{% /pill %} {% pill url="/core-features/explore-graph" %} Explore the Graph{% /pill %} {% pill url="/core-features/distribute-task-execution" %} Distribute Task Execution{% /pill %} {% pill url="/core-features/integrate-with-editors" %} Integrate with Editors{% /pill %} {% pill url="/core-features/automate-updating-dependencies" %} Automate Updating Nx{% /pill %} {% pill url="/core-features/enforce-module-boundaries" %} Enforce Module Boundaries{% /pill %} {% pill url="/core-features/plugin-features/use-task-executors" %} Use Task Executors{% /pill %} {% pill url="/core-features/plugin-features/use-code-generators" %}🚫 Use Code Generators{% /pill %} {% pill url="/core-features/automate-updating-dependencies" %}🚫 Automate Updating Framework Dependencies{% /pill %}

Setup workspace

Create a new Nx workspace

create-nx-workspace@latest acme --preset=ts-standalone --nx-cloud=true

Add @nx/vite, vue, and other dependencies to your workspace

{% tabs %} {%tab label="npm"%}

npm install --save-dev @nx/vite @nx/js vue vue-tsc vitest vite-tsconfig-paths vite-plugin-dts vite @vitejs/plugin-vue @vitejs/plugin-vue-jsx

{% /tab %} {%tab label="yarn"%}

yarn add --dev @nx/vite @nx/js vue vue-tsc vitest vite-tsconfig-paths vite-plugin-dts vite @vitejs/plugin-vue @vitejs/plugin-vue-jsx

{% /tab %} {%tab label="pnpm"%}

pnpm add --dev @nx/vite @nx/js vue vue-tsc vitest vite-tsconfig-paths vite-plugin-dts vite @vitejs/plugin-vue @vitejs/plugin-vue-jsx

{% /tab %} {% /tabs %}

Create the application

touch index.html

And add the following content:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Acme</title>

    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/x-icon" href="./favicon.ico" />
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="./src/main.ts"></script>
  </body>
</html>

Navigate to src/index.ts and change it to src/main.ts and add the following content:

import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');

Create a new file src/App.vue with the following content:

<script setup lang="ts">
import { ref } from 'vue';

const count = ref(0);

function increment() {
  count.value++;
}
</script>
<template>
  <div>count is {{ count }}</div>
  <button @click="increment">Increment</button>
</template>

Configure Nx to use build and serve your Vue application

Navigate to vite.config.ts and add the following content:

// Add this to your imports
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

export default defineConfig({
  plugins: [
    //.... other plugins
    vue(),
    vueJsx(),
  ],
});

Create a new file env.d.ts for example at the root of the project and add the following content:

/// <reference types="vite/client" />
/* eslint-disable */
declare module '*.vue' {
  import type { DefineComponent } from 'vue';
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const component: DefineComponent<object, object, any>;
  export default component;
}

We need this file to ensure that Vue types are available to the compiler.

Update your tsconfig.lib.json to be tsconfig.app.json an add the following content:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "dist/out-tsc",
    "types": ["node", "vite/client"],
    "jsxImportSource": "vue"
  },
  "files": [],
  "exclude": [
    "src/**/*.spec.ts",
    "src/**/*.test.ts",
    "src/**/*.spec.tsx",
    "src/**/*.test.tsx",
    "src/**/*.spec.js",
    "src/**/*.test.js",
    "src/**/*.spec.jsx",
    "src/**/*.test.jsx"
  ],
  "include": [
    "src/**/*.ts",
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "**/*.vue",
    "vite.config.ts",
    "env.d.ts"
  ]
}

We include vite.config.ts and env.d.ts to ensure that the types are available to the compiler.

Navigate to project.json and update it with the following content:

    "build": {
      "executor": "@nx/vite:build",
      "outputs": ["{options.outputPath}"],
      "defaultConfiguration": "production",
      "options": {
        "outputPath": "dist/acme"
      },
      "configurations": {
        "development": {
          "mode": "development"
        },
        "production": {
          "mode": "production"
        }
      }
    },
    "serve": {
      "executor": "@nx/vite:dev-server",
      "defaultConfiguration": "development",
      "options": {
        "buildTarget": "acme:build"
      },
      "configurations": {
        "development": {
          "buildTarget": "acme:build:development",
          "hmr": true
        },
        "production": {
          "buildTarget": "acme:build:production",
          "hmr": false
        }
      }
    },

This allows us to use nx build and nx serve to build and serve our Vue application.

Test it out

Build

nx build acme

Serve

nx serve acme

Create a library

Instead of having our Counter directly defined in the app we can instead create a library that exports the Counter component.

{% callout type="note" title="Directory Flag Behavior Changes" %} The command below uses the as-provided directory flag behavior, which is the default in Nx 16.8.0. If you're on an earlier version of Nx or using the derived option, omit the --directory flag. See the workspace layout documentation for more details. {% /callout %}

Create a new library nx generate @nx/js:library --name=Counter --directory=libs/counter --unitTestRunner=vitest --bundler=vite --importPath=@acme/counter

Create your Counter component at counter/src/lib/Counter.vue and copy the contents of your src/App.vue into it.

Update your libs/counter/src/lib/index.ts to export your Counter component.

export { default as Counter } from './Counter.vue';

{% callout type="Note" %} The default is very import here as it allows us to import the component using import { Counter } from '@acme/counter' instead of import Counter from '@acme/counter'. {% /callout %}

Update your root ./vite.config.ts to include the following:

export default defineConfig({
  //... other config
  resolve: {
    alias: {
      '@acme/counter': fileURLToPath(
        new URL('./counter/src/index.ts', import.meta.url)
      ),
    },
  },
});

This allows the runtime to resolve the @acme/counter import to the correct location.

Finally update your src/App.vue to use the Counter component.

<script setup lang="ts">
import { Counter } from '@acme/counter';
</script>
<template>
    <Counter />
</template>

Test it out

Build

nx build acme

Serve

nx serve acme

More Documentation

A larger example including libraries, tests, and more is available at Nx Vue Example on Github.