首先我们来看非单文件开发的方式
案例一 : 语法篇
在 App.vue 中,定义了一个包含多个子组件的父组件。该组件包含模板、JavaScript 代码和样式,它们被分散在不同的文件中:
App.vue:
<template>
<div class="app">
<h1>{
{ title }}</h1>
<my-header></my-header>
<my-content></my-content>
<my-footer></my-footer>
</div>
</template>
<script src="./App.js"></script>
<style scoped>
.app {
background-color: #f0f0f0;
}
</style>
App.js:
import MyHeader from './MyHeader.js';
import MyContent from './MyContent.js';
import MyFooter from './MyFooter.js';
export default {
name: 'App',
components: {
MyHeader,
MyContent,
MyFooter,
},
data() {
return {
title: 'My App',
};
},
};
MyHeader.js:
export default {
name: 'MyHeader',
template: `
<div class="header">
<h2>{
{ headerTitle }}</h2>
</div>
`,
data() {
return {
headerTitle: 'My Header',
};
},
};
MyContent.js:
export default {
name: 'MyContent',
template: `
<div class="content">
<p>{
{ contentText }}</p>
</div>
`,
data() {
return {
contentText: 'Hello, world!',
};
},
};
MyFooter.js:
export default {
name: 'MyFooter',
template: `
<div class="footer">
<p>{
{ footerText }}</p>
</div>
`,
data() {
return {
footerText: 'My Footer',
};
},
};
在上面的代码中,我们将 App 组件的模板、JavaScript 代码和样式拆分到不同的文件中。这样做会使组件的结构混乱,并且代码的维护困难度会增加。同时,由于样式没有 scoped 属性,可能会存在样式泄漏到其他组件中的问题。此外,每个组件都需要单独进行导入和注册,增加了代码的复杂性和冗余。
相比之下,使用单文件组件可以将组件的相关代码和样式整合到一个文件中,使得组件更结构化、清晰和易于维护。
案例二 : 缺点篇
以下是一个更复杂的非单文件开发示例,展示了其潜在的一些弊端:
App.vue:
<template>
<div class="app">
<h1>{
{ title }}</h1>
<my-header></my-header>
<my-content></my-content>
<my-footer></my-footer>
</div>
</template>
<script>
import MyHeader from './components/MyHeader.js';
import MyContent from './components/MyContent.js';
import MyFooter from './components/MyFooter.js';
export default {
name: 'App',
components: {
MyHeader,
MyContent,
MyFooter,
},
data() {
return {
title: 'My App',
};
},
};
</script>
<style scoped>
.app {
background-color: #f0f0f0;
}
</style>
components/MyHeader.js:
<template>
<div class="header">
<h2>{
{
headerTitle }}</h2>
<my-logo></my-logo>
</div>
</template>
<script>
import MyLogo from '../shared/MyLogo.js';
export default {
name: 'MyHeader',
components: {
MyLogo,
},
data() {
return {
headerTitle: 'My Header',
};
},
};
</script>
<style>
.header {
padding: 20px;
background-color: #ccc;
}
</style>
components/MyContent.js:
<template>
<section class="content">
<h3>{
{
contentTitle }}</h3>
<p>{
{
contentText }}</p>
<my-comments></my-comments>
</section>
</template>
<script>
import MyComments from '../shared/MyComments.js';
export default {
name: 'MyContent',
components: {
MyComments,
},
data() {
return {
contentTitle: 'My Content',
contentText: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
};
},
};
</script>
<style>
.content {
padding: 20px;
background-color: #fff;
}
</style>
components/MyFooter.js:
<template>
<footer class="footer">
<p>{
{
footerText }}</p>
<my-social-icons></my-social-icons>
</footer>
</template>
<script>
import MySocialIcons from '../shared/MySocialIcons.js';
export default {
name: 'MyFooter',
components: {
MySocialIcons,
},
data() {
return {
footerText: 'My Footer',
};
},
};
</script>
<style>
.footer {
padding: 20px;
background-color: #f0f0f0;
}
</style>
我们创建了一个更复杂的应用程序,并将其拆分为多个组件。每个组件的模板、JavaScript 代码和样式都被分散到不同的文件中,并且存在多层的组件嵌套结构。
这种非单文件开发方式存在几个潜在的问题:
文件路径混乱:随着组件数量的增加,文件结构可能变得混乱,开发者需要花费更多时间来查找和管理文件路径。
依赖管理困难:在组件中导入其他组件时,需要考虑正确的相对路径,并确保文件引用的准确性。
构建配置复杂:使用非单文件开发时,构建工具需要配置以支持将拆分的组件文件正确地合并和编译为最终的应用程序。
代码重复和冗余:每个组件都需要单独导入和注册,这可能导致代码的重复和冗余。
维护困难:当应用程序变得更加复杂时,非单文件开发方式可能导致组件关系和代码之间的交互变得困难,增加了项目的维护成本和难度。
综上所述,尽管非单文件开发在某些情况下是可行的,但它可能存在一些潜在的问题,特别是在大型项目和复杂组件结构的情况下。因此,单文件组件是更推荐的 Vue.js 开发实践方式,可以提供更清晰、结构化和易于维护的代码。
接着我们来到单文件组件开发
来一个经典的ToDoList案例
组件是 Vue.js 中最基本和最重要的概念之一,它提供了一种抽象和封装的方式,让我们可以将一个页面拆分成多个相互独立的功能块,每个功能块即为一个组件。在实际开发中,组件可以是任何大小的、自定义的HTML元素。
假设我们需要创建一个 TodoList 应用,每个 Todo 项都包含一个标题和一个完成状态。我们可以将每个 Todo 项作为一个组件,然后通过组合多个 Todo 组件实现整个 TodoList 应用。
创建 Todo 组件
<!-- Todo.vue -->
<template>
<div class="todo">
<h3>{
{ title }}</h3>
<label>
<input type="checkbox" :checked="completed" @change="$emit('toggle')">
完成
</label>
</div>
</template>
<script>
export default {
props: {
title: String,
completed: Boolean,
},
};
</script>
<style scoped>
.todo {
margin-bottom: 10px;
}
</style>
在这个组件中,我们定义了一个 title 属性和一个 completed 属性,分别表示 Todo 的标题和完成状态。在模板中,我们使用了标准的 HTML 元素和指令来渲染组件的外观和交互逻辑。注意到我们使用了父组件传递过来的属性值 title 和 completed 来动态绑定组件的内容。
创建 TodoList 组件
<!-- TodoList.vue -->
<template>
<div class="todo-list">
<h2>Todo List</h2>
<div class="todos">
<todo v-for="(todo, index) in todos" :key="index" :title="todo.title" :completed="todo.completed" @toggle="toggle(index)"></todo>
</div>
<div class="add-todo">
<input type="text" v-model="newTodoTitle">
<button @click="addTodo">添加</button>
</div>
</div>
</template>
<script>
import Todo from "./Todo.vue";
export default {
components: {
Todo,
},
data() {
return {
newTodoTitle: "",
todos: [
{
title: "学习Vue", completed: false },
{
title: "写代码", completed: true },
],
};
},
methods: {
addTodo() {
this.todos.push({
title: this.newTodoTitle, completed: false });
this.newTodoTitle = "";
},
toggle(index) {
this.todos[index].completed = !this.todos[index].completed;
},
},
};
</script>
<style scoped>
.todo-list {
margin: 20px;
}
.todos {
margin-bottom: 10px;
}
</style>
在这个组件中,我们使用了上面定义的 Todo 组件来渲染 TodoList 应用中的所有 Todo 项。在数据中,我们定义了一个 todos 数组来存储所有 Todo 项的信息。在模板中,我们使用了 v-for 指令遍历 todos 数组,并动态绑定每个 Todo 项的属性值。同时,我们还定义了一个输入框和一个按钮,用于添加新的 Todo 项。在方法中,我们实现了添加新 Todo 项和切换 Todo 完成状态的功能。
创建根组件
<!-- App.vue -->
<template>
<div class="app">
<todo-list></todo-list>
</div>
</template>
<script>
import TodoList from "./TodoList.vue";
export default {
components: {
TodoList,
},
};
</script>
<style scoped>
.app {
margin: 20px;
}
</style>
在这个根组件中,我们只需要渲染最顶层的组件 TodoList 即可。
通过上面的代码示例,我们可以看到,在 Vue2 中,组件化编程的核心思想是将一个页面拆分成多个相互独立的功能块,每个功能块即为一个组件。在实际开发中,组件可以是任何大小的、自定义的HTML元素,而且可以方便地复用和组合。
全局组件在vue2开发时的使用
在单文件组件开发中,合理使用全局组件可以提高代码的复用性和可维护性。
全局组件可以在多个 Vue 实例中使用,在不同的单文件组件或页面中都可以方便地引用和调用。因此,在以下情况下,我们可以考虑使用全局组件:
当某个组件在多个地方需要使用时,将其注册为全局组件,可以避免在每个使用该组件的地方都单独引用和注册。
当多个单文件组件之间需要共享相同的逻辑或数据时,可以将其抽象成一个全局组件,以减少代码重复度。
但是在使用全局组件时,也需要注意以下事项:
全局组件将会在全局注册,如果有多个组件具有相同名称,会出现组件覆盖的情况。因此,需要避免命名冲突。
全局组件会增加应用程序的复杂度,而且在大型应用程序中过多的全局组件也会影响性能。因此,需要合理使用全局组件,避免过多的组件注册。
下面是一个全局组件的例子,以MyButton为例,先在main.js中注册:
import Vue from 'vue'
import App from './App.vue'
import MyButton from './components/MyButton.vue'
Vue.component('MyButton', MyButton)
new Vue({
render: h => h(App),
}).$mount('#app')
在App.vue中使用:
<template>
<div>
<MyButton text="点击我"></MyButton>
</div>
</template>
在其他组件也可以直接引用:
<template>
<div>
<MyButton text="确认" @click="submit"></MyButton>
</div>
</template>
通过这种方式,我们可以使用全局组件使得组件复用更加容易,代码的可维护性也会提高。
案例三:全局组件实现小而巧的功能
首先,在src/components文件夹中创建一个名为Modal.vue的单文件组件,用于显示弹框内容:
<template>
<div v-if="show" class="modal">
<div class="modal-content">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
data() {
return {
show: false,
};
},
methods: {
showModal() {
this.show = true;
},
hideModal() {
this.show = false;
},
},
};
</script>
<style scoped>
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background-color: #fff;
padding: 20px;
}
</style>
然后,在main.js中注册并创建全局组件:
import Vue from 'vue'
import App from './App.vue'
import Modal from './components/Modal.vue'
Vue.component('Modal', Modal)
new Vue({
render: h => h(App),
}).$mount('#app')
接下来,你可以在任何一个组件中使用全局的弹框组件,并调用其方法来显示弹框:
<template>
<div>
<button @click="showModal">显示弹框</button>
<Modal ref="modal">
<h2>我是弹框的内容</h2>
<p>这是一个全局的弹框组件</p>
<button @click="hideModal">关闭弹框</button>
</Modal>
</div>
</template>
<script>
export default {
methods: {
showModal() {
this.$refs.modal.showModal();
},
hideModal() {
this.$refs.modal.hideModal();
},
},
};
</script>
这样,通过全局组件实现的弹框功能可以在整个应用程序中使用,方便地显示和隐藏弹框内容。