如何在Ubuntu 20.04上用Nginx发布一个Vuetify应用程序

348 阅读18分钟

简介

组件是现代前端开发的一个关键特征。一个组件是一段代码,通常包括两部分。

  • 组件的逻辑:组件能做什么。
  • 模板:用户将如何与网络应用进行交互。

将你的应用程序组织成组件,有助于你编写现代和健壮的Web应用程序。JavaScript框架,如ReactVue.js,可以帮助你开发许多种类的组件,并被开发人员广泛使用。

然而,这些框架的痛点之一是,你需要创建许多组件,即使是输入文本字段这样简单的东西。正因为如此,一个更精简的方法是使用一个组件库,它有现成的组件,你可以根据需要选择和使用。有了组件库,你不需要担心CSS设计、颜色、尺寸和字体,你可以专注于功能。

对于Vue.js,有Vuetify,一个基于Material Design原则的Vue UI库。Vuetify是高度可配置和可定制的。你可以修改组件以适应你的需要,你也可以根据你的品牌风格设置你自己的主题以拥有一个一致的组件库。

在本教程中,你将创建一个基于Vuetify 的待办事项应用程序,并使用Nginx 作为反向代理发布它,这是部署Vue应用程序所需要的。

注意:由于Vue是一个前端框架,你为本教程创建的应用程序将在浏览器中运行。然而,对于额外的功能,如认证或数据持久性,你将需要一个后端。定义或开发这种后端功能不在本文的讨论范围之内。

先决条件

要学习本教程,你将需要以下条件。

  • 一台拥有sudo非root用户的Ubuntu 20.04服务器。要开始使用,请遵循我们的Ubuntu 20.04的初始服务器设置指南。在本教程中,非root用户是 sammy.
  • 安装Nginx,可以按照《[如何在Ubuntu 20.04上安装Nginx]》教程的第1-3步进行。
  • 一个完全注册的域名。本教程将使用 your_domain自始至终。你可以在Namecheap上购买一个域名,在Freenom上免费获得一个,或者使用你选择的域名注册商。
  • 安装Node.js(至少是14.0.0版)。
  • 熟悉Vue.js。

第1步 - 设置你的Vue应用程序

在这一步,你将配置你的Vue.js应用程序。Vue.js有一个客户端,你可以用它来创建项目的模板,这是一个从头开始的好方法。

你首先用这个命令全局安装Vue.js客户端。

sudo npm install -g @vue/cli

接下来,验证版本。

vue --version

编写本教程时,最新的版本是 5.0.8:

Output
@vue/cli 5.0.8

现在你已经安装了@vue/cli ,你可以用它来创建vuejs 应用程序。在本教程中,该应用程序将被称为 vuetify-meets-nginx-app但你可以把这个名字改成你喜欢的名字。

为了创建应用程序,运行这个命令。

vue create vuetify-meets-nginx-app

这个命令是交互式的,有多个选项。在本教程中,选择Default 选项为Vue 2

Output
Vue CLI v5.0.8
? Please pick a preset: (Use arrow keys)
  Default ([Vue 3] babel, eslint)
❯ Default ([Vue 2] babel, eslint)
  Manually select features

警告:在写这篇文章的时候,Vuetify 不支持Vue.js v3 。如果您尝试将Vuetify添加到Vue.js v3 ,您会看到这个错误。

Output
Error: you cannot call "get" on a collection with no paths. Instead, check the "length" property first to verify at least 1 path exists.**

一旦应用程序被创建,你会注意到Vue生成的文件和目录。

├── README.md
├── babel.config.js
├── jsconfig.json
├── node_modules
├── package-lock.json
├── package.json
├── public/
      ├── favicon.ico
      └── index.html
├── src
        ...
└── vue.config.js

这里有一个简单的概述。

  • babel.config.js:Babel是一个Javascript编译器,这个文件定义了它的行为。这对于运行、构建和生成最终的应用程序是必要的。
  • jsconfig.json:这个文件对于编译应用程序也是必要的。例如,这个文件将Javascript代码的版本设置为ECMAScript 2009 (ES5) ,即默认版本。欲了解更多信息,请查看该文件
  • node_modules:包含所有已安装和配置的库的目录。
  • package.json:你的应用程序的主要配置文件。你将在这里看到关于依赖关系的信息以及运行或构建你的应用程序的可用命令。
  • package-lock.json:这是一个关于你的应用程序所使用的所有依赖关系的转储文件。如果你想在另一台笔记本电脑或服务器上安装你的应用程序,这个文件特别有用,npm
  • public:这里,你有npm run serve 命令发布你的应用程序所需的基本代码。它是由@vue/cli 命令生成的。
  • vue.config.js:Vue 配置文件。

