【Vue3】Vue3 vs Vue2

Vue3 的新组件

Fragment

  • Vue2 中:组件必须有一个根标签
  • Vue3 中:可以没有根标签,内部会添加根标签 <fragment> </fragment>
  • 好处:减少标签层级,减少内存消耗
<template>
    <h1>App</h1>
    <p>superman</p>
</template>

<script>
export default {
      
       name: "App" };
</script>

Teleport

  • 用于将组件的 HTML 结构移动到指定位置
  • 用法:
    ① 用 Teleport 标签将需要移动的 HTML 结构包裹住
    ② 设置 to 属性,属性值为 选择器,以指定移动到的位置

默认情况下,子组件的 HTML 结构会显示到父组件的 HTML 结构里面;
使用 Teleport 标签包裹子组件的 HTML 标签,则能将该 HTML 结构显示到指定的位置

<template>
    <div class="box">
        <h1>App</h1>
        <button @click="bol = !bol">显示 / 隐藏</button>
        <Son v-if="bol" />
    </div>
</template>

<script>
import {
      
       ref } from "vue";
import Son from "./components/Son.vue";
export default {
      
      
    name: "App",
    components: {
      
       Son },
    setup() {
      
      
        let bol = ref(false);
        return {
      
       bol };
    },
};
</script>

<style>
.box {
      
      
    width: 200px;
    height: 200px;
    background: palevioletred;
    position: relative;
}
</style>
<template>
    <!-- 默认情况下,显示到 .box 里面 -->
    <h2>Son</h2>
    <!-- 通过 Teleport 标签,将 HTML 结构显示到 body 里面 -->
    <Teleport to="body">
        <h2>Teleport Son</h2>
    </Teleport>
</template>

<script>
export default {
      
       name: "Son" };
</script>

Suspense

  • 等待异步组件时,渲染一些额外的内容,提升用户体验感
  • 用法:
    ① 在父组件中,异步引入组件:defineAsyncComponent + 懒加载
    ② 在子组件中,配置 Suspense 标签
    ③ 在 Suspense 标签内,用 template 标签设置具名插槽
    default 插槽的内容:为异步引入的组件
    fallback 插槽的内容:为加载时显示的内容
  • 此时,异步引入的组件中 setup 可以是 async 函数
<template>
    <div class="box">
        <h1>App</h1>
        <Suspense>
            <!-- 需要显示的异步组件 -->
            <template v-slot:default>
                <Son></Son>
            </template>
            <!-- 异步组件显示之前,暂时先显示的内容 -->
            <template #fallback>
                加载中...
            </template>
        </Suspense>
    </div>
</template>

<script>
// 静态引入
// import Son from "./components/Son.vue"; 

// 异步引入
import {
      
       defineAsyncComponent } from 'vue';
const Son = defineAsyncComponent(() => import("./components/Son.vue"));

export default {
      
      
    name: "App",
    components: {
      
       Son },
};
</script>
<template>
    <h2>Son: {
   
   { p }}</h2>
</template>

<script>
export default {
      
      
    name: "Son",
    // 此时 setup 也可以是 async 函数
    async setup() {
      
      
        let p = await new Promise((resolve, reject) => {
      
      
            setTimeout(() => {
      
      
                resolve("superman")
            }, 1000);
        });
        return {
      
       p };
    }
};
</script>

文件对比

main.js

Vue2

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
    
    
    router,
    store,
    render: h => h(App)
}).$mount('#app')

Vue3

  • 引入的不再是 Vue 构造函数,而是工厂函数 createApp
    • 构造函数:通过 new 关键字调用,首字母大写
    • 工厂函数:直接调用,首字母小写
  • createApp 返回:应用实例对象 (相当于 Vue2 中的 vm,但比 vm 轻)
  • 可以在 createApp 之后链式调用其它方法
import {
    
     createApp } from 'vue' // 引入 createApp 方法
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(store).use(router).mount('#app') // 链式调用

store 文件

