从零到部署:用 Vue 和 Express 实现迷你全栈电商应用(一)

Vue 是尤雨溪在 2014 年创建的一个前端框架,目前 Github Star 数高达 150K,是 Star 数最高的前端项目,并且 Vue 有着极为活跃的社区生态以及专职团队进行维护以确保项目可以健康长久地发展。

目前中国很多互联网公司前端程序员的招聘要求都要求程序员掌握 Vue,像滴滴、美团、饿了么等大厂也在重度使用 Vue 进行开发,并且有着像 ElementmpvueiView 这样优秀的基于 Vue 开源项目存在,所以学习 Vue 是一个不错的投资,当你学会 Vue,就可以快速开发项目,这样不仅可以接外包挣外快,而且当有了一定的项目经验,还可以在一线互联网大厂找到一份不错的工作。

看到这里你心动了嘛?心动不如行动!而最幸运的是,本教程将会一步一步带你以实战的方式实现一个迷你全栈电商网站。并在实战的过程中,了解 Vue 的全貌,现在就打开电脑,跟随我的脚步,彻底掌握 Vue 开发!本系列教程的规划如下:

  1. 第一部分(也就是这篇教程):用 Vue 搭建前端项目的骨架,实现基于嵌套、动态路由的多页面跳转。
  2. 第二部分(✍️写作中):用 Express 实现后端 REST API,并使用 MongoDB 进行数据存储。
  3. 第三部分(✍写作中):通过 Vue 的双向数据绑定和模板语法实现数据添加,并用 Vuex 实现前端的状态管理。
  4. 第四部分(✍写作中):用 Element UI 组件库让界面更加专业美观,并且用 Docker 和 Docker Compose 部署我们的全栈项目。

如果您觉得我们的教程写得还不错,请记得给我们点个赞哦!鼓励我们更快更好地写完剩下的教程!你也可以在评论区留言,告诉我们想要实现什么功能,我们一定会仔细考虑的哦!

快速入门

代码

你可以在 Github 查看本教程最终的源码:源码地址

项目准备

安装依赖

安装 Node.js,你可以去 Node.js 官网下载安装包,通过安装包安装会同时安装 Node.js 包管理工具 Npm,用于便捷的管理项目依赖和下载第三方包。

打开终端,输入如下命令测试是否安装成功:

node -v # v10.16.0
npm -v # 6.9.0

如果在你的终端有如上输出,那么代表你安装成功。

提示

通过上面安装包安装,你会安装最新的 Node 稳定版本,这可能和我的机器上的 Node 版本不一致,但是不用担心,本教程使用到的代码语法适用于绝大多数新的或更老的 Node。

安装 vue-cli,在绝大多数场景下,我们使用 vue-cli 来初始化我们的 vue 项目,本教程也不例外,在终端运行如下命令来安装:

npm install -g vue-cli

打开终端,输入如下命令测试是否安装成功:

vue --version # 2.9.6

提示

虽然 Vue 3.x 已经正式推出,但是这篇教程用的是主流、稳定的 Vue 2.x 版本。由于 Vue 的整体思想基本没有改变,所以不影响我们的学习。在接下来的教程中,我们会迁移到 Vue 3.x,并通过讲解新老 Vue 版本的差异让你更好地理解 Vue 的演变。

上面两个安装步骤已经足够完成我们的教程的学习,但是我想额外推荐你一款编辑器,VSCode,你可以通过访问 VSCode 官网安装。

在 VSCode 里面找到 Vue 插件,可以获得代码语法高亮以及自动格式化非常便捷的功能,并且 VSCode 天然对 JavaScript 的支持,会大大提高我们的开发效率,本教程所涉及项目的开发都是使用 VSCode 完成的。

初始化项目

打开终端输入如下命令初始化我们的 Vue 项目:

vue init webpack vue-online-shop-frontend

命令行接着会显示一些列选项让你选择,具体我们的选择如下图:

提示

其中 Author 字段你可以填自己的昵称,然后我们在选择了安装 vue-router 之后,其他的都选择了 no,因为本篇教程是面向初学者的实战教程,如果引入了过多和 Vue 核心无关的概念,就会引起很多困惑,所以这里我们不配置它们

当项目初始化成功之后,接下来通过如下命令开启项目:

# code vue-online-shop-frontend # 如果你使用了 VSCode 编辑器,可以用这行命令打开项目
cd vue-online-shop-frontend && npm start

接着打开浏览器,访问 http://localhost:8080/ 查看我们初始好的项目效果。

注意

如果你使用 VSCode 编辑器打开项目进行开发,在运行 code project-name 之前需要安装 code 脚本,具体我找了一篇教程:戳我访问

初探脚手架代码