src 文件夹中,你会看到以下文件和目录。

src
├── App.vue
├── assets
│ ├── logo.png
│ └── logo.svg
├── components
│ └── HelloWorld.vue
├── main.js

这里有一个简单的概述。

  • App.vue:Vue.js应用程序的顶级组件。所有其他组件将在这里定义的组件内。
  • assets:所有资产,如图片、CSS文件和字体,都必须放在这里。
  • components:这里包含你创建的所有组件。@vue/cli 命令生成了一个名为HelloWorld.vue 的组件。
  • main.js:应用程序的主文件。如果你需要配置一个库或插件,这就是这个文件。它还创建了Vue应用程序。

现在你可以导航到vuetify-meets-nginx-app 目录。

cd vuetify-meets-nginx-app

要在开发模式下启动应用程序,运行以下命令。

npm run serve

你的输出将看起来像这样。

Output
INFO  Starting development server...

DONE  Compiled successfully in 27235ms

 App running at:
 - Local:   http://localhost:8080/
 - Network: unavailable

 Note that the development build is not optimized.
 To create a production build, run npm run build.

一旦开发服务器启动了,就可以到localhost:8080 ,看看这个应用程序。

Default home screenshot of a VueJS application

注意:如果你在远程服务器上学习本教程,你可以使用端口转发来在浏览器中看到该应用程序。确保端口8080 在你的服务器上是开放的。当开发服务器仍在运行时,在你的本地计算机上打开另一个终端,输入以下命令以启动端口转发。

ssh -L 8080:localhost:8080  your_non_root_user@your_server_ip

连接到服务器后,在你本地机器的网络浏览器上导航到http://localhost:8080 。在本教程的其余部分,保持第二个终端的开放。

在这一步,你创建了你的Vue.js应用程序。接下来,您将把Vuetify添加到该项目中。

第2步 - 将Vuetify整合到Vue应用程序中

在这一步,您将把Vuetify加入您的Vue.js应用程序。

如果没有像Vuetify这样的组件库,您将不得不使用HTML输入,如divbutton ,为您的网络应用程序设计CSS,如果您想要一些可重复使用的区块,还需要创建自己的组件。但是有了Vuetify库,您只需要导入您想要的组件并将它们添加到您的模板中。

Vuetify也是高度可定制的。例如,您可以有主题。一个主题是一个CSS库,包括诸如调色板、自定义屏幕尺寸和字体。例如,如果您的primary 颜色是blue ,您可以以这种方式配置Vuetify,每次您使用CSS类primary ,Vuetify就会使用蓝色的颜色。

要开始添加Vuetify,请在运行开发服务器的终端键入CTRL+C ,终止您在上一步中启动的开发服务器。

接下来,在vuetify-meets-nginx-app 目录中运行以下命令。

vue add vuetify

这个命令使用Vue.js客户端安装Vuetify。

在预设选项列表中选择default 的配置。

Output
📦  Installing vue-cli-plugin-vuetify...

added 38 packages, and audited 39 packages in 2s

7 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilitiesSuccessfully installed plugin: vue-cli-plugin-vuetify

? Choose a preset: (Use arrow keys)
  Configure (advanced)
❯ Default (recommended)
  Vite Preview (Vuetify 3 + Vite)
  Prototype (rapid development)
  Vuetify 3 Preview (Vuetify 3)

几分钟后,你可以再次启动开发服务器。

npm run serve

导航到localhost:8080 ,在那里你可以看到具有新的 "vuetified "风格的应用程序。

Default home screenshot of a VueJS application with Vuetify installed

在这一点上,您已经创建了一个基本的应用程序,并添加了Vuetify的样式。接下来,您将创建一个具有额外功能的待办事项应用程序。

步骤3 - 创建一个待办事项的Vuetify应用程序

