Vue是一套用于构建用户界面的渐进式 JavaScript框架
渐进式 JavaScript框架 : 表示我们可以在项目中一点点来引入和使用Vue,而不一定需要全部使用Vue来开发整个项目;
常见的基础指令
v-once : 只执行一次
v-pre : 跳过该元素及其所有子元素的编译
v-once : 只执行一次
v-cloak : 用于隐藏尚未完成编译的 DOM 模板 搭配css [v-cloak] { display : none ;}
v-memo : 只有满足的值,才会重新渲染页面 [代码实例]
class的绑定[代码实例] 和 style的绑定 [代码实例]
v-on : 事件绑定 传递多个参数 -- 固定值 $event
v-on的修饰符 :
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyAlias} - 仅当事件是从特定键触发时才触发回调。
.once - 只触发一次回调。
.left - 只当点击鼠标左键时触发。 .right - 只当点击鼠标右键时触发。 .middle - 只当点击鼠标中键时触发。
.passive - { passive: true } 模式添加侦听器
v-if , v-else-if ,v-else 和 v-show
template元素可以当做不可见的包裹元素,并且在v-if上使用,但是最终template不会被渲染出来
区别 :
v-show是不支持template; v-show不可以和v-else一起使用;
v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有存在的,只是通过CSS的display属性来进行切换;
v-if当条件为false时,其对应的原生压根不会被渲染到DOM中;
如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show; 如果不会频繁的发生切换,那么使用v-if;
v-for : 循环
被监听的方法有 : push() , pop() , unshift() , shift() , splice() , reverse() , sort() [这些方法都会直接修改原来的数组]
不会修改原数组的方法,那么就直接替代它 : filter() , concat() , slice()
v-for中的key的作用 : 主要用在vue的虚拟Dom算法:在新旧VNode比较
(key用于虚拟Dom,而虚拟Dom又可以做新旧VNode对比,又会生产有虚拟Dom树 : 可以跨平台编译,可以增强性能)
v-model : 双向数据绑定[都用在表单]
它相当于 : v-bing绑定value属性的值 ; v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中
1.v-model 绑定 checkbox [代码实例]
2.v-model绑定 radio [代码实例]
3.v-model绑定 select [代码实例]
4.修饰符 :
.lazy : 会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发
.trim : 自动过滤用户输入的守卫空白字符
两个个Options Api :(computed,watch)
1.computed 计算属性
特点 : 1.有缓存 ; 2.在调用的时候不用加();
简写模式 : 跟函数写法一样
原始模式 : get 和 set [代码实例]
2.watch 监听器
简写模式 : 跟函数写法一样 , 想要监听那个,就在那个()
原始模式 : [代码实例]
特别模式 : $watch [代码实例]
全局组件 : [代码实例]
局部组件 : [代码实例]
脚手架
安装 : npm install @vue/cli -g
创建 : Vue create 项目的名称 / vite创建 npm init vue@latest
创建的信息 :
组件之间的通信
1.父传子: props
在组件上可以写一些元素, 子组件可以通过props来接收到
A: props的默认写法 : 数组 [弊端 : 不能对类型进行验证 , 没有默认值的] [代码实例]
B: props的对象写法-数组写法 : [代码实例]
C:当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的Attribute;
默认会将这些 attribute 会添加到子组件的根元素上
如果不希望组件的根元素继承 attribute ,可以在组件中设置 inheritAttrs: false;
****子组件想到得到这些属性可以用 $attrs 来接收 [代码实例]
2.子传父: $emit
子组件通过 $emit发出自定义事件,父组件接收自定义事件
普通形式 : [代码实例]
vue3新特性 : [代码实例]
3.祖孙组件的传递
祖先采用 : Provide 发送 一般都是写成函数形式
孙子采用 : Inject 接收
4.全局事件总线
vue3已经没有提供全局事件总线的off,$once方法了
这里我们采用这个库 : npm i hy-event-store
emit : 发送 on : 监听 off : 移除
使用的方法 : [代码实例]
插槽
子组件 用 标签来表示插槽 , 属性name 表示插槽的名字
父组件 插入 指定那个插槽 用 : v-slot:名字 简化 : #名字
作用域插槽 子组件通过在 标签上传递信息 , 父组件通过props接受
1.普通插槽 [代码实例]
3.作用域插槽 [代码实例]
零散知识点 :
1. $refs的使用 : 直接获取到元素对象或者子组件实例 [代码实例]
2.动态组件的使用 : 以前我们可以通过v-if来让哪些组件显示;现在有了动态组件了
动态组件 , 通过一个特殊的attribute is 来实现 : <component is=' '>
传值和监听事件都是一样的写法 [ is 的来源是子组件的name的值 ]
3.keep-alive 保存组件的状态,不会被销毁掉
3个属性 :
include - string | RegExp | Array。只有名称匹配的组件会被缓存;
exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存;
max - number | string。最多可以缓存多少组件实例,一旦达到这个数字,那么缓存组件中最近没有被访问的实例会被销毁;
使用了它后,会出现2个声明周期 : activated 和 deactivated
4 .分包 : 组件的分包可以通过 defineAsyncComponent 来实现异步组件 [异步组件就是为了可以分包使用]
5.组件的v-model : [代码实例] 不常用的!
6.mixin的基本使用
mixin的全局混入 :
vue3的 Composition API开始
setup 函数 : 它代替了 : options API 的 methods、computed、watch、data、生命周期等等
它的两个参数 : props 和 context
props : 是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取
context,我们也称之为是一个SetupContext,它里面包含三个属性 :
1.attrs:所有的非prop的attribute;
2.slots:父组件传递过来的插槽(这个在以渲染函数返回时会有作用,后面会讲到);
3.emit:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件);
返回值 : 可以在模板template中被使用, 也就是说我们可以通过setup的返回值来替代data选项;
第一个API : reactive
在 setup 中定义的数据提供响应式的特性,那么我们可以使用 reactive 的函数
1.我们使用reactive函数处理我们的数据之后,数据再次被使用时就会进行依赖收集;
2.当数据发生改变时,所有收集到的依赖都是进行对应的响应式操作(比如更新界面
3.事实上,我们编写的data选项,也是在内部交给了reactive函数将其编程响应式对象的;
reactive 函数 只能处理复杂类型 : object array
第二个API : ref
原因 : 因为reactive只能使用复杂类型,所以出现了 r ef 来使用简单类型(string,boolean,number) [而且 ref 也可以使用复杂类型]
使用 ref 返回的是一个 ref对象 ,我们的值是对象里面的value : 一般 ref对象.value
注意 :
1.在模板中使用,vue会自动帮我们解包,不需要 .value [解包 : 自动取出其中的value]
2.在setup中,需要 ref对象.value
3.ref 是一个浅层的解构 : 在template使用的时候可以不用.value , 在template修改一定需要.value [ 代码实例 ]
总结 : ref 和 reactive Api
reactive :
一般是应用于本地的数据 ;
多个数据之间是有联系的 ;
ref :
一般都是定义本地的一些简单数据;
定义从网络请求获得的数据 [ref对象.value = [请求数据] ]
第三个API :readonly
vue是一个单向数据流(规范) : 父组件传递的数据,子组件只能使用,不能修改;
原因 : 正常子组件却是可以修改父组件传递的数据的 ; 所以vue3产生了 readonly 来防止 子组件的修改行为
解决 : 如果子组件确实想修改 : 应该发送数据, 让父组件来修改 [这样做才知道哪个子组件来修改父组件的值]
错误实例 (没采用单向数据流) : [ 代码实例 ]
正确实例(采用readonly) : [ 代码实例 ]
注意 : 经过readonly 处理后的对象是不能被修改的 , 但是原本的对象我们可以修改
第四.Reactive判断的API 和 ref判断的API
isProxy
检查对象是否是由 reactive 或 readonly创建的 proxy。
isReactive
检查对象是否是由 reactive创建的响应式代理:
如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;
const a = reactive({name:'吴亦凡'}) isReactive(a) = true
const b = readonly({name:'吴亦凡'}) isReactive(b) = false
const c = readonly({a}) isReactive(c) = true
isReadonly
检查对象是否是由 readonly 创建的只读代理。
toRaw
返回 reactive 或 readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)。
shallowReactive
创建一个响应式代理,浅层的响应式,不对深层的监听。
shallowReadonly
创建一个 proxy,浅层的只读,不对深层的只读。
isRef
判断值是否是一个ref对象
shallowRef
创建一个浅层的ref对象[ref创建的对象,也是一个深层的响应式的]
triggerRef
手动触发和shallowRef创建的浅层的,变为深层的****
第五个API : toRefs 和 toRef
原因 : 我们通过reactive创建出来的数据,想要对它进行解构; 但是解构出来的值,就失去了响应式了
解决 : toRefs 就来解决失去响应式的问题
toRefs(值1) : 值1 : 对象
想要单独解构一个值 : 使用 toRef
toRef( 有值1和值2) : 第一个值是对象 , 第二个值是要解构对象中的哪个数据
第六个API : ref 获取元素和组件
想要获取,我们要在onMounted中,才可以获取到.因为 setup 出现的时候,template还没编译呢
第七个API : provide 和 inject 祖孙传递数据
provide(值1,值2) : 值1(提供的属性名称) ; 值2(提供的属性值)
inject(值1,值2) : 值1(provide提供的属性名称) 值2(可以提供默认值)
computed 计算函数
watch 和 watchEffect 监听函数
watch
1.watch 监听ref的简单类型(默认都是深度监听) [代码实例]
2.watch 监听ref的复杂类型(需要加上deep,才可以监听到,但是得不到原对象) [代码实例]
由于第二种情况,我们得不到值,这里建议监听复杂类型的,我们要使用reactive,不要用ref
3.watch 监听reactive (并且可以得到原对象) [代码实例]
watchEffect [ 代码实例 ]
1.watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
2.只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;
3.watchEffect会返回一个值,想要停止的时候,调用它就好了
生命周期函数
setup的语法糖
1.组件导入后,不需要用components 注册, 可以直接使用
2.顶层绑定会暴露给末班, 也就是不需要使用 return 了
3.接受父组件传递过来的东西 : defineProps 函数 [不需要导入,可以直接使用]
4.发送自定义事件 : defineEmits 函数 [不需要导入,可以直接使用]
5.defineExpose :
在setup的语法糖中,想要把方法,属性暴露出去,需要使用 defineExpose ,
暴露出去后,父组件可以通过 ref 来获取子组件,从而来调用子组件的方法和属性
vue的路由器
一般我们直接用脚手架选线的,就有给我们配置了
1.路由分包也叫路由懒加载 : 其实就是为了打包后分开,不合在app.js的包里面.所以才有懒加载,可以优化渲染速度.
懒加载可以给他们加上打包后的名称 : component: () => import( / webpackChunkName: "about" / '../views/AboutView.vue')
2.如何使用?
1. 来展示路由页面的
2. Home 来显示切换的 to属性指定的是路由的名称 ,这里还有一个隐藏的当前切换的class属性,可以自己找
3.路由的重定向 + 路由的name和meta属性 + 没有匹配到路由 + 子路由 [ 代码实例 ]
4.动态路由 -- useRoute - params [ 代码实例 ]
5.编程式路由 -- useRouter - query [ 代码实例 ]
叫做编程式路由 : 就是因为实在script中写到代码,所以才这样叫编程式路由
router.push : 跳转
router.replace :跳到新页面(没有历史记录)
router.back() 返回
router.forward() 前进
router.go(数字) 正数 : 进, 负数 : 退
6.动态添加路由
一般都是在后台管理系统出现,目前后台管理系统有2种做法 :
1.根据不同角色,生产不同的菜单 ; 对应的菜单的路由还是有注册的
2.根据不同角色,生产不同的菜单 ; 菜单和路由都是动态注册的 === 这就应用到了动态添加路由
添加路由 :router.addRoute
删除路由 :router.removeRoute
检查路由是否存在 : router.hasRoute()
获取一个包含所有路由记录的数组 : router.getRoutes()
// 1.动态管理路由
let isAdmin = true
if (isAdmin) {
// 一级路由
router.addRoute({
path: "/admin",
component: () => import("../Views/Admin.vue")
})
// 添加home,下面的vip子页面
router.addRoute("home", {
path: "vip",
component: () => import("../Views/HomeVip.vue")
})
}
7.路由导航守卫
to : 进入的页面。from :离开的页面
beforeEach
// 路由导航守卫
// 进行任何的路由跳转之前, 传入的beforeEach中的函数都会被回调
// 需求: 进入到订单(order)页面时, 判断用户是否登录(isLogin -> localStorage保存token)
// 情况一: 用户没有登录, 那么跳转到登录页面, 进行登录的操作
// 情况二: 用户已经登录, 那么直接进入到订单页面
router.beforeEach((to, from) => {
// 1.进入到任何别的页面时, 都跳转到login页面
// if (to.path !== "/login") {
// return "/login"
// }
// 2.进入到订单页面时, 判断用户是否登录
const token = localStorage.getItem("token")
if (to.path === "/order" && !token) {
return "/login"
}
})
其他的导航守卫 : 地址
完整的导航解析流程:
1.导航被触发。
2.在失活的组件(同个组件 == 动态路由)里调用 beforeRouteLeave 守卫。
3.调用全局的 beforeEach 守卫。
4.在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
5.在路由配置里调用 beforeEnter。
6.解析异步路由组件。
7.在被激活的组件里调用 beforeRouteEnter。
8.调用全局的 beforeResolve 守卫(2.5+)。
9.导航被确认。
10.调用全局的 afterEach 钩子。
11.触发 DOM 更新。
12.调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
状态管理工具 : vuex 和 pinia
能用 pinia 尽量别用 vuex
vue2 + vuex
state(数据) : [ 代码实例 ]
getters(计算属性) : [ 代码实例 ]
参数1:state 参数2:getters
mutations : [ 代码实例 ]
修改 state 的数据,必须通过 mutation 来修改 [ 只写同步操作!!! 想发送异步的 : 采用actions]
mutations 参数1:state的 参数2.传递过来的数据
mutations的出现也是为了 devtools 来捕捉快照
actions(方法) : [ 代码实例 ] 推荐使用基本,不用映射
action 类似于 mutation,不同于 :
action提交的是mutation,而不是直接变更状态;
action可以包含任意异步操作
在store里面通过 commit 来提交 mutation
在使用的时候通过dispatch来提交action
modules : [ 代码实例 ]
1.使用state时, 是需要state.moduleName.xxx
2.使用getters时, 是直接getters.xxx
3.派发事件时, 默认也是不需要跟模块名称
4.提交mutation时, 默认也是不需要跟模块名称
vue3 + vuex [不推荐使用!!!,vue3推荐pinia]
state(数据) : [ 代码实例 ] 推荐使用基本,不用映射
getters(计算属性) : [ 代码实例 ] 推荐使用基本,不用映射
参数1:state 参数2:getters
mutations : [ 代码实例 ] 推荐使用基本,不用映射
修改 state 的数据,必须通过 mutation 来修改 [ 只写同步操作!!! 想发送异步的 : 采用actions]
mutations 参数1:state的 参数2.传递过来的数据
mutations的出现也是为了 devtools 来捕捉快照
mutations 在使用的时候通过 commit('') 来提交
actions(方法) : [ 代码实例 ] 推荐使用基本,不用映射
action 类似于 mutation,不同于 :
action提交的是mutation,而不是直接变更状态;
action可以包含任意异步操作
在store里面通过 commit 来提交 mutation
在使用的时候通过dispatch来提交action
发送网络请求 : [ 代码实例 ]
modules : [ 代码实例 ]
1.使用state时, 是需要$store.state.模块名.xxx
2.使用getters时, 是直接$store.getters.xxx
3.派发事件时, 默认也是不需要跟模块名称
4.提交mutation时, 默认也是不需要跟模块名称
在模块中的话,getters,会有第三个参数 : rootState 得到的是根的state
getters: {
doubleCount(state, getters, rootState) {
return state.count + rootState.rootCounter
}
},
vue3 + pinia [强烈推荐使用]
没有mutations ; ts支持 ; 不用module ; 非常简单好用
npm i pinia
创建和使用 : [ 代码实例 ]
state(数据) - getters(计算属性)
重置方法 : $reset
修改信息 : $patch
actions(方法) : [代码实例]
axios网络请求库 npm i axios
2. get请求(params) 和 post请求(data) 的 2种方式 : [ 代码实例 ]
3.创建axios实例 [ 代码实例 ]
原因:
当我们从axios模块中导入对象时, 使用的实例是默认的实例;
当给该实例设置一些默认配置时, 这些配置就被固定下来了.
但是后续开发中, 某些配置可能会不太一样;
比如某些请求需要使用特定的baseURL或者timeout等.
这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息.
4.请求和响应拦截器
axios.interceptors.request.use(请求成功拦截, 请求失败拦截)
axios.interceptors.response.use(响应成功拦截, 响应失败拦截)
5.项目操作 => (axios请求库封装)