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
Keep Nx Package Versions In SyncMake sure to install the @nx/vite
and @nx/js
versions that matches the version of nx
in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can fix Nx version mismatches with this recipe.
❯
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.