在这一步,您将创建一个待办事项的应用程序。一个待办事项应用程序是一个任务列表,需要一些基本的功能。

  • 一种添加新任务的方法。
  • 一种将它们标记为已完成的方法。
  • 一种显示它们的方法,让用户看到什么是待定的。

为了给你的应用程序添加这些功能,你将修改App.vue 文件,它是应用程序的顶级组件。所有其他组件将在这里定义的组件内。

注意:Vue.js,像许多其他框架一样,默认使用热重载。如果你在开发代码时不断运行npm run serve 命令,你会在保存修改后立即在浏览器中看到更新。

导航到src/App.vue ,用nano 或你喜欢的文本编辑器打开它进行编辑。

cd src
nano App.vue

这里是默认的代码。

vuetify-meets-nginx-app/src/App.vue

<template>
  <v-app>
    <v-app-bar
      app
      color="primary"
      dark
    >
      <div class="d-flex align-center">
        <v-img
          alt="Vuetify Logo"
          class="shrink mr-2"
          contain
          src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
          transition="scale-transition"
          width="40"
        />

        <v-img
          alt="Vuetify Name"
          class="shrink mt-1 hidden-sm-and-down"
          contain
          min-width="100"
          src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
          width="100"
        />
      </div>

      <v-spacer></v-spacer>

      <v-btn
        href="https://github.com/vuetifyjs/vuetify/releases/latest"
        target="_blank"
        text
      >
        <span class="mr-2">Latest Release</span>
        <v-icon>mdi-open-in-new</v-icon>
      </v-btn>
    </v-app-bar>

    <v-main>
      <HelloWorld/>
    </v-main>
  </v-app>
</template>

<script>
import HelloWorld from './components/HelloWorld';

export default {
  name: 'App',

  components: {
    HelloWorld,
  },

  data: () => ({
    //
  }),
};
</script>

每个组件都有两部分:一个模板(通常是HTML代码)和一个用Javascript编写的功能的脚本。

模板是终端用户在浏览器中看到的东西,决定了用户如何与你的应用程序互动。通常情况下,你必须导入组件才能在模板中使用,但由于你把Vuetify 作为一个插件安装,所有的组件都可以在模板中使用,而不需要明确导入。

在模板块中,有大量的v- HTML标签。虽然对HTML来说是非标准的,但这些标签是Vuetify的组件,并且总是以v-

在模板中,你目前有。

  • v-app:主组件,它被附在网站的主体上。
  • v-app-bar: 默认的边栏。
  • v-img: 一个加载图片的组件。
  • v-icon: 一个显示图标的组件。
  • v-spacer:一个使下一个组件向右对齐的组件。

关于App.vue 文件中的script 块,Vuetify的安装并没有在这里添加任何代码,所以你所拥有的是由Vue cli 命令生成的起始代码和一个Vue组件所需的最低代码。

现在你已经看完了App.vue 文件中的默认代码,你准备开始创建你的待办事项应用程序。第一步将是删除一些你不会使用的默认代码。

清理App.vue文件

默认的HelloWorld 组件对你的待办事项应用程序来说是不必要的,所以你将从App.vue 文件中删除它。

要在另一个组件或视图中使用一个Vue组件,你必须将该组件导入文件的脚本块中。在你的App.vue 文件中,你在第一行中导入了HelloWorld 组件。

vuetify-meets-nginx-app/src/App.vue

...
import HelloWorld from './components/HelloWorld';
...

因为你不会使用这个组件,所以删除import 行。

下一步是将该组件从App.vue 页面的组件依赖列表中删除。在script block ,找到以下几行。

vuetify-meets-nginx-app/src/App.vue

...
<script>
  ...

  components: {
    HelloWorld,
  },

  ...
</script>

删除 HelloWorld行,从components 的列表中删除。

最后一步是从模板块中删除它。

vuetify-meets-nginx-app/src/App.vue

...
<template>
    ...
    <v-main>
      <HelloWorld/>
    </v-main>
  </v-app>
</template>
...

删除该 HelloWorld行。

现在,默认的HelloWorld 组件已经被移除,你可以开始创建你的待办事项应用程序。

添加组件的数据字段

