Vue3 的基础使用
npm init vue@latest
ref
赋值一个新的对象,那么响应式也会存在
shallowRef 只保留最顶层的响应式更新
reactive
赋值一个新的对象,失去响应性
直接访问对象中的属性,当访问到某个响应式数组(Map这样的原生集合类型)中的 ref元素时,不会解包
shallowReactive 只保留最顶层的响应式更新
ref 检测及转换
isRef 用来判断某个值是不是一个 ref 对象
isRef(name)
unref 如果参数是 ref 对象 返回 .value 如果不是 原样返回
toRef 基于响应式对象上的一个属性来创建一个 ref,如果你更改两者其中一个,另外一个也会响应式的更新
const friend = reactive({ name: 'coder', age: 15 })
const ageRef = toRef(friend, 'age')
toRefs 将一个响应式对象转换为普通对象,每个属性都是指向源对象相应属性的 ref
const friend = reactive({ name: 'coder', age: 15 })
const obj = toRefs(friend)
console.log(obj.age.value)
console.log(obj.name.value)
isReactive 用于检测是不是通过 reactive 或者 shallowReactive 创建的代理对象
isReadonly 用于判断值是不是一个只读对象
处理 reactive 响应式变更
let user = reactive([])
const res = [{ id: 1, name: 'aaa'}, { id: 2, name: 'bbb' }]
- 利用数组的变更方法
user.push(...res)
- 利用对象的属性
const user = reactive({user: []
})
user.user = res
- 利用 ref 来处理
const user = ref([])
user.value = res
作用域插槽
<h1>子组件</h1>
<slot :data="1"></slot>
<slot name="content" :data="2"></slot><!-- 默认插槽 -->
<h1>父组件</h1>
<Child v-slot="slotProps">{{ slotProps.data }}
</Child><!-- 具名插槽 -->
<h1>父组件</h1>
<Child><template v-slot:content="slotProps">{{ slotProps.data }}</template>
</Child><!-- 兼有 -->
<h1>父组件</h1>
<Child><template v-slot:default="slotProps">{{ slotProps.data }}</template><template v-slot:content="slotProps">{{ slotProps.data }}</template>
</Child>
路由
设置路由的历史记录方式
hash: https://xxxxx/#/path,createWebHashHistory(),hash 后面不会发送到服务端,不利于抓包和搜索引擎优化
html5:https://xxxxx/path (推荐)
import.meta.env.BASE_URL: 基础路径
'/' localhost:5174/about
'/code' localhost:5174/code/about
const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: []
})
routes
routes: [{path: "/",components: {Top,default: Home},},{path: "/",// 通过 path 重定向 homeredirect: "/home",redirect: {// 通过 name 重定向 homename: "home",},},{path: "/home",name: "home",component: Home,},{path: "/about",component: About,children: [// 为了解决父路由点击过去没有显示子路由的内容{path: "",component: AboutIntroduce,},{path: "",redirect: "/profile/p1",redirect: {name: "p1",},},{path: "p1",name: "p1",component: P1,},{path: "product",component: AboutProduct,}],// sensitive: true, // 大小写敏感// strict: true, // 限制末尾不带 /},{// /xxxxx/sssd/sdsdsd/sdsd/sdsd// /xxxpath: "/:pathMatch(.*)*",component: NotFound,},],sensitive: true,strict: true,
});
export default router;
RouterLink
<p>App.vue</p>
<!-- to:切换到不同的页面 string / object -->
<RouterLink to="/about">about</RouterLink>
<RouterLink :to="{ path: '/about' }">about</RouterLink>
<!-- replace:替换当前的页面 然后跳转到下一页 -->
<RouterLink to="/about" replace>about</RouterLink>
<!-- active-class:添加活跃态的样式 -->
<!-- exact-active-class 精准匹配到这个路由的时候才显示样式类 active-class 模糊匹配就会给 -->
<RouterLink to="/about" active-class="red">about</RouterLink>
<RouterLink to="/about" exact-active-class="red">about</RouterLink>
<!-- custom:渲染出自定义的内容 -->
<RouterLink to="/about" custom v-slot="linkProps"><button @click="linkProps.navigate">about</button>
</RouterLink>
<!-- 残障人士 -->
<RouterLink to="/about" aria-current-value="page">About</RouterLink><RouterView name="Top"></RouterView>
<RouterView></RouterView>
嵌套路由
<template><p>About.vue</p><!-- 需要按照二级路由的方式配置 /xxxx/product --><RouterLink to="/about">introduce</RouterLink><RouterLink to="/about/product">product</RouterLink><RouterView></RouterView>
</template>
路由参数
<RouterLink :to="`/user/${id}`">User</RouterLink>
<RouterLink :to="{ path: `/user/${id}`, query: { name: 'coderMonkey', age: 18 }}"
>User</RouterLink>
import { useRoute, useRouter } from 'vue-router'
import router1 from '../router';
const route = useRoute()
const router = useRouter()
console.log(route.params.id)
console.log(router === router1)
console.log(router)
<p>User</p>
<p>{{ $route.params.id }}</p>
<p>{{ $router }}</p>
编程式导航和命名式导航
{path: "/profile/:name",name: "profile",component: Profile,
},
import { useRouter } from 'vue-router'
<!-- import router from '../router'; -->
const router = useRouter()
<!-- 编程式导航 -->
// push - string
router.push('/profile/coder')
// push - object - path
router.push({path: '/profile',query: {coderName: 'coderMonkey1', coderAge: 19}
})
// push - object - name
router.push({name: 'profile',params: {name: 'coder4'},query: {coderName: 'coderMonkey2', coderAge: 20}
})
// push - replace - object - name
router.push({
// router.replace({name: 'profile',params: {name: 'coder4'},replace: true,query: {coderName: 'coderMonkey2', coderAge: 20}
})
// 没有参数 刷新
router.go()
// 正数 1 前进一页 router.forward()
router.go(1)
// 负数 -1 返回一页 router.back()
router.go(-1)
<!-- 命名式导航 -->
<RouterLink :to="{ name: 'profile', params: { name: 'coder'},query: { coderName: 'coderMonkey', coderAge: 18 }}">Profile</RouterLink>
<RouterView></RouterView>
路由守卫
{path: "/profile/:id",component: Profile,// 路由独享的导航守卫// beforeEnter: (to, from) => {// console.log("beforeEnter");// },beforeEnter: [(to, from) => {console.log("first");return true;},(to, from) => {console.log("second");return true;},],meta: {title: "xxxxxx-xxxxx",name: "coder",age: 18,},
},
const isLogin = true;
// 全局的导航守卫 -> 路由独享的导航守卫 -> 组件内的导航守卫 -> beforeResolve -> afterEach
// to 跳转到的那个页面 from 触发跳转的页面
router.beforeEach((to, from) => {// 默认返回 undefined 和 return true 是一样的 都会发生跳转// 不会跳转 return false / return {}if (!isLogin && to.path !== "/login") {return {path: "/login",};}// console.log("router.beforeEach", to, from);
});router.beforeResolve((to, from) => {// console.log("router.beforeResolve", to, from);console.log("router.beforeResolve", to.meta);
});router.afterEach((to, from, failure) => {if (!failure) {document.title = to.meta.title || "aaa";} else {console.log("router.afterEach", failure);}
});
import { onBeforeRouteLeave, onBeforeRouteUpdate } from "vue-router";
// 组件内的导航守卫
onBeforeRouteLeave(() => {console.log('onBeforeRouteLeave')
})onBeforeRouteUpdate(() => {console.log('onBeforeRouteUpdate')
})
KeepAlive
<template><p>App</p><RouterLink to="/">Home</RouterLink><RouterLink to="/profile">Profile</RouterLink><RouterLink to="/about">About</RouterLink><RouterView v-slot="{ Component }"><!-- include: 用于缓存组件 --><!-- string include="About,Profile" --><!-- reg :include="/About|Profile/" --><!-- array :include="['About', 'Profile']" --><!-- exclude 用于不缓存的组件 --><!-- string reg array --><!-- max 最多缓存两个组件 --><!-- https://devtools.vuejs.org/guide/installation.html --><KeepAlive :max="1"><component :is="Component" /></KeepAlive></RouterView>
</template>
<script setup>
import { onActivated, onDeactivated, onMounted, onUnmounted, ref } from 'vue';
onActivated(() => {console.log('活跃')
})
onDeactivated(() => {console.log('失活')
})
</script>
<template><p>Profile</p>
</template>
Pinia
/stores/user.js
import { defineStore } from "pinia";
import { computed, ref } from "vue";export const useUserStore = defineStore("user", () => {const name = ref("coderMonkey");const age = ref(18);const doubleAge = computed(() => age.value * 2);const changeAge = () => age.value++;const multipleCount = computed(() => (multiple) => count.value * (multiple || 1));const getUser = async () => {const { data } = await axios.get("/api/info");console.log(data);}return {}
})
页面上使用
<script setup>
import { storeToRefs } from 'pinia';
import { useUserStore } from './stores/user'const userStore = useUserStore()
// 切记
const { name, age } = storeToRefs(userStore)
const { changeAge, getUser } = userStore
// 访问数据 store.xxx
console.log(userStore.name)// 修改数据 store.xxx = 'xxx'
const handleClick = () => {// 修改数据 1 store.xxx = 'xxx'userStore.name += '1'userStore.age += 1// 修改数据 2-1 store.$patch({})// userStore.$patch({// name: 'aaa',// age: 100// })// 修改数据 2-2 store.$patch((state) => {})// userStore.$patch((state) => {// state.name = 'bbb'// state.age = 999// })
}// 重置
// 插件的使用 pinia
// pinia.use
const reset = () => {console.log('reset')// userStore.$reset()
}// 订阅 store 的变化
userStore.$subscribe((mutation, state) => {console.log('mutation, state', mutation, state)
}, {detached: true
})userStore.$onAction(({ after, args, name, onError, store }) => {console.log('args', args)console.log('name', name)console.log('store', store)after(() => {console.log('after')})onError(() => {console.log('err')})
})
</script><template><!-- 访问数据 --><p>{{ userStore.name }}</p><p>{{ userStore.age }}</p><p>{{ userStore.doubleAge }}</p><p>{{ name }}</p><p>{{ age }}</p><!-- <button @click="userStore.changeAge(1, $event)">store changeAge</button> --><button @click="userStore.changeAge">store changeAge</button><button @click="handleClick">change</button><p>{{ counterStore.multipleCount(5) }}</p><button @click="counterStore.$reset">reset</button>
</template>
main.js 使用插件
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";const app = createApp(App);
const pinia = createPinia();pinia.use(({ store }) => {const initialStore = JSON.parse(JSON.stringify(store));store.$reset = () => {store.$patch((state) => {Object.assign(state, initialStore);});};
});app.use(pinia);
app.mount("#app");
vite proxy 配置
proxy: {// http://121.4.100.140:9091/info// env === prd ? /info : /api/info"/api": {target: "http://121.4.100.140:9091",changeOrigin: true,// http://121.4.100.140:9091/inforewrite: (path) => path.replace(/^\/api/, ""),},
}