Composition API

1.setup

新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次
函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用

2.ref

  1. 作用: 定义一个数据的响应式
  2. 语法: const xxx = ref(initValue):
  3. 创建一个包含响应式数据的引用(reference)对象
  4. js中操作数据: xxx.value
  5. 模板中操作数据: 不需要.value
  6. 一般用来定义一个基本类型的响应式数据
<template>
    <div>
        <button
            v-for="(item, index) in girls"
            v-bind:key="index"
            @click="selectGirlFun(index)"
        >
            {{ index }} : {{ item }}
        </button>
    </div>
    <div>你选择了【{{ selectGirl }}】为你服务</div>
</template>

<script lang="ts">
import { ref } from "vue";
export default {
    // 定义变量时必须用ref包起来,修改值是,必须用.value修改
    setup() {
        const girls = ref(["大脚", "刘英", "晓红"]);
        const selectGirl = ref("");
        const selectGirlFun = (index: number) => {
            selectGirl.value = girls.value[index];
        };
        //因为在模板中这些变量和方法都需要条用,所以需要return出去。
        return {
            girls,
            selectGirl,
            selectGirlFun,
        };
    },
};
</script>

<style>
</style>

3.reactive(优化)

使用reactive时,不用ref定义变量,不用.value进行修改,但是tempalte中,必须data.XX进行获取

  1. 作用: 定义多个数据的响应式
  2. const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
  3. 响应式转换是“深层的”:会影响对象内部所有嵌套的属性
  4. 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
<template>
    <div>
        <button
            v-for="(item, index) in data.girls"
            v-bind:key="index"
            @click="data.selectGirlFun(index)"
        >
            {{ index }} : {{ item }}
        </button>
    </div>
    <div>你选择了【{{ data.selectGirl }}】为你服务</div>
</template>

<script lang="ts">
import { reactive } from "vue";
interface DataProps {
    girls: string[];
    selectGirl: string;
    selectGirlFun: (index: number) => void;
}
export default {
    // 使用reactive时,不用ref定义变量,不用.value进行修改,但是tempalte中,必须data.XX进行获取
    setup() {
        const data:DataProps = reactive({
            girls: ["大脚", "刘英", "晓红"],
            selectGirl: "",
            selectGirlFun: (index: number) => {
                data.selectGirl = data.girls[index];
            },
        });

        return {
            data,
        };
    },
};
</script>

<style>
</style>

4.toRefs(再次优化)

使用reactive+toRefs,解决了empalte中,必须用data.XX,使用toRefs()进行解构赋值

<template>
    <div>
        <button
            v-for="(item, index) in girls"
            v-bind:key="index"
            @click="selectGirlFun(index)"
        >
            {{ index }} : {{ item }}
        </button>
    </div>
    <div>你选择了【{{ selectGirl }}】为你服务</div>
</template>

<script lang="ts">
import { reactive,toRefs } from "vue";
interface DataProps {
    girls: string[];
    selectGirl: string;
    selectGirlFun: (index: number) => void;
}
export default {
    // 使用reactive+toRefs,解决了empalte中,必须用data.XX,使用toRefs()进行解构赋值
    setup() {
        const data:DataProps = reactive({
            girls: ["大脚", "刘英", "晓红"],
            selectGirl: "",
            selectGirlFun: (index: number) => {
                data.selectGirl = data.girls[index];
            },
        });
        const refData = toRefs(data);
        return {
            ...refData,
        };
    },
};
</script>

<style>
</style>

5.vue-router vuex

5.1安装

npm install vue-router --save
npm install vuex --save
npm i vue-router@next vuex@next -S

5.2使用

router.js

import { createRouter, createWebHashHistory } from 'vue-router'
import store from '/@/store'

import Login from '../views/Login/index.vue'
import Index from "/@/views/Index/index.vue"
import Details from '/@/views/Details/index.vue' //工程信息设置
import Error from '/@/views/Error/index.vue'
import NotFound from '/@/views/NotFound/index.vue'
import System from '/@/views/System/index.vue' //用户管理
import User from '/@/views/System/User/index.vue' //用户管理
import Group from '/@/views/System/Group/index.vue' //工程信息设置

