Vue CLI 简介

在第五章 “添加 Vue 组件” 中,我们使用 webpack 创建了自定义的 Vue SFC 应用。作为一名开发者,了解复杂事物的内部机制很有用,我们也必须理解如何使用通用和标准的模式与他人协作。因此,现在我们倾向于使用框架。Vue CLIVue 应用开发的标准工具。它完成了我们的 webpack 自定义工具所做的工作,甚至更多。如果你不想创建自己的 Vue SFC 开发工具,Vue CLI 是一个绝佳的选择。它开箱即用地支持 BabelESLintTypeScriptPostCSSPWA、单元测试和端到端测试。要了解更多关于 Vue CLI 的信息,请访问 https://cli.vuejs.org/zh/。

安装 Vue CLI

使用 Vue CLI 非常容易上手。请执行以下步骤:

  1. 使用 npm 全局安装它:

    $ npm i -g @vue/cli
  2. 在你想要创建项目时执行:

    $ vue create my-project
  3. 你将被提示选择一个预设 - 默认或手动选择功能,如下所示:

    Vue CLI v4.4.6
    ? Please pick a preset: (Use arrow keys)
    > default (babel, eslint)
      Manually select features
  4. 选择默认预设,因为我们稍后可以手动安装我们需要的东西。安装完成后,你应该在终端中看到类似于以下输出最后一部分的内容:

    Successfully created project my-project.
    Get started with the following commands:
    $ cd my-project
    $ npm run serve
  5. 将你的目录更改为 my-project 并开始开发过程:

    $ npm run serve

    你应该会看到类似这样的内容:

    DONE  Compiled successfully in 3469ms
    
      App running at:
      - Local:   http://localhost:8080/
      - Network: http://199.188.0.44:8080/
    
      Note that the development build is not optimized.
      To create a production build, run npm run build.

在接下来的章节中,我们将使用 Vue CLI 将你在前面章节中学到的导航守卫转换为适当的中间件。这意味着我们将把所有的钩子和守卫分离到单独的 .js 文件中,并将它们保存在一个名为 middlewares 的公共文件夹中。但是,在此之前,我们应该首先了解 Vue CLI 为我们生成的项目目录结构,然后添加我们自己需要的目录。让我们开始吧。

理解 Vue CLI 的项目结构

使用 Vue CLI 创建项目后,如果你查看项目目录内部,你会看到它为我们提供了一个基本的结构,如下所示:

├── package.json
├── babel.config.js
├── README.md
├── public
│     ├── index.html
│     └── favicon.ico
└── src
├── App.vue
├── main.js
├── router.js
├── components
│     └── HelloWorld.vue
└── assets
      └── logo.png

从这个基本结构,我们可以构建和扩展我们的应用。因此,让我们在 /src/ 目录下开发我们的应用,并使用一个路由文件向其添加以下目录:

└── src
    ├── middlewares/
    ├── store/
    ├── routes/
    └── router.js

我们将创建两个路由组件,loginsecured,作为 SFC 页面,并将 secured 页面设置为 403 保护页面,这将要求用户登录以提供他们的姓名和年龄才能访问该页面。以下是我们这个简单的 Vue 应用在 /src/ 目录下需要的文件和结构:

└── src
    ├── App.vue
    ├── main.js
    ├── router.js
    ├── components
    │   ├── secured.vue
    │   └── login.vue
    ├── assets
    │   └── ...
    ├── middlewares
    │   ├── isLoggedIn.js
    │   └── isAdult.js
    ├── store
    │   ├── index.js
    │   ├── mutations.js
    │   └── actions.js
    └── routes
        ├── index.js
        ├── secured.js
        └── login.js

我们现在对我们的应用需要哪些目录和文件有了一个概念。接下来,我们将继续编写这些文件的代码。

使用 Vue CLI 编写中间件和 Vuex store

查看 package.json,你会发现 Vue CLI 附带的默认依赖项非常基础和精简:

// package.json
"dependencies": {
  "core-js": "^2.6.5",
  "vue": "^2.6.10"
}

