Vue 基础二(进阶使用)
一、Vue的响应式系统
(一)Vue 响应式的原理
Vue 的核心特性之一是响应式数据绑定系统。它允许我们轻松地将数据与视图进行绑定,当数据发生变化时,视图会自动更新。Vue 内部通过 Object.defineProperty
或 Proxy
来实现这一特性。
-
Object.defineProperty
:-
在 Vue 2.x 中,Vue 使用
Object.defineProperty
来劫持对象的属性。 -
当我们访问或修改被
Object.defineProperty
定义的属性时,Vue 会触发相应的 getter 和 setter 方法。 -
Getter 用于读取属性值并追踪依赖,Setter 用于设置属性值并通知视图更新。
-
-
Proxy
:-
在 Vue 3.x 中,Vue 使用
Proxy
来实现响应式系统。 -
Proxy
提供了更强大和灵活的拦截机制,可以拦截对象的所有操作,包括属性的读取、设置、删除等。 -
与
Object.defineProperty
相比,Proxy
能够更高效地实现深度响应式。
-
(二)响应式 API
Vue 提供了一系列响应式相关的 API,用于手动创建响应式对象或追踪数据。
-
ref
:用于创建一个响应式的引用,返回一个包含值的ref
对象。
JavaScript复制
import { ref } from 'vue';
const count = ref(0);
console.log(count.value); // 0
-
reactive
:用于将一个普通对象转换为响应式对象。
JavaScript复制
import { reactive } from 'vue';
const state = reactive({
count: 0
});
console.log(state.count); // 0
-
computed
:用于创建一个计算属性,基于响应式依赖,自动追踪依赖并缓存计算结果。
JavaScript复制
import { ref, computed } from 'vue';
const firstName = ref('John');
const lastName = ref('Doe');
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`;
});
console.log(fullName.value); // John Doe
-
watch
:用于监听响应式数据的变化,并执行回调函数。
JavaScript复制
import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newVal, oldVal) => {
console.log(`count 变化了:从 ${oldVal} 变为 ${newVal}`);
});
二、Vue 生命周期
Vue 组件有一个完整的生命周期,从创建到销毁,会依次调用一系列的生命周期钩子函数。以下是 Vue 生命周期的主要钩子函数:
-
beforeCreate
:-
在实例初始化之后,数据观测和事件配置之前被调用。
-
此时,
data
和methods
等属性尚未初始化。
-
-
created
:-
在实例创建完成后被调用。
-
此时,
data
和methods
等属性已经初始化完毕,可以访问和操作。
-
-
beforeMount
:-
在挂载开始之前被调用。
-
此时,模板已经编译完成,但尚未挂载到 DOM 上。
-
-
mounted
:-
在实例挂载完成后被调用。
-
此时,DOM 已经渲染完成,可以进行 DOM 操作。
-
-
beforeUpdate
:-
在数据更新之前被调用。
-
此时,数据已经更新,但 DOM 尚未更新。
-
-
updated
:-
在数据更新完成并重新渲染 DOM 后被调用。
-
此时,可以重新操作 DOM。
-
-
beforeUnmount
:-
在实例销毁之前被调用。
-
可以在此清理资源,如定时器、事件监听器等。
-
-
unmounted
:-
在实例销毁后被调用。
-
此时,Vue 实例已经完全销毁,无法再进行操作。
-
三、Vue 的组件
(一)组件的基础知识
-
组件的注册
-
全局注册:使用
Vue.component
注册的组件可以在全局范围内使用。
-
JavaScript复制
Vue.component('my-component', {
template: '<div>This is a global component</div>'
});
-
局部注册:在单个 Vue 实例或组件中注册的组件。
JavaScript复制
const AppComponent = {
template: '<div>This is a local component</div>'
};
const app = new Vue({
components: {
'my-component': AppComponent
}
});
-
组件的通信
-
父组件向子组件传递数据:通过
props
。
-
vue复制
<!-- ParentComponent.vue -->
<template>
<div>
<child-component :message="parentMessage"></child-component>
</div>
</template>
<script>
export default {
data() {
return {
parentMessage: 'Hello from parent'
};
}
};
</script>
vue复制
<!-- ChildComponent.vue -->
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: {
message: {
type: String,
required: true
}
}
};
</script>
-
子组件向父组件传递事件:通过
$emit
。
vue复制
<!-- ChildComponent.vue -->
<template>
<div @click="$emit('child-event', 'Hello from child')">Click me</div>
</template>
<script>
export default {
methods: {
handleClick() {
this.$emit('child-event', 'Hello from child');
}
}
};
</script>
vue复制
<!-- ParentComponent.vue -->
<template>
<div>
<child-component @child-event="handleChildEvent"></child-component>
</div>
</template>
<script>
export default {
methods: {
handleChildEvent(message) {
console.log(message); // Hello from child
}
}
};
</script>
-
动态组件:使用
<component>
标签和is
属性动态切换组件。
vue复制
<template>
<div>
<button @click="currentComponent = 'ComponentA'">Show A</button>
<button @click="currentComponent = 'ComponentB'">Show B</button>
<component :is="currentComponent"></component>
</div>
</template>
<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
export default {
components: {
ComponentA,
ComponentB
},
data() {
return {
currentComponent: 'ComponentA'
};
}
};
</script>
(二)组件的高级特性
-
插槽(Slots):插槽允许父组件向子组件传递自定义内容。
-
默认插槽:
-
vue复制
<!-- ChildComponent.vue -->
<template>
<div>
<slot></slot>
</div>
</template>
vue复制
<!-- ParentComponent.vue -->
<template>
<div>
<child-component>
<p>This is inserted into the slot</p>
</child-component>
</div>
</template>
-
具名插槽:
vue复制
<!-- ChildComponent.vue -->
<template>
<div>
<slot name="header"></slot>
<slot name="content"></slot>
</div>
</template>
vue复制
<!-- ParentComponent.vue -->
<template>
<div>
<child-component>
<template v-slot:header>
<h2>Header</h2>
</template>
<template v-slot:content>
<p>Content</p>
</template>
</child-component>
</div>
</template>
-
作用域插槽:
vue复制
<!-- ChildComponent.vue -->
<template>
<div>
<slot :user="user"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: 'John' }
};
}
};
</script>
vue复制
<!-- ParentComponent.vue -->
<template>
<div>
<child-component v-slot:default="slotProps">
<p>{{ slotProps.user.name }}</p>
</child-component>
</div>
</template>
-
动态组件与缓存:使用
keep-alive
缓存动态组件,避免重复渲染。
vue复制
<template>
<div>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</div>
</template>
-
异步组件:允许组件以异步的方式加载,提高首屏加载速度。
JavaScript复制
const AsyncComponent = () => ({
component: import('./AsyncComponent.vue')
});
四、Vue 的状态管理
(一)Vuex 简介
Vuex 是 Vue 官方的状态管理库,用于集中管理Vue 应用中的状态。
-
安装 Vuex
bash复制
npm install vuex --save
-
基本使用
JavaScript复制
// store.js
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
}
},
getters: {
doubleCount: state => state.count * 2
}
});
export default store;
-
在组件中使用 Vuex
vue复制
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="incrementAsync">Increment Async</button>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
}
},
methods: {
increment() {
this.$store.commit('increment');
},
incrementAsync() {
this.$store.dispatch('incrementAsync');
}
}
};
</script>
(二)Vuex 的核心概念
-
State:存储应用的状态数据。
JavaScript复制
state: {
count: 0
}
-
Getter:类似于计算属性,用于从 state 中派生出一些状态。
JavaScript复制
getters: {
doubleCount: state => state.count * 2
}
-
Mutations:用于改变 state 的唯一方式,必须是同步操作。
JavaScript复制
mutations: {
increment(state) {
state.count++;
}
}
-
Actions:用于提交 mutations,可以包含异步操作。
JavaScript复制
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
}
}
五、Vue Router
Vue Router 是 Vue 官方的路由管理器,用于实现单页应用中的页面跳转。
(一)安装 Vue Router
bash复制
npm install vue-router --save
(二)基本使用
JavaScript复制
// main.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import Home from './components/Home.vue';
import About from './components/About.vue';
Vue.use(VueRouter);
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
];
const router = new VueRouter({
routes
});
new Vue({
router,
render: h => h(App)
}).$mount('#app');
(三)路由导航
vue复制
<!-- App.vue -->
<template>
<div>
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<router-view></router-view>
</div>
</template>
(四)路由参数
JavaScript复制
// routes.js
{ path: '/user/:id', component: User }
// User.vue
export default {
computed: {
userId() {
return this.$route.params.id;
}
}
};
(五)路由守卫
JavaScript复制
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 在渲染该组件的对应路由之前调用
console.log('beforeEach');
next();
});
// 全局后置守卫
router.afterEach((to, from) => {
console.log('afterEach');
});
// 路由独享守卫
const Home = {
template: '<div>Home</div>',
beforeEnter(to, from, next) {
console.log('beforeEnter Home');
next();
}
};
六、Vue 的 Mixins
Mixins 是一种分发 Vue 组件中可复用功能的灵活方式。它允许我们将多个组件共用的逻辑提取到一个对象中,然后在多个组件中使用。
JavaScript复制
// mixin.js
export const myMixin = {
data() {
return {
message: 'Hello from mixin'
};
},
created() {
console.log('Mixin created');
},
methods: {
myMethod() {
console.log('Mixin method');
}
}
};
在组件中使用 Mixin:
JavaScript复制
import { myMixin } from './mixin.js';
export default {
mixins: [myMixin],
created() {
console.log('Component created');
}
};
七、Vue 的插件
Vue 的插件可以扩展 Vue 的功能,通常用于全局功能的添加,如添加全局方法或属性、注册组件等。
JavaScript复制
// myPlugin.js
export default {
install(Vue, options) {
// 添加全局方法
Vue.myGlobalMethod = (message) => {
console.log(message);
};
// 添加实例方法
Vue.prototype.$myMethod = (message) => {
console.log(message);
};
// 注册全局组件
Vue.component('my-component', {
template: '<div>This is a global component</div>'
});
}
};
在项目中安装插件:
JavaScript复制
import Vue from 'vue';
import MyPlugin from './myPlugin.js';
Vue.use(MyPlugin);
八、Vue 的过渡与动画
Vue 提供了过渡和动画的支持,可以轻松地为 DOM 变化添加效果。
(一)使用 transition
进行过渡
vue复制
<template>
<div>
<button @click="show = !show">Toggle</button>
<transition name="fade">
<p v-if="show">Fade Transition</p>
</transition>
</div>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
(二)使用 animate.css
实现动画
vue复制
<template>
<div>
<button @click="show = !show">Toggle</button>
<transition name="bounce"
enter-active-class="animate__animated animate__bounceIn"
leave-active-class="animate__animated animate__bounceOut">
<p v-if="show">Bounce Animation</p>
</transition>
</div>
</template>
九、Vue 的测试
(一)单元测试
Vue 提供了丰富的测试工具和框架,如 Jest 和 Vue Test Utils。
安装:
bash复制
npm install --save-dev jest @vue/test-utils vue-jest
示例:
JavaScript复制
// MyComponent.spec.js
import { shallowMount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';
describe('MyComponent.vue', () => {
it('renders a message', () => {
const wrapper = shallowMount(MyComponent);
expect(wrapper.text()).toContain('Hello World');
});
});
(二)端到端测试
可以使用 Cypress 或 Nightwatch 等工具进行端到端测试。
安装 Cypress:
bash复制
npm install cypress --save-dev
编写测试:
JavaScript复制
describe('MyApp', () => {
it('should display the title', () => {
cy.visit('/');
cy.contains('h1', 'Welcome to Vue.js');
});
});
十、Vue 的优化
(一)代码分割
通过代码分割,可以将大文件拆分成多个小文件,按需加载,提高首屏加载速度。
JavaScript复制
// 使用动态导入
const MyComponent = () => import('./MyComponent.vue');
(二)性能优化
-
避免不必要的渲染:使用
v-once
或v-memo
。 -
使用
keep-alive
缓存组件:减少组件的重复渲染。 -
优化数据绑定:减少不必要的
watch
和computed
。
(三)SEO 优化
Vue 应用可以通过以下方式优化 SEO:
-
服务器端渲染(SSR):使用 Vue Server Renderer 渲染页面内容。
-
预渲染:使用
prerender-spa-plugin
预渲染页面。
总结
Vue 是一个功能强大且灵活的前端框架,通过进阶学习,可以更好地掌握其核心特性,如响应式系统、生命周期、状态管理、路由、动画等。同时,了解如何优化 Vue 应用,进行单元测试和端到端测试,对于构建高效、可维护的现代 Web 应用至关重要。希望本文能够帮助你更好地理解和应用 Vue 的进阶知识。