来源
1、main.js
引入方式不一样
// vue3
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
// 没有全局的Vue
// vue2.X
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
2、全局API
Vue3里面没有全局的Vue,但是有个新的全局API:createApp
import { Vue, createApp } from 'vue'
console.log(Vue)
// undefined
const app = createApp({})
console.log(app)
component: ƒ component(name, component)
config: Object
directive: ƒ directive(name, directive)
mixin: ƒ mixin(mixin)
mount: (containerOrSelector) => {…}
provide: ƒ provide(key, value)
unmount: ƒ unmount()
use: ƒ use(plugin, ...options)
version: "3.0.0-rc.10"
_component: {}
_container: null
_context: {app: {…}, config: {…}, mixins: Array(0), components: {…}, directives: {…}, …}
_props: null
_uid: 1
get config: ƒ config()
set config: ƒ config(v)
2.x 全局 API | 3.x全局 API |
---|---|
Vue.config | app.config |
Vue.config.productionTip | 移除 |
Vue.config.ignoredElements | app.config.isCustomElement |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
使用第三方插件
示例:使用router,引入 app.use()
const app = createApp(MyApp)
app.use(VueRouter)
3、Composition API
import {ref, reactive, watch, computed, reactive, toRefs, createApp} from 'vue'
1. setup
要开始使用Composition API,我们首先需要一个可以实际使用它的地方。在Vue组件中,我们将此位置称为setup。
setup执行时尚未创建组件实例,所以不能使用this,此时this 为 undefined。除了props,无法访问组件中声明的任何data、computed、methods。
参数 (props, context)
// props 父组件传过来的props props是具有反应性的(传入新的props时会自动更新)
// context {attrs, emit, slots}
setup(props, context) {
console.log(context)
/**
* attrs: Proxy
* emit: (event, ...args) => instance.emit(event, ...args)
* slots: Proxy
*/
}
setup 生命周期钩子
钩子函数 | setup钩子 |
---|---|
beforeCreate | 没有 |
created | 没有 |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
由于setup是围绕beforeCreate和created生命周期挂钩运行的,因此您无需显式定义它们。换句话说,应该在这些钩子中编写的任何代码都应直接在setup函数中编写。
jsx
setup() {
const root = ref(null)
return () => <div ref={root} />
}
2. watch监听
从 vue 中引入 watch 可以参考 第4点 Reactivity(反应性)
- 监听 ref 声明的 反应性数据
- 监听 reactive 声明的反应性数据
监听 props 里面的数据
<script> //一定使用之前引入 import {ref, watch} from 'vue' setup(props) { //监听 ref 数据 let count = ref(0) watch(count, (val, old) => { // 新数据、老数据 }) //监听 reactive 数据 对象 let person = reactive({ age: 18 }) watch(() => person.age, (val, old) => { //新数据、老数据 }) //监听 reactive 数据 数组 let arr = reactive([1, 2, 3]) watch(arr, (val, old) => { //新数据、老数据 }) //监听 props 的数据 加入props传了一个 testkey watch(() => props.testkey, () => { }) //要注意 return return { count, person, arr } } </script>
3. computed 计算属性
从 vue 中引入 computed
<script> import {ref, computed} from 'vue' setup(props) { let count = ref(0) //当前组件的计算属性 let countCom = computed(() => { return count.value * 2 }) //props传进来的计算属性 let countCom2 = computed(() => { return props.propsKey * 2 }) } </script>
4. setup 生命周期钩子使用
setup 的 onMounted 执行 在 整个组件的 mounted 之前
- setup 的 onMounted 执行 在 setup 同步组件执行之后
- setup 的 onMounted 里面以及 setup 不能使用this
```
<a name="nClCu"></a> ##### 5. provide和inject **从 vue 中引入** <a name="ck7GP"></a> ###### 父组件 provide
<a name="lxBQr"></a> ###### 子孙组件 inject
<a name="6bs2M"></a> ###### provide 带有反应性的数据 父组件值修改,子孙组件值会对应的修改
<a name="U4zET"></a> ###### provide 一个 函数
<a name="jms43"></a> #### 4、Reactivity(反应性) Vue最独特的功能之一是不引人注目的反应系统。 > 当您将纯JavaScript对象作为data选项传递给应用程序或组件实例时,Vue将遍历其所有属性,并使用带有getter和setter的处理程序将它们转换为Proxies - **跟踪更改它的函数**:在代理的getter中进行此操作 effect - **触发函数,以便它可以更新最终值**:在代理中的setter中进行操作 trigger <a name="XdxFX"></a> ##### 声明反应性数据 一般使用 ref、reactive、readonly 来声明数据 - ref 声明基本类型 - reactive 声明引用类型 - readonly 声明只读引用类型
import { ref, reactive, readonly } from ‘vue’ export default { setup () { // 一般用 ref 声明 基本 类型 // 用 reactive 声明 引用 类型 let count = ref(0) // 在 script 使用 count.value,在 template 使用 {{count}} let state = reactive({ name: ‘Bob’ }) // 在 script 使用 state.name, 在 template 使用 {{state.name}}
// 用 ref 声明 引用类型 let obj1 = ref({ count: 1 }) // 用 reactive 声明 基本 类型 警告⚠️ 可以正常使用,但是没有反应性 let num1 = reactive(10) //value cannot be made reactive: 10 // readonly // readonly 和 reactive 一样,但是声明的是只读数据 声明基本类型和 reactive 一样警告提醒 let person = readonly({age: 18}) setTimeout(() => { // 定时器 修改 警告 ⚠️ person.age = 10 // Set operation on key "age" failed: target is readonly }); // 一定要return 才可以在 template 和 script 其他地方使用 return { count, state, obj1 } }
}
count打印出的数据
RefImpl {_rawValue: 0, _shallow: false, v_isRef: true, _value: 0} v_isRef: true _rawValue: 0 _shallow: false _value: 0 value: 0
state打印出的数据
Proxy {name: “Bob”} [[Handler]]: Object deleteProperty: ƒ deleteProperty(target, key) get: ƒ (target, key, receiver) has: ƒ has(target, key) ownKeys: ƒ ownKeys(target) set: ƒ (target, key, value, receiver) [[Target]]: Object name: “Bob” [[IsRevoked]]: false
obj1打印出的数据
RefImpl {_rawValue: {…}, _shallow: false, v_isRef: true, _value: Proxy} v_isRef: true _rawValue: {count: 1} _shallow: false _value: Proxy {count: 1} value: Proxy [[Handler]]: Object [[Target]]: Object count: 1 [[IsRevoked]]: false
num1打印的数据
10
<a name="BdNMn"></a> ##### template中使用
count: {{count}} state: {{state.name}}<a name="hRBzm"></a> #### 5、filters 过滤器 在 vue2.x 中使用 filters 过滤器,一种很方便也很实用的语法。<br />但是在 **vue3.x 中过滤器属性已删除,不再支持** > 在3.x中,过滤器已删除,不再受支持。相反,我们建议将它们替换为方法调用或计算的属性。 <a name="K8Cnf"></a> #### 6、directive 自定义指令 在 vue2.x 中,会使用自定义指令来进行一些新的指令的绑定,例如,fouces,drag等<br />在 vue3.x 中,也有自定义指令的使用,只是方法改掉了 <a name="EsgU4"></a> ##### 在 2.x 中的钩子函数 - bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 - inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 - update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。 <a name="9aKYw"></a> ##### 3.x 中的钩子函数 | 2.x | 3.x | | --- | --- | | bind | beforeMount | | inserted | mounted | | | beforeUpdate: 新增(这在元素本身更新之前被调用) | | update | 已删除 | | componentUpdated | updated | | | beforeUnmount 新增(在卸载元素之前立即调用。) | | unbind | unmounted | <a name="OW5GR"></a> ##### 3.x 中的自定义指令
const MyDirective = { beforeMount(el, binding, vnode, prevVnode) {}, mounted() {}, beforeUpdate() {}, updated() {}, beforeUnmount() {}, unmounted() {} }
<a name="vwNBs"></a> ##### 把 2.x 中的自定义指令 改成 3.x 的 这里有 [2.x Vue自定义拖拽指令](https://guoqiankun.blog.csdn.net/article/details/107764504)<br />**vue3.x 中使用自定义指令**
import { createApp } from ‘vue’ import App from ‘./App.vue’ let app = createApp(App) app.directive(‘drag’, { beforeMount (el, binding) { console.log(el, binding) let oDiv = el; //当前元素 oDiv.onmousedown = function (e) { //鼠标按下,计算当前元素距离可视区的距离 let disX = e.clientX - oDiv.offsetLeft; let disY = e.clientY - oDiv.offsetTop; document.onmousemove = function (e) { //通过事件委托,计算移动的距离 let l = e.clientX - disX; let t = e.clientY - disY; //移动当前元素
oDiv.style.left = l + ‘px’; oDiv.style.top = t + ‘px’; //将此时的位置传出去 binding.value({x:e.pageX,y:e.pageY}, el) }; document.onmouseup = function () { document.onmousemove = null; document.onmouseup = null; }; }; } }) app.mount(‘#app’)**template中使用**
<a name="eayts"></a> #### 7、defineAsyncComponent 异步组件 > 创建一个仅在必要时加载的异步组件。 > 在Vue 3中,由于功能组件被定义为纯函数,因此需要通过将异步组件定义包装在新的defineAsyncComponent帮助器中来明确定义异步组件定义 **从 vue 中引入**
//没有 options 参数时 const testCom = defineAsyncComponent(() => import(‘./testCom.vue’)) 有 options 参数时 const asyncPageWithOptions = defineAsyncComponent({ loader: () => import(‘./NextPage.vue’), delay: 200, timeout: 3000, errorComponent: ErrorComponent, loadingComponent: LoadingComponent })
<a name="yetN7"></a> ##### 所有的参数
const AsyncComp = defineAsyncComponent({ // 工厂函数 loader: () => import(‘./Foo.vue’) // 加载异步组件时要使用的组件 loadingComponent: LoadingComponent, // 加载失败时使用的组件 errorComponent: ErrorComponent, // 显示加载组件之前的延迟。 默认值:200ms。 delay: 200, // 如果提供并超过了超时,则将显示错误组件。 默认值:无穷大 timeout: 3000, // 一个返回布尔值的函数,该值指示加载程序承诺拒绝时异步组件是否应重试 retryWhen: error => error.code !== 404, // 允许的最大重试次数 maxRetries: 3, // 定义组件是否 suspensible: false })
<a name="NrxOG"></a> #### 8、子元素 emit 事件 由于 setup 里面不能使用 this,所以不能像 vue2.x 一样使用 this.$emit(‘emitFun’, val),setup 里面有两个参数,第二个参数可以进行结构得到 emit,也可以直接直接使用 context.emit 进行 emit 事件<br />**在setup里面 emit 事件**
setup(props, context) { let clickCom = () => { context.emit(‘emit-fun’, {emit: true}) } return { clickCom } }
<a name="jQZsR"></a> #### 9、inline-template 内联模板 > 在 vue3.x 中对内联模板功能的支持已删除。 在2.x中,Vue inline-template在子组件上提供了属性,以使用其内部内容作为其模板,而不是将其视为分布式内容<br />**3.x不在支持此功能** <a name="SMYjH"></a> ##### 迁移 <a name="dgdSO"></a> ###### 1. 使用
``` const MyComp = { template: '#my-comp-template' // ... }
默认插槽
<my-comp v-slot="{ childState }"> {{ parentMsg }} {{ childState }} </my-comp>
<!-- 子组件中 --> <template> <slot :childState="childState" /> </template>
10、keyCodes 修饰符
更改点
v-on不再支持使用数字(即keyCodes)作为修饰符
- config.keyCodes不再受支持
由于KeyboardEvent.keyCode已弃用,因此Vue 3也不再继续支持此功能。因此,现在建议将kebab-case名称用于要用作修饰符的任何键。
<!-- Vue 2.x --> <input v-on:keyup.13="submit" /> <input v-on:keyup.enter="submit" /> <!-- Vue 3.x --> <input v-on:keyup.delete="confirmDelete" />
11、render() 渲染
更改点
- h 现在已全局导入,而不是传递给渲染函数作为参数
- 渲染函数参数已更改为在有状态和功能组件之间更加一致
- VNode现在具有扁平的道具结构
h 需要从 vue 中导入
import { h } from 'vue'
在3.x中,由于render函数不再接收任何参数,因此它将主要在setup()函数内部使用。
import { h, reactive } from 'vue' export default { setup(props, { slots, attrs, emit }) { const state = reactive({ count: 0 }) function increment() { state.count++ } return () => h( 'div', { onClick: increment }, state.count ) } }
在 3.x 中VNode props结构被平铺
{ class: ['button', 'is-outlined'], style: { color: '#34495E' }, id: 'submit', innerHTML: '', onClick: submitForm, key: 'submit-button' }
12、slots 插槽
更改点
- this.$slots 公开为功能
- this.$scopedSlots已删除