为了建立你的待办事项应用程序,你将为你的应用程序添加数据字段。组件的data 是一个函数,它返回所有你将能在模板中使用的数据模型。所有这些数据模型都是一个对象内的Javascript变量,也将被组件的方法所访问。

script 块中找到data 字段。

vuetify-meets-nginx-app/src/App.vue

...
<script>
  ...
  data: () => ({
    //
  }),
};
</script>

你将修改data 函数来存储你的任务列表。将以下突出显示的行添加到data 函数中。

vuetify-meets-nginx-app/src/App.vue

...
<script>
    ...
    data: () => ({
        tasks: ['task 1', 'task 2', 'task 3'],
        newTask: null
    }),
};
</script>

通过这次更新,你添加了两个数据模型:一个是存储任务名称的newTask 变量,另一个是任务列表的tasks 。这两个数据模型现在都可以在模板和方法中使用。

向你的应用程序添加功能

接下来,你将添加功能。在Vue.js组件中,功能在一个名为methods 的函数列表内。在data 模型下的script 块中,添加高亮的行来添加三个函数。

vuetify-meets-nginx-app/src/App.vue

...
<script>
export default {
    name: 'App',

    data: () => ({
        tasks: ['task 1', 'task 2', 'task 3'],
        newTask: null
    }),
    methods: {
        addNewTask() {
            this.tasks.push(this.newTask);
            this.clearNewTask();
        },
        clearNewTask() {
            this.newTask = '';
        },
        removeTask(i) {
            this.tasks.splice(i, 1);
        }
    }
};

你添加了三个函数。

  • addNewTask:要把newTask 数据模型里面的新任务添加到tasks 列表中。
  • clearNewTask:要清除newTask 的数据模型。
  • removeTask:根据一个数组索引从tasks 中删除一个任务。

现在你已经为你的待办事项应用程序添加了功能。接下来,你将修改模板以使用这些方法。

更新模板

你的待办事项应用程序的最后一块是模板。在这一节中,你将更新模板以使用你在前几节中添加的方法和数据模型。

你需要从v-app-bar 中删除一些你不需要的组件。删除v-btn 块。然后,你的代码将看起来像这样。

vuetify-meets-nginx-app/src/App.vue

<template>
    <v-app>
        <v-app-bar
            app
            color="primary"
            dark
        >
        <div class="d-flex align-center">
            <v-img
                alt="Vuetify Logo"
                class="shrink mr-2"
                contain
                src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
                transition="scale-transition"
                width="40"
            />

            <v-img
                alt="Vuetify Name"
                class="shrink mt-1 hidden-sm-and-down"
                contain
                min-width="100"
                src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
                width="100"
            />
        </div>

        <v-spacer></v-spacer>

        </v-app-bar>
        ...
    </v-app>
</template>

接下来,你将添加一些组件来定义你的应用程序的基本布局。首先是一个v-container ,这是一个提供居中和水平垫高你的应用程序内容的组件。关于这个组件和其他容器的更多信息可以在Vuetify的网格系统文档中找到。

v-container ,您将添加一个v-card 组件,这是另一个Vuetify容器。它对组织屏幕上的内容很有用,比如一个面板或一个静态图像。

在当前模板中找到v-main 块,并添加高亮的行。

vuetify-meets-nginx-app/src/App.vue

...
<v-main>
    <v-container>
        <v-card elevation="0">
        </v-card>
    </v-container>
</v-main>
...

正如你在代码中看到的,在v-card 组件中有一个elevation="0" 属性。仰角是Vuetify组件内的一个常见属性,它可以调整两个组件之间的相对Z值距离。在这种情况下,你不需要任何距离,而0 ,就是去除海拔的值。

接下来,你将使用两个v-card 功能组件。 v-title,它为卡片标题提供默认的字体大小和填充,以及 v-card-text,它将包含卡片的内容。将突出显示的行添加到你的v-card 组件中。

vuetify-meets-nginx-app/src/App.vue

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title></v-card-title>
            <v-card-text></v-card-text>
       </v-card>
    </v-container>
</v-main>
...

一个功能性组件是一个只渲染模板的组件。它没有任何逻辑,由于它只是一个模板,所以它的渲染速度更快。

