vue全文知识点
- vue3 学习
- 1 关于项目初始化
- 2 vue3 语法 setup
- 3 vue3 语法 和 vue2 语法兼容
- 4 setup 语法糖
- 5 响应式数据-基础
- 6 响应式数据-ref 定义对象类型数据
- 6 toRef,toRefs 将多个非响应式数据转变为响应式数据 refImpl
- 7 computed 计算属性
- 8 watch 监听 [ref对象(包含计算属性),getter方法(对象属性),响应式对象,数组(包含前三类)]
- 9 ref绑定
- 10 props 子组件接收父组件传值
- 11 vue3 生命周期
- 12 hooks 组件封装
- 13 router 路由
-
- 14 pinia 状态管理器
- 安装配置与基础使用
- 修改 state 数据
- actions 操作函数定义
- getter 函数和 storeToRefs 响应式解构
- $subscribe 监听数据变化
- 15 组件
- 组件切换
- 组件传值
- defineProps
- v-bind/v-on/v-model
- ref 和 $parent
- provide/inject
- 其他
- 组件插槽
- 默认插槽
- 具名插槽(手动指定插槽) v-slot 可以简化
- 作用域插槽(指定插槽并可以操作插槽绑定的数据)
- 其他[事件修饰符、自定义指令、多环境配置]
-
vue3 学习
1 关于项目初始化
index.html App.vue main.ts
index.html : 根html文件 指定渲染的根元素
App.vue : 根组件
main.ts : 项目入口文件
index.html 中引入 main.ts
main.ts 引入 App.vue 并指定挂载位置
2 vue3 语法 setup
选项式 转 结构式
setup return 可以直接返回渲染页面
3 vue3 语法 和 vue2 语法兼容
vue3 语法 和 vue2 语法是兼容的 data() methods();
但是:
data() 中可以读取 setup 中的数据 用 this. 访问
setup() 中 读取不到 data() 中的数据
不要混用
4 setup 语法糖
setup 语法糖 减少了 setUp(){} 结构 并且可以自动将内部定义的数据和方法
进行rutern 暴露出去
但 对组件的暴露不能 写在 一个 script 里面
默认情况:组件名同文件名
1、新写一个 script 单独用于暴露组件
2、插件 vite-plugin-vue-setup-extend
需要配置插件到 vite.config.ts
5 响应式数据-基础
ref() : 基本数据类型响应式数据
在js 中需要通用 .value 进行操作和访问,模板中可以省略
reactive : 只能定义对象数据类型响应式数据,对象,数组 。。。
直接访问即可
注意:对 reactive 定义的变量重赋值会导致响应式失效
对象:此时需要通过 object.assign() 重新赋值
=》 carA = object.assign({},car) || object.assign(carA,car)
object.assign(o1,o2,o3,o4,...) 将 o1 后的所有对象数据解构到 o1 身上
数组:需要通过 数组方法 进行操作
6 响应式数据-ref 定义对象类型数据
ref() : 对象类型数据
注意:ref 定义的对象类型响应式数据需要通过 ref.value.xxx 访问
ref 定义的是一个 refimpl 对象
访问refimpl 对象的 value 属性
原理:ref 定义对象类型数据时,是通过 reactive() 方法将数据转换成 proxy 对象
最终:在定义响应式数据时,基本数据类型和不太深的对象数据 都用 ref
6 toRef,toRefs 将多个非响应式数据转变为响应式数据 refImpl
toRef(单个值),toRefs
toRef("21号"); toRef(Preson, 'name');
let { name,age } = toRefs(Preson);
对于 {} 对象,单纯创建一个 refImpl 对象
对于 reactive() 方法创建的 proxy 对象,需要通过 toRefs() 方法将 proxy 对象中的数据转换为 refImpl 对象
7 computed 计算属性
// 简写 仅可读
let fullName = computed(()=>{
return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + "-" + lastName.value;
});
// 完整 可读写
let AllFullName = computed({
get(){
return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + "-" + lastName.value;
},
set(val){
firstName.value = val.split("-")[0];
lastName.value = val.split("-")[1];
}
});
8 watch 监听 [ref对象(包含计算属性),getter方法(对象属性),响应式对象,数组(包含前三类)]
watch(监视内容,回调函数()=>{},监视属性{})
watch(sum,(newv,oldv)=>{ // 基本数据
console.log("");
},{deep:true,immediate:false});
watch(Preson,(newv,oldv)=>{ // 注意对于 reactive 定义的对象会自动开启强制的 深度监视
console.log("");
},{deep:true,immediate:false});
watch(return preson.age,(newv,oldv)=>{
console.log("");
},{deep:true,immediate:false});
watch([()=>preson.name,sum,Preson],(newv,oldv)=>{
console.log("");
},{deep:true,immediate:false});
9 ref绑定
原生可以使用 id 唯一指定页面元素,但在 vue 单页面应用中当前路由中涉及的所有组件都会加载到页面,此时可能会导致 id 重复,取值异常。
使用 ref 指定唯一页面元素,就算是在单页面中存在多个相同的 ref 值,vue 均可以正常取到相应组件的相应元素(ref 组件作用域)。
ref 普通html标签
<div ref="refDiv"></div>
let refDiv = ref(); // 必须同页面元素绑定的 ref 值完全相同
refDiv.value : 必须在组件挂载后才能取到值,对 ref 元素进行操作。
ref 组件标签
可以获取到子组件,但默认情况下获取不到子组件的数据需要,子组件自己使用 defineExpose({}) 暴露才能获取
此时父组件 可以使用 refxx.value.xx 获取子组件对于的值,方法......
并可以进行修改,会同步到子组件 【子-》父组件传值】
10 props 子组件接收父组件传值
<Preson234 :persoonList="presonList"></Preson234> 父组件绑定需要传的值
子组件使用: let props = defineProps(['desc','persoonList']) 接收,模板中可以直接使用,js中需要 props.xxx
withDefaults(defineProps<{persoonList?:Presons,desc:string}>(),{ // 限制接收类型 + 可以不传 + 默认值
persoonList:()=>{[{id:"张三",name:"wda",age:12}]},
desc:"adddd"
});
11 vue3 生命周期
setup() 创建时期 setup语法糖
onBeforeMount 和 onMounted 挂载时期
onBeforeUpdate 和 onUpdated dom更新时期
onBeforeUnmount 和 onUnmounted 组件销毁时期
路由生命周期
onActivated onDeactivated 对应缓存组件还有,组件激活,组件失活
12 hooks 组件封装
命名规范: useXXX
hooks 集中封装需求组件的属性、数据、方法 等
1、在 hooks 目录中新建对应组件的封装文件 useXXX (use 开头)
2、定义 function 进行暴露
3、使用 return 返回封装内部数据、方法、属性
4、在调用组件中引入 hook,并使用
let { dogList,getImg } = useDog();
let { sum,add } = useSum();
13 router 路由
路由配置
依赖 vue-router 组件依赖
定义
在 router 目录下 index.js 中定义 router
引入并全局挂载到vue实例 app.use(router)
引入
局部 :在需要使用的组件内部引入 (useRouter,useRoute)
全局 : export {} declare global {xxx}
在需要的地方直接 : let router = useRouter();
路由使用
<router-link></router-link>
<router-view></router-view>
使用
router-link : 使用 to 指定跳转
redirect : 路由重定向
参数
query : 指定参数 =》 {name:val,......}
注:query 传递参数时
params : 指定cans => {name:val,....} 。但要求路由定义为 path:/a/b/:name1/:name2
注:parms 传参时只能使用name
props:
布尔模式 : 设置 props:true
组件中通过 defineProps 获取。注:仅能获取 parmas 参数
函数模式 : 设置 props(route){return xxx}
组件中通过 defineProps 获取
对象模式 : 设置 props :{xxx:xx,xxx:xx}
组件中通过 defineProps 获取. 静态数据
函数路由
router.push(replace) 追加/替换
router.go(num) 动步跳转
组件缓存
缓存路由组件
KeepAlive
<!-- 使用 v-slot 结合 KeepAlive -->
<RouterView v-slot="{ Component }">
<KeepAlive>
<component :is="Component" />
</KeepAlive>
</RouterView>
<!-- 缓存一个组件 -->
<RouterView v-slot="{ Component }">
<KeepAlive include="MessagePage">
<component :is="Component" />
</KeepAlive>
</RouterView>
<!-- 缓存多个组件,使用:include -->
<RouterView v-slot="{ Component }">
<KeepAlive :include="['MessagePage', 'SettingPage']">
<component :is="Component" />
</KeepAlive>
</RouterView>
缓存组件生命周期
对于缓存的组件 UnMount Mount 组件生命周期不会生效
onActivated : 组件激活
onDeactivated : 组件失活
路由守卫
路由守卫
全局路由守卫:beforeEach,beforeResolve,afterEach [router.js 配置]
beforeEach((to,from,next)=>{}):路由跳转之前
beforeResolve((to,from,next)=>{}):路由解析之前。导航被确认之前执行
afterEach((to,from)=>{}):路由跳转之后
参数(to,from,next){} to:预跳转路由。from:当前路由。next():放行
路由独享守卫 :beforeEnter [router.js 的 具体路由项]
beforeEnter(to,from,next)=>{}
组件内守卫 : onBeforeRouteUpdate , onBeforeRouteLeave [组件内调用]
组件内部的守卫,它们只在当前组件的路由发生变化时触发
onBeforeRouteUpdate:在当前路由改变时被调用
onBeforeRouteLeave:在导航离开组件的路由时调用。可以用来阻止导航
路由模式
路由模式:
hash: # 及其后面的字符称为 hash,hash 的变化不会导致浏览器向服务器发送请求,因此页面不会重新加载。
history: 无 # 路由的切换是通过 HTML5 History API 实现的,这种方式可以实现 URL 的无刷新跳转
配置:[router.js] history: createWebHistory() / createWebHashHistory()
根路由:history: createWebHistory(import.meta.env.BASE_URL)
import.meta.env.BASE_URL : [vite.config.ts] 中配置 base
14 pinia 状态管理器
安装配置与基础使用
mian.ts 引入:import { createPinia } from 'pinia'; 创建:const Pinia = createPinia(); 挂载:app.use(Pinia);
配置目录 /store
新增对应的 store 模块 (单模块)
模块中新建 defineStore("模块名",{配置项[state.getter,actions]})
const useCounterStore = defineStore("counter",{
state:() => ({
count : 'pinia store'
}),
});
基础使用
import useCounterStore from '@/store/count'
const counterStore = useCounterStore();
{{ counterStore.count }}
console.log("counterStore.count:",counterStore.count);
修改 state 数据
一、直接修改:
counterStore.count += 1
二、通过 $patch 函数可批量修改
counterStore.$patch({
count:count.count -1
});
三、通过 actions 函数修改 (见以下)
actions 操作函数定义
定义
在 @/store/count 对应模块中定义 actions{} 区域
在其中定义方法函数 funcName(params){this} this 可获取到 state 中的数据
方法函数中可以定义 异步方法 或其他的 复杂操作
使用
counterStore.countAdd(5);
counterStore.CountDecrement(5);
getter 函数和 storeToRefs 响应式解构
定义
在 @/store/count 对应模块中定义 getters{} 区域
在 getters 方法函数中获取入参 (state){操作 state中的数据}
使用
doubleCount 可以同 state 中定义的数据一样直接使用
可以使用解构赋值 和 storeToRefs 可以简化数据操作并保证数据的响应式
let { count,doubleCount } = storeToRefs(counterStore);
$subscribe 监听数据变化
store.$subscribe((mutation,state)=>{})
mutation:一个包含变化信息的对象,它描述了状态是如何被改变的(比如通过直接设置、通过 action 修改等),但它不直接包含旧状态的值。
state:store 的当前状态的快照。
counterStore.$subscribe((mutation, state) => {
console.log('状态变化了:', mutation, '新状态:', state)
console.log('新的count:', state.count)
});
15 组件
组件切换
组件切换
v-if
component :is="指定活动组件"
组件传值
defineProps
默认传值:
withDefaults(defineProps<{person:IPerson, list?:IPerson[]}>(), {
list: () => [{ id: '100', username: 'haha', age: 14 }]
});
父->子组件传值
<HomePage num="2125" :name="pageName" :preson="preson"></HomePage>
通过调用的子组件给其通过 给子组件 绑定值传递
子组件通过 defineProps(['','']) 获取值
defineProps<{num:string,name:string,preson:Presons}>() ts中可以指定传递的值类型
v-bind/v-on/v-model
子->父组件传值
事件绑定传值
:bindName='funcName'
defineProps(['bindName'])
props.bindName(parames);
自定义事件传值
@bindName='funcName'
defineEmits(['bindName'])
emit('bindName',parames);
v-model 双向绑定传值(重要)
原理: v-model 绑定的属性,会自动绑定 v-bind 和 v-on,v-bind:value="value" v-on:input="value = $event.target.value"
例子:<HomePage v-model:list="list"></HomePage> === <HomePage :list="list" @update:list="list = $event"></HomePage>
<HomePage v-model:list="pageItem"></HomePage> === <HomePage :list="pageItem" @update:list="pageItem = $event"></HomePage>
子组件:
let emit = defineEmits(['update:list']); //相当于接受自定义事件
let props2 = defineProps(['list']); //接收父组件传值
emit('update:list',arr); //通过emit方法,将数据传递给父组件
ref 和 $parent
getCurrentInstance : 获取当前实例,vue3 中不能获取 this 的指向,需要通过 getCurrentInstance 获取
getCurrentInstance()?.proxy.$parent / getCurrentInstance()?.proxy.$refs
ref 和 $parent
ref(父组件中获取子组件实例[数据,方法,属性等]):
普通元素,获取普通页面元素的值(html)
组件: 获取组件的实例,通过实例可以获取组件中的数据、方法、属性等 (核心)
1、子组件定义属性、方法、数据;并通过 defineExpose 暴露出去。
2、父组件引用子组件并标记 ref 值;并且需要定义一个相同的 ref 值。let childRef = ref(null);
3、通过子组件 ref 实例调用
list.value = childRef?.value?.list //属性
childRef?.value?.addItem('vue') //方法
$parent(子组件中获取所有的父组件暴露的属性和方法数据)
1、父组件通过 defineExpose 暴露。
2、子组件通过 $parent 获取父组件暴露的属性和方法数据。
3、调用
import { getCurrentInstance } from 'vue';
let parents = getCurrentInstance()?.proxy.$parent;
parents.parentData = '通过 $parent 修改父组件值' ; parents.updateParentData('通过 $parent 调用父组件方法')
provide/inject
provide/inject (父->后代)暴露和引入传值
可以在任意后代组件 inject 获取父组件 通过 provide 暴露出的数据、属性、方法
使用:
1、父组件暴露 provide(暴露名)
provide('msg',msg);
provide('changeMsg',changeMsg);
provide('preson',preson);
2、后代组件引入并使用 inject('暴露名',默认值)
let msg = inject('msg');
let preson = inject('preson',{ id: '100', username: 'haha', age: 14 });
let changeMsg = inject('changeMsg',(val:string)=>{});
其他
1、attrs:通过内部 $attrs 可以实现父组件向后代组件传值;原理:vue3 中父组件向子组件传值,子组件未明确指定获取的值,会存放到 $attrs
2、mitt(任意):同vue2 的pubsub。通过下载并配置 mitt 插件,在组件中通过 mitt.emit('事件名',参数) 除非事件,通过 mitt.on('事件名',回调函数) 监听事件,mitt.off
('事件名') 取消监听事件
3、vuex/pinia(任意): 通过全局数据状态管理,共享属性
组件插槽
插槽:包括 默认插槽、具名插槽、作用域插槽
1、所有插槽都包含默认值 <slot>默认内容</slot> 当使用该插槽定义的组件时,如果没有传递内容,则显示默认内容
2、默认插槽因为不进行区分,因此在存在多个默认插槽时,会全部相同
默认插槽
默认插槽:默认值,多个默认插槽值相同
具名插槽(手动指定插槽) v-slot 可以简化
子组件定义插槽: <slot name="header">顶部区域</slot>
组件使用处:
<template v-slot:header>
头部具名插槽
</template>
<template #footer>
底部具名插槽
</template>
作用域插槽(指定插槽并可以操作插槽绑定的数据)
子组件定义插槽: <slot name="user" :users="users"> 其中:users 为子组件中定义的数据
组件使用处:
<template v-slot:user="scope">
<tr v-for="user in scope.users" :key="user.age">
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
</tr>
</template>
其他[事件修饰符、自定义指令、多环境配置]
事件修饰符
.stop 阻止事件冒泡:vue事件会从点击处向外层冒泡触发每一层绑定的相同事件
.prevent 阻止默认行为,阻止 a,input 等默认带的事件
.capture 事件捕获,从外到内依次触发每层的事件直到真实的事件触发点
.self 事件触发控制,当且仅当是元素本身触发事件时才会执行事件。会跳过冒泡和捕获(跳过非拦截)
.once 事件仅触发一次
<button @click.xxx>事件触发</button>
自定义指令
全局指令 (main.ts/js)
app.directive("zlmc",{})
参数1:指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,但是在调用的时候,必须在指令名称前加上 v- 前缀来进行调用。
参数2:是一个对象,这个对象身上有一些指令相关的回调函数,这些函数可以在特定的阶段执行相关的操作。
指令的回调函数有两个参数:
第一个参数:是指令所绑定的DOM元素,它是HTMLElement类型,通过这个参数,你可以直接访问和操作DOM。例如,你可以改变元素的样式、属性、或者监听事件等。
第二个参数:是一个对象,它包含了关于指令的详细信息,如指令的值、参数、修饰符等。
私有指令 (vXxxxxx) v 开头
const vDoubi = {
// 在每个函数中,第一个参数都是element,表示被绑定了指令的那个DOM元素,这个element参数,是一个原生的JS对象
// 第二个参数 binding 提供了关于指令绑定的信息。
beforeMount(element: any, binding: any) {
// 此时元素刚绑定了指令,还没有插入到DOM中去
console.log("执行beforeMount函数:", element, binding);
},
// mounted表示元素插入到DOM中的时候会执行mounted函数,只执行一次
mounted(element: { innerText: string; }, binding: { value: string; }) {
console.log("执行mounted函数:", element, binding);
element.innerText = "逗比: " + binding.value; // 初始化,前面添加上逗比
},
// 当模板中有数据更新的时候,即使不是当前指令用到的数据,都会执行updated,可能会触发多次
updated(element: { innerText: string; }, binding: { value: string; }) {
console.log("执行updated函数:", element, binding);
element.innerText = "逗比: " + binding.value; // 数据更新后,前面添加上逗比
},
}
多环境配置
项目根目录创建环境配置文件:
创建普通全局变量 .env
创建开发环境变量 .env.development
创建测试环境变量 .env.test
创建生产环境变量 .env.production
创建环境变量:
格式为 VITE_ 开头,格式是:VITE_变量名=值
使用环境变量:
import.meta.env.VITE_API_URL
预定于环境变量:
import.meta.env.MODE:自带的默认变量 development 、test 、 production
import.meta.env.BASE_URL:基础路径配置,对应vite.config.ts中的 base 配置项
指定启动环境:
在 package.json 文件中,你可以通过 --mode 参数指定构建或启动时使用的环境
"scripts": {
"dev": "vite --mode development", // 以开发环境运行
"dev:test": "vite --mode test", // 以测试环境运行
"build:test": "vite build --mode test", // 以测试环境打包
"build:prod": "vite build --mode production", // 以生产环境打包
// ...其他
},