通过 vue-cli 初始化的脚手架代码中,我们在整个教程中需要了解的就是以下五个文件:

  • src/main.js
  • index.html
  • src/App.vue
  • src/router/index.js
  • src/components/HelloWorld.vue

src/main.js

首先我们来看一下 src/main.js,这个是 Vue 应用的入口。我们通过导入 Vue 类、App 组件、router 路由,再加上 el ,将这些参数传给 Vue 类,生成一个 Vue 实例。

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import App from './App';
import router from './router';

Vue.config.productionTip = false;

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>',
});

index.html

接着我们来看一下 index.html 文件,它的代码是这样的:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>vue-online-shop</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

当我们开启项目之后,Vue 所使用的构建工具 Webpack 将会:

  • 根据入口文件 src/main.js 里面声明的 el 属性(#app),找到 index.html 中 id 为app 的 DOM 节点
  • 把编译好的视图模板代码挂载到这个 DOM 节点下面
  • 将项目涉及的 JavaScript 和 CSS 代码以 scriptlink 的方式插入到 index.html
  • 开启开发服务器,打开浏览器,进而浏览器将 index.html 渲染,我们就可以看到写好的 Vue 页面效果。

src/App.vue

src/App.vue 就是 Vue 为我们提供的组件文件,使得我们可以以组件的形式来组织代码,并通过组件的组合来构建任意规模的项目,代码如下:

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',
};
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

可以看到, App 组件包含了三个部分的代码:

  • template
  • script
  • style

其实就是对应了传统 Web "三剑客",HTMLJavaScriptCSS

这里在 template 部分展示了一张 Vue 的 logo 图片,然后显示此刻渲染的路由组件:<router-view />。我们将在后面继续讲解路由,这里不懂也没有关系哦。script 部分,主要是导出了一个名为 App 的组件。style 部分就是我们熟悉的 CSS 代码了。

提示

上面所讲的是比较小的组件的写法,当组件中涉及的代码较多时,我们需要把 scriptstyle 抽成独立的 .js.css 文件。就像下面这样:

<!-- ./src/App.vue -->
<template>
<div id="app">
 <img src="./assets/hello.png">
</div>
</template>
<script src="./app.js"></script>
<style src="./app.css"></style>

Vue 组件和模板语法是 Vue 的核心概念,我们在后面会以实战的形式重点讲解这些内容。

src/router/index.js

src/router/index.js 文件是 Vue 为我们提供的路由文件,代码如下:

import Vue from 'vue';
import Router from 'vue-router';
import HelloWorld from '@/components/HelloWorld';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld,
    },
  ],
});

首先我们导出了 Vue 类,以及 Router 类,以及我们的 HelloWorld 组件。

接着我们使用 Vue.use(Router) 告诉 Vue,我们应用接入了路由。

在导出的路由实例中,我们通过 routes 数组定义了项目所有的页面,每个页面是一个类似 { path, name, component } 的对象。

比如初始化时生成的 HelloWorld.vue 就是我们的网站首页 -- 也就是打开浏览器访问到的第一个页面,因为它的路径(path)定义为 / 。此外一个页面定义还需要 name,它代表此页面在 vue-router 中的标识符,component 则代表此页面渲染时的所用到的组件。

提示

这里我们可以看到导出 HelloWorld 组件时,我们在路径最前面加上了 "@",那是因为我们在 webpack 配置中将会 "@"
映射成 resolve('src'),也就是我们项目目录下 src 文件夹的路径,最后我们的 '@/components/HelloWorld' 的实际上的效果相当于取到了项目目录 src 文件夹里面的 components/HelloWorld 组件。

src/components/HelloWorld.vue

最后是 src/components/HelloWorld.vue 文件,是脚手架代码为我们提供的一个实例组件,代码如下:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>Essential Links</h2>
    <!-- 省略其他模板代码 ... -->
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      msg: 'Welcome to Your Vue.js App',
    };
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

可以看到,其结构大致与 App.vue 类似。其中也有一些不同,比如 script 里面的 data 字段,还有 template 中的 {{ }} 语法。这个我们会在下一节进行讲解。

当我们打开浏览器时,地址为:http://localhost:8080/ 此时路径为 /,激活 HelloWorld.vue 组件,所以最后我们整个项目渲染 App.vue 的内容,显示的结构即为:

  • 一张 Vue logo 图
  • 我们的 HelloWorld.vue 组件的内容

小结

通过一窥 vue-cli 为我们初始化的项目代码,我们可以学到如下的知识:

  • Vue 通过组件来组织项目,单个组件就是我们传统的 Web "三剑客":HTML、JavaScript、CSS。
  • Vue 通过路由来定义多个页面,并且进行页面之间的跳转。
  • 一个页面是一个组件,一个组件可以由很多组件组成,通过这种组合式的思想,我们可以编写任意复杂的项目。

