小南再谈Vue(Q&A)
之前看着文档,迷迷糊糊的学了一遍 Vue,现在有了些自己的理解,但自认为目前还是小白,不是很懂,理解大都来自自学,如有不当,恳求指出。🥳🥳🥳
附个链接,之前的 vue 梳理 - 上,之前的 vue 梳理 - 下。
vue 的生命周期
简单来说就是创建 -> 挂载 DOM -> 更新数据 -> 销毁的这么一个过程,按自己理解的话,可大致理解为:
- beforeCreate:vue 实例的挂载元素 el 和数据对象 data 都还没有进行初始化,还是一个 undefined 状态;
- created: 此时实例的数据对象 data 已经有了,可以访问里面的数据和方法,el 还没有,也没有挂载 dom(一般在这时就可以绑定事件处理函数了);
- beforeMount: 此时 el 和数据对象都有了,只不过在挂载到虚拟的 dom 节点上;
- mounted: vue 实例已经挂在到真实的 dom 上,可以操作 dom 节点;
- beforeUpdate: 这时响应式数据更新开始调用,(数据是新的,页面是旧的)
- updated:数据更新完成,新的 dom 已经更新;
- beforeDestory: vue 实例在销毁前调用,在这里还可以使用;
- destoryed:所有事件监听器会被移除;
什么是 MVVM,原理
MVVM 分别代表:model 数据源,view 为组件视图,viewModel 将 model 和 view 关联起来,是一个层级模型,简单理解就是 V 和 M 的联调器;
响应式原理 :实际就是通过 Object.defineProperty 方法,对于实例数据被读取就会调用 get 方法,当修改数据时就自动调 set 方法,检测到数据更新了通知观察者 Watcher,生成新的 Dom 树,对比新旧 DOM 结点,通知对应节点进行数据新。
其实是这么个情况: M(data)要该改变 V(template) 或者 V 更改 M 数据同步更新,是通过 VM 的双向数据绑定操作操作的,V 和 M 对 VM 都是双向通道,VM 通过双向数据绑定把 V 层和 M 层连接了起来,所以可以同步更新。
简单说就是:数据变化更新视图,视图变化更新数据。
提一句:
单向绑定 v-bind
:数据只能从 data 流向页面;
双向绑定 v-model
:数据可以从 data 流向页面,也可以从页面流向 data;
Vuex 是什么
还没怎么用过,大致理解为:专为 Vue.js 应用程序开发的状态管理模式,在中大型项目中使用;
v-show 和 v-if 有什么区别
v-if是操作DOM元素是否删除来显示隐藏的,这有可能会引发重绘或重排的问题。
v-show则是不管条件怎样都会通过display:是否为none ,来渲染DOM进行隐藏显示,这只是对css进行操作,没有删除DOM
所以,根据v-if
会删除 DOM 元素特性可知,这适用于不需要频繁切换条件的场景。v-show
则适用于需要频繁切换条件的场景。
为什么 v-for(必须要有 key) 和 v-if 不建议用在一起
可以那样使用,但不建议,因为 for 的优先级比 if 高,如果 if 要筛选的数据比较少,但要遍历的数组很大,就要都循环,这就造成性能浪费,如果条件可以,建议用 computed;
1 | <div v-for="(item,index) in itemList" v-if="condition"></div> |
像这个例子,是先进行遍历在判断,但实际业务情况往往是下面这种写法,先判断是否存在在遍历。
1 | <template v-if="condition"> |
vue2 组件传值
1.父子传值 props
父组件引入子组件后,在 data 中定义所传参数,在子组件标签直接传递,子组件通过 props 接收即可
1 | <!-- 父组件 --> |
2.子父传值:$emit(’事件名’,’数据’)
在传值组件定义个自定义事件名称,接收组件通过@接收在触发即可
1 | //传值组件 |
3.兄弟传值:在 Vue 原型上再定义个新实例,通过$emit和$on 传递
1 | //1.新定义个实例(好几种方法) |
4.爷孙传值 provide / inject
provide
:可以让我们指定想要提供给后代组件的数据或方法
inject
:在任何后代组件中接收想要添加在这个组件上的数据或方法
但是!!!
在传值组件中,provide:{ }这样使用不能获取 methods 中的方法,provide(){ return{} }使用不能获取 data 中的属性。
1 | // 1.在传值组件中 |
4.slot 插槽传值了,hhhh
引入插槽后直接点出插槽数据即可
1 | // Child.vue |
方法肯定不止上述这些,笔者只是写了日常中比较常用的方案,更多请移步掘金。
vue3.2 组件传值
1.父子传值,props
很重要的一个 API,贴个超链,官方介绍
跟 vue2 差不多,只是语法变了,在 <script setup>
中必须使用 defineProps
API 来声明 props
,不要要额外引入。
1 | //父组件 |
2.子父传值,emits
还是跟 vue2 差不多,以上述为例子
1 | <div>父组件:{{ message }}</div> |
这里可以用 v-model
语法糖实现相同效果,我觉得其本质还是使用了 emits 传值
1 | <Child v-model="message" /> |
3.子父传值,expose / ref
这个比较简单,一个把数据暴露出去,一个 ref 拿就好
子组件可以通过 expose
暴露自身的方法和数据,父组件通过 ref
获取到子组件并调用其方法或访问数据。
是不是有点像 vue2 的 provide / inject
, 哈哈
1 | <div>父组件:拿到子组件的message数据:{{ msg }}</div> |
4.Vue3 当然可以使用插槽,用法和 Vue2 一样
5.provide / inject,用法和 Vue2 一样
6.总线 Bus,用法和 Vue2 一样
computed(计算属性) 和 watch (监视属性)区别
computed:要用的属性不存在,要通过已有属性计算得来,他底层借助了 Object.defineProperty 方法提供的 getter 和 setter,如果 computed 的内容用于 v-model 的话,必须要有 set 和 get 函数,否则会报错!
watch:当被监视的属性变化时,回调函数自动调用,进行相关操作;
computed 有缓存,且依赖其他属性值,其他属性值不变的话,computed内容不会重新计算
watch 没有缓存,一般用于数据监听回调;
可以这么记:computed 所完成的功能,watch 也可以完成,反之不然。
v-bind(: )、v-model 和 v-on(@) 的区别
v-bind 一般用来设置 HTML 的属性和传递数据,看官方文档的例子吧
v-model 一般在表单中使用,实现双向数据绑定的,在表单元素外不起使用,语法糖
v-on 绑定事件监听器。
消息订阅
发布订阅模式,一种组件间通信的方式,适用于任意组件间通信;
可以想象成食堂打饭,打饭的同学是订阅方,打菜阿姨是发布方。
1 | //安装pubsub |
vue 脚手架配置代理
方法一:一种简单的配置代理方法,默认端口 8080;不能配置多个,如果请求了前端不存在的资源时,那么该请求会转发给服务器
1 | //在vue.config.js中添加如下配置: |
方法二:编写 vue.config.js 配置具体代理规则:
1 | devServer:{ |
插槽
基本使用: 写个插槽文件,在需要的地方 import 插槽名字 from 路径 导入,slot 的内容会被更新。
v-slot: 可以使用#替换
1 | <!-- MySlotCpn组件 --> |
具名插槽: 在插槽文件设定 name,引用时 v-slot:对应名字即可。
一个不带 name 的 slot,会带有隐含的名字 default
1 | <!-- NavBar组件 --> |
动态的插槽名: 给 name 绑定:,引用时在通过 [] 解构
1 | <!-- 组件里的name是通过props动态绑定的 --> |
还有个作用域插槽
大体就是,父组件想用插槽组件的数据,可以在调用的时候通过
1 | //插槽组件,将数据传递给调用方 |
路由
首先前端路由有 hash 和 history 两种模式,最直观的区别就是 hash 的 URL 中有丑丑的#符号。
hash 模式:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。(本地项目启动,通常是 hash 模式)
history 模式:利用了 HTML5 History Interface 中新增的 pushState()
和 replaceState()
方法,这两个方法应用于浏览器的历史记录栈,在当前已有的 back
、forward
、go
的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
浏览器 url 信息一般存储在 window.history.state
中,
下面说下 history 模式的 history.pushState()
方法,它相较于 hash 直接修改#后面参数具有以下优势:
- hash 模式只能修改# 后面的部分,因此只能设置与当前 URL 同文档的 URL,而 history 则可以修改任意同源 URL;
- 新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
- pushState() 可额外设置 title 属性供后续使用;
1 | //下载 npm vue-router |
值得注意的是,路由组件通常存放在 pages 文件夹,一般组件通常存放在 components 文件夹。多级路由使用 children 配置项,
路由的 query 参数
1 | //跳转并携带query参数,to的字符串写法 |
路由命名
在多级路由跳转的时候,可以 name:XX 对路由命名children: [ { path: '/welcome', component: Welcome,name:good } ]
然后通过命名跳转: router-link :to="{name:'hello'}">跳转</router-link>
params 参数
跟命名差不多,在 children: [ { path: 'detail/:id/:title', component: Welcome,name:good } ]
path 中声明占位符接收 params 参数,然后 通过下例跳转。
1 | //传递参数 |
另外:路由携带 params 参数时,若使用 to 的对象写法,则不能使用 path 配置项,必须使用 name 配置项。
除此之外,还有 编程式路由导航,可不借助
编程式路由导航
1 | //$router的两个API |
缓存路由组件
他可以让不展示的路由组件保持挂载,不被销毁。
1 | <keep-alive> |
路由懒加载
如果不懒加载,打包后文件很大,进入首页需要加载的东西很多。
将路由对应的组件分割成不同的代码块,访问时加载对应组件。
1 | //传统 |
ref 属性
他是用来给组件注册引用信息的,应用在 html 上是获取 DOM 对象,应用在组件上是获取实例对象。
使用: <h1 ref="xxx"></h1>或<school ref="xxx"></school>
获取: this.$refs.xxx
props 配置项
一般用于父子传值,让组件接受外部传过来的数据: <Demo name="xxx">
接受数据:
- 只接收
props:[]
- 第二种方式(限制类型):
props:{name:String}
- 第三种方式(限制类型、限制必要性、指定默认值):
props:{ xxx:{type:String, required:true, default:"xxx" }}
组件的自定义事件(子 ->父通信)
使用场景:A 是父组件,B 是子组件,B 想给 A 传数据,那么就要在 A 中给 B 绑定自定义事件,事件的回调在 A 中
绑定自定义事件:
2.1 在父组件中:<Demo @自定义事件名="回调函数名">
;
2.2 在父组件中:javascript1
2
3
4
5<Demo ref="子组件名">
mounted(){
this.$refs.子组件名.$on("自定义事件名",this.回调函数名)
}2.3 若想让自定义事件只触发一次,可以使用
once
修饰符,或$once
方法触发自定义事件:
this.$emit("自定义事件名",数据)
解绑自定义事件:
this.$off("自定义事件名")
这里顺带提一下事件修饰符:prevent:阻止默认事件,stop:阻止事件冒泡事件,capture:使用事件的捕获模式;
再说下事件基本使用注意事项吧:事件的回调如果配置在methods对象中,最终会在vm上(this指向vm或组件实例),在methods使用箭头函数的话,this就不是vm了;
聊下vite打包
鉴于vite的启动速度和热更新,现在写demo用vite创建了,简直不要太香,速度嘎嘎的。但为什么相较于cli脚手架,webpack这些,速度提升那么多,好奇之余查了下,主要的原因是浏览器以前不支持ES module
,现在支持,vite利用这一特性(给script 标签加上 type="module"
属性),将打包的活一部分交给交给浏览器,分担了压力。
vite主要对依赖和源码进行打包:依赖使用Go语言编写 的esbuild
进行预构建,以及将零散的文件整合到一起,减少网络请求和配置 ESM 模块语法来契合浏览器支持。源码的话采用按需加载的,这样的好处是用到的组件模块才进行打包。
热更新块是因为,Vite 同时利用 HTTP头来加速整个页面的重新加载,源码的请求会进行协商缓存,而依赖则会通过设置 Cache-Control
进行强缓存,所以项目一旦被缓存下来,下次相同资源将不需要再次请求,所以热更新秒快。
vite在开发模式下广受好评,但在生产模式下,有两种声音,一是有很多小问题,不好配置,然后求稳选择webpack;二是拥抱新技术,还好,大项目都在用,每没出现啥大问题,具体啥情况,我不清楚但保持乐观的态度 …
结束语:加油!努力沉淀变成光吧!