// 需要动态添加的路由
const user = {path: '/system/user', component: User,  name: 'user',meta: {requiresAuth: true}}
const group = {path: '/system/group', component: Group,  name: 'group',meta: {requiresAuth: true}}
// 动态路由根据名称匹配跳转页面
const ruleMapping = {
    '/system/user': user,
    '/system/group': group
};

const routes = [{
        path: '/',
        redirect: "/login"
    },
    {
        path: '/login',
        name: 'login',
        component: Login
    }, {
        path: '/index',
        name: 'index',
        component: Index
    },{
        path: '/nav/:type/:id',
        name: 'nav',
        component: Details,
        meta: {
            requiresAuth: true
        } // 开启登录拦截
    },
    // {
    //     path: '/system',
    //     name: 'system',
    //     component: System,
    //     redirect:"/system/user",
    //     children:[{
    //         path: 'user',
    //         name: 'user',
    //         component: User,
    //     },{
    //         path: 'group',
    //         name: 'group',
    //         component: Group,
    //     }]
    // },
    {
        path: '/notFound',
        name: 'notFound',
        component: NotFound
    }, {
        // 没有路由的访问权限时,访问的页面404
        path: '/:pathMatch(.*)*',
        name: '404',
        component: Error
    }

]

const router = createRouter({
    history: createWebHashHistory(),
    routes
})

/*
    路由导航守卫
    to 要跳转到哪个界面
    from 从哪跳转来的
    next 是否要放行
*/
router.beforeEach((to, from, next) => {
    if (to.path === '/login') {
        // 如果是要跳转登录就直接放行
        next()
    } else {
        // 检查路由元信息,是否开启登录验证
        if (to.matched.some(record => record.meta.requiresAuth)) {
            const token = store.state.user.token;
            // 判断用户是否已经登录
            if (!token) {
                // 没登陆还是跳转登录页
                next('/notFound')
            } else {
                // 正常情况就放行,设置当前路由地址
                window.sessionStorage.setItem('activePath', to.path)
                next()
            }
        } else {
            // 没开启登录验证,放行
            if(to.matched.some((record) => {return record.name=="404"})&&(to.path=="/system/user"||to.path=="/system/group")){
                next(to.path)
            }else{
                next()
            }
        }
    }
});


// 动态导入路由
export function initRoutes() {
    const token = store.state.user.token;
    if (!token) {
        router.login
    } else {
        const currentRoutes = router.options.routes; // 获取要添加路由的当前路由
        const navList = store.state.navList; // 获取state中缓存的根据权限获取的路由
        /*navList =[{
                    id: 1,
                    title: '用户管理',
                    icon: 'iconuser',
                    path: '/system/user'
                }, {
                    id: 2,
                    title: '分类管理',
                    icon: 'icondaohang',
                    path: '/system/group'
                }];*/
        if (navList.length > 0) {
            const aa = store.state.navList[0].path;
            const currentIndex = {path: '/system', component: System, meta: {requiresAuth: true}, redirect: aa, children: []}
            currentRoutes.push(currentIndex);
            navList.forEach(item => { // 循环获取的路由
                const temp = ruleMapping[item.path]; // 获取需要动态添加的路由,对应的跳转页面配置
                //temp.meta = item.path; // 将接口返回的路由对应的权限赋给当前路由(在按钮权限控制时使用)
                currentIndex.children.push(temp); // 将匹配的路由,添加到当前路由的对应子路由位置
            });
            for (let x of currentRoutes) {
                router.addRoute(x)// 执行动态添加路由方法,完成动态路由添加
            }
        }
    }
}
// 重置路由(退出登录,切换账号)
export function resetRouter() {
    const newRouter = createRouter()
    router.matcher = newRouter.matcher // the relevant part
}

export default router

vuex.js

