前言
适合于看了vue2之后学习vue3的学习者复习vue3使用
可根据目录按需查阅
觉得这里主题看着不舒服可以去这里看
vue3备忘录
1、创建项目
-
使用
npmjavascriptnpm init vue@latestjavascriptnpm ijavascriptnpm run dev -
使用
pnpmjavascriptpnpm create vue@latestjavascriptpnpm ijavascriptpnpm dev
2、reactive和ref
-
reactive可以将对象类型的数据转换为响应式的数据 -
ref可以将简单类型和对象类型的数据转换为响应式的数据但是ref转换简单类型的数据的本质是把简单类型的数据外面包一层转换为对象类型的数据
-
reactive的使用javascriptimport { reactive } from 'vue' const msg = reactive({ count: 100 }) {{ msg.count }} @click="msg.count++" -
ref的使用javascript<script setup> import { ref } from 'vue' const count = ref(0) // 在括号里面填写数据 const addCount = () => { count.value++ } // 在script脚本中使用是需要加上.value </script> <template> {{ count }} // 在template里面使用时 <button @click="count++">按钮</button> // 这里也是在template里面所以访问修改直接使用count </template>ref在template里面时直接使用名称即可访问在script脚本里面使用时需要加上==.value==
最佳实践:统一使用ref定义响应式数据
3、定义方法
-
直接定义为
javascriptconst 方法名 = () => { 方法逻辑 } -
在基于setup语法糖的情况下后面可以直接调用
4、计算属性
-
先导入
javascriptimport { computed } from 'vue' -
简单写法
javascriptconst 计算属性名 = computed(() => { return 计算后的值 }) -
完整写法
javascriptconst 计算属性名 = computed({ get: () => { return 计算后的值 }, set: (val) => { 编写逻辑 } })
最佳实践:避免直接修改计算属性的值,除特殊情况无法避免
5.watch侦听
-
先导入
javascriptimport { watch } from 'vue' -
侦听单个
javascriptwatch(监听的ref对象, (newVal, oldVal) => { 发生变化时执行的逻辑 }) -
侦听多个
javascriptwatch([监听的ref对象1, 监听的ref对象2], (newVal, oldVal) => { 发生变化时执行的逻辑 此时的 newVal 是 [ref对象1的值, ref对象2的值] }) -
深度侦听
javascrptwatch(监听的ref对象, (newVal, oldVal) => { 逻辑 },{ immediate: true, // 立即执行一次逻辑 deep: true // 开启深度侦听 }) -
精确侦听对象中的某个值
javascriptwatch( () => 对象.value.键名, (newVal, oldVal) => 发生变化时执行的操作 )
在不开启深度侦听的情况下
watch进行的是浅层侦听
就是只有当整个发生变化时才会触发
eg:[1, 2, 3, 4, 5, 6] 如果 把整个数组都改变 那浅层也能侦听到
如果只改了数组里面的某一个元素的值,那就只有开启深度侦听才能侦听到
6、生命周期函数
| vue2 | vue3 | 时机 |
|---|---|---|
| created | setup | 一进来就调用(实例初始化完成以后调用) |
| mounted | onMounted | 实例挂载到DOM完成后调用 |
注意vue3中的声明周期函数需要导入,完整语法演示如下
javascriptimport { onMounted } from 'vue' <script setup> const getData = () => { console.log("发送请求,获取数据") } onMounted(() => { console.log("触发onMounted生命周期函数") }) getData() // 调用setup生命周期函数 与写的位置无关 此项会在onMounted之前执行 </script>可以多次调用,多次调用时会按顺序执行,不会冲突
7、组件注册
-
局部导入
在script里面导入后可以直接使用
不需要注册
javascript<script setup> // 导入 import SonCom from '@/components/son-com.vue' </script> <template> <div> 这是父组件的区域,下面是子组件 // 直接使用 <son-com></son-com> </div> </template>
8、父传子(对应vue2中的props)
-
在父组件中给子组件标签添加自定义属性
-
在子组件中使用
javascriptconst props = defineProps({ 自定义属性名称: 类型 }) // 或完整写法 const props = defineProps({ 自定义属性名: { type: 类型, // 更多配置项 } }) -
在子组件中对于props传递过来的数据
-
在模版中( template )
-
可以直接使用自定义属性名访问
javascript{{ 自定义属性名 }} -
在脚本中( script )
-
必须使用
props.自定义属性名的格式才能访问javascriptprops.自定义属性名
-
-
也可以传递动态的值使用
:data
9、子传父(对应vue2中的$emit)
-
在子组件中使用编译器宏注册自定义事件
-
在父组件中监听自定义事件
-
子组件中
javascriptconst emit = defineEmits(['自定义事件名']) emit('自定义事件名', 数据) -
父组件中
javascript@自定义事件名 = ""注意在这里的自定义事件名后面同样跟vue2一样可以直接使用
$event直接获取传过来的参数
10、模版引用和defineExpose(对应vue2中的ref和$refs)
-
首先定义一个ref对象里面
-
然后使用ref绑定到标签
-
后续可以使用
名称.value获取dom对象或组件实例 -
获取dom对象演示
javascriptconst Href = ref(null) <input type="text" ref="Href"></input> // 获取 onMounted(() => { console.log(Href.value) // 获取dom对象 Href.value.focus() // 聚焦输入框 }) -
获取组件实例演示
javascriptconst Gref = ref(null) <HmCom ref="Gref"></HmCom> // 绑定到组件javascript// 在组件中 const data = "这是一个数据" const getData = () => { console.log(data) } 使用defineExpose()将数据暴露出去 defineExpose({ data, getData })javascriptconst Gref = ref(null) <HmCom ref="Gref"></HmCom> // 绑定到组件 // 获取 Gref.value.data // 获取 data 数据 Gref.value.getData() // 调用 getData 方法,可以正常调用使用
使用**defineExpose({})**暴露属性方法
11、provide和inject
-
用于跨多层组件的通信
-
在父组件中
javascriptimport { provide } from 'vue' provide('名称', 数据) // 可以传静态数据也可以传响应式数据 provide('名称', (val) => { 也可以传递函数 }) -
在子孙组件中
javascriptimport { inject } from 'vue' const message = inject('名称') // 接收数据 // 如果是响应式的ref数据同样可以直接使用 {{ message }} 渲染 const 函数名称 = inject('传过来的函数名称') 函数名称(参数值)
注意只能用于更高层的组件向自己的子孙组件传递
不能反过来
12、defineOptions
-
这里面可以写OptionsAPI中的内容
-
原因是有些属性不能再setup里面写
-
演示
javascript<script setup> defineOptions({ name: 'LoginIndex' }) </script>比如给组件命名这样的操作就需要在这里面写
写在里面的内容是与setup平级的
13、defineModel
-
在vue2中v-model是通过绑定value属性和监听==@input==事件来实现双向绑定
-
在vue3中v-model改为了绑定modelValue和监听==@updata:modelValue==
注意vue3中的
v-model等于v-model:modelValue可以改写为
v-mode:任意名称此时相当于绑定了任意名称和监听==@updata:任意名称==
相当于做了vue2中的
.sync的结合 -
演示:
父组件:
javascript<script setup> import SonCom from '@/components/son-com.vue' import { ref } from 'vue' const data = ref('100') </script> <template> <div> <h1>{{ data }}</h1> <son-com v-model="data"></son-com> </div> </template>子组件:
javascript<script setup> const props = defineProps({ modelValue: String }) const emit = defineEmits(['update:modelValue']) </script> <template> <input type="text" :value="modelValue" @input="e => emit('update:modelValue', e.target.value)"> </template> <style></style> -
为了解决这个繁琐的操作,提供了defineModel编译器宏
这个功能在vue3.3中是实验性功能,在vue3.4中正式发布
-
首先在父组件中通过v-model传值给子组件
javascriptconst data = ref('100') <son-com v-model="data"></son-com> -
然后在子组件中使用defineModel接收
javascriptconst 自定名称 = defineModel() 下面就可以使用修改这个值了(相当于已经实现双向绑定) <input type="text" :value="自定名称" @input="e => 自定名称 = e.target.value">
在父组件中用v-model将数据传过去
然后在子组件中使用defineModel接收
然后在子组件中就可以使用和修改这个数据,且父组件中可以同步修改
14、Pinia(相当于vuex)
-
定义
javascript// 新建一个store文件夹用于存放pinia仓库中的数据 import { defineStore } from 'pinia' export const useChannelStore = defineStore('仓库名称', () => { const list = ref([]) // 相当于 vuex 中的 state const setList = () => { // 这里面可以直接修改上面的数据 } const getList = computed(() => { return 数据 // 这个相当于之前的的getters }) }) -
使用
javascript// 在想要使用的组件中导入 import { 定义时导出的名称 } from '@/...' const 自定义名称 = 定义时导出的名称() // 然后就可以直接使用 自定义名称.list 自定义名称.setList() 自定义名称.getList -
表格对比
Vuex Pinia 备注 mutations 没有 修改vuex中用于修改state种的数据,pinia中可以直接修改,不需要 state const data = ref() Pinia中直接定义数据就是state中的数据 getters const getList = computed(() => { return 返回数据 }) Pinia中直接定义computed就是计算属性 actions const 函数名 = () => { 函数内容 } 在Pinia中可以直接在函数中执行同步或者异步操作
注意在
const 自定义名称 = 定义时导出的名称()此处导入时如果直接对==数据(普通数据、计算属性)==使用解构则会丢失响应式
可以对方法直接解构
对数据解构的话要使用
const { 解构数据 } = storeToRefs(自定义名称)要多写一行,不能直接
const { 解构数据 } = 定义时导出的名称()且需要导入
import { storeToRefs } from 'pinia'
15、Pinia数据持久化
开始 | Pinia Plugin Persistedstate
-
下载插件
bashpnpm add pinia-plugin-persistedstate -
在main.js中对piniause这个插件对象
javascriptimport { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(piniaPluginPersistedstate) -
使用时
javascript// 对你创建的defineStore添加第三个参数 persist: true // eg: export const counterStore = defineStore('counter', () => { // ..... return { } }, { persist: true // 添加这个 }) -
这时刷新页面数据就会持久化存储不会重新初始化
-
如果需要对插件进行配置
javascriptexport const counterStore = defineStore('counter', () => { // ..... return { } }, { persist: { // 在这里面填写配置项 } })- 具体配置看官方文档配置 | Pinia Plugin Persistedstate
16、路由配置
vue3中的路由配置与vue2中有所不同的地方,但是大致相同,初始化项目后配置如下
javascript
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [],
})
export default router
-
createWebHistory是指以history模式创建 -
createWebHashHistory是以hash模式创建 -
里面的参数是路径的前缀,默认是
/如果
import.meta.env.BASE_URL换成/aa,那么所有的路径前面都会加上/aa,eg:localhost:3000/aa/listimport.meta.env这个是环境变量的前缀,这里的是base环境变量,可以在vite.config中通过base配置项更改 -
区别
Vue版本 获取路由对象 获取路由信息 备注 2 this.$router $route 无需导入 3 useRouter() useRoute() 需要导入 -
演示
javascript<script setup> import { useRouter, useRoute } from 'vue-router' const router = useRouter() const route = useRoute() router.push() route.query.参数名 </script>
-
-
拓展:
-
直接导入的router对象
javascriptimport router from '@/router' router.push('/home') -
和使用钩子函数导入的
javascript// 导入方式 import { useRouter } from 'vue-router' const router = useRouter() router.push('/home')
区别:
前者可以在任何地方使用,后者由于useRouter()只能在setup中调用,所以后者只能在组件中使用
-
17、element-plus的使用
-
对应vue2中的element-ui
-
执行下面的命令
bashpnpm install element-plus pnpm install -D unplugin-vue-components unplugin-auto-import -
将下面代码集成到
vite.config.jsjavascriptimport { defineConfig } from 'vite' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' export default defineConfig({ // ... plugins: [ // ... AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ], }) -
接下来就可以直接使用了,会自动导入
-
注册完成过后你的组件注册也可以直接使用
他会自动导入
javascript<script setup> // 不需要在这里引入 </script> <template> <el-button>按钮</el-button> <HmCom></HmCom> </template>
18、axios拦截器的配置(拓展)
-
配置
javascriptconst instance = axios.create({ baseURL, // 请求链接 timeout: 5000, // 超时时间 }) instance.interceptors.request.use( (config) => { if (TokenStore.token) { config.headers.Authorization = TokenStore.token // 如果有token的话配置响应头 Authorization 专门用来设置token } return config }, (err) => { Element.error('请求配置阶段出错,请刷新页面重试') // 额外反应 Promise.reject(err) // 这个相当于是告知失败 但他不会处理失败(页面不会有额外反应) }, ) instance.interceptors.response.use( (res) => { if (res.data.code === 0) { // res.data 是返回的那个东西的整体 return res } ElMessage.error(res.data.message || '服务异常') return Promise.reject(res.data) // 返回一个对象类型的 }, (err) => { ElMessage.error(err.response.data.message || '服务异常') // err.response.data.messsage 可以获取错误的具体信息 if (err.response?.status === 401) { router.push('/login') } return Promise.reject(err) }, )
19、element-plus表单组件的使用
-
自定义校验规则步骤
-
基础模版
html<el-form> <el-item> <el-input></el-input> </el-item> </el-form> -
1、首先定义一个响应式的对象用于存放校验的数据
javascriptconst userForm = ref({ username: '', password: '', repassword: '' }) -
2、定义一个非响应式的对象用于存放校验规则
javascriptconst rules = { username:[ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 3, max: 10, message: '用户名必须是 3 - 10 位', trigger: 'change' } ], password:[ { validator: (rule, value, callback) => { // rule 为当前校验规则的相关信息 // value 为当前的表单元素值 // callback 代表的是是否通过校验通过callback(new Error(错误信息))来返回错误信息 } } // 这里没有配置 trigger 默认为 change ] }一个里面可以填写多条规则,每条规则以对象的形式编写
trigger中有两个值blur代表 失焦时进行规则校验
change代表 发生改变时进行规则校验
自定义校验时注意callback( )无论成功失败都要返回
成功
callback()失败
callback(new Error(错误信息))且最好不要省略else
即:
javascriptif(...){ callback() }else{ callback(new Error(错误信息)) } // 最好不要写成下面的形式 if(...){ callback() }callback(new Error(错误信息))当存在
validator时可以填写其他信息,但是此时的其他信息会被视为自定义属性,不会被视为校验规则eg:
javascriptrepassword: [ { pattern: /^\S{6,15}$/, message: '密码必须为6-15位的非空字符', trigger: 'blur' }, { min: 7, validator: (rule, value, callback) => { if (value !== userForm.value.password) { callback(new Error('两次输入密码不一致')) } else { callback() } } } ]- 此时的min不会被视为校验规则,只会被视为一个普通的自定义属性
-
3、将userForm通过
:model绑定到el-form,将rules通过:rules绑定到el-formhtml<el-form :model="userForm" :rules="rules"> <el-form-item> <el-input></el-input> </el-form-item> </el-form> -
4、给
el-input通过v-model绑定userForm中的对应属性html<el-form :model="userForm" :rules="rules"> <el-form-item> <el-input v-model="userForm.username"></el-input> </el-form-item> </el-form> -
5、给
el-item通过prop绑定rules中的对应属性注意不需要"对象.属性"html<el-form :model="userForm" :rules="rules"> <el-form-item prop="username"> 注意此处 <el-input v-model="userForm.username"></el-input> </el-form-item> </el-form>
-
-
拓展:
注意事项
在配置了拦截器时,调用接口后不要使用
alert()他会使拦截器里面的有些方法失效eg:
javascriptconst register = async () => { // 注册成功之前先再次验证 验证成功之后开始注册逻辑 验证失败的话他也会自动给出提示 await form.value.validate() await userRegisterService(userForm.value) ElMessage.success('注册成功') // alert('注册成功') 这里如果使用了 alert() 的话 拦截器中的 ElMessage.error() 不会生效 isRegister.value = false }form.value.validate( )是按需进行验证的,他会自动的匹配当前表单中有的项目进行验证,没有的他不会验证,因为这一点,可以给登录注册共用一个对象进行绑定,在验证时也不会产生冲突
20、element-plus菜单组件的使用
-
常用演示
html<el-menu active-text-color="#ffd04b" background-color="#232323" :default-active="$route.path" text-color="#fff" router > <el-menu-item index="/article/channel"> <el-icon><Management/></el-icon> <span>名称</span> </el-menu-item> </el-menu> -
active-text-color这个参数是指激活时的文字颜色,就是点到哪个时哪个的文字颜色就会变成参数值 -
:default-active这个是默认激活的菜单项 -
router指的是开启vue-router模式当开启时,会在激活导航时以 index 作为 path 进行路由跳转 使用default-active来设置加载时的激活项
-
$route.path是指获取的当前路由
21、导航守卫
vue2导航守卫参考文档导航守卫 | Vue Router
vue3导航守卫参考文档导航守卫 | Vue Router
-
vue3中返回
undefined或者true就是放行返回
false就是拦回from的地址页面 -
return '/login'或return { name: 'Login' }就是返回具体地址 -
vue3导航守卫示例
javascriptrouter.beforeEach((to) => { const userStore = useTokenStore() if ( !userStore.token && to.path !== '/login' ) { return '/login' } })vue3中不再通过next来进行拦截,而是通过返回值来进行判断
22、下拉菜单
-
示例代码
html<template> <el-dropdown @command="handleCommand"> <span class="el-dropdown-link"> Dropdown List<el-icon class="el-icon--right"><arrow-down /></el-icon> </span> <template #dropdown> <el-dropdown-menu> <el-dropdown-item command="a">Action 1</el-dropdown-item> <el-dropdown-item command="b">Action 2</el-dropdown-item> <el-dropdown-item command="c">Action 3</el-dropdown-item> <el-dropdown-item command="d" disabled>Action 4</el-dropdown-item> <el-dropdown-item command="e" divided>Action 5</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </template>javascriptconst handleCommand = (key) => { // 这里的key就是触发时的对应参数 (这里是a、b、c、d、e) // 操作逻辑 } -
拓展
javascriptconst handCommand = async (key) => { if (key === 'logout') { await ElMessageBox.confirm('你确定要退出登录吗', '温馨提示', { type: 'warning', confirmButtonText: '确定', cancelButtonText: '取消' }) tokenStore.removeToken() tokenStore.setUserInfo({}) router.push('/login') } else { router.push(`/user/${key}`) } }使用
async和await配合来达到消息确认框的效果
23、其余组件用法
由于组件用法较多此处不再一一演示
更多用法查阅官网即可
官网解释非常详细