编写你的第一个 Vue 页面

下面我们来编写电商应用的首页。

编写页面组件

src/components 下面创建 Home.vue 文件,然后编写如下代码:

<template>
  <div>
    <div class="title">
      <h1>{{msg}}</h1>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'home',
    data () {
      return {
        msg: 'Welcome to Your Vue.js App'
      };
    },
  }
</script>

在上面,我们创建了一个名为 Home.vue 的 Vue 组件,可以看到它和我们之前的 HelloWorld.vue 的内容大致相仿,但是也有一些不同的地方:

  • 首先,我们暂时没有写样式(没有 style 部分),而是先专注于基础知识的讲解。在后续教程中,我们会使用 Element UI 组件库让我们的界面变得专业美观。
  • 其次,我们在 script 中引入了 data ,在 template 引入了插值语法 {{var}}。其中 data 是声明此组件的初始化数据,而 {{var}} 插值语法是方便将数据渲染到视图模板中;这里我们将在 script 中定义的 data 中的 msg 插值到视图模板中,最终会渲染一个 h1 标签,标签内容就为 msg 的内容。

接入路由

接着,我们在 src/router/index.js 路由中将主页路由 / 所绑定的组件从默认的 HelloWorld 修改为刚才写好的 Home 组件:

import Vue from 'vue';
import Router from 'vue-router';
import Home from '@/components/Home';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
    },
  ],
});

现在我们保存代码,打开浏览器,就会显示刚才在 Home.vue 里面填写的那个标题:

使用路由进行多页面跳转

一个经典的电商应用通常包括如下部分:

  • 商品展示列表 (Home.vue
  • 商品详情(Detail.vue
  • 购物车(Cart.vue

这里因为我们追求简单,也将商品的后台管理页面 (Admin.vue)放入了项目中。

现在我们先来实现商品展示列表、购物车和后台管理页面的模板内容。因为商品详情页后面将会使用组件进行复用,所以这里我们暂时先不创建。

添加首页导航

首先修改 App.vue ,加入三个导航链接 router-link,方便让用户跳转到到自己想看的页面,代码如下:

<template>
  <div id="app">
    <nav>
      <div class="container">
        <ul class="nav__left">
          <li>
            <router-link to="/">Home</router-link>
          </li>
          <li>
            <router-link to="/admin">Admin</router-link>
          </li>
          <li>
            <router-link to="/cart">Cart</router-link>
          </li>
        </ul>
      </div>
    </nav>

    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',
};
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

这里我们在头部添加了三个 router-link 代表我们的头部导航。

router-linka 标签类似,只不过 Vue 为它添加一些额外的优化逻辑。

创建后台管理页面

接着我们编写 Admin.vue 组件,但这里稍微有点不同,即我们在 src/pages 下创建 Admin.vue 组件,因为对于页面级组件,我们倾向于将其放到一个特殊 pages 文件夹,这样方便组织项目。其代码如下:

<template>
  <div>
    <div class="title">
      <h1>{{msg}}</h1>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'home',
    data () {
      return {
        msg: 'Welcome to the Admin Page'
      }
    }
  }
</script>

可以看到这个页面内容和我们之前的 Home.vue 内容相似。

创建购物车页面

然后是我们的购物车页面 Cart.vue,代码如下:

<template>
  <div>
    <div class="title">
      <h1>{{msg}}</h1>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'home',
    data () {
      return {
        msg: 'Welcome to the Cart Page'
      }
    }
  }
</script>

可以看到这个页面内容和我们之前的 Home.vue 内容相似。

将新页面导入路由

最后,我们把上一步中创建的 Home.vue 移到 src/pages 目录中,并在路由文件 src/routes/index.js 中导入这三个页面,代码如下:

import Vue from 'vue';
import Router from 'vue-router';
import Home from '@/pages/Home';
import Admin from '@/pages/Admin';
import Cart from '@/pages/Cart';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
    },
    {
      path: '/admin',
      name: 'Admin',
      component: Admin,
    },
    {
      path: '/cart',
      name: 'Cart',
      component: Cart,
    },
  ],
});

保存代码,然后打开浏览器,我们可以看到下面的效果:

你可以点击头部的三个导航链接,下面的标题内容会随着点击的链接变化。恭喜你,你已经成功地实现了一个基于 Vue 的多页面网站了!

使用嵌套路由和动态路由合理组织页面

随着页面的增多,如果我们把所有的页面都塞到一个 routes 数组里面会显得很乱,你无法确定哪些页面存在关系。还好 vue-router 提供了嵌套路由的功能,让我们能把相关联的页面组织在一起。

