介绍
Vue.js创建于2014年,无疑是目前领先的前端框架之一,并且随着社区和生态系统的不断扩大,它的地位似乎在相当长的一段时间内是稳固的。几年前,我曾与 Vue 2 合作过一些项目,我发现这是一次愉快的经历。
我认为现在是时候用最新版本以及Vite和Pinia等较新的工具升级我的工具集了。
本指南将详细介绍使用 Vue 3 创建功能示例书店单页应用程序并使用 Vite 运行它的步骤。它还包括有关如何使用 Pinia(Vuex 后继者)添加状态管理和使用 Vue Router 进行路由的详细信息。
将涵盖的核心概念是:
- 使用 Vite 创建 Vue 3 单页应用程序
- 使用 Vue 路由器管理路由
- 使用 Pinia 管理应用程序状态
- 使用 Vite 运行、构建和部署应用程序
- 编写和运行 Vue 组件测试
- 使用 Nightwatch 编写和运行自动化的端到端测试.js
这可能看起来很多,但我认为完全有可能在不到20分钟的时间内完成所有这些工作。上面列出的一些主题可以扩展到它们自己的整个教程中,但现在我只介绍启动和运行所有内容所需的内容。
需要提到的最后一件事是,本教程中未介绍后端。虽然数据是使用浏览器Fetch API(XHR的后继者)加载的,但本身没有服务器端组件,因此可以轻松添加后端组件。
对于所有帐户和目的,我们将在此处构建的应用程序可以部署为静态网站。
如果您渴望直接开始编码,并且想立即进入其中,则可以通过以下方式启动并运行项目:
git clone https://github.com/beatfactor/middlemarch
npm install
npm run dev
或者在Github上fork项目:https://github.com/beatfactor/middlemarch
使用创建 vite 基架工具设置应用程序
我们将使用官方脚手架工具来设置项目结构,因此请确保您安装了NPM 6 +的Node 12 +。他们也支持 Yarn 和 PNPM 作为包管理器,但我们只介绍 NPM。create-vite
该工具还将为您创建项目文件夹,因此请确保先cd到父文件夹中:create-vite
cd /workspace
使用以下命令安装和初始化项目:Vite
npm init vite@latest
然后,系统将提示您输入项目名称并选择要使用的库。从列表中选择:vue
~/workspace % npm init vite@latest
npx: installed 6 in 1.051s
✔ Project name: … vue-bookstore
? Select a framework: › - Use arrow-keys. Return to submit.
vanilla
❯ vue
react
preact
lit
svelte
然后选择,因为我们不会使用TypeScript:vue
? Select a variant: › - Use arrow-keys. Return to submit.
❯ vue
vue-ts
看到以下输出:
npx: installed 6 in 1.051s
✔ Project name: … vue-bookstore
✔ Select a framework: › vue
✔ Select a variant: › vue
Scaffolding project in /Users/andrei/workspace/vue-bookstore...
Done. Now run:
cd vue-bookstore
npm install
npm run dev
按照上述说明操作后,我们将从Vite获得以下输出,告诉我们该应用程序正在运行:
vite v2.7.7 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 611ms.
让我们访问localhost:3000
。欢迎页面如下所示:
使用 Vue 路由器添加路由,使用 Pinia 进行状态管理
让我们回顾一下该工具创建的项目的目录结构:create-vite
vue-bookstore/
├── public/
| ├── favicon.ico
├── src/
| ├── assets/
| | └── logo.png
| ├── components/
| | └── HelloWorld.vue
| ├── App.vue
| └── main.js
├─── package.json
├─── README.md
└─── vite.config.js
在本指南的这一部分中,我们将向项目添加两个新的依赖项:和 。让我们继续从NPM安装它们。vue-routerpinia
Vue 路由器
vue-router是 Vue.js 的官方路由器。我们需要安装与 Vue 3 兼容的版本 router-4:
npm install vue-router@4 --save
Pinia
Pinia是 Vue 生态系统中出现的最新项目之一,它是 Vue.js 应用程序的新官方状态管理工具。它的api与(Vuex)非常相似,它被设计得更快,更轻量级。
您可以使用以下命令从 NPM 进行安装:pinia
npm install pinia --save
设置路由
如果您不熟悉单页应用程序中的路由或状态管理,请不要担心;这两个概念都非常容易理解,一旦你看到它是如何工作的,它们就会自我解释。
什么是单页应用程序?
单页应用程序只是一个 Web 应用程序,当您导航到其另一个子页面时,它不会重新加载页面。但是,浏览器的URL被修改了,就好像页面已被重新加载一样,这是使用HTML5 History API完成的。
在 Vite 中使用 Vue 组件
使用该工具创建的基架添加了一个非常基本的 Vue 组件,
create-vite src/components/HelloWorld.vuesrc/App.vue
还有另外两个重要文件:
- index.html
- src/main.js
该文件是浏览器导航到我们应用程序的页面时看到的内容,也是 Vue.js 应用程序的入口点。index.html ,main.js
这些文件的内容如下:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
src/main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
添加路由
现在是时候创建应用程序的主要路由了。在 Vue 中,每个路由都必须对应于一个组件。对于此应用程序,我们将考虑每个子页面的组件,如下所示:
- Homepage主页 - 我们的书店主页
- Cart购物车 - 购物车和结账页面
- Sign-In登录 - 用户登录页面
对于基本的HTML和CSS,我也使用Bootstrap 5来处理UI下拉列表和表单等内容,但当然你可以使用任何你想要的UI库。
现在,我们将创建空的页面组件,以便设置路由。新的目录结构将如下所示:src
src/
├── components/
| └── TopNavbar.js
├── lib/
| ├── router.js
| └── store.js
├── pages/
| ├── cart/
| | ├── cart.css
| | ├── cart.html
| | └── Cart.vue
| ├── home/
| | ├── home.css
| | ├── home.html
| | └── Home.vue
| ├── sign-in/
| | ├── sign-in.css
| | ├── sign-in.html
| | └── SignIn.vue
| └── routes.js
├── App.vue
└── main.js
我们添加了三个页面,每个页面都将保留非常基本。我们只需添加组件即可使导航在不重新加载页面的情况下正常工作。TobNavbar
为 添加以下内容,然后:
- src/pages/cart/Cart.vue
- src/pages/home/Home.vue
- src/pages/sign-in/SignIn.vue
<script setup>
import TopNavbar from '../../components/TopNavbar.vue';
</script>
<template>
<TopNavbar />
</template>
<style></style>
<script>
export default {
components: {
TopNavbar
},
computed: {},
mounted() {
},
data() {
return {
};
},
};
</script>
TopNavbar组件将仅包含导航链接。
<template>
<router-link to="/">Home</router-link>
<router-link to="/cart/">Cart</router-link>
<router-link to="/sign-in/">Sign In</router-link>
</template>
pages/routes.js文件包含应用程序的所有路由声明。
import {createRouter} from 'vue-router'
import Homepage from './home/Home.vue';
import SignIn from './sign-in/SignIn.vue';
import Cart from './cart/Cart.vue';
const routes = [
{
path: '/',
component: Homepage
},
{
path: '/sign-in/',
component: SignIn
},
{
path: '/cart/',
component: Cart
},
]
export default function (history) {
return createRouter({
history,
routes
})
}
1) 创建路由器并将其添加到主 Vue 应用程序实例中, 在 :src/main.js
import { createApp } from 'vue'
import { createWebHistory } from 'vue-router'
import createRouter from './pages/routes.js'
import App from './App.vue'
const router = createRouter(createWebHistory())
const app = createApp(App)
app.use(router).mount('#app')
2) 添加组件:
<template>
<router-view></router-view>
</template>
重新运行,然后导航到启用路由的 Vue 3 应用程序。npm run devhttp://localhost:3000
使用 Pinia 设置状态管理
Pinia 是 Vue 核心团队.js一个新项目,现在是使用应用程序状态的推荐方法。如果你已经熟悉Vuex,那么习惯Pinia会很简单。事实上,Pinia api比Vuex稍微容易一些,也不那么冗长。
使用Pinia,在Vue 3应用程序中,有一个根store,然后是任意数量的单个store。对于我们的书店应用,我们将仅使用两个store:
- 目录store:可用书籍的列表
- 购物车store:用户想要订购的图书
创建Pinia
"pinia"是我们必须首先创建的根存储,并将其传递给 Vue 实例。
我们将在其中执行此操作,并将其更新为:src/main.js
import { createApp } from 'vue'
import { createWebHistory } from 'vue-router'
import { createPinia } from 'pinia'
import createRouter from './pages/routes.js'
import App from './App.vue'
const store = createPinia()
const router = createRouter(createWebHistory())
const app = createApp(App)
app.use(router).use(store).mount('#app')
下一步是创建单个目录和购物车store,并在组件中使用它们。
添加目录存储
创建Pinia store主要意味着两件事:
- 定义store
- 在一个或多个组件中使用存储
定义应用store
与 Vuex 一样,Pinia 存储包含状态和两种类型的方法:getter
和actions
。
关于store需要考虑的一些事项:
- Getters是用于从状态中检索数据的同步函数
- Actions也可以是异步的函数,用于更新状态
- 被定义为返回初始状态的函数state
在里面创建目录store了:src/stores/catalog.js
import { defineStore } from 'pinia'
export const useCatalog = defineStore('catalog-store', {
state: () => {
return {
newArrivals: [],
fetching: false
}
},
getters: {
results(state) {
return state.newArrivals;
},
isFetching(state) {
return state.fetching;
}
},
actions: {
async fetchNewArrivals() {
this.fetching = true;
const response = await fetch('/data/new-arrivals.json');
try {
const result = await response.json();
this.newArrivals = result.books;
} catch (err) {
this.newArrivals = [];
console.error('Error loading new arrivals:', err);
return err;
}
this.fetching = false;
}
}
})
查看上面的源代码,您会注意到我们有两个 getter 和一个action。我们不是真正的后端,而是有一个json文件,其中包含一些我们将用作目录的书籍。/data/new-arrivals.json
我们的 getter 不会对数据做任何特殊的事情,所以他们有点不必要,但我认为展示如何定义它们仍然很好。
在template中使用"store"
将上述定义链接到模板也非常简单。
让我们创建一个调用的新组件,我们将在其中使用它作为页面组件。NewArrivals
src/components/NewArrivals.vueHome.vue
<script setup>
import {useCatalog} from '../../store/catalog.js'
</script>
<template>
</template>
<style scoped></style>
<script>
import { mapState, mapActions } from 'pinia'
export default {
computed: {
...mapState(useCatalog, {newArrivals: 'results'})
},
methods: {
...mapActions(useCatalog, ['fetchNewArrivals']),
addToCart() {
// we'll populate this later
}
},
created() {
// when the template is created, we call this action
this.fetchNewArrivals();
}
};
</script>
组件变为:Home.vue
<script setup>
import TopNavbar from '../../components/TopNavbar.vue';
import NewArrivals from '../../components/NewArrivals.vue';
</script>
<template>
<TopNavbar />
<NewArrivals />
</template>
<style></style>
<script>
export default {
components: {
TopNavbar,
NewArrivals
},
computed: {},
mounted() {},
data() {
return {};
},
};
</script>
下面是存储和组件如何在应用程序中协同工作的图:
我还为购物车编写了一个store和一个components,但我不会将其包含在教程中,因为机制是相似的,您可以检查存储库中的源代码,该源代码将所有内容添加到一起,甚至是某些样式。
测试 Vue.js 组件
组件测试是一种 UI 测试类型,其中组件在隔离状态下呈现,而不使用其他应用组件,以验证其功能。它通常是一种测试策略,发生在端到端测试步骤之前,我们将在下一节中详细阐述。
我们需要安装 Vue TestUtils 项目,这是 Vue 的官方单元测试库.js我们需要一个面向 Vue 3 的项目。您可以使用以下命令从 NPM 安装它:
npm install @vue/test-utils@next --save-dev
安装 Nightwatch.js 和 ChromeDriver
我们将使用 Nightwatch.js 进行组件测试和端到端测试。Nightwatch已经是Vue.js团队推荐的测试框架之一,并且与Vue同时发布。
vite-plugin-nightwatch获得了对Vue组件测试的支持。我们将继续安装Nightwatch(版本2.0):
npm install nightwatch --save-dev
我们还需要前面提到的:vite-plugin-nightwatch
npm install vite-plugin-nightwatch --save-dev
Nightwatch使用W3C WebDriver API
执行浏览器自动化任务,我们还需要安装NPM包,因为我们将使用Chrome来运行我们的测试。
npm install chromedriver --save-dev
测试<组件
其中包括一个测试渲染器页面,Nightwatch已经包含运行我们组件的初始测试所需的一切。vite-plugin-nightwatch
创建一个文件夹,并在其中添加两个子文件夹:test
- component- 这将举行组件测试
- e2e- 这将举行端到端测试
我们还需要一个配置文件nightwatch.conf.js,但是我们可以直接运行Nightwatch,并且将自动为我们创建配置文件。只需确保也安装了(当然还有Chrome浏览器)。
确保当前工作目录是项目根目录,然后只需运行与Nightwatch捆绑在一起的示例测试即可。我们将选择测试运行duckDuckGo
npx nightwatch examples/tests/duckDuckGo.js
项目结构现在应如下所示:
vue-bookstore/
├── public/
| ├── data/
| └── favicon.ico
├── src/
├── ...
| └── main.js
├── test/
| ├── component/
| └── e2e/
├─── nightwatch.conf.js
├─── package.json
├─── README.md
└─── vite.config.js
我们将继续创建一个名为 inside 的新文件。在其中,我们只需添加一个基本测试,该测试将挂载组件并检查是否可以在页面中找到返回的元素(即组件已挂载)。
newArrivalsTest.js
test/component
describe('New Arrivals Component Test', function() {
it('checks if the component has been mounted', async (browser) => {
const component = await browser.mountVueComponent('/src/components/new-arrivals/NewArrivals.vue', {
plugins: {
router: '/src/lib/router.js'
}
});
expect(component).to.be.present;
});
});
Nightwatch使用与Mocha相同的语法。如果您已经熟悉Mocha,您甚至可以将其用作测试运行程序,但我们现在不打算这样做。describe()
我们将使用Chrome运行Nightwatch,如下所示:
npx nightwatch test/component/newArrivalsTest.js --env chrome
将打开 Chrome 浏览器并呈现组件,然后执行测试。如果您不喜欢在测试期间看到浏览器窗口弹出,则可以传递参数,如下所示:--headless
npx nightwatch test/component/newArrivalsTest.js --env chrome --headless
测试输出应如下所示:
[New Arrivals Component Test] Test Suite
──────────────────────────────────────────────────────────────
ℹ Connected to ChromeDriver on port 9515 (652ms).
Using: chrome (97.0.4692.99) on MAC OS X.
Running tests the component:
──────────────────────────────────────────────────────────────
✔ Expected element <web element{e53f9b1e-11d3-4dc4-8728-4d3cd077343e}> to be present (1ms)
OK. 1 assertions passed. (781ms)
可以查阅 Nightwatch 跑步者提供的所有 CLI 选项,方法是转到文档页面或运行:
npx nightwatch --help
评论区