Vue2

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
    
    
    state: {
    
    },
    mutations: {
    
    },
    actions: {
    
    },
    modules: {
    
    }
})

Vue3

import {
    
     createStore } from 'vuex' // 引入 createStore 方法

export default createStore({
    
    
    state: {
    
    },
    mutations: {
    
    },
    actions: {
    
    },
    modules: {
    
    }
})

Vuex 的使用

  • 先配置好 store 文件:
import {
    
     createStore } from 'vuex'

export default createStore({
    
    
    state: {
    
    
        name: "superman",
        arr: [1, 2, 3]
    },
    mutations: {
    
    
        muModify(state, val) {
    
    
            console.log("commit muModify", val)
            state.name += val
        }
    },
    actions: {
    
    
        acModify(context, val) {
    
    
            console.log("dispatch acModify", val)
            context.commit("muModify", val)
        }
    },
    getters: {
    
    
        getArr(state) {
    
    
            return state.arr.map(item => item * 2)
        }
    },
    modules: {
    
    }
})
  • Vue3 中需要通过 useStore 方法使用 vuex
<template>
    <p>普通使用:</p>
    <p>$store.state.name: {
   
   { $store.state.name }}</p>
    <p>$store.state.arr: {
   
   { $store.state.arr }}</p>
    <p>$store.getters.getArr: {
   
   { $store.getters.getArr }}</p>
    <hr />
    <p>Vue 3:</p>
    <p>name: {
   
   { name }}</p>
    <p>arr: {
   
   { arr }}</p>
    <p>getArr: {
   
   { getArr }}</p>
    <hr />
    <button @click="coModify">coModify name</button> |
    <button @click="diModify">diModify name</button>
</template>

<script>
import {
      
       useStore } from "vuex";
export default {
      
      
    name: "App",
    setup() {
      
      
        // 通过 useStore 使用 Vuex
        const store = useStore();
        // 获取数据
        let name = store.state.name;
        let arr = store.state.arr;
        let getArr = store.getters.getArr;
        // 调用 dispatch 方法
        function diModify() {
      
      
            store.dispatch("acModify", "(Actions)");
        }
        // 调用 commit 方法
        function coModify() {
      
      
            store.commit("muModify", "(Mutations)");
        }
        return {
      
       name, arr, getArr, coModify, diModify };
    },
};
</script>
  • 但是,通过该方法获取的数据不是响应式的
  • 响应式数据设置如下:需要配合 computed 方法使用
let name = computed(() => store.state.name);
let arr = computed(() => store.state.arr);
let getArr = computed(() => store.getters.getArr);

router 文件

Vue2

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [{
    
    
        path: '/',
        name: 'Home',
        component: Home
    },
    {
    
    
        path: '/about',
        name: 'About',
        component: () => import('../views/About.vue')
    }
]

const router = new VueRouter({
    
    
    mode: 'history', // 设置路由模式为 history
    base: process.env.BASE_URL, // 根据 webpack 环境,设置 [根路径]
    routes
})

export default router

Vue3

import {
    
    
    createRouter, // 引入 createRouter 方法
    createWebHistory // 引入 createWebHistory 方法
} from 'vue-router'

import Home from '../views/Home.vue'

const routes = [{
    
    
        path: '/',
        name: 'Home',
        component: Home
    },
    {
    
    
        path: '/about',
        name: 'About',
        component: () => import('../views/About.vue')
    }
]

const router = createRouter({
    
    
    history: createWebHistory(process.env.BASE_URL),
    routes
})

export default router

vue-router 的使用

  • 先配置 router 文件
import {
    
    
    createRouter,
    createWebHistory
} from 'vue-router'

import Home from '../views/Home.vue'

const routes = [{
    
    
        path: '/',
        name: 'Home',
        component: Home
    },
    {
    
    
        path: '/about',
        name: 'About',
        component: () => import('../views/About.vue')
    }
]

const router = createRouter({
    
    
    history: createWebHistory(process.env.BASE_URL),
    routes
})

export default router
  • Vue3 中需要通过 useRoute 方法使用 vue-router
