一、引言
Vue 是一款用于构建用户界面的渐进式 JavaScript 框架,具有易上手、高性能、灵活等特点,能够帮助开发者快速开发出响应式的单页面应用。本技术文档旨在全面介绍 Vue 的相关技术知识,为开发人员提供参考和指导。
二、环境搭建
2.1 安装 Node.js
Vue 的开发依赖 Node.js 环境,可以从官方下载地址下载适合你操作系统的安装包进行安装,在安装过程中,会自动将 Node.js 以及 npm(Node.js 的包管理工具)安装到你的系统中。
安装完成后,可以通过在命令行中输入以下命令来验证安装是否成功:
node -v
npm -v
分别查看 Node.js 和 npm 的版本号,若能正常显示版本号,则说明安装成功。
2.2 安装 Vue CLI
Vue CLI 是 Vue 的命令行工具,可以帮助我们快速创建和管理 Vue 项目。安装命令如下:
npm install -g @vue/cli
安装完成后,可以通过以下命令查看 Vue CLI 的版本号,验证安装是否成功:
vue --version
2.3 创建 Vue 项目
在命令行中运行以下命令来创建一个新的 Vue 项目:
vue create my-vue-project
其中,“my-vue-project” 是你项目的名称,可以根据实际情况进行替换。在执行该命令后,会提示你选择项目配置选项,包括预设、路由、Vuex(状态管理)、CSS 预处理器等,你可以根据项目需求进行选择。
待配置选择完成后,进入项目目录:
cd my-vue-project
然后启动开发服务器:
npm run serve
此时,开发服务器会启动并在浏览器中自动打开项目页面,通常访问地址为http://localhost:8080
,你就可以看到新创建的 Vue 项目的基础页面了。
三、项目文件结构
一个典型的 Vue 项目文件结构如下:
my-vue-project/
├── node_modules/ // 项目依赖的第三方模块
├── public/ // 静态资源目录
│ ├── index.html // 入口 HTML 文件
│ └── favicon.ico // 网站图标
├── src/ // 源代码目录
│ ├── assets/ // 项目静态资源,如图片、样式文件等
│ ├── components/ // Vue 组件文件夹
│ ├── views/ // Vue 页面文件夹
│ ├── App.vue // 项目的根组件
│ ├── main.js // 项目的入口 JavaScript 文件
│ └── router.js // 路由配置文件
├── .browserslistrc // 浏览器兼容性配置文件
├── .eslintrc.js // ESLint 配置文件,用于代码规范检查
├── .gitignore // Git 忽略文件配置
├── babel.config.js // Babel 配置文件,用于 JavaScript 代码的转译
├── package.json // 项目配置文件,包含项目依赖、脚本等信息
└── README.md // 项目的说明文档
四、Vue 核心概念
4.1 Vue 实例
一个 Vue 应用是由一个 Vue 实例创建的,基本语法如下:
new Vue({
el: '#app', // 指定 Vue 实例挂载的 DOM 元素,通常是一个具有特定 id 的元素
data: { // 数据对象,用于存储 Vue 实例中的数据,这些数据会在视图中进行展示,并且当数据发生变化时,视图会自动更新
message: 'Hello Vue!'
},
methods: { // 方法对象,定义 Vue 实例中的函数,可以通过事件绑定等方式来调用这些方法,实现用户交互等功能
sayHello: function () {
console.log(this.message);
}
}
});
在 HTML 中通过双花括号语法绑定数据:
<div id="app">
<p>{{ message }}</p> <!-- 这里会显示 data 中的 message 数据 -->
<button @click="sayHello">点击</button> <!-- 点击按钮时会调用 methods 中的 sayHello 方法 -->
</div>
当点击按钮时,会调用 sayHello
方法并输出 “Hello Vue!”。
4.2 数据绑定
Vue 提供了多种数据绑定方式,常见的有以下几种:
4.2.1 文本绑定
使用双花括号语法 {{ }}
将数据绑定到文本内容中,例如:
<p>{{ message }}</p>
当 data
中的 message
数据发生变化时,页面上的文本内容会自动更新。
4.2.2 属性绑定
使用 v-bind
指令(可以简写为 :
)将数据绑定到 HTML 元素的属性上,例如:
<img :src="imageSrc" alt="示例图片">
其中,imageSrc
是 data
中的一个数据项,表示图片的路径,当 imageSrc
的值改变时,图片会自动更新。
4.2.3 表单绑定
使用 v-model
指令实现表单元素(如输入框、复选框、单选按钮等)与数据的双向绑定,例如:
<input v-model="inputValue" type="text">
当用户在输入框中输入内容时,data
中的 inputValue
数据会实时更新;反之,如果 inputValue
数据发生变化(如通过代码修改),输入框中的内容也会相应地更新。
4.3 指令
指令是 Vue 中带有 v-
前缀的特殊属性,用于在 HTML 中声明式地绑定数据和执行操作。
4.3.1 条件指令
v-if
用于根据表达式值的真假来控制元素的显示与隐藏,例如:
<div v-if="isVisible">只有当 isVisible 为 true 时才显示</div>
v-else
用于表示 v-if
的对立条件,例如:
<div v-if="isLoggedIn">已登录</div>
<div v-else>未登录</div>
v-show
也是根据表达式值的真假来控制元素的显示与隐藏,但它始终会渲染元素到 HTML 中,只是通过 CSS 的 display
属性来切换显示状态,与 v-if
不同的是,v-if
是通过销毁和重建 DOM 元素来实现显示隐藏的,性能消耗较大,但 v-show
不适合频繁切换的情况,例如:
<div v-show="isVisible">内容</div>
4.3.2 循环指令
v-for
用于根据数组或对象来渲染列表,例如:
<ul>
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>
其中,items
是 data
中的一个数组,v-for
会遍历数组中的每个元素,并为每个元素生成一个 <li>
元素,index
表示元素在数组中的索引位置,key
是一个特殊的属性,用于给每个列表元素提供一个唯一的标识,有助于 Vue 更高效地进行 DOM 更新。
对于对象的循环也是类似的,例如:
<div v-for="(value, key, index) in object" :key="index">
{{ key }}: {{ value }}
</div>
其中,object
是 data
中的一个对象,key
表示对象的属性名,value
表示对应的属性值,index
表示对象属性的索引。
4.4 组件
组件是 Vue 中的核心概念之一,它允许我们将界面拆分成独立、可复用的部分,每个组件都有自己的模板、逻辑和样式,提高了代码的组织性和可维护性。
4.4.1 组件的创建
可以通过 Vue.component()
方法全局注册一个组件,例如:
Vue.component('my-component', {
template: '<div>这是一个全局组件</div>'
});
然后在任何一个 Vue 实例管理的 DOM 中都可以使用 <my-component>
来渲染该组件。
也可以在单个 Vue 实例中局部注册组件,例如:
new Vue({
el: '#app',
components: {
'local-component': {
template: '<div>这是一个局部组件</div>'
}
}
});
这样,这个组件只能在该 Vue 实例所管理的范围内使用。
不过,在实际开发中,尤其是大型项目中,我们通常会使用单文件组件(.vue 文件)来定义组件,这种文件将组件的模板、脚本和样式集中在一个文件中,结构清晰,便于管理和复用。例如,一个名为 MyComponent.vue
的文件内容如下:
<template>
<div>
<h2>{{ title }}</h2>
<p>{{ content }}</p>
</div>
</template>
<script>
export default {
name: 'MyComponent',
data() {
return {
title: '组件标题',
content: '组件内容'
};
}
};
</script>
<style scoped>
div {
border: 1px solid #ccc;
padding: 10px;
margin: 10px;
}
</style>
其中,<template>
部分定义了组件的 HTML 模板结构,<script>
部分定义了组件的逻辑部分,包括数据、方法、生命周期等,<style scoped>
部分定义了组件的局部样式,scoped
属性使得这些样式只作用于该组件的模板部分,而不会影响到其他组件或页面中的元素。
4.4.2 组件的注册与使用
对于单文件组件,需要先将其导入到使用它的 Vue 文件中,然后再进行注册,才能使用。例如,在 main.js
中导入和注册组件:
import Vue from 'vue';
import App from './App.vue';
import MyComponent from './components/MyComponent.vue';
Vue.config.productionTip = false;
Vue.component('my-component', MyComponent);
new Vue({
render: h => h(App),
}).$mount('#app');
这样,在项目的任何 Vue 组件中都可以使用 <my-component>
标签来渲染该组件。
4.4.3 父子组件通信
4.4.3.1 父组件向子组件传递数据
通过 props
属性来实现父组件向子组件传递数据,子组件通过 props
选项接收父组件传递的数据。例如,在父组件中:
<child-component :parent-message="message"></child-component>
其中,:parent-message
是绑定的属性名,它会将父组件中的 message
数据传递给子组件,注意这里的 :parent-message
是动态绑定的,如果父组件的 message
数据发生变化,子组件会自动接收到新的值。
在子组件中,通过 props
选项接收:
props: ['parentMessage']
然后在子组件的模板中就可以使用 parentMessage
来展示该数据了。
4.4.3.2 子组件向父组件传递数据
子组件可以通过 $emit
方法来触发一个自定义事件,并将数据作为事件的参数传递给父组件,父组件通过监听该事件来接收子组件传递的数据。例如,在子组件中:
this.$emit('child-event', data);
其中,child-event
是自定义事件名,data
是要传递的数据。
在父组件中监听该事件:
<child-component @child-event="handleChildEvent"></child-component>
然后在父组件的 methods
中定义 handleChildEvent
方法来处理子组件传递过来的数据:
methods: {
handleChildEvent(data) {
console.log('子组件传递的数据:', data);
}
}
五、Vue 路由(Vue Router)
Vue Router 是 Vue 官方的路由管理器,它允许我们构建单页面应用(SPA),通过定义不同的路由规则,将不同的组件映射到不同的 URL 地址,实现页面的切换和数据的保持。
5.1 安装 Vue Router
可以通过 npm 安装 Vue Router,命令如下:
npm install vue-router
5.2 创建路由配置文件
在项目中创建一个路由配置文件,例如 router.js
,内容如下:
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from './views/Home.vue'; // 引入主页组件
import About from './views/About.vue'; // 引入关于页面组件
import User from './views/User.vue'; // 引入用户页面组件
Vue.use(VueRouter); // 安装路由插件
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
},
{
path: '/user/:id', // 带参数的路由,:id 表示动态参数
name: 'user',
component: User
}
];
const router = new VueRouter({
mode: 'history', // 使用 history 模式,这样 URL 地址看起来更整洁,没有 # 号
base: process.env.BASE_URL, // 基础路径
routes
});
export default router;
5.3 在主文件中引入路由
在 main.js
中引入路由配置文件,并将其挂载到 Vue 实例上:
import Vue from 'vue';
import App from './App.vue';
import router from './router'; // 引入路由配置
Vue.config.productionTip = false;
new Vue({
router, // 挂载路由
render: h => h(App)
}).$mount('#app');
5.4 使用路由链接和视图
在应用中,我们使用 <router-link>
组件来创建导航链接,使用 <router-view>
组件作为路由出口,渲染对应的组件。例如:
<div id="app">
<nav>
<router-link to="/">首页</router-link> |
<router-link to="/about">关于</router-link> |
<router-link :to="{ name: 'user', params: { id: 123 }}">用户 123</router-link> |
<router-link :to="{ path: '/user/456' }">用户 456</router-link>
</nav>
<router-view></router-view>
</div>
当用户点击不同的链接时,<router-view>
中会根据当前的路由路径渲染对应的组件。
5.5 路由传参(就个人喜好选择)
在前面的示例中,我们已经看到了通过路由传参的方法,即在路由路径中定义动态参数(如 /user/:id
),然后在组件中通过 this.$route.params
来获取参数值,例如在 User.vue
组件中:
export default {
name: 'User',
mounted() {
const userId = this.$route.params.id;
console.log('用户 ID:', userId);
// 可以根据 userId 去获取对应的用户数据并进行展示
}
}
此外,还可以通过查询参数的形式进行传参,例如:
<router-link :to="{ path: '/about', query: { name: 'Vue', version: '3.x' }}">关于</router-link>
在组件中通过 this.$route.query
获取查询参数:
mounted() {
const { name, version } = this.$route.query;
console.log('查询参数:', name, version);
}
六、状态管理(Vuex)
Vuex 是 Vue 官方的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化,适用于中大型的 Vue 项目,帮助我们更好地管理共享状态。
6.1 安装 Vuex
同样,可以通过 npm 安装 Vuex,命令如下:
npm install vuex
6.2 创建 Vuex Store
在项目中创建一个 store.js
文件,定义 Vuex 的状态存储:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex); // 安装 Vuex 插件
export default new Vuex.Store({
state: {
count: 0, // 状态数据
todos: [ // 列表数据
{ id: 1, text: '学习 Vue', done: true },
{ id: 2, text: '学习 Vuex', done: false }
]
},
mutations: {
// mutations 是同步的方法,用于修改 state 中的状态,必须通过提交 mutation 来修改状态
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
},
addTodo(state, todo) {
state.todos.push(todo);
},
toggleTodo(state, id) {
const todo = state.todos.find(todo => todo.id === id);
if (todo) {
todo.done = !todo.done;
}
}
},
actions: {
// actions 类似于 mutations,但是 actions 可以包含异步操作,它提交 mutation 来改变状态
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
},
addTodoAsync({ commit }, todo) {
setTimeout(() => {
commit('addTodo', todo);
}, 1000);
}
},
getters: {
// getters 可以对 state 进行计算或过滤等操作,类似于 Vue 实例中的 computed 属性
doubleCount(state) {
return state.count * 2;
},
doneTodos(state) {
return state.todos.filter(todo => todo.done);
},
doneTodosCount: (state) => {
return state.todos.filter(todo => todo.done).length;
}
}
});
6.3 在 Vue 实例中使用 Store
在 main.js
中引入并挂载 Vuex Store:
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store'; // 引入 Vuex Store
Vue.config.productionTip = false;
new Vue({
router,
store, // 挂载 Vuex Store
render: h => h(App)
}).$mount('#app');
6.4 在组件中使用 Vuex 状态
6.4.1 读取状态
在组件中可以通过 this.$store.state
来访问 Vuex Store 中的状态,例如:
export default {
computed: {
count() {
return this.$store.state.count;
},
todos() {
return this.$store.state.todos;
}
}
}
也可以通过 Vuex 提供的 mapState
辅助函数来简化代码:
import { mapState } from 'vuex';
export default {
computed: mapState({
count: state => state.count,
todos: state => state.todos
})
}
或者更简洁的写法:
import { mapState } from 'vuex';
export default {
computed: mapState(['count', 'todos'])
}
6.4.2 提交 mutations 修改状态
要修改 Vuex Store 中的状态,必须通过提交 mutation。在组件中可以通过 this.$store.commit
方法来提交 mutation,例如:
methods: {
incrementCount() {
this.$store.commit('increment');
},
decrementCount() {
this.$store.commit('decrement');
},
addTodo() {
const newTodo = { id: 3, text: '学习 Vue Router', done: false };
this.$store.commit('addTodo', newTodo);
},
toggleTodo(id) {
this.$store.commit('toggleTodo', id);
}
}
同样,可以使用 Vuex 提供的 mapMutations
辅助函数来简化代码:
import { mapMutations } from 'vuex';
export default {
methods: {
...mapMutations(['increment', 'decrement']),
addTodo() {
const newTodo = { id: 3, text: '学习 Vue Router', done: false };
this.$store.commit('addTodo', newTodo);
},
toggleTodo(id) {
this.$store.commit('toggleTodo', id);
}
}
}
-------------------------------------------混入和过滤器等跳过
十、Vue 的插槽(Slots)
插槽是 Vue 中用于内容分发的机制,允许父组件向子组件的特定位置插入内容。
10.1 单插槽
在子组件中定义插槽:
<template>
<div>
<h2>这是一个子组件</h2>
<slot></slot> <!-- 定义插槽 -->
</div>
</template>
在父组件中使用子组件并插入内容:
<child-component>
<p>这是父组件插入的内容,会显示在子组件的插槽位置。</p>
</child-component>
10.2 具名插槽
当子组件中有多个插槽时,可以通过 name
属性来区分它们,即具名插槽。例如,子组件中:
<template>
<div>
<h2>这是一个子组件</h2>
<slot name="header"></slot> <!-- 定义具名插槽 -->
<div class="content">
<slot name="content"></slot>
</div>
</div>
</template>
父组件中使用:
<child-component>
<template v-slot:header>
<h3>这是头部内容</h3>
</template>
<template v-slot:content>
<p>这是内容区域的内容</p>
</template>
</child-component>
或者使用更简洁的语法:
<child-component>
<h3 slot="header">这是头部内容</h3>
<p slot="content">这是内容区域的内容</p>
</child-component>
10.3 作用域插槽
作用域插槽允许父组件在插入内容时能够访问子组件中的一些数据,即子组件可以向父组件传递数据,供父组件在插槽中使用。例如,子组件中:
<template>
<div>
<h2>列表组件</h2>
<ul>
<li v-for="(item, index) in items" :key="index">
<slot :item="item" :index="index"></slot>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
items: ['苹果', '香蕉', '橙子', '葡萄']
};
}
};
</script>
父组件中使用:
<list-component>
<template v-slot="slotProps">
<p>第 {{ slotProps.index + 1 }} 项:{{ slotProps.item }}</p>
</template>
</list-component>
此时,父组件可以通过 slotProps
访问到子组件传递过来的 item
和 index
数据,用于渲染列表项内容。
其实这三个插槽本质都是一个插槽,不管参数或者name是什么样的,最后的结果是三者是一个变式的关系。