Vue3
1、前言
Vue3
目前还是 Beta
版本,我们可以通过多种不同的方式来使用 Vue3
。
注意因为是 Beta 的原因,虽然主要的一些 API 基本是稳定的,但是这里涉及到的一些使用方式或许会在以后有变更
同时当前的一些生态也还没有完全完全完善,所以不要在生产环境中使用,即使作为学习,也应该随时关注官方的变化。
- vue-next-webpack-preview
- Vue Composition API插件
- vue-cli-plugin-vue-next
2、vue-next-webpack-preview
https://github.com/vuejs/vue-next-webpack-preview
这种方式是使用 webpack
独立配置构建 Vue3
项目。
因为只是配置了基本特性(如单文件组件支持等),也没有构建上的优化,推荐作为学习使用。
3、Vue Composition API 插件
https://github.com/vuejs/composition-api
首先,Vue Composition API
只是 Vue3
中的其中一项特性(重要),该插件可以让我们在 Vue2
中只使用 Vue Composition API
,而不需要升级所有特性。
在vue2中安装使用
npm install @vue/composition-api
# or
yarn add @vue/composition-api
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
// use the APIs
import { ref, reactive } from '@vue/composition-api'
4、vue-cli-plugin-vue-next
https://github.com/vuejs/vue-cli-plugin-vue-next
一个把 Vue2
项目升级为 Vue3
的工具。
功能并不完善。
4-1、步骤
- 首先使用
vue-cli
工具构建一个Vue2
的项目 - 在项目根目录下使用命令:
vue add vue-next
构建成基于Vue3
的项目。
5、new Vue() 与 createApp()
Vue3
之前使用 new Vue()
来构建应用根组件,现在改为 createApp()
,调用 $mount
方法进行挂载。
let app = new Vue() => let app = createApp(App);
Vue3
之前使用 Vue.use() 注册api,现在是挂到具体实例对象上
Vue.use => 实例对象.use()
6、Fragment
- 之前的每一个独立的组件有且仅有一个顶层节点(元素),许多时候,我们不得不去为组件添加一层没有太多意义的顶层结构。
- 现在
Vue3
支持了一个Fragment
的特性,当组件中的顶层节点不是一个的时候,会通过Fragment
来进行处理。
运行没问题
7、Composition API(重点)
optionsAPI 选项式API
- vue通过选项(参数)的方式,对外提供了一组用于构建或使用它的 api,选项式编程,这就叫optionsAPI
- 选项式API 根据类型来组织数据(结构)
Vue.component({
name: "HelloWorld",
props: {
msg: String
}
});
Composition API 组合式API
- 根据功能组织代码
- 不同功能进行分类
setup
setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。
-
调用时机
创建组件实例,然后初始化 props ,紧接着就调用setup 函数。从生命周期钩子的视角来看,它会在 beforeCreate 钩子之前被调用 -
原来视图中可使用的数据来源很多:
data,props,computed,methods,inject
这种做法数据来源太多,容易造成混乱 -
vue3 视图中要使用的数据必须通过
setup
函数返回
setup() {
console.log("setup");
let title = "lc"
const changeTitle = () => {
title = "hello vue";
console.log(title);
}
return {
title,
changeTitle
}
}
但是这样写的数据并不是响应式的
响应式数据
ref
// 把普通值变为一个可代理对象
let title = ref("lc")
console.log(title);
数据修改
const changeTitle = () => {
title.value = "hello vue";
console.log(title);
}
reactive
import { ref, reactive } from "vue";
export default {
name: "Register",
setup() {
let formData = reactive({
username: "",
password: "",
repassword: ""
});
console.log(formData);
const submitRegister = () => {
console.log("发送登录请求", formData);
};
return {
formData,
submitRegister
};
}
};
computed
let errorMessage = computed(() => {
return "abc"
})
let errorMessage = computed(() => {
if (
formData.username.trim() === "" ||
formData.password.trim() === "" ||
formData.repassword.trim() === ""
) {
return "注册信息不符合要求";
} else {
return "可以提交"
console.log("发送登录请求", formData);
}
});
watchEffect
立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
watch
- watch API 完全等效于 2.x this.$watch (以及 watch 中相应的选项)。
- watch 需要侦听特定的数据源,并在回调函数中执行副作用。
- 默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调。
对比 watchEffect,watch 允许我们:
- 懒执行副作用;
- 更明确哪些状态的改变会触发侦听器重新运行副作用;
- 访问侦听状态变化前后的值。
复用数据逻辑
把可复用的数据放到一个文件下
import { ref, reactive, computed } from "vue";
let type = ref("register");
let formData = reactive({
username: "",
password: "",
repassword: ""
});
let errorMessage = computed(() => {
if (
formData.username.trim() === "" ||
formData.password.trim() === "" ||
(
type.value === "register" &&
formData.repassword.trim() === ""
)
) {
return "注册信息不符合要求";
} else {
return "可以提交";
console.log("发送登录请求", formData);
}
});
const useSubmitRegister = () => {
console.log("注册", formData);
};
const useSubmitLogin = () => {
console.log("登录", formData);
};
export {
type,
formData,
errorMessage,
useSubmitRegister
};
生命周期钩子函数
可以通过on格式 onXXX
直接导入函数来注册生命周期钩子:
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
},
}
这些生命周期钩子注册函数只能在 setup() 期间同步使用, 因为它们依赖于内部的全局状态来定位当前组件实例(正在调用 setup() 的组件实例), 不在当前组件下调用这些函数会抛出一个错误。
组件实例上下文也是在生命周期钩子同步执行期间设置的,因此,在卸载组件时,在生命周期钩子内部同步创建的侦听器和计算状态也将自动删除。
新增调试钩子函数
新增的钩子函数
除了和 2.x 生命周期等效项之外,组合式 API 还提供了以下调试钩子函数:
- onRenderTracked
- onRenderTriggered
两个钩子函数都接收一个 DebuggerEvent,与 watchEffect 参数选项中的 onTrack 和 onTrigger 类似:
export default {
onRenderTriggered(e) {
debugger
// 检查哪个依赖性导致组件重新渲染
},
}
setup 参数
- 使用setup也需要通过options来定义props,通过第一个参数获取
- 第二个参数为上下文对象,里面存入了非props参数
$attrs
,提交事件的方法emit
props: {
msg: String
},
setup(props, context) {
console.log("props", props);
console.log("context", context);
}
访问组件对象
import { getCurrentInstance } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String
},
setup() {
let instance = getCurrentInstance();
console.log(instance);
}
};
8、Teleport 传送门
https://github.com/vuejs/rfcs/pull/112
Vue3
新增的内置组件 <teleport>
,又来解决如下的一个需求:
有时,组件模板的一部分在逻辑上属于这个组件,而从技术的角度来看(即:样式需求),最好将模板的这一部分移到 DOM
中的其他位置。
<div id="app">
<h1>Move the #content with the portal component</h1>
<teleport to="#endofbody">
<div id="content">
<p>
this will be moved to #endofbody.<br />
Pretend that it's a modal
</p>
<Child />
</div>
</teleport>
</div>
<div id="endofbody"></div>
...
components: {
Child: { template: "<div>Placeholder</div>" }
},
...
9、Suspense
https://github.com/vuejs/rfcs/pull/148
异步组件
这里的异步组件工厂函数也可以返回一个如下格式的对象:
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
10、一些杂项
10-1、v-model
https://github.com/vuejs/rfcs/pull/140
10-2、filters
https://github.com/vuejs/rfcs/pull/97
10-3、Custom Directive API Change
https://github.com/vuejs/rfcs/pull/32