现在你有了你的容器组件,你必须添加一个 v-text-field组件来处理新任务的名称。在你刚刚添加的v-card-title 组件中,插入高亮线。

vuetify-meets-nginx-app/src/App.vue

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title>
              <v-text-field
                v-model="newTask"
                label="Task Name"
                prepend-icon="mdi-content-save"
                clear-icon="mdi-close-circle"
                clearable
                filled
                type="text"
                @click:prepend="addNewTask"
                @click:clear="clearNewTask"
                ></v-text-field>
            </v-card-title>
           ...
        </v-card>
    </v-container>
</v-main>
...

v-text-field 是一个为新任务命名的必要组件。

它有以下属性。

  • v-model="newTask" 将数据模型附加到组件上。你输入的任何文本也将被添加到数据模型中。
  • label="Task Name" 是输入类型的占位符中的文本。
  • prepend-icon="mdi-content-save" 将在文本框的左角显示保存图标。
  • clear-icon="mdi-close-circle"清除按钮的图标。
  • clearable 显示**"**清除 "图标。
  • filled 在组件上应用一个替代的填充输入样式。
  • type="text" 设置底层HTML输入字段的输入类型。其他选项包括 或 。email password
  • @click: prepend="addNewTask"保存按钮的点击事件连接到 函数。addNewTask
  • @click: clear="clearNewTask" 将 "保存"按钮的点击事件连接到 函数。clearNewTask

下一步是在任务列表模型中显示每个任务。为此,你将使用 v-timeline组件,它是一个显示组件,用于显示基于时间或顺序的信息。你要把它添加到v-card-text ,也就是v-card 体组件。在你已经添加的v-card-text 组件内,插入突出显示的行。

vuetify-meets-nginx-app/src/App.vue

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title>
            ...
            </v-card-title>
            <v-card-text>
              <v-timeline
                  v-if="tasks.length > 0"
                  dense
              ></v-timeline>
            </v-card-text>
            ...
      </v-card>
   </v-container>
</v-main>
...

v-timeline 显示列表中的所有任务。 是为了在数据模型中至少有一个任务时才显示该组件。 是为了浓缩内容(基本上,它从组件的CSS样式中删除了一些填充和边距)。v-if dense

现在,为了显示每个任务的名称,你需要使用一个v-timeline 功能组件:v-timeline-item 。在v-timeline 组件内添加以下几行。

vuetify-meets-nginx-app/src/App.vue

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title>
            ...
            </v-card-title>
            <v-card-text>
              <v-timeline
                  v-if="tasks.length > 0"
                  dense
                >
                    <v-timeline-item
                        v-for="(t, index) in tasks"
                        :key="index"
                    >
                         {{ t }}
                    </v-timeline-item>
              </v-timeline>
            </v-card-text>
        </v-card>
    </v-container>
</v-main>
...

这段代码使用一个 v-for循环为你的任务列表模型中的每个任务显示一个v-timeline-item 组件。由于v-timeline-itemv-timeline 的一个功能组件,它将以按时间顺序排列的列表的风格呈现。

你在v-for 循环中添加索引作为键,因为它是v-for Vue指令的必经之路。

通过{{ t }} 行,你将在v-timeline-item 组件内显示任务的名称。

下一步是添加一个按钮来从列表中删除任务。但在这之前,你需要添加一些额外的网格系统组件来组织任务名称和按钮。在v-timeline-item 组件内添加高亮线。

vuetify-meets-nginx-app/src/App.vue

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title>
            ...
            </v-card-title>
            <v-card-text>
              <v-timeline
                  v-if="tasks.length > 0"
                  dense
                >
                    <v-timeline-item
                        v-for="(t, index) in tasks"
                        :key="index"
                    >
                        <v-row class="display-1 text-capitalize">
                            <v-col cols="7">
                                {{ t }}
                            </v-col>
                            <v-col
                                class="text-right"
                                cols="5"
                            >
                            </v-col>
                        </v-row>
                     </v-timeline-item>
               </v-timeline>
             </v-card-text>
         </v-card>
     </v-container>
</v-main>
...

