Add a Vue Project
The code for this example is available on Github:
Example repository/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.
✅ Run Tasks ✅ Cache Task Results ✅ Share Your Cache ✅ Explore the Graph ✅ Distribute Task Execution ✅ Integrate with Editors ✅ Automate Updating Nx ✅ Enforce Module Boundaries ✅ Use Task Executors 🚫 Use Code Generators 🚫 Automate Updating Framework Dependencies
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
❯
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
Create the application
❯
touch index.html
And add the following content:
1
2<html lang="en">
3 <head>
4 <meta charset="utf-8" />
5 <title>Acme</title>
6
7 <meta name="viewport" content="width=device-width, initial-scale=1" />
8 <link rel="icon" type="image/x-icon" href="./favicon.ico" />
9 </head>
10 <body>
11 <div id="app"></div>
12 <script type="module" src="./src/main.ts"></script>
13 </body>
14</html>
15
Navigate to src/index.ts
and change it to src/main.ts
and add the following content:
1import { createApp } from 'vue';
2import App from './App.vue';
3
4createApp(App).mount('#app');
5
Create a new file src/App.vue
with the following content:
1<script setup lang="ts">
2import { ref } from 'vue';
3
4const count = ref(0);
5
6function increment() {
7 count.value++;
8}
9</script>
10<template>
11 <div>count is {{ count }}</div>
12 <button @click="increment">Increment</button>
13</template>
14
Configure Nx to use build and serve your Vue application
Navigate to vite.config.ts
and add the following content:
1// Add this to your imports
2import vue from '@vitejs/plugin-vue';
3import vueJsx from '@vitejs/plugin-vue-jsx';
4
5export default defineConfig({
6 plugins: [
7 //.... other plugins
8 vue(),
9 vueJsx(),
10 ],
11});
12
Create a new file env.d.ts
for example at the root of the project and add the following content:
1/// <reference types="vite/client" />
2/* eslint-disable */
3declare module '*.vue' {
4 import type { DefineComponent } from 'vue';
5 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6 const component: DefineComponent<object, object, any>;
7 export default component;
8}
9
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:
1{
2 "extends": "./tsconfig.json",
3 "compilerOptions": {
4 "outDir": "dist/out-tsc",
5 "types": ["node", "vite/client"],
6 "jsxImportSource": "vue"
7 },
8 "files": [],
9 "exclude": [
10 "src/**/*.spec.ts",
11 "src/**/*.test.ts",
12 "src/**/*.spec.tsx",
13 "src/**/*.test.tsx",
14 "src/**/*.spec.js",
15 "src/**/*.test.js",
16 "src/**/*.spec.jsx",
17 "src/**/*.test.jsx"
18 ],
19 "include": [
20 "src/**/*.ts",
21 "src/**/*.d.ts",
22 "src/**/*.tsx",
23 "**/*.vue",
24 "vite.config.ts",
25 "env.d.ts"
26 ]
27}
28
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:
1 "build": {
2 "executor": "@nx/vite:build",
3 "outputs": ["{options.outputPath}"],
4 "defaultConfiguration": "production",
5 "options": {
6 "outputPath": "dist/acme"
7 },
8 "configurations": {
9 "development": {
10 "mode": "development"
11 },
12 "production": {
13 "mode": "production"
14 }
15 }
16 },
17 "serve": {
18 "executor": "@nx/vite:dev-server",
19 "defaultConfiguration": "development",
20 "options": {
21 "buildTarget": "acme:build"
22 },
23 "configurations": {
24 "development": {
25 "buildTarget": "acme:build:development",
26 "hmr": true
27 },
28 "production": {
29 "buildTarget": "acme:build:production",
30 "hmr": false
31 }
32 }
33 },
34
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.
Directory Flag Behavior ChangesThe 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.
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.
1export { default as Counter } from './Counter.vue';
2
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'
.
Update your root ./vite.config.ts
to include the following:
1export default defineConfig({
2 //... other config
3 resolve: {
4 alias: {
5 '@acme/counter': fileURLToPath(
6 new URL('./counter/src/index.ts', import.meta.url)
7 ),
8 },
9 },
10});
11
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.
1<script setup lang="ts">
2import { Counter } from '@acme/counter';
3</script>
4<template>
5 <Counter />
6</template>
7
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.