<template>
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
    <router-view />
</template>

<script>
export default {
      
      
    name: "App",
};
</script>
<template>
    <h1>About</h1>
</template>

<script>
import {
      
      
    useRoute,
    useRouter,
    onBeforeRouteLeave,
    onBeforeRouteUpdate,
} from "vue-router";
export default {
      
      
    name: "About",
    setup() {
      
      
        // 当前路由对象
        let route = useRoute();
        console.log("route", route);
        // 总路由对象
        let router = useRouter(router);
        console.log("router", router);
        // 路由守卫
        onBeforeRouteLeave(() => {
      
      
            console.log("onBeforeRouteLeave:离开路由时触发");
        });
        onBeforeRouteUpdate(() => {
      
      
            console.log("onBeforeRouteUpdate:复用路由时触发");
        });
        return {
      
      };
    },
};
</script>
<template>
    <h1>Home</h1>
</template>

<script>
export default {
      
       name: "Home" };
</script>

全局 API

  • 在 Vue3 中,全局和内部 API 都经过了重构
    全局 API 现在只能作为 ES 模块构建的命名导出进行访问
import {
    
     nextTick } from 'vue'
nextTick(() => {
    
    
    // 一些和DOM有关的东西
})
  • Vue.XXXapp.XXX

在这里插入图片描述

全局 directive

Vue2 - Vue.directive("自定义指令名", 配置对象)

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

Vue.directive("focus", {
    
    
    bind() {
    
    
        console.log("指令与元素绑定时触发");
    },
    inserted(el) {
    
    
        console.log("指令所在元素插入页面时触发");
        el.focus();
    },
    update(el) {
    
    
        console.log("模板重新渲染时触发");
        el.focus();
    }
});

new Vue({
    
     render: h => h(App) }).$mount('#app')

  • 也可以使用简写(回调函数执行完后,DOM 才会插入到页面中):
Vue.directive("focus", (el, msg) => {
    
    
    console.log(el, msg); // el:挂载的元素;val:挂载的信息
    console.log(msg.value); // 获取指令的属性值
});

Vue3 - app.directive("自定义指令名", 配置对象)

import {
    
     createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// 自定义指令,指令具有一组生命周期钩子:
app.directive('myIns', {
    
    
    // 在绑定元素的 attribute 或事件监听器被应用之前调用
    created() {
    
    },
    // 在绑定元素的父组件挂载之前调用
    beforeMount() {
    
    },
    // 在绑定元素的父组件挂载之后调用
    mounted() {
    
    },
    // 在包含组件的 VNode 更新之前调用
    beforeUpdate() {
    
    },
    // 在包含组件的 VNode 及其子组件的 VNode 更新之后调用
    updated() {
    
    },
    // 在绑定元素的父组件卸载之前调用
    beforeUnmount() {
    
    },
    // 在绑定元素的父组件卸载之后调用
    unmounted() {
    
    }
});

app.mount('#app')

钩子函数接收 2 个参数:

  1. el:指令绑定到的元素,可用于直接操作 DOM。eg:el.focus()
  2. binding:配置对象
    1. instance:使用指令的组件实例
    2. value:传递给指令的值
    3. oldValue:先前的值,仅在 beforeUpdateupdated 中可用。无论值是否有更改都可用
    4. arg:传递给指令的参数(如果有的话)
      eg:v-my-directive:foo 中,arg 为 "foo"
    5. modifiers:修饰符对象(如果有的话)
      eg:v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}
    6. dir:配置对象本身

全局属性

  • 全局属性:在任何组件中都可以访问
  • 与组件的属性发生命名冲突时,组件的属性优先级更高

Vue2 - Vue.prototype

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

Vue.prototype.$myData = "superman" // 设置全局属性

new Vue({
    
     render: h => h(App) }).$mount('#app')

Vue3 - app.config.globalProperties

import {
    
     createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.config.globalProperties.$myData = "superman" // 设置全局属性

app.mount('#app')

猜你喜欢

转载自blog.csdn.net/Superman_H/article/details/126294704