前言
Vue是一种单页应用
,在 Vue 单页应用中,通常整个应用只有一个主页面(HTML 文件),通过 JavaScript 在不同的视图之间进行动态切换,而无需每次切换都重新加载整个页面。即事实上一个页面就是一段js代码片段,让这段代码片段挂载到index.html并生效。
组件化开发
是一种将软件系统拆分成独立的
、可复用
的组件的开发方式。
就像我们在平常的页面开发中,常常碰到一些例如导航栏部分每个页面都一样不需要动,但是下面有几个模块不一样需要切换,我们可以称之为这是两个组件(我的关注、推荐),事实上这两个组件就是两份代码,我们点击我的关注,我的关注这份代码就生效,点击推荐,另一份就生效。
组件化开发实战应用
效果
购物车组件
js
<template>
<div class="app">
<button class="btn" @click="changeTab(1)">购物车</button>
<button class="btn" @click="changeTab(2)">todos</button>
<!-- shopping -->
<Shopping v-if="tabIndex === 1"></Shopping>
<!-- todos -->
<Todos v-else></Todos>
</div>
</template>
<script setup>
import Shopping from "./components/Shopping.vue";
import Todos from "./components/Todos.vue";
import { ref } from "vue";
let tabIndex = ref(1)
const changeTab = (index) => {
// console.log(index);
tabIndex.value = index
}
</script>
<style lang="css" scoped>
.app {
text-align: center;
}
.btn {
font-size: 20px;
width: 100px;
height: 40px;
margin: 20px;
}
</style>
在App.vue中创建两个按钮并绑定两个点击事件@click
,在这里我们取名为changeTab并添加了实参
,目的就是打标记
,当标记为1时购物车生效,标记为2时todos生效
js
const changeTab = (index) => {
// console.log(index);
tabIndex.value = index
}
注意:将标记tabIndex做成响应式数据(ref),当响应式数据更新,页面也会重新加载
为两个按钮添加一些简单的样式
css
.app {
text-align: center;
}
.btn {
font-size: 20px;
width: 100px;
height: 40px;
margin: 20px;
}
接下来创建组件(Shopping.vue),在App.vue中声明一个Shopping组件,todos类似。
js
import Shopping from "./components/Shopping.vue";
import Todos from "./components/Todos.vue";
声明完成便可以使用了
js
<!-- shopping -->
<Shopping v-if="tabIndex === 1"></Shopping>
<!-- todos -->
<Todos v-else></Todos>
vue中直接对标签做判断v-if,判断标记的值是1/2,是1则购物车标签生效否则todos生效。
创建Shopping.vue
js
<template>
<div class="shopping">
<table>
<thead>
<th>序号</th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</thead>
<tbody>
<tr v-for="(book, index) in books">
<td>{{ index + 1 }}</td>
<td>{{ book.name }}</td>
<td>{{ book.date }}</td>
<td>{{ book.price }}</td>
<td>{{ book.count }}</td>
<td>
<button @click="minusCount(index)" v-bind:disabled="book.count <= 0">-</button>
<span class="counter">{{ book.count }}</span>
<button @click="addCount(index)">+</button>
</td>
</tr>
</tbody>
</table>
<h3>总价格:{{ totalPrice }}</h3>
</div>
</template>
<script setup>
import { reactive, computed } from 'vue'
const books = reactive([
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count: 1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count: 1
},
])
const totalPrice = computed(() => {
let total = 0
books.forEach(item => {
total += item.price * item.count
// console.log(total, item.price);
})
return total
})
const addCount = (index) => {
// console.log(index);
books[index].count++
}
const minusCount = (index) => {
books[index].count--
}
</script>
<style lang="css" scoped>
table {
margin: 0 auto;
border: 1px solid #e9e9e9;
border-collapse: collapse;
border-spacing: 0;
}
th,
td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
}
.counter {
margin: 0 5px;
}
</style>
- 注意
语义化标签
,thead、tbody - tr中应该是数据库中有多少条数据,就展示多少个tr,因此不能写死。在这里我们创建一个
响应式数据books
,是一个数组,数组里有多个对象,每个对象就是一条数据。用这个响应式books来模拟数据库数据
。
js
const books = reactive([
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count: 1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count: 1
},
])
- 在tbody中数据不能写死,因此我们通过v-for对books数组做遍历,拿到每一个book对象和下标
- 两个花括号挖坑,把对象中对应的属性值丢进去即可
- 两个加减按钮是操作书籍的数量按钮,因此首先绑定两个点击事件,注意我们只需要把实参index丢进去,即可分辨页面上是哪一个加号/哪一个减号
js
<tbody>
<tr v-for="(book, index) in books">
<td>{{ index + 1 }}</td>
<td>{{ book.name }}</td>
<td>{{ book.date }}</td>
<td>{{ book.price }}</td>
<td>{{ book.count }}</td>
<td>
<button @click="minusCount(index)" v-bind:disabled="book.count <= 0">-</button>
<span class="counter">{{ book.count }}</span>
<button @click="addCount(index)">+</button>
</td>
</tr>
</tbody>
- 通过index识别到位置后直接进行操作
js
const addCount = (index) => {
// console.log(index);
books[index].count++
}
const minusCount = (index) => {
books[index].count--
}
- 但是需要注意的是,当减号减到0,就不能再减了,因此我们需要对标签添加disabled属性,值为true则禁用,但是我们不能写死,当我们
需要动态的操作标签上的某个属性,则可以通过v-bind:对属性进行绑定
,添加条件:当book.count小于等于0时。 - 在最后的总价格中设计到了计算,因此我们使用computed(()=>{})接收一个函数,并返回一个值。计算每一项的单价乘以数量然后累加起来。
js
const totalPrice = computed(() => {
let total = 0
books.forEach(item => {
total += item.price * item.count
// console.log(total, item.price);
})
return total
})
todos组件
创建Todos.vue
js
<template>
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input type="text" class="new-todo" placeholder="想做的事情:" @keydown.enter="addTodo">
</header>
<section class="main">
<input type="checkbox" class="toggle-all">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<li class="todo" v-for="(todo, index) in state.todos">
<div class="view">
<input type="checkbox" class="toggle" v-model="todo.completed">
<label>{{ todo.title }}</label>
<button class="destroy"></button>
</div>
</li>
</ul>
</section>
</section>
</template>
<script setup>
import { ref, reactive } from 'vue'
const state = reactive({
todos: [
{ id: 1, title: '学习', completed: false },
{ id: 2, title: '吃饭', completed: false },
{ id: 3, title: '睡觉', completed: false },
{ id: 4, title: '打豆豆', completed: true },
]
})
// 1. 输入new todo 能增加列表
const addTodo = () => {
console.log();
}
// 2. 手动完成某件事想
// 3. 手动移除某件事
// 4. 点击全选/全不选
</script>
<style lang="css" scoped></style>
- 这里用到了已经封装好的样式,省的我们再去写css了,如果有需要,查看npm | Home (npmjs.com)
bash
npm i todomvc-app-css
- 下载之后做声明,我们直接在全局的main.js中添加,接下来只需要按照他写好的类名去写就能完成样式了。
js
import { createApp } from 'vue'
import App from './App.vue'
import 'todomvc-app-css/index.css'
createApp(App).mount('#app')
- 注意
语义化
标签,header
、section
- 在这个组件中每一个li的原理和购物车中类似,不做赘述
- 用v-model实现数据的双向绑定,当一方变更,另一方也会变更。
js
<input type="checkbox" class="toggle" v-model="todo.completed">
- 这里我们用来响应这个li的状态是否是完成状态。
介绍到这里,这两个组件的开发就基本结束了,但是在todos这个组件中,还剩余一些小功能没实现。
- 输入new todo 能增加列表
- 点击,手动完成某件事项
- 手动移除某件事
- 点击全选/全不选
小结
Vue 组件化开发思想是将一个大型应用拆分成多个相对独立且可复用的组件,每个组件都有自己特定的功能和视图。学习完本文,你应该能理解组件化开发有以下特点:
- 独立性:组件自身的功能和逻辑相对独立,不依赖于其他组件的内部实现细节。
- 可重用性:可以在不同的场景和项目中重复使用已有的组件,大大提高开发效率。
- 封装性:将相关的代码、数据和视图封装在组件内部,对外提供清晰的接口。
- 可维护性:当需要修改或扩展功能时,只需针对特定组件进行操作,不会影响其他组件。
- 组合性:通过不同组件的组合和嵌套,可以灵活构建各种复杂的界面结构。
- 测试方便:由于组件的独立性,对其进行单独测试较为容易。
- 关注点分离:将视图展示、业务逻辑和数据管理等不同方面的内容分别放在组件的不同部分,便于管理和理解。
- 高效开发:减少代码冗余,提高代码的质量和可扩展性。