Vue项目中的问题汇总(持续更新中)


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

  1. reactive底层本质是将传入的数据包装成一个Proxy
  2. reactive参数必须是对象(或者数组)类型数据
  3. 如果要让对象的某个元素实现响应式时比较麻烦。需要使用toRefs
  4. reactive都不需要添加 value

ref

  1. ref通常用来定义基本类型数据
  2. 如果参数是对象类型时,其实底层的本质还是reactive,内部会通过reactive转为代理对象
  3. ref响应式原理是依赖于Object.defineProperty()get()set()的。
  4. 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>

解决方案二: 利用脚本的异步加载,需要用到两个属性 asyncdefer

这两个属性是在外部导入 js 文件时使用,当外部导入 js 的文件的script标签在元素节点前面时,同样会存在获取不到元素节点的情况,这时就需要用到asyncdefer

代码说明:

<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-columntypeselection时,显示为勾选框。

但是会在勾选框旁边显示一个或多个实心的小点。

原因:

这是因为在设置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% 是根据自己的盒子宽度自定义设置的
//方法1flex: 0 0 auto;
width: 50%;

//方法2flex: 0 0 50%;

//方法3flex-shrink: 0;
width: 50%;

//方法4min-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 | Number
      default: 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 也都可以获得值

所以,jquerywidth()与 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就是一般情况下Vuehtml根文件,所以这里实质上就是将App这个html页面进行了渲染,当然在App页面中又有挂载的路由组件,进而可以渲染各种挂载的路由组件

22.【前端技巧】git 已经 push 的代码如何修改 commit

  1. 修改倒数第 n 次的 commit,输入命令:
# 最后的数字1可以是倒数第n次
git rebase -i HEAD~1
  1. 回车后,按 i 进入编辑模式,找到需要修改注释的那一行,将其开头的 “pick” 改为 “edit” ,按 ESC 退出编辑模式,再输入:wq!保存退出。

  2. 更正 commit 注释内容,输入命令:

git commit --amend
  1. 回车后,按i进入编辑模式,将第一行中的注释(不是下方注释)修改为正确的内容,按 ESC 退出编辑模式,再输入:wq!保存退出。

  2. rebase 确认,输入命令(该部可省略):

git rebase --continue
  1. 强制 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.$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 工具的项目就能够正常运行,所以得搞清楚其中的具体原因,到底是哪些地方影响了项目运行。

具体详看:

解决方法:

方法 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 不能直接捕获 thencatch 方法中的错误。这是因为 thencatch 方法返回的是新的 Promise,而它们中的回调函数是异步执行的。

为了捕获 Promise 中的错误,你应该使用 Promise 链中的.catch()方法,或者在代码中,使用 async/awaittry...catch 结合来捕获异步错误。

使用 .catch() 方法

someAsyncFunction()
    .then(result => {
        // 处理结果
    })
    .catch(error => {
        // 捕获错误
        console.error("捕获到错误:", error);
    });

使用 async/awaittry...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()的调用,但错误是在 Promisethen 回调中抛出的,因此 try...catch 无法捕获这个错误。要捕获这个错误,你必须在 Promise 链中使用.catch()方法,或者像前面提到的那样使用 async/await

32.vsCode 中的 css 代码提示 reference 怎么关闭

打开设置搜索,将其取消勾选即可

Code Lens

取消 Editor:Code Lens 的勾选

33.el-table、vxe-table 在修改页面宽度时,表格宽度没有变化,还是初始宽度的问题

父级添加overflow: hidden;

34.vue 数组的更新,watcholdValnewVal值一样

场景:在 vue2 或 vue3 中,对数组使用push, unshift, splice等操作更新数据,会发现watch监听下的oldValnewVal值一样。

原因如下:

  1. oldValnewVal值一样的原因是它们索引同一个对象/数组。Vue 不会保留修改之前值的副本。
  2. 根据源码,push,unshift,splice三个方法触发后,在这里手动observe,其他方法的变更会在当前的索引上进行更新,所以不需要再执行ob.observeArray

35.Component emitted event “formValidate” but it is neither declared in the emits option nor as an “onFormValidate” prop.

  1. 报错原因
    组件触发了事件”confirmForm”,但它没有在 emit 中声明

2.错误代码

const emits = defineEmits("formValidate");
  1. 正确
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 表单提交事件,导致页面刷新。如果有多个表单元素则不会出现这个问题。

原因分析:

  1. 输入框回车导致页面刷新的原因主要是由于浏览器的默认行为。
  2. 按照 W3C 标准的说法是:当 form 元素中只有一个输入框时,在该输入框中按下回车应提交该表单;
  3. 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 下的 handleCheckedAllCheckboxRowcheckSelectionStatus 两个方法
按照建议 2 进行修改:加入 showHeader 判断,不推荐使用 1 的一刀切修改方法

详情查看 checkStrictly 父子不关联逻辑调整,父子不关联只控制表格 Body 里的数据关联性,建议父子不关联,不影响全选功能,很多业务场景需要父子不关联的情况下,需要全选功能

39.使用 flex:1 导致 vxe-table 出现滚动一卡一卡的

若因flex:1导致 vxe-table 出现滚动一卡一卡的,给 flex:1 的容器添加 overflow: hidden 就可以

更多查看

40.ElMessage offset 修改

element-plus 下生成 eslib 两个文件夹,分别为 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, //修改此次为60
    zIndex: 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: 16readonly 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;//修改此次为60
    readonly 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, //修改此次为60
    zIndex: 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: 16readonly 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;//修改此次为60
    readonly 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;
}

这种写法的主要问题在于:

  1. 错误处理的逻辑问题:
  • 使用了 await 关键字直接等待 addFormRef.value.validate() 的结果,这要求 validate() 方法返回一个 Promise
  • 通过 .catch(err => { return err; }) 处理任何可能出现的错误,将错误直接返回给 isValid。这种方式下,当验证失败(发生错误)且没有正确抛出被 catch 捕获时,catch 会捕获错误并返回错误对象
  • 错误对象不是 truefalse ,而是一个包含错误信息的对象,这可能导致判断结果不准确
  • 这时 isValid 的值会是一个错误对象,而不是预期的 boolean
  1. 类型判断问题:
  • if (valid === true) 使用严格相等,期望比较的是布尔值
  • 但如果 isValid 是错误对象,这个比较就会失败
  • 导致验证失败时的逻辑可能不符合预期,出现误判

第二种写法:

const isValid = await new Promise(resolve => {
    addFormRef.value.validate(valid => resolve(valid));
});

这种写法更好:

  1. 使用了 Element Plus 表单验证的标准回调方式
  2. validate 方法的回调函数直接接收验证结果(boolean 值)
  3. 直接使用 Promise 包装验证过程,通过 Promise resolve 正确返回验证结果(布尔值)
  4. 确保 isValid 一定是布尔值,不会出现额外类型判断的问题

总结:

推荐使用第二种写法,因为:

  1. 代码更简洁
  2. 错误处理更准确,第一种写法在处理验证失败的情况时可能会有问题,因为错误对象与 true 的比较会导致不准确的结果
  3. 返回结果更可靠,因为它总是能得到正确的布尔值结果
  4. 符合 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-drawerel-message-boxel-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;

文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录