通过上面的代码,你添加了。

  • 一个带有两个类的v-row 组件,用来设置任务文本的大小(display-1 ,类似于HTML中的H1),并且所有的字符都是大写字母(text-capitalize )。
  • 在行内有一个v-col 组件,显示每个任务的名称,需要7/12部分的空间(cols="7" 属性)。
  • 另一个v-col ,用于放置删除按钮。它需要5/12部分的空间(cols="5" 属性),并且里面的所有组件都向右对齐,由text-right 类决定。

最后,是时候包含一个 v-btn组件,将removeTask 的功能附加到一个按钮组件上。为了简单起见,你将使用一个图标按钮,这是一个只有图标而没有文字的按钮。要做到这一点,你将需要一个 v-icon组件。

添加突出显示的行。

vuetify-meets-nginx-app/src/App.vue

...
<v-timeline
    v-if="tasks.length > 0"
    dense
>
    <v-timeline-item
        v-for="(t, index) in tasks"
        :key="index"
    >
        <v-row class="display-1 text-capitalize">
            <v-col cols="7">
                {{ t }}
            </v-col>
            <v-col
                class="text-right"
                cols="5"
            >
                <v-btn
                    icon
                    @click="removeTask(index)"
                >
                    <v-icon color="red lighten-1" large>
                        mdi-sticker-remove
                    </v-icon>
                </v-btn>
            </v-col>
        </v-row>
    ...
    </v-timeline-item>
</v-timeline>

在你刚刚添加的代码中,v-btn 组件的icon 属性指定了不需要文本。它修改了该组件,使其只具有一个v-icon 组件的样式。

@click 组件事件将你的removeTask 方法与按钮的点击事件绑定。所以每次底层的按钮点击事件产生时,你的方法都会被调用。

你用v-for 循环提供的索引作为removeTask 方法的参数。

最后,v-icon 的颜色将是red lighten-1 ,大小将是large ,你使用了mdi-sticker-remove 材料设计的图标。

现在你已经用一些Vuetify 组件更新了模板,将它们配置为使用和显示你的数据模型的内容,并允许你的应用程序的用户使用你的页面方法与它们交互。

下面是App.vue 文件的最终代码。

vuetify-meets-nginx-app/src/App.vue

<template>
    <v-app>
        <v-app-bar
            app
            color="primary"
            dark
        >
        <div class="d-flex align-center">
            <v-img
                alt="Vuetify Logo"
                class="shrink mr-2"
                contain
                src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
                transition="scale-transition"
                width="40"
            />

            <v-img
                alt="Vuetify Name"
                class="shrink mt-1 hidden-sm-and-down"
                contain
                min-width="100"
                src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
                width="100"
            />
        </div>

        <v-spacer></v-spacer>

        </v-app-bar>

        <v-main>
            <v-container>
                <v-card elevation="0">
                    <v-card-title>
                        <v-text-field
                            v-model="newTask"
                            label="Task Name"
                            prepend-icon="mdi-content-save"
                            clear-icon="mdi-close-circle"
                            clearable
                            filled
                            type="text"
                            @click:prepend="addNewTask"
                            @click:clear="clearNewTask"
                        ></v-text-field>
                    </v-card-title>
                    <v-card-text>
                        <v-timeline
                            v-if="tasks.length > 0"
                            dense
                        >
                            <v-timeline-item
                                v-for="(t, index) in tasks"
                                :key="index"
                            >
                                <v-row class="display-1 text-capitalize">
                                    <v-col cols="7">
                                        {{ t }}
                                    </v-col>
                                    <v-col
                                        class="text-right"
                                        cols="5"
                                    >
                                        <v-btn
                                            icon
                                            @click="removeTask(index)"
                                        >
                                            <v-icon color="red lighten-1" large>
                                                mdi-sticker-remove
                                            </v-icon>
                                        </v-btn>
                                    </v-col>
                                </v-row>
                            </v-timeline-item>
                        </v-timeline>
                    </v-card-text>
                </v-card>
            </v-container>
        </v-main>
    </v-app>
</template>

<script>

export default {
    name: 'App',

    data: () => ({
        tasks: ['task 1', 'task 2', 'task 3'],
        newTask: null
    }),
    methods: {
        addNewTask() {
            this.tasks.push(this.newTask);
            this.clearNewTask();
        },
        clearNewTask() {
            this.newTask = '';
        },
        removeTask(i) {
            this.tasks.splice(i, 1);
        }
    }
};
</script>

