为什么要升级到vue3.0
当将当前的技术栈从Vue 2.0升级到Vue 3.0时,有许多值得考虑的理由。以下是10个升级到Vue 3.0的理由:
- 更好的性能: Vue 3.0引入了一种全新的响应式系统,使用了Proxy代理,相比Vue 2.0中的Object.defineProperty,在性能方面有显著的提升。这意味着更快的渲染速度和更高的性能。
- 更小的文件大小: Vue 3.0通过使用模块化的设计和Tree-shaking技术,使得打包后的文件更小。这可以减少初始加载时间并提高应用程序的整体性能。
- Composition API: Vue 3.0引入了Composition API,这是一种新的组件组织方式,能够更好地组织和重用逻辑代码。相比于Vue 2.0的Options API,Composition API提供了更灵活、可组合和可维护的代码结构。
- TypeScript支持: Vue 3.0在设计时考虑了更好的TypeScript支持,提供了更好的类型推断和类型检查。这使得在Vue 3.0项目中使用TypeScript更加容易和高效。
- Teleport: Vue 3.0引入了Teleport(传送门)特性,它可以帮助您在DOM中的任何位置渲染组件。这对于在应用程序中创建弹出窗口、对话框或通知等动态内容非常有用。
- Fragments: Vue 3.0引入了片段(Fragments)特性,可以让您在不额外增加DOM节点的情况下渲染多个元素。这提高了组件的可读性和灵活性。
- 新的组件生命周期钩子: Vue 3.0引入了一些新的组件生命周期钩子函数(如
setup
),用于更好地控制组件的初始化和渲染过程。这使得编写和管理组件更加直观和灵活。 - 更好的TypeScript支持: Vue 3.0在设计上考虑到了更好的TypeScript支持,提供了更好的类型推断和类型检查。这使得在Vue 3.0项目中使用TypeScript更加容易和高效。
- 更好的开发工具支持: Vue 3.0的发布也伴随着更新的Vue Devtools,以支持新的API和功能。这将大大提高开发者在调试和分析Vue 3.0应用程序时的效率。
- 良好的生态系统支持: 尽管Vue 3.0是一个较新的版本,但它已经开始逐渐获得更多的支持和生态系统的迁移。许多常用的Vue插件和库也已更新以支持Vue 3.0,这为升级提供了更好的支持和生态环境。
这些理由强调了从Vue 2.0升级到Vue 3.0的许多优势和潜在好处。然而,在实际升级过程中,请务必测试和验证您的应用程序是否与Vue 3.0版本兼容,并根据您的项目需求和时间限制来评估升级的收益和风险。
怎么使用vue3.0呢?
vite中文网:https://cn.vitejs.dev/guide/
使用 NPM:
bash
$ npm create vite@latest
使用 Yarn:
bash
$ yarn create vite
使用 PNPM:
bash
$ pnpm create vite
安装依赖项
pnpm install
运行
pnpm dev
熟悉常用的API
Vite 提供了多个常用的 API 用于创建和配置项目。以下是一些常见的 Vite API:
- createApp(): 这是 Vite 中最常用的 API 之一,用于创建应用程序实例。它返回一个实例,您可以使用该实例来注册全局组件、挂载应用程序以及执行其他应用程序级别的操作。
import { createApp } from 'vue';
const app = createApp();
- use():
use
方法用于注册插件或中间件。您可以使用此方法在应用程序中使用各种插件、路由器、状态管理等功能。
app.use(plugin);
- mount():
mount
方法用于将应用程序挂载到特定的 DOM 元素上。您需要指定一个 DOM 选择器作为参数,以确定挂载的位置。
app.mount('#app');
- component():
component
方法用于注册全局组件。您可以使用此方法将组件注册为全局可用,以便在应用程序中的任何地方使用它。
app.component('my-component', MyComponent);
- directive():
directive
方法用于注册全局指令。您可以使用此方法注册自定义指令,以便在模板中使用它们来操作 DOM 元素。
app.directive('my-directive', myDirective);
- provide() / inject():
provide
和inject
方法用于在父组件中提供数据,并在子组件中注入数据。这是一种用于跨组件层次传递数据的高级技术。
app.provide('myData', data);
在子组件中:
const myData = inject('myData');
这些是一些 Vite 常用的 API,可以帮助您创建、配置和管理项目中的组件、插件和应用程序状态。Vite 还提供了其他一些 API,如路由器和状态管理的 API,您可以根据需要查阅 Vite 的官方文档以获取更多详细信息和用法示例。
常用的API及其使用方式
当使用 Vue 3 和 Vite 进行开发时,您还可以使用一些其他的 API 来处理数据响应式和操作 DOM 元素。以下是几个常见的 API:
- ref():
ref
函数用于创建一个响应式引用。它接受一个初始值作为参数,并返回一个可通过.value
属性访问的响应式对象。当引用的值发生变化时,相关的组件将自动重新渲染。
import { ref } from 'vue';
const count = ref(0);
console.log(count.value); // 输出:0
count.value++; // 修改引用的值
console.log(count.value); // 输出:1
- reactive():
reactive
函数用于创建一个响应式对象。它接收一个普通对象作为参数,并返回一个响应式的代理对象。当代理对象的属性发生变化时,相关的组件将自动重新渲染。
import { reactive } from 'vue';
const state = reactive({
message: 'Hello',
count: 0
});
console.log(state.message, state.count); // 输出:Hello 0
state.message = 'Hi'; // 修改响应式对象的属性
state.count++; // 修改响应式对象的属性
console.log(state.message, state.count); // 输出:Hi 1
- toRefs():
toRefs
函数用于将一个响应式对象的属性转换为具有.value
属性的普通引用。这对于在使用reactive
创建的对象作为组件的props
时特别有用。
import { reactive, toRefs } from 'vue';
const state = reactive({
message: 'Hello',
count: 0
});
const stateRefs = toRefs(state);
console.log(stateRefs.message.value, stateRefs.count.value); // 输出:Hello 0
- watch():
watch
函数用于观察响应式数据的变化并执行相应的操作。您可以使用watch
监视单个响应式值、多个响应式值或副作用函数的变化。
import { reactive, watch } from 'vue';
const state = reactive({
count: 0
});
watch(
() => state.count, // 要监视的响应式数据
(newCount, oldCount) => {
console.log(`Count changed from ${oldCount} to ${newCount}`);
}
);
state.count++; // 输出:Count changed from 0 to 1
这些是处理数据响应式和操作 DOM 元素时常用的 API。Vue 3 的响应式系统在处理状态管理和组件间数据传递方面非常强大和灵活。您可以根据需求使用这些 API 来构建动态和交互式的应用程序。请记住,在Vite中使用这些API时,您需要导入相应的函数和模块,如示例中所示。
其他的API(vue3响应式相关知识点)
响应式: 核心
ref()接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value。
computed ()接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。
reactive()返回一个对象的响应式代理。
readonly()接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
watchEffect()立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
watch()侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
响应式: 工具
isRef()检查某个值是否为 ref。
unref()如果参数是 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 计算的一个语法糖。
toRef()基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。
toRefs()将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。
isProxy()检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。
isReactive()检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。
isReadonly()检查一个对象是否是由 readonly() 或 shallowReadonly() 创建的代理。
响应式: 进阶
shallowRef()ref() 的浅层作用形式。
triggerRef()强制触发依赖于一个浅层 ref 的副作用,这通常在对浅引用的内部值进行深度变更后使用。
customRef()创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
shallowReactive()reactive() 的浅层作用形式。
shallowReadonly()readonly() 的浅层作用形式
toRaw()根据一个 Vue 创建的代理返回其原始对象。
markRaw()将一个对象标记为不可被转为代理。返回该对象本身。
增强的功能
当使用 Vite 进行开发时,还有一些其他的 API 和特性可用于增强您的开发经验。以下是一些常见的 API 和特性:
- watchEffect():
watchEffect
函数用于创建一个副作用函数,该函数会自动追踪其依赖的响应式数据,并在其依赖项发生变化时自动执行。它类似于watch
,但不提供先前值的追踪。
import { reactive, watchEffect } from 'vue';
const state = reactive({
count: 0
});
watchEffect(() => {
console.log(`Count is: ${state.count}`);
});
state.count++; // 输出:Count is: 1
- computed():
computed
函数用于创建一个计算属性,它根据其依赖的响应式数据进行计算,并返回一个响应式的结果。计算属性是惰性求值的,只有在其依赖项发生变化时才重新计算。
import { reactive, computed } from 'vue';
const state = reactive({
count: 0
});
const doubleCount = computed(() => state.count * 2);
console.log(doubleCount.value); // 输出:0
state.count++;
console.log(doubleCount.value); // 输出:2
- onMounted() / onUnmounted():
onMounted
和onUnmounted
是生命周期钩子函数,在组件挂载和卸载时分别执行指定的回调函数。它们用于执行一些与组件生命周期相关的操作。
import { onMounted, onUnmounted } from 'vue';
onMounted(() => {
console.log('Component mounted');
});
onUnmounted(() => {
console.log('Component unmounted');
});
- createRouter():
createRouter
函数用于创建路由器实例,用于管理应用程序的路由。您可以使用该实例定义路由规则、导航和监听路由变化。
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
});
app.use(router);
- createStore():
createStore
函数用于创建一个全局状态管理的 store,用于共享和管理应用程序的状态。它提供了状态的存储、修改和派发操作。
import { createStore } from 'vuex';
const store = createStore({
state() {
return {
count: 0
};
},
mutations: {
increment(state) {
state.count++;
}
},
actions: {
asyncIncrement(context) {
setTimeout(() => {
context.commit('increment');
}, 1000);
}
}
});
app.use(store);
这些是 Vite 中常用的一些 API 和特性,可用于处理副作用、计算属性、生命周期、路由和状态管理等方面。有了这些功能强大的 API,您可以更好地开发出高效、可扩展和交互式的应用程序。请注意,这些示例中的 API 来自 Vue 3 的生态系统,但在 Vite 中使用方法类似,只需根据需要导入相应的函数和模块。
生命周期
setup 中也有新的生命周期 onBeforeMount -> onMounted -> onBeforeUpdate -> onUpdated -> onBeforeUnmount -> onUnmounted -> onErrorCaptured 跟 options api 混用时 onBeforeMount 在 beforeMount 之前,onMounted 在 mounted 之前。。。之后都是。
vue 中父子顺序 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted 在 setup 中声明周期同样。
路由的用法
非常抱歉,Vite 中的路由跳转确实不需要使用 this
。以下是在 Vite 中进行路由跳转的示例:
- 安装和配置
vue-router
:在项目目录中使用以下命令安装vue-router
:
npm install vue-router@next
然后,在项目的入口文件(通常是 main.js
或 main.ts
)中进行配置:
import { createApp } from 'vue';
import App from './App.vue';
import { createRouter, createWebHashHistory } from 'vue-router';
// 导入组件
import Home from './components/Home.vue';
import About from './components/About.vue';
// 创建路由实例
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
});
// 创建 Vue 应用程序实例并使用路由
const app = createApp(App);
app.use(router);
app.mount('#app');
- 在组件中使用路由
在 AppComponent.vue 组件模板中,您可以使用
组件来导航到不同的页面,使用
组件来显示当前匹配的路由组件。
Home
About
- 跳转到不同的路由
要在组件或方法中进行路由跳转,可以使用 useRouter
函数获取路由实例,然后使用 push
方法进行跳转。
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter();
const goToAbout = () => {
router.push('/about');
};
return {
goToAbout
};
}
};
通过 useRouter
获取到的 router
实例可以直接使用 push
方法进行路由跳转,无需通过 this
。在组件的 setup
函数中,通过定义一个 goToAbout
方法并使用 router.push
进行跳转。
请确保已正确导入 vue-router
相关的模块和组件,并在 Vue 应用程序实例中使用路由实例。这样,您就可以使用
组件或编程式导航实现路由跳转,而无需使用 this
。
获取路由传递的参数
在 Vite 中,可以使用 useRoute
函数来获取当前路由的信息,包括路由参数。以下是一个在路由中获取参数的示例:
- 安装和配置
vue-router
:在项目目录中使用以下命令安装vue-router
:
npm install vue-router@next
然后,在项目的入口文件(通常是 main.js
或 main.ts
)中进行配置:
import { createApp } from 'vue';
import App from './App.vue';
import { createRouter, createWebHashHistory } from 'vue-router';
// 导入组件
import Home from './components/Home.vue';
import About from './components/About.vue';
// 创建路由实例
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about/:id', component: About }
]
});
// 创建 Vue 应用程序实例并使用路由
const app = createApp(App);
app.use(router);
app.mount('#app');
- 在组件中获取路由参数
在 About.vue 组件中,可以使用 useRoute
函数来获取路由信息,包括参数。以下是一个获取路由参数并显示在组件中的示例:
About Page
Current route params: {{ routeParams }}
在上述示例中,通过 useRoute
获取到当前路由的信息,并将参数存储在 routeParams
变量中。然后,可以在模板中使用 routeParams
来显示路由参数。
请注意,路由参数通常使用 :
定义,并在路由定义中进行配置(例如:/about/:id
)。在组件中使用 useRoute
函数可以轻松地获取这些参数并在组件中使用。
这样,您就可以在 Vite 中通过 useRoute
函数获取路由传递的参数,而无需使用 this
。
常用的hook有哪些
在 Vite 项目中,您可以使用以下常用的 Vue Composition API 钩子函数(Hooks):
setup()
:setup
函数是组件内使用的主要入口点。它是一个特殊的钩子函数,用于设置组件的初始状态、处理响应式数据和副作用等。可以在setup
函数中返回组件的数据、方法和计算属性等,使其在模板中可用。onMounted()
:onMounted
是在组件挂载到 DOM 之后执行的钩子函数。您可以在其中执行一些初始化的异步操作、添加事件监听器等。onUnmounted()
:onUnmounted
是在组件从 DOM 中卸载之前执行的钩子函数。您可以在其中清理定时器、取消订阅、解绑事件监听器等。watch()
:watch
是一个用于观察响应式数据变化的钩子函数。您可以使用watch
监听特定的响应式变量,并在其值发生变化时执行相应的操作,例如执行网络请求、计算依赖等。reactive()
:reactive
函数用于创建响应式对象。您可以使用它将普通对象转换为响应式对象,从而使其在数据更新时自动触发 UI 的重新渲染。ref()
:ref
函数用于创建一个包装器,将原始值转换为响应式对象。常见的用法是用于包装基本类型的数据,如数字、布尔值等。computed()
:computed
是一个计算属性的工具函数。您可以使用computed
创建一个响应式的计算属性,根据依赖的响应式数据进行计算,并在其依赖变化时自动更新。provide()
和inject()
:provide
和inject
是为了实现跨组件通信而引入的 API。provide
函数用于在一个祖先组件中提供数据,以便后代组件可以通过inject
函数进行注入使用。这样,您可以在不通过 props 传递的情况下,在组件之间共享数据。inject
函数用于从祖先组件中注入数据,以便在当前组件中使用。它接受一个可选的默认值,在没有找到提供的数据时可以提供一个备用值。
以上是一些在 Vite 项目中常用的 Vue Composition API 钩子函数。使用这些钩子函数可以更好地组织和管理组件的状态、副作用和逻辑。除了这些常用的钩子函数,还有许多其他的钩子函数可根据需要使用,具体根据项目的需求而定。
如何封装一个组件
抱歉,我之前提供的代码中没有使用 setup
语法糖形式。以下是使用 setup
语法糖的修正代码:
{{ greeting }}
Count: {{ count }}
在修正后的代码中,我们使用了 script setup
的语法糖形式。在这种形式下,我们无需明确指定 setup
函数,而是将变量和逻辑直接放在 标签中的
script setup
中。
通过 defineProps
函数来声明和验证组件的 props,并赋值给 props
变量。响应式数据、计算属性和方法都可以在 script setup
中直接定义,无需显式返回一个对象。
这样,我们便使用了 setup
的语法糖形式来封装一个接收参数的组件。您可以像之前指导的那样使用组件并传递自定义的参数值。
如何封装hook
如果您要在 Vite 中封装两个方法,一个是增加函数,一个是减少函数,并使用 use
开头封装成自定义的 Hook,您可以按照以下示例进行编写。
// useCounter.js
import { ref } from 'vue';
export function useCounter() {
// 响应式数据
const count = ref(0);
// 增加函数
function increment() {
count.value++;
}
// 减少函数
function decrement() {
count.value--;
}
// 返回自定义的 Hook
return {
count,
increment,
decrement
};
}
在上述代码中,我们创建了一个名为 useCounter
的自定义 Hook。通过 ref
函数,我们将 count
变量转换为响应式数据。然后,我们定义了 increment
函数和 decrement
函数来增加或减少 count
的值。
最后,我们将 count
、increment
和 decrement
返回为一个对象,使其可以在组件中使用。
在组件中使用这个自定义 Hook 的示例:
Count: {{ counter }}
在上述示例中,我们引入了自定义的 useCounter
Hook,并使用解构赋值从 Hook 返回的对象中获取到 count
、increment
和 decrement
。这样,我们就可以在模板中直接使用这些变量和函数了。
通过这种方式,您可以按照习惯的命名方式将逻辑封装成自定义的 Hook,并在多个组件中共享和重复使用这些逻辑。使用 use
开头的自定义 Hook 可以让我们更好地复用逻辑和维护代码。
proxy和Reflect对象
Proxy 和 Reflect 是 JavaScript 的两个内置对象,它们在 ECMAScript 6 (ES6) 中引入,并为开发者提供了对对象行为的拦截和修改能力。
Proxy 对象用于定义自定义行为以拦截目标对象的操作。通过使用 Proxy,我们可以拦截对目标对象的操作,比如访问属性、修改属性、调用方法等,并可以在拦截器中自定义相应的行为。这使得我们可以在运行时对对象进行拦截和修改,实现诸如属性校验、数据劫持、日志记录等功能。
下面是使用 Proxy 的一个示例:
const target = {}; // 目标对象
const handler = {
get: function (target, propKey, receiver) {
console.log(`正在访问属性:${propKey}`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`正在设置属性:${propKey} = ${value}`);
return Reflect.set(target, propKey, value, receiver);
}
};
const proxy = new Proxy(target, handler); // 创建代理对象
proxy.name = 'John'; // 设置属性:name = John
console.log(proxy.name); // 访问属性:name
在上述示例中,我们创建了一个空对象 target
作为目标对象,然后定义了一个 handler
对象,其中使用了 get
和 set
拦截器来拦截对目标对象属性的访问和修改操作。在拦截器中,我们打印了相应的日志,并使用 Reflect
对象调用原始的操作。
最后,我们使用 new Proxy()
创建了一个代理对象 proxy
,该代理对象会拦截对目标对象的操作。当我们通过代理对象设置属性 proxy.name = 'John'
时,拦截器会触发并打印日志;当我们通过代理对象访问属性 proxy.name
时,拦截器同样会触发并打印日志。
Reflect 对象提供了一组与对象操作相关的方法,这些方法与对象操作的默认行为相对应。通过使用 Reflect,我们可以在代码中更方便、更一致地调用和处理对象操作,而不需要直接操作目标对象。
下面是使用 Reflect 的一个示例:
const obj = {
name: 'John',
age: 25
};
console.log(Reflect.has(obj, 'name')); // 判断 obj 中是否存在属性 name
const values = Reflect.ownKeys(obj); // 获取 obj 的所有属性
values.forEach(key => {
console.log(Reflect.get(obj, key)); // 获取 obj 中每个属性的值
});
在上述示例中,我们使用 Reflect.has()
方法来判断 obj
中是否存在属性 name
。接下来,我们使用 Reflect.ownKeys()
方法获取 obj
的所有属性,并通过 forEach
循环遍历获取到的属性,然后使用 Reflect.get()
方法获取每个属性的值,并打印输出。
Reflect 提供了一系列方法,例如 get()
、set()
、deleteProperty()
、apply()
等,通过这些方法,我们可以更方便地对对象进行操作,同时也使代码更加易读和一致。
Proxy 和 Reflect 的组合使用可以使我们更灵活地拦截和修改对象的操作,并提供了对 JavaScript 对象行为的细粒度控制。
全局状态管理vuex在组件中使用(setup语法糖)
store也是用过userStore函数来创建的。
通过
import{useStore}from'vuex'
const store=useStore();
来创建一个store,就可以使用自己定义的全局状态属性和各种方法了。
setup中的async和await
我们学习async和await进行异步操作的时候,都知道他们要配合使用,否则会报错。
在setup语法糖里面,可以直接使用awiat而不需要再使用async,因为 setup 会自动变成 async setup
这个使用起来还是很方便的。
组件的传参
在 Vue 3 的 Composition API 中,setup()
函数中没有直接访问实例的 this
。在这种情况下,你可以使用 context
对象来访问父级组件的属性和方法。以下是两种不使用 this
来给父级组件发送数据的方法:
1. 使用事件:
子组件示例:
父级组件示例:
在子组件中,通过 instance.emit('data', data)
触发一个名为 data
的自定义事件,并将 data
作为参数传递给父级组件。在父级组件中,使用 @data="handleData"
绑定该自定义事件,并在 handleData
方法中接收传递的数据。
2. 使用 provide
/inject
:
父级组件示例:
子组件示例:
{{ injectedData }}
在父级组件中,使用 provide('data', data)
提供了一个名为 'data'
的数据给子组件。同时,我们使用 instance.appContext.config.globalProperties
注册了一个名为 handleData
的方法,在子组件中可以调用该方法将数据传递给父级组件进行处理。
在子组件中,使用 inject('data')
注入并接收了父级组件提供的数据,并将其绑定到 injectedData
变量上。然后,通过 instance.appContext.config.globalProperties.handleData(injectedData)
调用父级组件中的 handleData
方法,将数据传递给父级组件进行处理。
这两种方式均避免了直接使用 this
,并符合 Vue 3 的 Composition API 的用法。
css使用变量
在 Vue 的组件中,你可以使用 setup()
函数来定义变量,并将其传递到模板中的样式中。
下面是一个示例,演示如何在 setup()
函数中定义颜色变量,并将其传递到组件样式中:
Hello, Vite!
在上面的示例中,我们使用了 ref
函数来创建可响应式的变量 primaryColor
和 secondaryColor
。然后,我们将这些变量从 setup()
函数返回,使其可以在模板中访问到。
在样式中,我们可以使用 var()
函数引用这些变量,并将它们应用于对应的 CSS 属性。这样,当 primaryColor
或 secondaryColor
的值发生改变时,样式也会自动更新。
请注意,setup()
函数是 Vue 3 中的 Composition API 的一部分,它提供了更灵活和功能丰富的组件编写方式。使用 setup()
函数可以方便地定义和传递变量到组件的模板和样式中,以实现更高度的可定制化。