[TOC]

来源

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(反应性)

  1. 监听 ref 声明的 反应性数据
  2. 监听 reactive 声明的反应性数据
  3. 监听 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 生命周期钩子使用
  4. setup 的 onMounted 执行 在 整个组件的 mounted 之前

  5. setup 的 onMounted 执行 在 setup 同步组件执行之后
  6. 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'
      // ...
    }
    
    1. 默认插槽

      <my-comp v-slot="{ childState }">
      {{ parentMsg }} {{ childState }}
      </my-comp>
      
      <!-- 子组件中 -->
      <template>
      <slot :childState="childState" />
      </template>
      

      10、keyCodes 修饰符

      更改点
    2. v-on不再支持使用数字(即keyCodes)作为修饰符

    3. 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() 渲染

    更改点
    1. h 现在已全局导入,而不是传递给渲染函数作为参数
    2. 渲染函数参数已更改为在有状态和功能组件之间更加一致
    3. 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 插槽

    更改点
    1. this.$slots 公开为功能
    2. this.$scopedSlots已删除