升级路由

在我们的商城项目中,后台管理页 Admin 涉及到很多操作页面,比如:

  • /create 创建新的商品
  • /edit 编辑商品信息

让我们通过嵌套路由的方式将它们组织在一起。首先在路由文件 src/router/index.js 中声明后台管理所有用到的页面组件(我们马上就会去实现它们):

import Vue from 'vue';
import Router from 'vue-router';

import Home from '@/pages/Home';
import Cart from '@/pages/Cart';

// Admin Components
import Index from '@/pages/admin/Index';
import New from '@/pages/admin/New';
import Products from '@/pages/admin/Products';
import Edit from '@/pages/admin/Edit';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
    },
    {
      path: '/admin',
      name: 'Admin',
      component: Index,
      children: [
        {
          path: 'new',
          name: 'New',
          component: New,
        },
        {
          path: '',
          name: 'Products',
          component: Products,
        },
        {
          path: 'edit/:id',
          name: 'Edit',
          component: Edit,
        },
      ]
    },
    {
      path: '/cart',
      name: 'Cart',
      component: Cart,
    },
  ],
});

嵌套路由的用法就是给需要归为一类的页面设置一个入口页面,然后把这一类页面都放到这个路由页面路由定义的 children 字段数组中。

通过上面的代码我们可以看到,Admin 类别下有四个组件,Index 是我们 Admin 类别的入口组件,也是作为 path = /admin 的渲染组件,然后其他组件就放到 path = /admin 这个路由定义的 children 数组里,其定义和其他父级一致。

这样的嵌套写法带来了两个好处:

  • 很清晰的组织了一类页面,方便阅读。
  • 在定义路由的 path 的时候,复用了父级的 path,即现在我们的 New 这个路由,它在浏览器中访问的路径为:'/admin' + 'new',如果我们统一放到 routes 数组的第一级定义,那么后面的 ProductsEditpath 都要带上诸如 /admin/admin/edit/:id 这样长长的路径,显得特别复杂。

这里还有一个改变就是,我们发现 Edit 这个路由的 path 有点不太一样,它有个特殊的标志 edit/:id,这种写法被称为动态路由,即 :id 会匹配任意字符串,所以用户访问 /admin/edit/<any-string> 都会激活 Edit 路由,从而渲染 Edit.vue 组件。

创建 Admin 的子页面

首先创建 Index.vue 入口组件,代码如下:

<template>
  <div>
    <div class="admin-new">
      <div class="container">
        <div class="col-lg-3 col-md-3 col-sm-12 col-xs-12">
          <ul class="admin-menu">
            <li>
              <router-link to="/admin">View Products</router-link>
            </li>
            <li>
              <router-link to="/admin/new">New Products</router-link>
            </li>
          </ul>
        </div>
        <router-view></router-view>
      </div>
    </div>
  </div>
</template>

可以看到,它作为嵌套路由的入口级组件,和我们之前在 App.vue 里面看到的样子类似,在其中会有 router-link 导向更深层级的路由.

router-view 用于渲染子路由组件,比如我们此时访问 /admin/new 页面,那么 router-view 部分会被替换成 New.vue 组件的内容,因为我们在之前的嵌套路由定义中 pathnew 的渲染组件为 New.vue

创建 src/pages/admin/Edit.vue 组件,代码如下:

<template>
  <div>
    <div class="title">
      <h1>This is Admin/Edit/{{$route.params.id}}</h1>
    </div>
  </div>
</template>

可以看到,当用户访问 /admin/edit/:id,会激活渲染 Edit.vue 组件,我们可以通过 $route.params.id 的方式获取用户输入的路径 :id 部分。比如我们在浏览器中输入 /admin/edit/52tuture,那么浏览器将会以 h1 的形式打出 "This is Admin/Edit/52tuture"

提示

$route 这个变量是 Vue 在运行时为我们自动插入到所有组件属性中的,所有我们不用手动去管理它。

创建 src/pages/admin/New.vue,代码如下:

<template>
  <div>
    <div class="title">
      <h1>This is Admin/New</h1>
    </div>
  </div>
</template>

创建 src/pages/admin/Products.vue ,代码如下:

<template>
  <div>
    <div class="title">
      <h1>This is Admin</h1>
    </div>
  </div>
</template>

保存我们编写的内容,打开浏览器,我们可以看到如下内容:

至此,我们的迷你全栈电商应用的第一部分就完成了,在接下来的教程中,我们将用 Express 和 MongoDB 搭建这个电商应用的后端 API,不见不散哦~

想要学习更多精彩的实战技术教程?来图雀社区逛逛吧。