import {createStore} from 'vuex'

export default createStore({
    state: {
        // 用户
        user: {
            username: window.sessionStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.sessionStorage.getItem('user' || '[]')).username,
            token: window.sessionStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.sessionStorage.getItem('user' || '[]')).token,
            type: window.sessionStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.sessionStorage.getItem('user' || '[]')).type,
            userid: window.sessionStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.sessionStorage.getItem('user' || '[]')).userid
        },
        // 动态路由
        navList: JSON.parse(window.sessionStorage.getItem('navList') || '[]')
    },
    mutations: {
        // 设置user信息
        setUser(state, data) {
            state.user = data;
            window.sessionStorage.setItem("user", JSON.stringify(data));
        },
        // 导航
        setNavList(state, data) {
            state.navList = data;
            window.sessionStorage.setItem("navList", JSON.stringify(data));
        }
    },
    getters: {
        // 获取user信息
        getUser(state) {
            return state.user;
        }
    },
    actions: {
        
    },
    modules: {}
})

main.js挂载

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

import axios from 'axios'
import VueAxios from 'vue-axios'

import api from '/@/api/index'
const app = createApp(App)
app.config.globalProperties.$api = api

app.use(router).use(store).use(VueAxios,axios).mount('#app')


在vue.js文件中使用vue-router vuex $api

<script lang="ts">
    import { useRouter } from 'vue-router'
    import { reactive,toRefs,getCurrentInstance  } from "vue";
    import { useStore} from 'vuex'
    import {initRoutes} from "/@/router/index.js"//动态添加路由
    export default {
        name: "login",
        setup(){
            const { proxy } = getCurrentInstance()
            const $api = proxy.$api
            const route = useRouter()
            const store = useStore();
            const data= reactive({
                username:"test1",
                password:"111111",
                savePwd:true,
            });

            const refData = toRefs(data);
            return {
                ...refData,
            };
        }
    }
</script>

<style  lang="scss">
    @import "./index.scss";
</style>

6.计算属性与监视

<template>
  <h2>App</h2>
  fistName: <input v-model="user.firstName"/><br>
  lastName: <input v-model="user.lastName"/><br>
  fullName1: <input v-model="fullName1"/><br>
  fullName2: <input v-model="fullName2"><br>
  fullName3: <input v-model="fullName3"><br>

</template>

<script lang="ts">
/*
计算属性与监视
1. computed函数: 
  与computed配置功能一致
  只有getter
  有getter和setter
2. watch函数
  与watch配置功能一致
  监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
  默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
  通过配置deep为true, 来指定深度监视
3. watchEffect函数
  不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
  默认初始时就会执行第一次, 从而可以收集需要监视的数据
  监视数据发生变化时回调
*/

import {
  reactive,
  ref,
  computed,
  watch,
  watchEffect
} from 'vue'

export default {

  setup () {
    const user = reactive({
      firstName: 'A',
      lastName: 'B'
    })

    // 只有getter的计算属性
    const fullName1 = computed(() => {
      console.log('fullName1')
      return user.firstName + '-' + user.lastName
    })

    // 有getter与setter的计算属性
    const fullName2 = computed({
      get () {
        console.log('fullName2 get')
        return user.firstName + '-' + user.lastName
      },

      set (value: string) {
        console.log('fullName2 set')
        const names = value.split('-')
        user.firstName = names[0]
        user.lastName = names[1]
      }
    })

    const fullName3 = ref('')

    /* 
    watchEffect: 监视所有回调中使用的数据
    */
    /* 
    watchEffect(() => {
      console.log('watchEffect')
      fullName3.value = user.firstName + '-' + user.lastName
    }) 
    */

    /* 
    使用watch的2个特性:
      深度监视
      初始化立即执行
    */
    watch(user, () => {
      fullName3.value = user.firstName + '-' + user.lastName
    }, {
      immediate: true,  // 是否初始化立即执行一次, 默认是false
      deep: true, // 是否是深度监视, 默认是false
    })

    /* 
    watch一个数据
      默认在数据发生改变时执行回调
    */
    watch(fullName3, (value) => {
      console.log('watch')
      const names = value.split('-')
      user.firstName = names[0]
      user.lastName = names[1]
    })

    /* 
    watch多个数据: 
      使用数组来指定
      如果是ref对象, 直接指定
      如果是reactive对象中的属性,  必须通过函数来指定
    */
    watch([() => user.firstName, () => user.lastName, fullName3], (values) => {
      console.log('监视多个数据', values)
    })

    return {
      user,
      fullName1,
      fullName2,
      fullName3
    }
  }
}
</script>

