1.vue 循环 span 标签产生了间隙
代码如下:
<template><div class="box"><span v-for="(item,index) in items" ::key="index">{{ item }}</span><span>修改</span><span>删除</span></div>
</template>
<script>export default {data() {return {items: ["审批", "拒绝审批", "核销"],};},};
</script>
<style lang="less">.box {span {display: inline-block;width: 40px;height: 20px;}}
</style>
原因和解决方法:
如果有display:inline
或是display:inline-block
的元素相邻,并且它们之间有强制回车换行,那么就会自动产生一段间隙。
可以在这些元素的父元素上设置font-size:0;
,就可以消除换行带来的间隙。
这种情况下,如果代码不换行,就不会产生间隙。
2.ElementUI 的表单resetFields()
方法无法清空
为每个 form-item
加上 prop
属性(大部分的问题就是出在这)
弹框时:
记得使用this.$nextTick(()=>{})
3.Vue3 报错: <router-view>
can no longer be used directly inside <transition>
or <keep-alive>
最近在学习 vue3,在搭建项目的时候,使用 keep-alive 的包裹 router-view 会有警告信息
<router-view> can no longer be used directly inside <transition> or <keep-alive>
代码如下:
<transition:name="!noTransition ? 'fade-transform' : ''":mode="!noTransition ? 'out-in' : ''"
><keep-alive :include="cachedViews"><router-view :key="key" /></keep-alive>
</transition>
提示以下警告
[Vue Router warn]: <router-view> can no longer be used directly inside <transition> or <keep-alive>.
Use slot props instead:<router-view v-slot="{ Component }"><transition><component :is="Component" /></transition>
</router-view>
从警告信息可以看出是路由包裹出现了问题,现做如下调整
<router-view :key="key" v-slot="{ Component }"><transition:name="!noTransition ? 'fade-transform' : ''":mode="!noTransition ? 'out-in' : ''"><keep-alive :include="cachedViews"><component :is="Component"></component></keep-alive></transition>
</router-view>
4.Vue3 reactive
不能直接赋值的解决方法
1. 改为 ref 定义
const arr = ref([]);
arr.value = [1, 2, 3];
2. push 新增数据
const arr = reactive([]);
arr.push(...[1, 2, 3]);
3.再封装一层数据,改成对象(推荐!)
const state = reactive({arr: [],
});
state.arr = [1, 2, 3];
reactive 与 ref 区别
reactive
reactive
底层本质是将传入的数据包装成一个Proxy
reactive
参数必须是对象(或者数组)类型数据- 如果要让对象的某个元素实现响应式时比较麻烦。需要使用
toRefs
reactive
都不需要添加 value
ref
ref
通常用来定义基本类型数据- 如果参数是对象类型时,其实底层的本质还是
reactive
,内部会通过reactive
转为代理对象 ref
响应式原理是依赖于Object.defineProperty()
的get()
和set()
的。ref
操作数据需要.value,在 js 中需要手动.value,template
模板中不需要。
ref、toRef、toRefs 的区别
ref:复制,修改响应式数据不影响以前的数据;数据改变,界面自动更新
toRef:引用,修改响应式数据会影响以前的数据;数据改变,界面不自动更新
toRefs:
(1)接收一个对象作为参数,它会遍历对象身上所有属性,然后调用单个toRef
(2)将对象的多个属性变成响应式数据,并且要求响应式数据和原始数据关联,且更新响应式数据的时候不会更新界面,用于批量设置多个数据为响应式
5.Vue 获取当前页面路由
//当前页面
window.location.href; //完整url可以用
this.$route.path; //路由路径可以用
this.$route.params; //路由路径参数
监听路由
直接在监听后传入一个方法对应的字符串,在路由发生变化的时候自动执行方法
watch: {// 如果路由有变化,会再次执行该方法'$route': 'fetchData'
},
methods:{fetchData () {//...}
}
如果我们要通过判断路由发生的特定变化来执行方法,可以使用
handler
watch:{"$route":{handler(route){const that=this;if(route.name=='Hello'){that.fetchData();}}}
}
6.Vue 请求本地 JSON 文件的方法
使用 vue-cli2.0 生成的项目,静态文件是 static 文件。
使用 vue-cli3.0 生成的项目,静态文件变成了 public 文件。(vue3 相同)
把 json 文件放到静态文件里面,使用的时候 vue 会默认请求到静态文件里面。
案例:
this.$axios.get("data.json").then(res => {// 200响应console.log(res); // 此处的res对象包含了json的文件信息和数据,看控制台点出来即可},err => {// 500响应console.log(err);}
);
7.解决 script 标签写在元素节点前面无法获取元素节点的问题
文档的加载是按照文档树的顺序加载的,所以获取 script 脚本节点后面加载的元素节点 是获取不了的。
两个方法,如下:
解决方案一:使用onload事件,window.onload
当页面加载完成后触发。
由于 script
标签写在 div 块前面,运行代码会报错,无法获取到 box 元素,使用window.onload
即可解决。
代码说明:
<style>.box {width: 100px;height: 100px;background-color: blue;}
</style>
<script>window.onload = function () {//页面加载完后执行var box = document.getElementById("box");box.style.background = "green";};
</script><div id="box">我是box</div>
解决方案二: 利用脚本的异步加载,需要用到两个属性 async
和 defer
这两个属性是在外部导入 js 文件时使用,当外部导入 js 的文件的
script
标签在元素节点前面时,同样会存在获取不到元素节点的情况,这时就需要用到async
和defer
。
代码说明:
<style>.box {width: 100px;height: 100px;background-color: blue;}
</style>
<script scr="index.js" async></script><div id="box">我是box</div>
创建一个 index.js 文件
window.onload = function () {//页面加载完后执行var box = document.getElementById("box");box.style.background = "green";
};
详情查看:JS 基础篇:JS 脚本调用策略
8.justify-content: space-between;
没有两端对齐,没有生效
原因:
伪元素::after
,::before
影响
解决方法:
去掉伪元素
代码:
.el-row::before {display: none;
}
.el-row::after {display: none;
}
9.ElementUI 中el-table-column
的 type 为 selection 时选择框旁边有个点
场景:
使用el-table
的多选框时,el-table-column
的type为selection
时,显示为勾选框。
但是会在勾选框旁边显示一个或多个实心的小点。
原因:
这是因为在设置el-table-column
的宽度为30太窄导致的。
<el-table-column type="selection" width="30" align="center" />
解决方法:
将其宽度调大点。
<el-table-column type="selection" width="55" align="center" />
10.Vue 3 ::v-deep usage as a combinator has been deprecated. Use ::v-deep() instead
原因新的 vue3.0 单文件规范::v-deep
写法已经被废弃了,使用 :deep()
替换::v-deep
https://github.com/vuejs/rfcs/blob/master/active-rfcs/0023-scoped-styles-changes.md
https://vuejs.org/api/sfc-css-features.html#scoped-css
/* Vue 2.0 写法 */
::v-deep .carousel-btn.prev {left: 270px;
}/* Vue 3.0 更改为以下写法 */
:deep(.carousel-btn.prev) {left: 270px;
}
/* 或是 */
:deep() {.class {}
}
深度选择器扩展:
有时我们可能想要明确地制定一个针对子组件的规则。
最初我们支持>>>
组合器使选择器“deep
”。但是,一些 CSS 预处理器(例如 SASS)在解析它时存在问题,因为这不是一个官方的 CSS 组合器。
我们后来切换到/deep/
,它曾经是 CSS 的实际提议添加(甚至在 Chrome 中原生提供),但后来放弃了。这给一些用户造成了困惑,因为他们担心/deep/
在 Vue SFC 中使用会使他们的代码在已删除该功能的浏览器中不受支持。然而,与>>>
一样,/deep/
仅被 Vue 的 SFC 编译器用作编译时提示来重写选择器,并在最终的 CSS 中被删除。
为了避免被丢弃的/deep/
组合器的混淆,我们引入了另一个自定义组合::v-deep
器,这一次更明确地表明这是一个特定于 Vue 的扩展,并使用伪元素语法,以便任何预处理器都应该能够解析它.
出于兼容性原因,当前的 Vue 2 SFC 编译器仍然支持以前版本的深度组合器,这再次让用户感到困惑。在 v3 中,我们弃用了对>>>
和的支持/deep/
。
当我们为 v3 开发新的 SFC 编译器时,我们注意到 CSS 伪元素实际上在语义上不是组合子。伪元素接受参数更符合惯用的 CSS,因此我们也在以::v-deep()
这种方式进行工作。如果您不关心显式v-
前缀,您也可以使用较短的:deep()
变体,它的工作原理完全相同。
仍然支持当前用作组合器的用法::v-deep
,但它被认为已弃用并会发出警告。
11.Do not access Object.prototype method ‘hasOwnProperty’ from target object
原因分析:
为何 ESLint 不允许从目标对象调用 Object 原型方法?
在 JS 中,往往通过改变原型链实现继承。一旦原型链发生改变,原先可以访问到的原型属性、方法便可能无法访问。考虑最极端的情况,若 obj 原先原型链的最顶端是Object
,此时可以通过原型链访问 Object.hasOwnProperty
方法;而若改变后,顶端不再是 Object
,那么访问 obj.hasOwnProperty
访问就会得到 undefined
。因此,直接从对象访问原型方法,很可能会带来隐藏的 BUG。
为了避免这种细微的 bug,最好总是从 Object.prototype
调用这些方法。即直接在 Object
对象上调用其方法,利用 call
改变其 this 指向到我们的目标对象上,即可安全使用 hasOwnProperty
方法了。
/*** 错误提示:Do not access Object.prototype method 'hasOwnProperty' from target object* 解决方法:foo.hasOwnProperty("bar") 改为 Object.prototype.hasOwnProperty.call(foo, "bar")*/
12.css display:flex 弹性布局 子标签设置固定宽度无效的问题
出现的情况:
当子级盒子宽度之和大于父级盒子宽度的时候,会出现子标签设置固定宽度无效的问题。
表面原因:
一个父标签中嵌入了两个子标签,当父标签设置display:flex
之后。即使子标签设置了宽度都是 50%,但是如果左边子标签内容多,则左边子标签会挤掉右边子标签的一部分,所以导致了右边的宽度显示有问题。
根本原因:
父元素设置了display:flex
,那么所有的子标签都会默认加上flex:0 1 auto
;其中 1 就是 flex
中的 flex-shrink
属性,表示开启了元素的收缩功能,所以才会有左边子标签会挤掉右边子标签的一部分的问题。
因此其中一种做法是:我们可以让该标签的自动收缩关闭,即flex:0 0 auto
;然后再设置该标签的宽度即可。
解决方法:
//针对需要设置固定宽度的子标签,其中 50% 是根据自己的盒子宽度自定义设置的
//方法1:
flex: 0 0 auto;
width: 50%;//方法2:
flex: 0 0 50%;//方法3:
flex-shrink: 0;
width: 50%;//方法4:
min-width: 50%;
12.eslint 导致 The “xxx” property should be a constructor vue/require-prop-type-constructor
场景:
在有 eslint 情况下,使用 String | Number
会报如上错误。
报错原因:
组件 props 有多种类型时要用数组写法
解决方法:
props: {pid: {type: [Number, String],//String | Numberdefault: 1}}
13.Vue style 里面使用@import 引入外部 css, 作用域是全局的解决方案
场景:
使用@import
引入外部css,作用域却是全局的
<style scoped>@import "../css/reset.css";/* 或 */@import url("../css/reset.css");
</style>或是
<script>import "../css/reset.css";
</script>
原因:
使用@import
引入外部样式表作用域是全局的
@import
并不是引入代码到<style></style>
里面,而是发起新的请求获得样式资源,并且没有加scoped
解决方法:
我们只需把@import
改成<style src=""></style>
引入外部样式,就可以解决样式是全局的问题
<style scoped src="../css/reset.css"></style>
<!-- 这种方法使用会出现一个玄学问题,路由首页使用,reset.css的样式会被本页面的样式覆盖,但是在 非路由首页使用(注:首页没有引入)时,reset.css会在样式最上方,相当于会覆盖本页面样式-->
<style scoped>/* 本页面样式 */
</style>
14.JS 在一个数组中过滤掉另一个数组的简易方法
// 方法1:
let Arr1 = [{id: 1,},{id: 12,},{id: 13,},{id: 14,},
];let Arr2 = [{id: 1,},{id: 12,},
];let newArr = [];newArr = Arr1.filter(itemA => {return Arr2.every(itemB => {return itemB.id !== itemA.id;});
});// 方法2:
let arr1 = [1, 2, 3, 4, 5, 6];
let arr2 = [3, 2];let newArr = [];Arr1.forEach(item => {if (!Arr2.includes(item)) {newArr.push(item);}
});// 方法3:
let arr1 = [1, 2, 3, 4, 5, 6];
let arr2 = [3, 2];let newArr = [];newArr = arr1.filter(item => arr2.indexOf(item) == -1);// 方法4:
let arr1 = [1, 2, 3, 4, 5, 6];
let arr2 = [3, 2];let newArr = [];
function getArrDifference(arr1, arr2) {return arr1.concat(arr2).filter(function (v, i, arr) {return arr.indexOf(v) === arr.lastIndexOf(v);});
}
newArr = getArrDifference(arr1, arr2);
15.解决 el_table 固定列下方多了一条线问题
::v-deep {.el-table__fixed-right,.el-table__fixed {height: 100% !important;}
}
16.解决 setInterval 方法在 if 条件语句判断无效的问题
同一页面,如果不通过clearInterval()
清除的话,if 条件语句的判断都是无效的,会一直定时执行
var timer = null;
var a = 1;
if (a === 1) {clearInterval(timer);timer = null;
} else {timer = setInterval(() => {}, 1000);
}
17.axios 的 post 请求为什么要使用 qs
结论:用不用取决于后端怎么接收参数
axios 默认的 content-type 是 application/json,即 json 格式,后台可以使用字符串进行接收,然后再解析即可
18.关于 js 的 style.width 取不到元素的宽度值的问题
以前一直用jquery
的.width()
方法来获取一个元素的当前的宽度。不管该元素是否设置了宽度,CSS 样式是内联、外联 or 内嵌,都可用此方式获得元素当前的宽度。
用原生 JS 想获取一个元素宽度时,写document.getElementById("id").style.width
或者document.getElementById("id").width
都取不到值。
原来,在以下情况下,js 无法取到.style.width
或者.width
的值。
- 1.元素未设置宽度值。
- 2.元素设置了宽度值,但,设置在内联或外联样式表中,而非内嵌式的。
比如:
css 代码
p {background: red;width: 200px;
}
html 代码
<p id="p1">一段很长的文字</p>
虽然这种方式取不到宽度值。但却可以设置元素的宽度值。比如:设置 p 元素宽度为 200px:
document.getElementById("p1").style.width = "200px";
所以,只有将元素的样式设置成内嵌式的,才可以通过 document.getElementById("id").style.width
来获取宽度值;
比如:
<p id="p1" style="144px;">一段很长的文字</p>
执行 js 代码
var w = document.getElementById("p1").style.width;
alert(w);
执行后输出结果为 144px。
那么,对于没有设置宽度的元素、亦或 CSS 样式非内嵌式的,js 原生写法可以通过offsetWidth
来获取宽度
即:
document.getElementById("p1").offsetWidth;
对于设置了 CSS 样式的元素(内联、内嵌、外联)
offsetWidth
也都可以获得值
所以,jquery
的width()
与 js 的offsetWidth
都可以获取元素的宽度,但有个区别:
jquery
的.width()
的值单纯是内容区域的宽度、不包括内外补丁和border
。ie6+和 chrome 相同。offsetWidth
:包括了内补丁和border
,不包括外补丁。ie6+和 chrome 相同
19.sass-loader 版本问题引发的错误:options has an unknown property ‘prependData’. These properties are valid
原因:npm 和 sass-loader 的版本高了。
由于 sass-loader 版本不同,loaderOptions 中 additionalData 的键名也不同。
sass-loader v8-,这个选项名是"data"
sass-loader v8,这个选项名是"prependData"
sass-loader v10+,这个选项名是"additionalData"
结果:
//原来的
css: {loaderOptions: {sass: {prependData: '@import "@/scss/settings.scss";';}}
}//修改后
css: {loaderOptions: {sass: {additionalData: '@import "@/scss/settings.scss";';}}
}
20.为什么 document.addEventListener(‘load’, function(){})不能生效?
原因:在document.addEventListener
中本身就不存在监听load
事件的,所以就导致代码如果没有报错,但是就是不起作用,不生效。
应该在window
对象上监听load
事件,所以正确的代码如下:
window.addEventListener("load", function () {console.log("load");
});
21.Vue 中的 render: h => h(App)什么意思?
这是文档里的内容:
在哪里见过呢,就是这里:
new Vue({el: "#app",router,store,render: h => h(App),
});//或
new Vue({router,store,render: h => h(App),
}).$mount("#app");
这是我们利用 Vue 新建项目最常见的一句话,但是这句话是什么意思呢?对于初学者,看到这句话一定是懵逼的(有没有?有没有?)
因为这句写的真是简洁…
那么,参考上面文档中的内容,这句话的意思其实就是:
这是一个ES6
箭头函数的写法,还原成一个函数,就是:
render: h => {return h(App);
};
要是还看不懂,再进行还原:
render: function(h) {return h(App);
}
然后,别忘了最先贴的官方文档,解释“h”的含义,进一步还原:
render: function(createElement) {return createElement(App);
}
而这里的render
就是一个渲染函数,而createElement
就是创建节点,App
就是一般情况下Vue
的html
根文件,所以这里实质上就是将App
这个html
页面进行了渲染,当然在App
页面中又有挂载的路由组件,进而可以渲染各种挂载的路由组件
22.【前端技巧】git 已经 push 的代码如何修改 commit
- 修改倒数第 n 次的 commit,输入命令:
# 最后的数字1可以是倒数第n次
git rebase -i HEAD~1
-
回车后,按 i 进入编辑模式,找到需要修改注释的那一行,将其开头的 “pick” 改为 “edit” ,按 ESC 退出编辑模式,再输入:wq!保存退出。
-
更正 commit 注释内容,输入命令:
git commit --amend
-
回车后,按
i
进入编辑模式,将第一行中的注释(不是下方注释)修改为正确的内容,按 ESC 退出编辑模式,再输入:wq!
保存退出。 -
rebase 确认,输入命令(该部可省略):
git rebase --continue
- 强制 push,输入命令:
git push --force
至此,commit 的 message 内容修改完成!
23.Vue3 新属性之 css 中使用 v-bind 的问题汇总(v-bind in css)
有以下 4 种定义:
// 进行拼接调用测试的数据
let width = 400;// 直接调用的数据
let div_height = "400px";
let div_color = "#e89393";// 对象调用的数据
let span = {width: "200px",height: "200px",color: "green",
};// 组合调用的数据
let transition = "cubic-bezier(0, 1.5, .6, 1)";
1.在css
中使用,使用v-bind()
进行绑定 :
直接使用
:完全没有问题
拼接使用
:这个在css
中没有问题,不过在scss
中会出现错误
对象调用
:对象的调用和直接使用类似,不过不同的是,需要使用引号的包裹才能正常使用,如果直接书写会报错
组合使用
:完全没问题
四种样式写法如下:
.div {/* 拼接使用 */width: v-bind(width + "px");/* 直接使用 */height: v-bind(div_height);background: v-bind(div_color);
}.span {/* 对象调用 */width: v-bind("span.width");height: v-bind("span.height");background: v-bind("span.color");display: flex;justify-content: center;align-items: center;
}.span_title {width: 100px;height: 100px;background: #000;color: white;/* 组合使用 */transition: all 0.9s v-bind(transition);
}.span:hover .span_title {border-radius: 50%;background: #a5f5b8;color: #ff0000;
}
2.在less
中使用,使用v-bind()
进行绑定 :
直接使用
:没有问题
拼接使用
:存在问题,详见下文
对象调用
:没问题
组合使用
:没问题
四种样式写法如下:
// 使用变量承接
@height: v-bind(div_height);.div {width: 400px;/* 直接使用 */height: @height;background: v-bind(div_color);.span {@width: v-bind("span.width");/* 对象调用 */width: @width;height: v-bind("span.height");background: v-bind("span.color");display: flex;justify-content: center;align-items: center;.span_title {@transition: v-bind(transition);width: 100px;height: 100px;background: #000;color: white;/* 组合使用 */transition: all 0.9s @transition;}&:hover .span_title {border-radius: 50%;background: #a5f5b8;color: #ff0000;}}
}
仅对拼接进行修改:
// 定义使用的数据
let width = 400;
// 1.直接拼接
@width: v-bind(width + "px");
width: @width;// 2.拿到内容后拼接
@width: v-bind(width) + "px";
width: @width;// 3.使用时拼接
@width: v-bind(width);
width: @width + "px";
其中第二种、第三种会没有效果,第一种会直接报错。
解决方式(思路):
将变量的内容在行内样式
使用拼接的方式进行定义,然后在定义的less或scss
中进行使用,因为定义的样式是行内样式,所以优先级和变量出现的位置,都是在使用之前,所以可以正常使用
使用数据的定义:
// 大部分数据和预先提供的数据一样(这里只写了新增数据)
let test = 400;
页面结构:
<!-- 改变的内容为下面这行(其他内容和原来内容保持一致)-->
<!-- 【在这里对要使用的数据进行单位的绑定】-->
<div class="div" :style="{'--test' :test + 'px'}"></div>
样式的使用:
// 承接使用
@width: var(--test);.div {/* 使用 */width: @width;/* 或者直接使用 */width: var(--test);height: 400px;background: red;
}
3.在scss
中使用,使用v-bind()
进行绑定 :
直接使用
:没有问题
拼接使用
:存在问题
对象调用
:没问题
组合使用
:没问题
// 使用变量承接
$width: v-bind(width + "px");
$height: v-bind(div_height);.div {/* 拼接使用 */width: $width; //没有生效/* 直接使用 */height: $height;background: v-bind(div_color);.span {$width: v-bind("span.width");/* 对象调用 */width: $width;height: v-bind("span.height");background: v-bind("span.color");display: flex;justify-content: center;align-items: center;.span_title {$transition: v-bind(transition);width: 100px;height: 100px;background: #000;color: white;/* 组合使用 */transition: all 0.9s $transition;}&:hover .span_title {border-radius: 50%;background: #a5f5b8;color: #ff0000;}}
}
更多详情,查阅:
-Vue3 新属性之 css 中使用 v-bind 的方法(v-bind in css)
24.设置网页为黑白色
/* 网页为黑白色 ,以表哀悼*/
html {filter: grayscale(100%);-webkit-filter: grayscale(95%);-moz-filter: grayscale(100%);-ms-filter: grayscale(100%);-o-filter: grayscale(100%);filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);-webkit-filter: grayscale(1);
}
25.解决 el-tree 点击行上面的操作按钮后,当前行变成白色背景的问题
// 此处是导致:el-tree点击上面的操作按钮后,当前行变成白色背景的根本原因,样式中单独看el-tree-node__content无法查看
.el-tree-node:focus > .el-tree-node__content {background-color: #28335f;
}
26.vue 子组件 watch 监听不到 prop 的解决
watch 添加immediate: true
,添加immediate: true
,添加immediate: true
27.关于 Vue eventBus 总线传值时的生命周期问题
https://www.jianshu.com/p/b1cb604dd4ae
1.解决 vue bus. e m i t 触发第一次 emit触发第一次 emit触发第一次on 监听不到问题
https://www.uoften.com/article/186494.html
28.for of 和 for in 的区别
https://blog.csdn.net/weixin_47148731/article/details/123852461
29.Vue 报错 error:0308010C:digital envelope routines::unsupported
原因:其实这不是 vue 的问题,是 nodejs 升级引起的构建错误
出现这个错误是因为 node.js V17 版本中最近发布的OpenSSL3.0
, 而OpenSSL3.0
对允许算法和密钥大小增加了严格的限制,可能会对生态系统造成一些影响。
但其实,并不是所有的项目都会出现,比如使用了最新版本的 webpack
工具的项目就能够正常运行,所以得搞清楚其中的具体原因,到底是哪些地方影响了项目运行。
具体详看:
- nodejs 升级引起的构建错误:0308010C:digital envelope routines::unsupported
解决方法:
方法 1.打开终端(按健 win+R 弹出窗口,键盘输入 cmd,然后敲回车)并按照说明粘贴这些:(不一定行,可能失败了)
Linux & Mac OS (windows git bash)
export NODE_OPTIONS=--openssl-legacy-provider
windows 命令提示符:
set NODE_OPTIONS=--openssl-legacy-provider
方法 2.尝试卸载 Node.js 17+版本并重新安装 Node.js 16+版本
方法 3.升级 webpack
如果我们把 webpack(5.61.0以上版本)
进行升级到较新的版本,就可以解决此类问题。
tips:但是,由于升级构建工具,可能随之带来很多不可知的问题,不少依赖包都会因为不支持而报错,升级带来的成本并不小,所以不推荐升级构建工具。
30.js 模板字符串里面用换行符不起作用
有时候会遇到赋值过程中的字符串需要换行的情况,但是像下面那样直接 \n
是不行的。
this.text = `提示: \n ${this.tip}`;
解决方法:
在它的 HTML 标签元素上,添加 css 样式设置:white-space:pre-line
; 这样以上这段代码就生效了
//html
<div style="white-space:pre-line">{{ text }}</div>;
//js
this.text = `提示: \n ${this.tip}`;
tips:若是要对
title
进行换行,如下,不需要添加white-space:pre-line
,因为样式是对标签里的内容生效的,直接使用即可。
<span :title="`${data[treeProps.label]}\n${data.absolute}`">{{ data[treeProps.label] }}</span
>
31.try catch 无法获取 then 的异常
在 JavaScript 中,try...catch
语句通常用于捕获同步代码块中的错误。然而,当你处理基于 Promise
的异步操作时,try...catch
不能直接捕获 then
或 catch
方法中的错误。这是因为 then
和 catch
方法返回的是新的 Promise,而它们中的回调函数是异步执行的。
为了捕获 Promise
中的错误,你应该使用 Promise
链中的.catch()
方法,或者在代码中,使用 async/await
与 try...catch
结合来捕获异步错误。
使用 .catch()
方法
someAsyncFunction().then(result => {// 处理结果}).catch(error => {// 捕获错误console.error("捕获到错误:", error);});
使用 async/await
与 try...catch
async function main() {try {const result = await someAsyncFunction();// 处理结果} catch (error) {// 捕获错误console.error("捕获到错误:", error);}
}main();
在 async/await
的例子中,await
关键字会暂停 async
函数的执行,直到 Promise
解决(resolve
)或拒绝(reject
)。如果 Promise
被拒绝,则 await
表达式会抛出一个错误,这个错误可以被外部的 try...catch
捕获。
注意,
async/await
是 ES2017 引入的,因此你需要确保你的环境支持这个特性,或者通过 Babel 等工具进行转译。
错误的例子:try…catch 无法捕获 then 中的错误
try {someAsyncFunction().then(result => {// 如果这里发生错误,try...catch无法捕获throw new Error("这是一个错误");});
} catch (error) {// 这个catch不会执行,因为上面的错误是在Promise的回调中抛出的console.error("这个错误不会被捕获:", error);
}
在上面的例子中,尽管有 try...catch
包围了 someAsyncFunction()的调用,但错误是在 Promise
的 then
回调中抛出的,因此 try...catch
无法捕获这个错误。要捕获这个错误,你必须在 Promise 链中使用.catch()
方法,或者像前面提到的那样使用 async/await
。
32.vsCode 中的 css 代码提示 reference 怎么关闭
打开设置搜索,将其取消勾选即可
Code Lens
取消 Editor:Code Lens 的勾选
33.el-table、vxe-table 在修改页面宽度时,表格宽度没有变化,还是初始宽度的问题
父级添加overflow: hidden;
34.vue 数组的更新,watcholdVal
和newVal
值一样
场景:在 vue2 或 vue3 中,对数组使用push, unshift, splice
等操作更新数据,会发现watch
监听下的oldVal
和newVal
值一样。
原因如下:
oldVal
和newVal
值一样的原因是它们索引同一个对象/数组。Vue 不会保留修改之前值的副本。- 根据源码,
push,unshift,splice
三个方法触发后,在这里手动observe
,其他方法的变更会在当前的索引上进行更新,所以不需要再执行ob.observeArray
- Vue 之 computed 和 watch
- 为什么 Vue3.0 使用 Proxy 实现数据监听(defineProperty 表示不背这个锅)
- 为什么 Vue3.0 使用 Proxy 实现数据监听(defineProperty 表示不背这个锅)
35.Component emitted event “formValidate” but it is neither declared in the emits option nor as an “onFormValidate” prop.
- 报错原因
组件触发了事件"confirmForm",但它没有在 emit 中声明
2.错误代码
const emits = defineEmits("formValidate");
- 正确
const emits = defineEmits(["formValidate"]);
tips:如果存在 emit 未知方法,则不能在 defineEmits 中传入任何值,否则会同样对未定义的事件名提示 warning。
但最好显式地声明你的组件将要触发的事件,以提高代码的可维护性和可读性,尤其是在 Vue 3 + TypeScript 中将会报错。
示例:
// 此时假如有其他emits,如"editCell","selectChange"等
// 当你不传递任何参数给 defineEmits 时,它默认允许组件触发任何事件,而不会对事件名进行任何验证
const emits = defineEmits(); //["editCell","selectChange"]const operateClick = (rowInfo, btn, rowIndex) => {if (btn.disabled &&(btn.disabled === true || btn.disabled(rowInfo, btn) === true))return;if (btn.clickFun) {if (verifyType(btn.clickFun, "Function") === true) {btn.clickFun(rowInfo, btn, rowIndex);} else {emits(btn.clickFun, rowInfo, btn, rowIndex);}} else {console.error("按钮未定义clickFun点击事件回调");}
};
36.absolute 固定在滚动容器里面失效
场景:一个浮层,固定在滚动容器内部的底部,滚动容器 relative 定位,浮层 absolute 定位,但是发现随着内容的增加,开始出现滚动条了,这个浮层定位就随着滚动条一起滚上去了
解决方法:
就把 position:absolute 换成 sticky
position: sticky;
若当滚动容器的内容不满一屏时,即没有滚动条产生时,这个浮层就自动弹上去了。可以给滚动容器一个弹性布局,给滚动内容添加 flex-grow:1,自动占满,即可解决。
//滚动容器
display: flex;
flex-direction: column;//滚动内容
flex-grow:1;
37.el-form 表单输入框回车事件导致页面刷新问题
场景:当 el-form 表单仅有一个 el-input 输入框的时候,在 el-input 输入框中回车,会触发默认的 el-form 表单提交事件,导致页面刷新。如果有多个表单元素则不会出现这个问题。
原因分析:
- 输入框回车导致页面刷新的原因主要是由于浏览器的默认行为。
- 按照 W3C 标准的说法是:当 form 元素中只有一个输入框时,在该输入框中按下回车应提交该表单;
- el-form 本质上也是表单(可通过 F12 查看网页源码,el-form 会转为 form 表单),所以遵循 HTML 默认规则。
解决方法:给 el-form 表单上加一个@submit.native.prevent
,阻止表单提交的默认行为。
注:vue3,需要使用
@submit.enter.prevent
,原因:vue3 中移除v-on.native
修饰符
<!-- Vue2 + element UI 禁用表单提交 -->
<!-- 在<from> 中添加@submit.native.prevent -->
<el-form @submit.native.prevent></el-form><!-- Vue3 + element Plus 禁用表单提交 -->
<!-- 在<from>中添加@submit.enter.prevent -->
<el-form @submit.enter.prevent></el-form>
38.vxe-table 父子不关联,不影响全选功能
场景:表格父子不关联,但此时的全选功能失效
解决方法:
修改 vxe-table\es\table\src\table.js 下的 handleCheckedAllCheckboxRow
和 checkSelectionStatus
两个方法
按照建议 2 进行修改:加入 showHeader 判断,不推荐使用 1 的一刀切修改方法
详情查看 checkStrictly 父子不关联逻辑调整,父子不关联只控制表格 Body 里的数据关联性,建议父子不关联,不影响全选功能,很多业务场景需要父子不关联的情况下,需要全选功能
39.使用 flex:1 导致 vxe-table 出现滚动一卡一卡的
若因flex:1
导致 vxe-table 出现滚动一卡一卡的,给 flex:1 的容器添加 overflow: hidden 就可以了
更多查看
- 1.外层使用 flex 自适应宽度时,表格宽度自适应有问题,每次窗口自适应都会有一个较长的变化过程
- 2.宽度自适应问题
40.ElMessage offset 修改
element-plus
下生成es
与lib
两个文件夹,分别为 ESM 与 CJS 两种格式。
import { ElMessage } from "element-plus";
方式引入
1.修改 node_modules\element-plus\es\components\message\src\message.mjs 里的 offset 为 60
const messageDefaults = mutable({customClass: "",center: false,dangerouslyUseHTMLString: false,duration: 3e3,icon: void 0,id: "",message: "",onClose: void 0,showClose: false,type: "info",offset: 16, //修改此次为60zIndex: 0,grouping: false,repeatNum: 1,appendTo: isClient ? document.body : void 0,
});
2.修改 node_modules\element-plus\es\components\message\src\message.d.ts 里的 readonly offset: 16
为 readonly offset: 60
export declare const messageDefaults: Mutable<{readonly customClass: "";readonly center: false;readonly dangerouslyUseHTMLString: false;readonly duration: 3000;readonly icon: undefined;readonly id: "";readonly message: "";readonly onClose: undefined;readonly showClose: false;readonly type: "info";readonly offset: 16;//修改此次为60readonly zIndex: 0;readonly grouping: false;readonly repeatNum: 1;readonly appendTo: HTMLElement;
}>;
const { ElMessage } = require("element-plus");
方式引入
1.修改 node_modules\element-plus\lib\components\message\src\message.js 里的 offset 为 60
const messageDefaults = typescript.mutable({customClass: "",center: false,dangerouslyUseHTMLString: false,duration: 3e3,icon: void 0,id: "",message: "",onClose: void 0,showClose: false,type: "info",offset: 16, //修改此次为60zIndex: 0,grouping: false,repeatNum: 1,appendTo: core.isClient ? document.body : void 0,
});
2.修改 node_modules\element-plus\lib\components\message\src\message.d.ts 里的 readonly offset: 16
为 readonly offset: 60
export declare const messageDefaults: Mutable<{readonly customClass: "";readonly center: false;readonly dangerouslyUseHTMLString: false;readonly duration: 3000;readonly icon: undefined;readonly id: "";readonly message: "";readonly onClose: undefined;readonly showClose: false;readonly type: "info";readonly offset: 16;//修改此次为60readonly zIndex: 0;readonly grouping: false;readonly repeatNum: 1;readonly appendTo: HTMLElement;
}>;
41.有关 form 表单 validate 的使用
第一种写法:
let isValid = await addFormRef.value.validate().catch(err => {return err; // 关键问题在这里
});
if (isValid === true) {return true;
} else {return false;
}
这种写法的主要问题在于:
- 错误处理的逻辑问题:
- 使用了
await
关键字直接等待addFormRef.value.validate()
的结果,这要求validate()
方法返回一个Promise
- 通过
.catch(err => { return err; })
处理任何可能出现的错误,将错误直接返回给isValid
。这种方式下,当验证失败(发生错误)且没有正确抛出被catch
捕获时,catch
会捕获错误并返回错误对象 - 错误对象不是
true
或false
,而是一个包含错误信息的对象,这可能导致判断结果不准确 - 这时
isValid
的值会是一个错误对象,而不是预期的boolean
值
- 类型判断问题:
if (valid === true)
使用严格相等,期望比较的是布尔值- 但如果
isValid
是错误对象,这个比较就会失败 - 导致验证失败时的逻辑可能不符合预期,出现误判
第二种写法:
const isValid = await new Promise(resolve => {addFormRef.value.validate(valid => resolve(valid));
});
这种写法更好:
- 使用了 Element Plus 表单验证的标准回调方式
validate
方法的回调函数直接接收验证结果(boolean
值)- 直接使用
Promise
包装验证过程,通过Promise resolve
正确返回验证结果(布尔值) - 确保
isValid
一定是布尔值,不会出现额外类型判断的问题
总结:
推荐使用第二种写法,因为:
- 代码更简洁
- 错误处理更准确,第一种写法在处理验证失败的情况时可能会有问题,因为错误对象与
true
的比较会导致不准确的结果 - 返回结果更可靠,因为它总是能得到正确的布尔值结果
- 符合 Element Plus 表单验证的标准用法
如果需要处理错误,建议使用这种写法:
const isValid = await new Promise((resolve, reject) => {addFormRef.value.validate((valid, errors) => {if (valid) {resolve(true);} else {reject(errors);}});
}).catch(errors => {console.error(errors);return false;
});
42.解决el-popup-parent–hidden 会携带 width: calc(100% - 8px) 样式的问题
场景:使用el-drawer
, el-message-box
, el-dialog
打开弹框时,body 会添加一个 class: el-popup-parent--hidden
,此时会概率携带一个类似width: calc(100% - 8px)
的样式;
更多查看:el-popup-parent–hidden
解决方法: 1.重写 el-popup-parent–hidden 的样式
.el-popup-parent--hidden {width: 100% !important;
}
2.在 main.js 中添加关闭 dialog 滚动条配置
// 此方法未验证会不会影响到弹框的滚动条,慎用
// vue2
import ElementUI from "element-ui";
// vue3
import ElementPlus from "element-plus";
import "element-ui/lib/theme-chalk/index.css";
// 关闭dialog滚动条
ElementUI.Dialog.props.lockScroll.default = false;
ElementPlus.Dialog.props.lockScroll.default = false;