保存并关闭你的文件。

如果你没有开发服务器仍在运行,请再次启动它。

npm run serve

现在你可以导航到localhost:8080 ,看看你的应用程序在工作。

The Vuetify to-do app now displays three tasks in a list: Task 1, Task 2, Task 3

在这一步,你创建了一个待办事项应用程序。你增加了功能并更新了用户界面。现在你已经写好了应用程序,你可以生成一个准备用于生产的版本。下一步将是为生产构建应用程序。

第4步 - 为生产构建你的应用程序

在上一步中,你写了你的待办事项应用程序。但在用Nginx 发布之前,你需要为生产准备应用程序。这个步骤被称为构建应用程序。

构建步骤将应用程序转换为可以被浏览器读取的东西。如果你试图在浏览器中打开你的src 文件,你将不会看到任何东西。这是因为它们是Vue.js文件,而不是浏览器可以读取的HTMLJSCSS 等文件。在用Nginx发布你的应用程序之前,你需要为生产构建应用程序,这就是你在这一步要做的。

你可以使用build 命令来自动完成。package.json 是应用程序的主要配置文件。它包含了关于依赖关系的信息以及可用于运行或构建你的应用程序的命令,例如build 命令,如图所示。

package.json

{
  ...
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  ...
}

要开始构建过程,用CTRL+C 停止开发服务器。

在同一个终端,导航到项目目录。

cd vuetify-meets-nginx-app

运行build 命令。

npm run build

当构建完成后,你将在目录dist 中有一个准备用于生产的应用程序的版本。

接下来,你将设置Nginx作为反向代理来部署和访问该应用程序。

第5步 - 将Nginx配置为一个反向代理

现在有了一个可以运行的应用程序,你必须将Nginx配置为一个反向代理,为应用程序的文件提供服务,并将其连接到你的域名。

反向代理是一个运行在服务器上的应用程序或服务,它将外部请求转发到另一个地方。在你的案例中,每当用户在浏览器中访问你的域名时,Nginx将处理这个请求,用你的应用程序的一个文件来回答。它将返回一个文件,因为当你构建应用程序时,会生成HTML、JS和CSS文件,Nginx将把这些文件当作你服务器上的任何其他静态文件或网站。

Nginx 与 。每个网站都是在一个文件中配置的不同网站。所有的配置文件都默认放在 。导航到这个目录。sites /etc/nginx/sites-available/

cd /etc/nginx/sites-available/

创建一个文件,名为 vuetify-meets-nginx-app:

sudo nano vuetify-meets-nginx-app

注意:你可以把文件命名为你想要的任何东西,但把它命名为你想要发布的应用程序或网站是惯例。

在你的vuetify-meets-nginx-app 文件中,添加以下几行,确保用你自己的信息更新server_name

/etc/nginx/sites-available/vuetify-meets-nginx-app

server {
  listen 80;
  listen [::]:80;
  server_name your_domain;
  autoindex on;
  root   /home/sammy/vuetify-meets-nginx-app/dist/;
  index  index.html;
}

在本教程中,你将配置Nginx ,以监听端口80 ,但你可以使用任何你想要的端口。用你的域名代替 your_domain替换为你的域名。如果是在本地开发环境中测试,也可以使用服务器的IP地址或localhost

使用root ,你要告诉Nginx所有的文件都在 /home/sammy/vuetify-meets-nginx-app/dist/目录中,该目录是在上一步中创建的。最后,用index 行告诉Nginx,主文件是index.html

保存并关闭你的文件。

接下来,你需要解决Nginx配置文件中的一个权限问题。

Nginx 是在你的服务器上运行的一个服务。你可以用下面的命令列出所有与Nginx有关的运行进程。

ps -fea | grep nginx

ps 命令带有-fea 标志,以全格式列出所有当前进程。然后对该命令的输出进行过滤,只列出与nginx 匹配的进程。

输出结果将类似于下面的内容。

Output
root       39922       1  0 Jul14 ?        00:00:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
www-data   39923   39922  0 Jul14 ?        00:00:01 nginx: worker process
sammy     117909  117434  0 21:27 pts/0    00:00:00 grep --color=auto nginx

如输出中所示,nginx 服务是以用户www-data 运行的。