7.setup细节(setup(props, {attrs, slots, emit}))

  1. setup(props, context) / setup(props, {attrs, slots, emit})
  2. props: 包含props配置声明且传入了的所有属性的对象
  3. attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
  4. slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
  5. emit: 用来分发自定义事件的函数, 相当于 this.$emit
<template>
  <div>
    <h3>{{n}}</h3>
    <h3>{{m}}</h3>

    <h3>msg: {{msg}}</h3>
    <h3>msg2: {{$attrs.msg2}}</h3>

    <slot name="xxx"></slot>

    <button @click="update">更新</button>
  </div>
</template>

<script lang="ts">

import {
  ref,
  defineComponent
} from 'vue'

export default defineComponent({
  name: 'child',
  props: ['msg'],
  emits: ['fn'], // 可选的, 声明了更利于阅读, 且可以对分发的事件数据进行校验

  // setup (props, context) {
  setup (props, {attrs, emit, slots}) {

    console.log('setup', this)
    console.log(props.msg, attrs.msg2, slots, emit)

    const m = ref(2)
    const n = ref(3)

    function update () {
      // console.log('--', this)
      // this.n += 2 
      // this.m += 2

      m.value += 2
      n.value += 2

      // 分发自定义事件
      emit('fn', '++')
    }

    return {
      m,
      n,
      update,
    }
  },
})
</script>

8.reactive与ref-细节

  1. 是Vue3的 composition API中2个最重要的响应式API
  2. ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
  3. 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
  4. ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
  5. reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
  6. ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)
<template>
  <h2>App</h2>
  <p>m1: {{m1}}</p>
  <p>m2: {{m2}}</p>
  <p>m3: {{m3}}</p>
  <button @click="update">更新</button>
</template>

<script lang="ts">
import {
  reactive,
  ref
} from 'vue'

export default {

  setup () {
    const m1 = ref('abc')
    const m2 = reactive({x: 1, y: {z: 'abc'}})

    // 使用ref处理对象  ==> 对象会被自动reactive为proxy对象
    const m3 = ref({a1: 2, a2: {a3: 'abc'}})
    console.log(m1, m2, m3)
    console.log(m3.value.a2) // 也是一个proxy对象

    function update() {
      m1.value += '--'
      m2.x += 1
      m2.y.z += '++'

      m3.value = {a1: 3, a2: {a3: 'abc---'}}
      m3.value.a2.a3 += '==' // reactive对对象进行了深度数据劫持
      console.log(m3.value.a2)
    }

    return {
      m1,
      m2,
      m3,
      update
    }
  }
}
</script>

9.使用$parent和 $refs( $children已被移除)

9.1$parent

import { getCurrentInstance  } from 'vue'
setup (props, ctx) {
    const { proxy } = getCurrentInstance()
    proxy.$parent.handleFun('我调用父亲')//该方法必须在父组件进行导出
}

9.2 $refs

<template>
    <div class="main-box">
      <RightBox ref="rightBoxRef" ></RightBox>
    </div>
</template>
<script>
    import RightBox from '/@/components/Index/RightBox.vue' // 右侧盒子
    import {reactive, toRefs, ref,getCurrentInstance, unref} from "vue";
 
    export default {
        components: {
            RightBox
        },
        setup() {
            const {proxy} = getCurrentInstance()
            const rightBoxRef = ref(null);
            setTimeout(()=>{
                const rightRef = unref(rightBoxRef);
                rightRef.openLink()//调用子组件方法,改方法必须在子组件进行导出
            },1000)
            return { 
            };
        },
    }