因此,我们将安装我们的项目依赖项并在以下步骤中编写所需的代码:

  1. 通过 npm 安装以下包:

    $ npm i vuex
    $ npm i vue-router
    $ npm i vue-router-multiguard

    请注意,Vue 不支持每个路由多个守卫。因此,如果你想为一个路由创建多个守卫,Vue Router Multiguard 允许你这样做。有关此软件包的更多信息,请访问 https://github.com/atanas-dev/vue-router-multiguard。

  2. 创建 stateactionsmutations,将认证的用户详细信息存储在 Vuex store 中,以便任何组件都可以访问这些详细信息:

    // src/store/index.js
    import Vue from 'vue'
    import Vuex from 'vuex'
    import actions from './actions'
    import mutations from './mutations'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: { user: null },
      actions,
      mutations
    })

    为了提高可读性和简洁性,我们将 storeactions 分离到单独的文件中,如下所示:

    // src/store/actions.js
    const actions = {
      async login({ commit }, { name, age }) {
        if (!name || !age) {
          throw new Error('错误的凭据')
        }
        const data = {
          name: name,
          age: age
        }
        commit('setUser', data)
      },
      async logout({ commit }) {
        commit('setUser', null)
      }
    }
    
    export default actions

    我们还将 storemutations 分离到单独的文件中,如下所示:

    // src/store/mutations.js
    const mutations = {
      setUser (state, user) {
        state.user = user
      }
    }
    
    export default mutations
  3. 创建一个中间件以确保用户已登录:

    // src/middlewares/isLoggedIn.js
    import store from '../store'
    
    export default (to, from, next) => {
      if (!store.state.user) {
        const err = new Error('你尚未连接')
        err.statusCode = 403
        next(err)
      } else {
        next()
      }
    }
  4. 创建另一个中间件以确保用户年满 18 岁:

    // src/middlewares/isAdult.js
    import store from '../store'
    
    export default (to, from, next) => {
      if (store.state.user.age < 18) {
        const err = new Error('你必须年满 18 岁')
        err.statusCode = 403
        next(err)
      } else {
        next()
      }
    }
  5. 通过使用 vue-router-multiguardbeforeEnter 中插入多个中间件,将这两个中间件导入到受保护的路由中:

    // src/routes/secured.js
    import multiguard from 'vue-router-multiguard'
    import secured from '../components/secured.vue'
    import isLoggedIn from '../middlewares/isLoggedIn'
    import isAdult from '../middlewares/isAdult'
    
    export default {
      name: 'secured',
      path: '/secured',
      component: secured,
      beforeEnter: multiguard([isLoggedIn, isAdult])
    }
  6. 使用一个简单的登录页面创建客户端身份验证。以下是我们登录(login)和注销(logout)方法所需的基本输入字段:

    // src/components/login.vue
    <template>
      <form @submit.prevent="login">
        <p>姓名: <input v-model="name" type="text" name="name"></p>
        <p>年龄: <input v-model="age" type="number" name="age"></p>
        <button type="submit">提交</button>
      </form>
    </template>
    
    <script>
    export default {
      data() {
        return {
          error: null,
          name: '',
          age: ''
        }
      },
      methods: {
        async login() {
          try {
            await this.$store.dispatch('login', { name: this.name, age: this.age });
            this.$router.push({ name: 'secured' });
          } catch (error) {
            this.error = error.message;
          }
        },
        async logout() {
          await this.$store.dispatch('logout');
          this.$router.push({ name: 'login' });
        }
      }
    }
    </script>
  7. 完成前面的登录和登出方法,通过在 trycatch 代码块中分发 loginlogout action 方法,如下所示:

    async login() {
      try {
        await this.$store.dispatch('login', {
          name: this.name,
          age: this.age
        });
        this.name = '';
        this.age = '';
        this.error = null;
      } catch (e) {
        this.error = e.message;
      }
    },
    async logout() {
      try {
        await this.$store.dispatch('logout');
      } catch (e) {
        this.error = e.message;
      }
    }
  8. 创建登录路由:

    // src/routes/login.js
    import login from '../components/login.vue'
    
    export default {
      name: 'login',
      path: '/',
      component: login
    }

    请注意,我们将此路由命名为 login,因为稍后当我们在前面的中间件中收到身份验证错误时,我们需要使用此名称来重定向导航路由。

  9. 将登录(login)和受保护(secured)的路由导入到索引路由中,如下所示:

    // src/routes/index.js
    import login from './login'
    import secured from './secured'
    
    const routes = [
      login,
      secured
    ]
    
    export default routes
  10. 将前面的索引路由导入到 Vue Router 实例中,并使用 router.onError 捕获路由错误,如下所示:

    // src/router.js
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Routes from './routes'
    
    Vue.use(VueRouter)
    
    const router = new VueRouter({
      routes: Routes
    })
    
    router.onError(err => {
      alert(err.message)
      router.push({ name: 'login' })
    })
    
    export default router

    在这一步中,我们使用 router.onError 来处理从中间件传递的 Error 对象,并使用 router.push 在未满足身份验证条件时将导航路由重定向到登录页面。对象的 name 必须与步骤 7 中的登录路由的 name 相同,即 login

  11. routerstore 导入到 main 文件中:

    // src/main.js
    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    
    new Vue({
      router,
      store,
      render: h => h(App),
    }).$mount('#app')
  12. 使用 npm run serve 运行项目,你应该看到应用加载在 localhost:8080 上。如果你在主页的输入字段中输入一个姓名和一个小于 18 的数字,然后点击登录按钮,当你尝试访问受保护的页面时,你应该会收到一个提示框,显示 “你必须年满 18 岁”。另一方面,如果你输入一个大于 18 的数字,你应该在受保护的页面上看到姓名和数字:

    姓名: John
    年龄: 20

    你可以在我们的 GitHub 仓库的 /chapter-11/vue/vuecli/basic/ 中找到此应用的完整代码。你也可以在 /chapter-11/vue/webpack/ 中找到使用自定义 webpack 的应用。

做得好!你已经成功完成了 Vue 项目中间件的所有章节。现在,让我们在接下来的章节中应用你刚刚学到的关于 Nuxt 项目的知识。