vue 问题整合
文章目录
- vue 问题整合
- @[toc]
- vue 组件代码规范
- process.env
- vue-router
- Vue 全局变量注入
- watch 介绍
-
- :title 与 tooltip
- Vue v-bind $attr
- Vue beforeRouteEnter beforeRouteleave
- v-model vs :value and on-change
-
- vue-cli
-
- html 标签的 target 属性
-
- vue.set 使用
- media Query
- vue 行内样式设置 hover
-
- vue css scoped module
- vuex使用
-
- 动态组件 component
- 混入
- created和mounted生命周期函数的区别
-
- computed 计算属性
- template标签的使用
- vuex使用
-
- vue清理console
- vuex 修改数据
- vue3
-
- Vue-i18n
文章目录
- vue 问题整合
- @[toc]
- vue 组件代码规范
- process.env
- vue-router
- Vue 全局变量注入
- watch 介绍
- :title 与 tooltip
- Vue v-bind $attr
- Vue beforeRouteEnter beforeRouteleave
- v-model vs :value and on-change
- vue-cli
- html 标签的 target 属性
- vue.set 使用
- media Query
- vue 行内样式设置 hover
- vue css scoped module
- vuex使用
- 动态组件 component
- 混入
- created和mounted生命周期函数的区别
- computed 计算属性
- template标签的使用
- vuex使用
- vue清理console
- vuex 修改数据
- vue3
- Vue-i18n
本文主要整合了自己在平常开发过程中遇到的一些vue相关的问题的总结
2020/04/19 add生命周期钩子函数
2020/04/20 add混入
2020/04/21 add 环境变量
2020/04/22 add 动态组件component
2020/05/06 add vuex使用
2020/05/28 add vue css scoped module
2020/06/01 add vue 行内样式设置 hover
2020/06/02 add media Query
2020/07/07 add vue data 新增 property 响应式
2020/07/28 add html 属性,使用 target 控制地址跳转
2020/08/10 add vue-cli + webpack configure
2020/08/10 add vue-cli + webpack configure
2020/09/15 add v-model vs :value and on-change
2020/11/04 add vue beforeRouteEnter beforeRouteleave
2020/12/12 add Vue.set this,$set
2020/12/20 add :title 与 tooltip
2021/02/10 add watch 介绍
2021/03/22 add process.env
2021/07/12 add props 与表单结合
2021/08/06 add vue 初始化顺序
2021/08/06 add vue 注入全局变量
2021/08/15 从 code review 中获得的经验
2021/08/18 add vue-router
2021/10/11 add eventBus vs vuex
2021/10/14 add vue3 学习之 composition API vs options API
2021/12/05 add vue 修改数据
2021/12/05 add vue 语言国际化
2022/01/15 add vue why app.js is so big
vue 组件代码规范
name
components
props
data
computed
watch
created
beforeDestroy
methods
process.env
我们可以修改环境变量,通过这种方式对不同环境的显示进行开关控制,开发中发现可以在 data(){} computed 中读取 process.env 但在模板中无法取到,这是什么原因呢?
如上我们知道 NODE_ENV, BASE_URL, and variables that start with VUE_APP_ will be statically embedded into the client bundle with webpack.DefinePlugin 但事实上文档中并未标明只能在 js 中使用,无法在模板中使用,因此如果想在模板中直接使用 process.env 需要做一个 hack 将其挂载在实例属性上即可
// main.js
Vue.prototype.process = Object.freeze({
env: process.env,
})
vue-router
一个网站可能不能在一个项目完成所有的开发,我们让多个项目衔接起来的方式可以通过 NGINX 配置完成,同时每个项目都是一个 SPA。在开发中遇到了一个问题:在 A 项目中想使用 $router.push 跳转到 B 项目的路由下,以失败告终。
Vue.js redirection to another page
Configuring Vue Router for a Single Page App
$router
的由来实际上就是 html5 history api, 它的出现为单页应用的出现提供了基石,因此如果想实现从 A 项目路由跳转到 B 项目的路由直接使用 location.href
即可。
Vue 全局变量注入
官方文档不再赘述:
https://cn.vuejs.org/v2/cookbook/adding-instance-properties.html
上一个 case 中我们 hack 了 process.env 让它能够在模板中使用,这一点其实不推荐毕竟官方没这么做。
watch 介绍
我们知道 watch 可以监听属性的变化,如 data 中的属性,state 中的值,在开发中有些场景发现 watch 无法监控到这些值的变化,是因为什么呢?
references:
watch 不起作用的原因
- 监控了原对象上不包含的属性
vue的绑定原理是建立在Object.defineProperty的set和get方法上的。给某个属性绑定set和get之后,只有改变该属性本身时,才会触发set和get的回调函数,而改变其内部属性时不会触发。
但实际上这个问题有解决方法就是添加 deep
参数即可。
- 组件已经被销毁,自然监听不到值的变化。
比如我的 case 里面就是由于加入了一个 loading 变量,而由于 v-if v-else 的使用导致组件重新被 mount 出现了监听不到预期的值的现象
:title 与 tooltip
当然 title 并不是 Vue 的特性,而是 html 标签的特性。其实 title 的表现效果就是如 tooltip 一般,但就是反应较慢,但鉴于使用方便,此处给予记录。(从代码优雅性上看还是用 tooltip 组件更好
Vue v-bind $attr
特别适用于写基础组件时,这个模式允许你在使用基础组件的时候更像是使用原始的 HTML 元素,而不会担心哪个元素是真正的根元素
Vue beforeRouteEnter beforeRouteleave
最近做需求有这样一个场景,进入一个页面启动一个 setTimeout,离开此路由后就停用 timer,显然 vue-router 中的 beforeRouteLeave 可以满足这个场景。同时它也可以用于在用户离开服务前是否提醒其进行保存等,而保存的内容
有以下几种方式进行存储: localStorage 或直接传给后端存储。
同时遇到这样一个 beforeRouteLeave 中的代码不执行的问题,原来在子组件中写这个方法会不执行,解决方式可以参考 https://stackoverflow.com/questions/42045433/vue-router-beforerouteleave-doesnt-stop-subcomponents
v-model vs :value and on-change
近期再使用组件的时候有这样一个场景:根据 state 中的值给组件赋值,同时期望用户手动使用该组件的时候触发事件。由此引出这个问题:
v-model 原理
官方文档中给出了自定义 v-model 的示例(https://cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model)由此也可以推断出 v-model 的本质:(双向数据绑定的原理此处不再赘述)
<input v-model="test">
<!--本质上就是如下语句:-->
<input :value="test" @input="test = $event.target.value">
- 方法 1:然而针对上面我们提到的场景,我们更希望在 @input 中做一些其他的处理,因此此处也就是这种场景下更适合使用 :value+on-change 的组合
- 方法 2:使用 v-model 和 state 结合 vuex中获取的数据使用 v-model 绑定
props vs v-model
场景:子组件 a 中含有一个表单,父组件 A 希望通过 props 将初始值传给子组件 a, 但显然表单的 v-model 不能和 props 绑定,否则将会遇到这种报错
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value.
首先撇开上面场景不谈,子组件中有表单的情况最佳是直接将 a 中的 data 中的值与 v-model 绑定,那其实再结合上面的提示,我们可以直接在声明 data 时将 props 传给 data: 如官方文档: https://vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
vue-cli
详细配置可以参考
而其中关于 configureWebpack
和 chainWebpack
的区别,前者用对象或者函数的方式去修改 webpack 配置,后者是通过链式操作的方式
注意:英文版文档更全面(这意味着中文版缺失了部分选项
why app.js is so big
最近将前端项目部署在开发机上并以开发模式启动后,在办公网下访问结果发现异常慢,打开控制台发现
app.js
体积有 20M+ 其实本质是因为development
模式下启动时没有任何的 optimization
Vue Cli 3 Bundle Size is too large ! ( app.js is now 8 MB)
vue-cli 模式和变量🌟🌟🌟🌟🌟
Quick guide to webpack bundle and code splitting with React🌟🌟🌟🌟
目前很多 Vue 项目都是使用 vue-cli 直接搭建,默认情况下NODE_ENV=development
创建一个 webpack 配置,该配置启用热更新,不会对资源进行 hash 也不会打出 vendor bundles,同时包含 webpack 的 HMR client ,这样 app.js 就异常大,目的是为了在开发的时候能够快速重新构建。
注释:vender bundles 包含了项目依赖的所有框架和库。通过将所有这些代码构建到一个 bundle 中,客户端可以有效地缓存捆绑包,并且您只需要在框架或库更新时重建捆绑包。
html 标签的 target 属性
标签 target 属性
- 使用
rel="noopener"
预防target="_blank"
漏洞
vue.set 使用
reference:
结论: 如果想更新已经在 data
中定义的数组或对象中添加新字段需要使用this.$set
来达到视图的更新。
export default {
data(){
return {
items:{
a:1,
}
}
}
}
// this.items.a=2; 此时是响应式的
// this.items.b=2; 此时不是响应式的
- 原因:由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除因为Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
- 因此此时应该用 set 方法为其新增一个响应式的属性:Vue.set( target, propertyName/index, value )
media Query
最近在工作中遇到了一些需要适配不同设备的开发,特此记录一下不同设备的区分
Media Queries: How to target desktop, tablet, and mobile?
vue 行内样式设置 hover
最近工作中有这样一个场景「利用v-for渲染了div,同时每个div的颜色是通过数据来改变的,即:设置为了行内样式,行内样式优先级最高,此时用 less 或 css 设置 hover 的 background-color 就会失去意义」
related work
- 通过组件内定义一个状态,监听 mouseover mouseout 事件对其进行改变,相应的
更新内联样式与之类似的就是监听 mouseover mouseout 事件动态添加 div 的 class 名称。不过这种方式仍然无法解决问题,仍然因为行内样式具有最高优先级
resolved
变通思路,通过数据变化来更新 div 的 class 名,在 css 中定义样式,此时再写 hover 的相关样式就不会有被高优覆盖的情况了。
vue css scoped module
除了官方文档中有提到 scoped 会有性能上的影响外,在大型项目中使用 module 有利于快速定位。
什么是 postCSS?
vuex使用
mapState
mapGetters
一般放在组件的computed
属性中mapMutations
mapActions
一般放在组件的methods
属性中
vuex mutation change state
我们已经知道不能直接修改 state,只能通过 mutation 修改。对于 object 类型的 state,我们在文档中也看到可以使用
Vue.set
或者对象解构替换的方式进行,当然采用如下这种利用浅拷贝特性的方式也可行。(如果不可行需要自行 check 一下对象传递过程中的其他问题)
state: {
test: {
a: 1,
b: 2,
}
},
mutations: {
CHANGE_TEST(_state, payload) {
_state.test = {
..._state,
a: 2,
}
},
CHANGE_TEST(_state, payload) {
let tmp = _state.test
tmp.a = 2 // it will change state.test.a
}
}
loading 加载时机与 vuex dispatch action
这里也在「FE 每周问题总结」中进行过同下整理
为什么将 loading 加载时机这种与业务强相关的内容和 vuex 联系起来呢,且看下面的分解
使用 loading 我们可以解决在 vuex 中设置了初始值 state,但通过 dispatch action 更新 state 这期间造成的“页面闪烁”问题。那么我们到底具体什么位置写 loading 呢?
- 最好是 store 中,将 loading 作为 state 的一个变量,在 action 中通过 mutation 修改
- 其次是在组件内部(适用于除了处理 loading 还要处理组件内部一些其他非公用 store 逻辑的场景),使用 mapActions 本质还是使用 store.dispatch,而 dispatch 本质就是 promise,我们可以在 promise.finally() 中处理 loading 逻辑
动态组件 component
渲染一个“元组件”为动态组件。依 is
的值,来决定哪个组件被渲染。
更多的可以参考我在github上的项目:vue_prac
混入
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
created和mounted生命周期函数的区别
vue中created钩子函数与mounted钩子函数的使用区别
created
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer), 属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
=====>
模板还没有被渲染成 html,也就是这时候通过 id 什么的去查找页面元素是找不到的
那么随之而来有一个问题,到底 props 先被注入到实例中还是 data 呢,当然前文
props vs v-model
中我提到了直接在 data 中使用 props 的案例,则答案不言而喻了.
执行顺序 beforeCreate
inject -> Props
-> Methods
-> Data
-> Computed
-> Watch
->provide
-> created
mounted
el 被新创建的 vm. e l 替 换 , 并 挂 载 到 实 例 上 去 之 后 调 用 该 钩 子 。 如 果 r o o t 实 例 挂 载 了 一 个 文 档 内 元 素 , 当 m o u n t e d 被 调 用 时 v m . el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm. el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当mounted被调用时vm.el 也在文档内。
=====>
mounted钩子函数一般是用来向后端发起请求拿到数据以后做一些业务处理。这时候vue模板已经渲染完毕。因此,Dom操作一般是在mounted钩子函数中进行的
computed 计算属性
通常来讲计算属性会在初级面试中和methods进行对比,计算属性更加的"智能",它总是根据它依赖的值的变化而变化,因此也基于这个原因,为了避免出现死循环,通常不建议在computed计算属性中修改data中的值
template标签的使用
官方的解释中:
一个字符串模板作为 Vue 实例的标识使用。模板将会 替换 挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽。
也就是说个人觉得可以认为是在标签里面又插进来一个模版。
可以首先参考这篇文章:
Vue中的template标签的使用和在template标签上使用v-for
vuex使用
关于vuex的讨论已经是老生常谈了,但因为好久不用未免生疏,个人觉得理解起来一定要从原理入手
state===>获取状态
mapState不同用法
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
// 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
// 而下面这段代码的意思是:b:state=>state.a.b
...mapState('a', ['b']),
getter===>类似于computed计算属性 依赖于state的变化
mutation===>
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
// mutations必须是同步函数
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
// 你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
action===>
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
vuex vs eventBus
我们知道 Vue 中的全局状态管理有 vuex 以及 eventBus,正如 Vue 核心开发者所言,通常我们会选择 vuex 而避免使用 eventBus,那么具体是什么原因呢,我们到底在什么场景下使用 eventBus 更好呢?
Clarification on not using global event bus
Vue.js: why event bus is bad idea
使用 eventBus 有如下的一些缺点:
- 事件名的维护成本
- 无法知道哪个组件正在发送事件,同时任何人都可发送事件
- 在组件中发送事件必须确保该组件 dom 存在,才能监听事件消息,这会让逻辑变复杂
相对于以上,vuex 有命名空间等更具有在状态管理方面的优势,当然 eventBus 也有一些可以使用它的场景,比如在处理全局的长连接错误上即可以全局的分发。
vue清理console
references:
babel-plugin-transform-remove-console
vue-cli4 有所改动参考 https://cli.vuejs.org/migrating-from-v3/#vue-cli-service,同时应该注意自己vue.config.js
配置中 lintOnSave
的设定
vuex 修改数据
tips:
- 当 state 中某个变量时对象数组时,如果在 mutation 中直接更新数组中的某个元素则不会引起数据的变化如
state: {
test: [{
name: 'jerry',
age:12,
}]
}
// 此时直接更新 `test[1]` 为某个对象,Vue 监测不到数据变化正确的方式是直接更新 `test[1].name`
vue3
vue3 composition API vs vue2 options API
Vue 3 Composition API vs. Options API
options API:
like data
, methods
, and mounted
and others in Options API
composition API
vue3 的核心,使用 setup 函数,根据逻辑相关性组织代码,提高可读性和可维护性。
options API 以业务相关性组织代码随着项目的逐渐变大会逐渐臃肿不易维护,而 vue3 的 composition API 以逻辑相关性组织代码,则弥补了它的缺点:
- 可组合代码共享(私以为这是最大的优点)
Vue-i18n
注意可以结合 localStorage 使用,同时在更换语言时向 html 添加 lang 以用以样式替换:
const html = document.querySelector('html');
html && html.setAttribute('lang', locale);
此时则可以使用
// container 中的文字在英文模式下为红色
.container:lang(en){
color: red;
}