</script>

<style lang="scss" scoped>
    @import './index.scss';
</style>

10. 生命周期

<template>
<div class="about">
  <h2>msg: {{msg}}</h2>
  <hr>
  <button @click="update">更新</button>
</div>
</template>

<script lang="ts">
import {
  ref,
  onMounted,
  onUpdated,
  onUnmounted, 
  onBeforeMount, 
  onBeforeUpdate,
  onBeforeUnmount
} from "vue"

export default {
  setup() {
    
    const msg = ref('abc')

    const update = () => {
      msg.value += '--'
    }

    onBeforeMount(() => {
      console.log('--onBeforeMount')
    })

    onMounted(() => {
      console.log('--onMounted')
    })

    onBeforeUpdate(() => {
      console.log('--onBeforeUpdate')
    })

    onUpdated(() => {
      console.log('--onUpdated')
    })

    onBeforeUnmount(() => {
      console.log('--onBeforeUnmount')
    })

    onUnmounted(() => {
      console.log('--onUnmounted')
    })
    
    return {
      msg,
      update
    }
  }
}
</script>

11.计算属性与监视

<template>
  <h2>App</h2>
  fistName: <input v-model="user.firstName"/><br>
  lastName: <input v-model="user.lastName"/><br>
  fullName1: <input v-model="fullName1"/><br>
  fullName2: <input v-model="fullName2"><br>
  fullName3: <input v-model="fullName3"><br>

</template>

<script lang="ts">
/*
计算属性与监视
1. computed函数: 
  与computed配置功能一致
  只有getter
  有getter和setter
2. watch函数
  与watch配置功能一致
  监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
  默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
  通过配置deep为true, 来指定深度监视
3. watchEffect函数
  不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
  默认初始时就会执行第一次, 从而可以收集需要监视的数据
  监视数据发生变化时回调
*/

import {
  reactive,
  ref,
  computed,
  watch,
  watchEffect
} from 'vue'

export default {

  setup () {
    const user = reactive({
      firstName: 'A',
      lastName: 'B'
    })

    // 只有getter的计算属性
    const fullName1 = computed(() => {
      console.log('fullName1')
      return user.firstName + '-' + user.lastName
    })

    // 有getter与setter的计算属性
    const fullName2 = computed({
      get () {
        console.log('fullName2 get')
        return user.firstName + '-' + user.lastName
      },

      set (value: string) {
        console.log('fullName2 set')
        const names = value.split('-')
        user.firstName = names[0]
        user.lastName = names[1]
      }
    })

    const fullName3 = ref('')

    /* 
    watchEffect: 监视所有回调中使用的数据
    */
    /* 
    watchEffect(() => {
      console.log('watchEffect')
      fullName3.value = user.firstName + '-' + user.lastName
    }) 
    */

    /* 
    使用watch的2个特性:
      深度监视
      初始化立即执行
    */
    watch(user, () => {
      fullName3.value = user.firstName + '-' + user.lastName
    }, {
      immediate: true,  // 是否初始化立即执行一次, 默认是false
      deep: true, // 是否是深度监视, 默认是false
    })

    /* 
    watch一个数据
      默认在数据发生改变时执行回调
    */
    watch(fullName3, (value) => {
      console.log('watch')
      const names = value.split('-')
      user.firstName = names[0]
      user.lastName = names[1]
    })

    /* 
    watch多个数据: 
      使用数组来指定
      如果是ref对象, 直接指定
      如果是reactive对象中的属性,  必须通过函数来指定
    */
    watch([() => user.firstName, () => user.lastName, fullName3], (values) => {
      console.log('监视多个数据', values)
    })

    return {
      user,
      fullName1,
      fullName2,
      fullName3
    }
  }
}
</script>
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