一、Vite
中文官网:Vite中文网
Vite是一种新型前端构建工具,能够显著提升前端开发体验
Vite 意在提供开箱即用的配置,同时它的 插件 API 和 JavaScript API 带来了高度的可扩展性,并有完整的类型支持。
搭建Vite项目相关命令:
使用 NPM:
$ npm create vite@latest
使用 Yarn:
$ yarn create vite
使用 PNPM:
$ pnpm create vite
要构建一个 Vite + Vue 项目,运行:
# npm 6.x
npm create vite@latest my-vue-app --template vue
# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template vue
# yarn
yarn create vite my-vue-app --template vue
# pnpm
pnpm create vite my-vue-app --template vue
Vite解决@问题
修改项目中的vite.config.js文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from "path"
export default defineConfig({
plugins: [vue()],
// 配置根路径
resolve: {
// ↓路径别名,主要是这部分
alias: {
"@": resolve(__dirname, "./src")
}
}
})
错误提示:找不到模块 “path” 或其相应的类型声明 或者 找不到名称 “__dirname”
对node进行类型声明
yarn add @types/node -D
npm i @types/node --D
二、Vue3
Vue是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。
Vue 的组件可以按两种不同的风格书写:选项式 API 和组合式 API。
选项式 API (Options API)
使用选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑,例如 data
、methods
和 mounted
。选项所定义的属性都会暴露在函数内部的 this
上,它会指向当前的组件实例。
组合式 API (Composition API)
通过组合式 API,我们可以使用导入的 API 函数来描述组件逻辑。在单文件组件中,组合式 API 通常会与 <script setup> 搭配使用。这个 setup
attribute 是一个标识,告诉 Vue 需要在编译时进行一些处理,让我们可以更简洁地使用组合式 API。比如,<script setup>
中的导入和顶层变量/函数都能够在模板中直接使用。
创建一个 Vue 应用
> npm init vue@latest
这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具
当你准备将应用发布到生产环境时,请运行:
> npm run build
此命令会在 ./dist
文件夹中为你的应用创建一个生产环境的构建版本。
setup
1、定义数据
死数据,不可以修改之类的,但是可以展示视图层:
let str = ''
响应式数据:ref(),在使用的时候需要:x.value,可以更改值
let str = ref('1')
const btn = () =>{
str.value = '2'
}
响应式数据:reactive(),在使用的时候不需要.value,可以直接使用,但是reactive只能写对象和数组(只能写引入类型)
const str = reactive([1,2,3])
const btn = () => {
str[2] = 4
}
2、setup语法糖插件
解决:import {ref, reactive ...} 引入的问题
下载安装:
npm i unplugin-auto-import -D
在vite.config.js中配置
//引入
import AutoImport from 'unplugin-auto-import/vite'
//在plugins里使用
plugins: [
vue(),
AutoImport({
imports:['vue', 'vue-router']
})
]
vue2和vue3数据拦截不同的点
vue2 => Object.defineProperty
vue3 => new Proxy
toRefs
解构 ==》 响应式数据
let obj = reactive({
name:'张三',
age:19
})
let {name, age} = toRefs( obj )
computed
接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value
暴露 getter 函数的返回值。它也可以接受一个带有 get
和 set
函数的对象来创建一个可写的 ref 对象。
//只读
let changeStr = computed(() => {
return str.value
})
//可写的
let changeStr2 = computed({
get(){
return str.value
}
set(val){
str.value = val
}
})
watch()
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
第一个参数是侦听器的源。这个来源可以是以下几种:
- 一个函数,返回一个值
- 一个 ref
- 一个响应式对象
- ...或是由以上类型的值组成的数组
第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。
第三个可选的参数是一个对象,支持以下这些选项:
immediate
:在侦听器创建时立即触发回调。第一次调用时旧值是undefined
。deep
:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。flush
:调整回调函数的刷新时机。onTrack / onTrigger
:调试侦听器的依赖。
//监听一个数据
watch(str, (newVal,oldVal) =>{
console.log(newVal,oldVal)
})
//同时监听多个数据,并在初始化的时候就监听一次
watch([str,num], (newVal,oldVal) =>{
console.log(newVal,oldVal)
},{
immediate:true
})
//监听对象某一个key,并且深度监听
watch( ()=>obj.m, (newVal,oldVal) =>{
console.log(newVal,oldVal)
},{
deep:true
}
//监听路由
let router = useRouter()
watch(()=> router.currentRoute.value, (newVal)=>{
console.log(newVal.value)
},{
immediate:true
})
watchEffect()
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1
生命周期钩子
Vue2------------------vue3
beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount ——组件挂载到节点上之前执行的函数
mounted -> onMounted——组件挂载完成后执行的函数
beforeUpdate -> onBeforeUpdate——组件更新之前执行的函数
updated -> onUpdated——组件更新完成之后执行的函数
beforeDestroy -> onBeforeUnmount——组件死亡(卸载)之前执行的函数
destroyed -> onUnmounted——组件完全死亡(卸载)后执行的函数
activated -> onActivated——被包含在 <keep-alive> 中的组件 会多出两个生命周期钩 子函数 被激活时执行
deactivated -> onDeactivated——从A组件 切换 到 B 组件 A组件消失时执行
errorCaptured -> onErrorCaptured——当前捕获一个子孙组件的异常时激活钩子函数
onBeforeUpdate(()=>{
console.log('修改前');
})
onUpdated(()=>{
console.log('修改后');
})
onBeforeUnmount(()=>{
console.log('销毁前');
})
onUnmounted(()=>{
console.log('销毁后');
})
路由
下载安装:
npm i vue-router -S
1、tag属性去除了
<router-link to="/about">跳转</router-link>
2、写法问题
let router = useRouter() //等同于vue2中的this.$router
let route = useRoute() //等同于vue2中的this.$route
const goAbout = ()=>{
router.push('/')
}
3、导航守卫
全局路由守卫(3个)
1、router.beforeEach((to, from, next) => {}) 全局前置守卫,路由跳转前触发
2、router.beforeResolve((to, from, next) => {})全局解析守卫,在所有组件内守卫和异步路由组件被解析之后触发
3、router.afterEach((to, from) =>{})全局后置守卫,路由跳转完成后触发
路由独享守卫
beforeEnter(to, from,next) 路由对象单个路由配置,单个路由进入前触发
组件路由守卫(3个)
1、beforeRouteEnter(to, from, next) 在组建生命周期beforeCreate阶段触发,Vue3中不可以使用beforeRouteEnter路由守卫
2、beforeRouteUpdate(to, from, next)当前路由改变时触发
3、beforeRouteLeave(to, from, next)导航离开该组件的对应路由时触发
// router/index.js 页面
router.beforeEach((to, from, next) => {
console.log(to, from);
next()
});
组件通讯
父传子
//父组件
<template>
<Son :msg="str2"></Son>
</template>
<script setup>
import Son from "@/components/son.vue";
let str2 = ref('今天天气不错')
</script>
//子组件
<template>
<div>
我是子组件-----{{msg}}
</div>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
msg:{
type:String,
default:'2222222'
}
})
</script>
子传父
//子组件
<template>
这是子组件----{{num}}
<button @click="changeNum">点击</button>
</template>
<script setup>
let num = ref(999)
const emit = defineEmits(['fn'])
const changeNum = () =>{
emit('fn',num)
}
</script>
//父组件
<template>
<Son @fn='changeAbout'></Son>
</template>
<script setup>
import Son from "@/components/son.vue";
const changeAbout = (n) =>{
console.log(n.value);
}
</script>
父子组件双向数据v-model
//父组件
<template>
<Son v-model:num="num"></Son>
</template>
<script setup>
import Son from "@/components/son.vue";
let num = ref(3)
</script>
//子组件
<template>
<div>
双向数据------{{num}}
<button @click="btn">子按钮</button>
</div>
</template>
<script setup>
const emit = defineEmits(['update:num'])
const props = defineProps({
num:{
type:Number,
default:100
}
})
const btn = ()=>{
emit('update:num',200)
}
</script>
兄弟组件之间的传值
1、下载安装
npm i mitt -S
2、新建plugins/Bus.js文件
//Bus.js
import mitt from 'mitt'
const emitter = mitt()
export default emitter
//C组件
<template>
C组件
<button @click="btn">C按钮</button>
</template>
<script setup>
import emitter from '../plugins/Bus'
let strC = ref('这个是C组件')
const btn = ()=>{
emitter.emit('fn',strC)
}
</script>
//D组件
<template>
<div>
D组件----{{strD}}
</div>
</template>
<script setup>
import { onBeforeMount } from 'vue'
import emitter from '../plugins/Bus'
let strD = ref('')
onBeforeMount(()=>{
emitter.on('fn',e=>{
strD.value = e.value
})
})
</script>
插槽
匿名插槽、具名插槽、作用域插槽、动态插槽名
匿名插槽
//父组件
<Son>
这个是XXXX数据
</Son>
//子组件
<div>
<slot></slot>
</div>
具名插槽
//父组件
<Son>
<template v-slot:yyy> //这里可以简写<template #yyy>
我是yyy数据
</template>
</Son>
//子组件
<div>
<slot name="yyy"></slot>
</div>
作用域插槽
让子组件在渲染时将一部分数据提供给插槽。
//父组件
<Son>
<template v-slot='{data}'> //这里可以简写成<template #default='{data}'>
{{data}}
</template>
</Son>
//子组件
<template>
<div v-for="item in list" :key="item.id">
<slot :data=item></slot>
</div>
</template>
<script setup>
let list = ref([
{id:1,name:'张三'},
{id:2,name:'李四'},
{id:3,name:'王五'},
])
</script>
动态插槽名
//父组件
<template>
<Son>
<template v-slot:[xxx]>
这个是XXXX数据
</template>
</Son>
</template>
<script setup>
let xxx = ref('xxx')
</script>
Teleport传送
<Teleport>
是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
<Teleport to="#modals">
<div>A</div>
</Teleport>
//渲染结果为
<div id="modals">
<div>A</div>
</div>
<Teleport>
接收一个to
prop 来指定传送的目标。to
的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。
动态组件
<component :is="动态去切换组件"></component>
异步组件
在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent,提升性能。
vueuse:https://vueuse.org/core/useIntersectionObserver/#useintersectionobserver
下载安装:npm i @vueuse/core -S
使用场景1
组件按需引入:当用户访问到了组件再去加载组件
<template>
<A></A>
<B></B>
<div ref="target">
<D v-if="targetIsVisible"></D>
</div>
</template>
<script setup>
import { useIntersectionObserver } from '@vueuse/core'
import A from '../components/A.vue'
import B from '../components/B.vue'
const D = defineAsyncComponent(() =>
import('../components/D.vue')
)
const target = ref(null)
const targetIsVisible = ref(false)
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }]) => {
targetIsVisible.value = isIntersecting
},
)
</script>
使用场景2
打包分包处理:打包完成后,异步组件有单独的js文件,是从主体js分包出来的
<template>
<Suspense>
<template #default>
<A></A>
</template>
<template #fallback>
Loading...
</template>
</Suspense>
</template>
<script setup>
const A = defineAsyncComponent(() =>
import('../components/A.vue')
)
</script>
Mixin混入
Mixin 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个 mixin 对象可以包含任意组件选项。当组件使用 mixin 对象时,所有 mixin 对象的选项将被“混合”进入该组件本身的选项。
在src目录下创建mixins/mixin.js文件在里面写复用的内容
import { ref } from 'vue'
export default function () {
let num = ref(1)
let fav = ref(false)
let favBtn = () => {
num.value += 1
fav.value = true
setTimeout(() => {
fav.value = false
}, 2000);
}
return {
num,
fav,
favBtn
}
}
在B、C组件中使用,这里只展示其中一个的代码
<template>
-----{{num}}---
<button @click="favBtn">
{{fav? '收藏中...' : '收藏'}}
</button>
</template>
<script setup>
import mixin from '../mixins/mixin.js'
let {num,fav,favBtn} = mixin()
</script>
在选项式API中使用
export const fnaBtn = {
data() {
return {
num:10
}
},
methods: {
favBtnAdd(params) {
this.num += params
}
},
}
<template>
<div>
-----{{num}}-----
<button @click="favBtnAdd(2)">获取</button>
</div>
</template>
<script>
import {fnaBtn} from '@/mixins/mixin'
export default {
mixins:[fnaBtn]
}
</script>
Provide / Inject 依赖注入
1、provide()
接受两个参数:第一个参数是要注入的 key,可以是一个字符串或者一个 symbol,第二个参数是要注入的值。2、inject()第一个参数是注入的 key。Vue 会遍历父组件链,通过匹配 key 来确定所提供的值。如果父组件链上多个组件对同一个 key 提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过 key 匹配到值,
inject()
将返回undefined
,除非提供了一个默认值。第二个参数是可选的,即在没有匹配到 key 时使用的默认值。第二个参数也可以是一个工厂函数,用来返回某些创建起来比较复杂的值。在这种情况下,你必须将
true
作为第三个参数传入,表明这个函数将作为工厂函数使用,而非值本身。
//提供
provide('count', count)
// 注入响应式的值
const count = inject('count')
Vuex
下载安装:
# npm
npm install vuex@next --save# yarn
yarn add vuex@next --save
创建/src/store/index.js文件
import { createStore } from 'vuex'
import user from './modules/user'
export default createStore({
state: { //存放数据的地方
},
getters:{//类似于组件的计算属性
},
mutations: {//状态更新的唯一方式:提交 mutation,store.commit('方法名')
},
actions: {//异步操作,通过dispatch 调用action 中定义的sum_action 方法
},
modules: {//模块儿化管理
user
}
})
在main.js文件中引入
import store from './store/index'
createApp(App).use(router).use(store).mount('#app')
在组件中使用时
import { useStore } from 'vuex' // 引入useStore 方法
const store = useStore() // 该方法用于返回store 实例
console.log(store.state.user.userInfo) // store 实例对象
let numStore = computed(()=> store.state.numStore)
const btn = () =>{
store.commit('changeNum',2)
store.dispatch('changeBtn')
}
持久化存储vuex-persistedstate
下载安装:npm install vuex-persistedstate --save
在store/index.js中引用
import persistedstate from 'vuex-persistedstate';
在modules下面添加plugins配置
import { createStore } from 'vuex'
import persistedstate from 'vuex-persistedstate';
import user from './modules/user'
export default createStore({
state: { //存放数据的地方
},
getters:{//类似于组件的计算属性
},
mutations: {//状态更新的唯一方式:提交 mutation,store.commit('方法名')
},
actions: {//异步操作,通过dispatch 调用action 中定义的sum_action 方法
},
modules: {//模块儿化管理
user
},
plugins: [persistedstate({
key: 'per-vuex',//浏览器中的名字
paths:['user'] //需要存储起来的参数模块,不写存储的就是指当前文件内的参数
})]
})
Pinia
大致总结:
1、支持选项式API和组合式API写法
2、pinia没有mutations,只有:state、getters、actions
3、pinia分模块不需要modules
4、TypeScript支持更好
5、自动化代码拆分
6、pinia体积更小(性能更好)
7、pinia可以直接修改数据
Pinia🍍 官网👉:Pinia | The intuitive store for Vue.js
下载安装
npm install pinia
// or
yarn add pinia
在main.js中引入
import { createPinia } from 'pinia'
createApp(App).use(router).use(createPinia()).mount('#app')
在store/index.js中引入
import { defineStore } from "pinia";
export const useStore = defineStore('storeId', {
state: () => {
return {
counter:0,
}
},
getters: {//有缓存机制,几乎和vuex是一样的
},
actions: {//几乎也没有什么变化
upNum(val) {
this.counter += val
}
}
})
在组件中使用
import {useStore} from '../store/index'
const store = useStore();
console.log(store.counter);
修改数据
import {useStore} from '../store/index'
import {storeToRefs} from 'pinia'
const store = useStore();
let {counter} = storeToRefs(store)
const changBtn = ()=>{
counter.value +=1
}
批量更新数据store.$patch()
import {useStore} from '../store/index'
import {storeToRefs} from 'pinia'
const store = useStore();
let {counter} = storeToRefs(store)
const changBtn = ()=>{
store.$patch((state)=>{
state.counter ++
})
}
使用actions中提供的方法
const btnAdd = () =>{
store.upNum(200)
}
模块儿化直接在store里创建模块儿化js文件,如user.js、shop.js等,shop.js文件内容在下面
在组件中引入使用,例如
import {shop} from '../store/shop'
import {storeToRefs} from 'pinia'
const shopStore = shop()
const {shopList} = storeToRefs(shopStore)
持久化存储
下载安装:
npm i pinia-plugin-persist --save
修改main.js文件
import store from './store/index'
createApp(App).use(router).use(store).mount('#app')
在store/index.js中
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPluginPersist)
export default store
在store/shop.js中配置
import { defineStore } from "pinia";
export const shop = defineStore('shopId', {
state: () => {
return {
shopList: [
{ id: 1, name: '苹果' },
{ id: 2, name: '橘子' },
]
}
},
getters: {
},
actions: {
},
persist: {//开启数据缓存
enabled: true,
strategies: [
{
key: 'my-shop',
storage: localStorage,
paths:['shopList'] //默认是全部需要持久化,也可以通过该属性指定
}
]
}
})
设置代理
在vite.config.js中添加
//设置代理解决跨域问题
server: {
proxy: {
'/api':'所代理的地址'
}
}
在src目录下创建utils/request.js文件
import axios from 'axios'
//创建axios对象
const service = axios.create();
//请求拦截器
service.interceptors.request.use(config => {
return config;
}, error => {
Promise.reject(error)
});
//响应拦截
service.interceptors.response.use(response => {
//判断code码
return response.data
}, error => {
return Promise.reject(error)
});
export default service;
在src目录下创建api文件夹,里面添加需要添加的API接口,例如
import request from '../utils/request'
export function getSliders() {
return request({url:'/api/slider/getSliders'})
}
在组件中使用
import {getSliders} from '../api/slider'
let list = ref([]);
onBeforeMount(()=>{
getSliders().then(res=>{
list.value = res.data.list
for (let index = 0; index < list.value.length; index++) {
if(index == 0){
list.value.splice(0,1)
}
}
})
})