接下来,用这个命令查看/home/sammy/vuetify-meets-nginx-app/dist/ 的权限。

ls -l /home/sammy/vuetify-meets-nginx-app/dist/

输出结果将类似于下面的内容。

Output
total 20
drwxrwxr-x 2 sammy sammy 4096 Jul 14 18:54 css
-rw-rw-r-- 1 sammy sammy 4286 Jul 14 18:54 favicon.ico
-rw-rw-r-- 1 sammy sammy  853 Jul 14 18:54 index.html
drwxrwxr-x 2 sammy sammy 4096 Jul 14 18:54 js

所有的文件和文件夹都有权限,可供用户sammy 。如果你配置Nginx 读取这些文件,将无法工作,因为Nginx用户www-data 没有执行权限。

有几个选项可以解决这个问题。

  • 授予Nginx对dist 文件夹的读、写和执行权限。然而,授予一个可以从整个网络访问的服务读取本地用户文件的权限是不安全的。此外,Nginx将需要访问所有的父文件夹的权限,因为它需要导航到最后的文件夹,这基本上会向全世界开放/home 目录。不建议这样做。
  • sudo 运行nginx 。这也是不安全的,因为现在你将有一个可以访问你服务器上所有文件的服务。
  • dist 的内容移到只有Nginx可以访问而其他人不能访问的地方。这是最安全的选择。

在本教程中,你将使用第三个选项。

在Ubuntu和其他一些基于Linux的发行版中,服务之间交换文件的共享位置是/var 。你将把文件复制到/var/www ,这是Nginx为网站使用的默认路径。

在你的项目目录下,运行下面的命令,将文件复制到/var/www

sudo cp -r /home/sammy/vuetify-meets-nginx-app/dist /var/www/vuetify-meets-nginx-app

所有的文件都将以之前的权限被复制,也就是sammy 用户的权限,所以你需要将sammy 用户添加到与www-data 相同的权限组。你可以用下面的命令来完成这个工作。

sudo usermod -aG www-data sammy

在这一点上,Nginx可以以安全的方式访问必要的文件。然而,每次生成新版本的应用程序时,你都需要复制项目文件,这将使自动部署、CI/CD工具等变得复杂。一个更好的解决方案是修改build 命令,在正确的路径上直接生成文件。

要做到这一点,请打开package.json 进行编辑,并添加突出显示的文本。

package.json

...
"build": "vue-cli-service build --dest /var/www/vuetify-meets-nginx-app",
...

保存并关闭你的文件。

现在你可以完成Nginx的配置了。打开Nginx配置文件,用新的应用程序路径更新它。

/etc/nginx/sites-available/vuetify-meets-nginx-app

server {
  listen 80;
  listen [::]:80;
  server_name your_domain OR your_server_IP;
  autoindex on;
  root   /var/www/vuetify-meets-nginx-app;
  index  index.html;
}

保存并关闭你的文件。

现在,你的网站文件已经准备好了,你需要启用它。为此,进入已启用的站点路径。

cd /etc/nginx/sites-enabled/

禁用默认站点,以确保你没有启用两个站点并在同一端口上监听(端口80 )。

sudo rm default

最后,为你的应用程序的配置文件创建一个simlink文件。

sudo ln -s /etc/nginx/sites-available/vuetify-meets-nginx-app

Nginx将只考虑放置在启用目录中的站点文件。你可以直接复制配置文件,但不建议有重复的文件,因为你有可能会出现差异。这就是为什么simlink ,一个可用文件的快捷方式,是一个更好的方法。

测试一下,确保你的Nginx文件中没有语法错误。

sudo nginx -t

你的输出将看起来像这样。

Output
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is
successful

为了应用这些变化,重新启动Nginx服务。

sudo systemctl restart nginx

现在,你可以导航到你的域名(或你的服务器IP地址),查看你的待办事项应用程序的准备和发布。

The Vuetify to-do app displays three tasks in a list: Task 1, Task 2, Task 3

在这一步中,你将Nginx配置为一个反向代理来发布你的应用程序。

总结

在本教程中,你创建了一个Vue.js应用程序,并安装和配置了Vuetify。然后,你生成了一个静态版本的应用程序,准备用于生产,最后,配置了Nginx服务来发布它。