前端开发问题集


锁死使用的 UI 版本库

由于 UI 库的特殊性,官方每次升级会对组件样式有细微调整,而每次的调整都可能存在与之前版本不兼容的情况,当后加入项目的人重新 npm i 或者自己执行了 npm i 时,就会导致样式错乱

锁死版本很简单,只需要在 package.json 里面将对应包名后面的尖括号删除即可。

{
"dependencies": {
"ant-design-vue": "1.3.17"
}
}

element-ui 的版本 2.8.2 到 2.13.0 版本号是不兼容的,如果出现原本的组件错乱问题,请锁死版本号,例如 element-ui 的 级联下拉组件

锁死版本号:

{
"dependencies": {
"element-ui": "2.8.2"
}
}

文件引用使用 @ 符号

1、项目之间存在一些代码互相拷贝,使用艾特符号就不用再去改一次路径了

2、更简洁方便

如果使用了 vue-cli@2.x,需要添加一下配置

1、安装 eslint-import-resolver-webpack

npm i eslint-import-resolver-webpack -D

2、修改 .eslintrc.js 文件的配置

settings: {
'import/resolver': {
webpack: {
config: './build/webpack.base.conf.js',
},
},
}

如果使用的是 vue/cli@3,放心使用,无需再加其他配置。

IE11 打不开项目的解决办法

asama-vue-zhichan 里面打不开是因为 element-ui 的引起的,如果你的项目里面使用 element-ui,可尝试进行以下改动:

// 全局引用 element-ui 的地方改成引用到 src 目录
import ElementUI from 'element-ui/src'

// 修改 build/webpack.base.conf.js 下的 module.rules.js 配置
{
test: /\.js$/,
loader: 'babel-loader',
include: [
resolve('src'),
resolve('test'),
resolve('node_modules/webpack-dev-server/client'),
// 添加对 element-ui 的解析
resolve('node_modules/element-ui')
],
// 排除对 popper 编译
exclude: [resolve('node_modules/element-ui/src/utils/popper.js')]
}

切记不要全局修改通用组件的样式

造成全局污染。

如果要改通用组件的样式,除非你确定项目中所有使用到该组件的地方都要统一,否则请局部定制。

嵌套的弹框一定要加 :append-to-body=“true”

<el-dialog :visible="visible1">
<div>
内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容
</div>
<div>
<el-button @click="visible2 = true">打开</el-button>
</div>

<div>
<el-dialog :visible="visible2" :append-to-body="true">
<div>
第二个弹框第二个弹框第二个弹框第二个弹框第二个弹框
</div>
</el-dialog>
</div>
</el-dialog>

否则会出现以下这种情况:虽然弹出的第二个弹框,但是出现了一层 z-index 更高的一个遮罩,将两个弹框都遮住了。

设置在 data 里面的监听属性,赋值时先拷贝再赋值

const a = {
b: {
c: 2
}
};

export default {
created() {
// this.a = a;
// 推荐写法
this.a = JSON.parse(JSON.stringify(a));
},
methods: {
initA() {
// this.a = a;
this.a = JSON.parse(JSON.stringify(a));
}
}
}

请求数据的容错

接口返回的某些字段可能为空的时候。

axios()
.then(res => {
if (res.success) {
// 需要的返回值是对象,兼容返回值为空的情况
this.record = res.data || {}
// 需要的返回值是数组,兼容返回值为空的情况
this.record = res.data || []
}
})

数据渲染的容错

如果接口返回的数据是一个 JSON 的字符串

<!-- 
首先这种写法是不推荐,如果你一定要有这种场景,容错写法如下:
-->
<div v-for="item in JSON.parse(files || '[]')"></div>
<!--
你可以在获取到数据的时候就处理好:
res.data.files = JSON.parse(res.data.files || '[]')
this.data = res.data
-->

获取字段容错

img

多层对象(a.b.c)、多层对象 + 数组取值(a.b[c].d),做好容错

// 多层对象:a.b.c
if (a.b && a.b.c) {}

// 多层对象 + 数组取值:a.b[c].d
if (a.b && a.b[c] && a.b[c].d) {}

通过索引修改data定义的数组项,视图不更新

1、出现问题的原因:

数组和对象是引用类型,只改变其中的值无法触发vue绑在元素(array)上的set方法,无法触发vue观察者,从而无法触发视图更新;涉及vue的双向数据绑定。

官方详解:https://cn.vuejs.org/v2/guide/reactivity.html#%E6%A3%80%E6%B5%8B%E5%8F%98%E5%8C%96%E7%9A%84%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9

2、解决办法 Vue.set,示例:

var vm = new Vue({
data:{
array:[
{id: 1, name:'xx'},
{id: 2, name: 'ss'},
]
}
})

let change = this.array[i]
change.name = 'change name'
vm.$set(vm.array, indexOfItem, change)
// this.$set(this.array, i, change) 全局this写法

除此之外,还可以使用 push、splice 方法

data () {
return {
banners: [1, 2, 3, 4]
}
}

// 添加一个元素
this.banners.push(5) // [1, 2, 3, 4, 5]

// 删除一个元素
this.banners.splice(0, 1) // [2, 3, 4]

// 替换元素
this.banners.splice(0, 1, 6) // [6, 2, 3, 4]

相同标签v-if判断时,无法正确渲染

1、出错场景,如下将不会正确响应事件

<a-button v-if='type==1' @click='tap1'>按钮1</a-button>
<a-button v-else @click='tap2'>按钮2</a-button>

tap1 () {
alert('tap1')
},
tap2 () {
alert('tap2')
},

2、出现问题的原因:相同元素,v-if无法正确判断

官方详解:https://cn.vuejs.org/v2/guide/conditional.html#%E7%94%A8-key-%E7%AE%A1%E7%90%86%E5%8F%AF%E5%A4%8D%E7%94%A8%E7%9A%84%E5%85%83%E7%B4%A0

3、解决办法增加不同的key ,示例:

<a-button v-if='type==1' @click='tap1' key='btn1'>按钮1</a-button>
<a-button v-else @click='tap2' key='btn2'>按钮2</a-button>

对后端返回的数据进行合并

页面需要的一部分数据有默认值,一部分需要从接口获取然后合并到一个 data 值中。

data () {
return {
values: {
name: '张三'
}
}
}

post.then(res => {
// 先对 data 进行容错处理
const data = res.data || {}
// 对其中的值进行非空判断,并赋值
data.users = data.users || []
data.type = data.type || '1'
// 最后将 data 和 this.values 进行合并之后再赋值给 this.values
this.values = {
...this.values,
...data
}
})

局部修改组件库样式的方式

1、使用 scoped + deep 关键字

<template>
<a-card
:bordered="false"
:class="['abnormal-card-view', viewClassName]"
>
<div class="abnormal-view">
<div class="img-box">
<img :src="imgUrl" />
</div>
<div>
<h2 class="status">{{status}}</h2>
<div class="tips">{{tips}}</div>
<a-button
type="primary"
@click="onGotoBack"
>
返回
</a-button>
</div>
</div>
</a-card>
</template>
<style lang="less" scoped>
.abnormal-card-view {
/deep/ .ant-card-body {
height: 100%;
}
&.abnormal-card-user-view {
background-color: transparent;
}
}
</style>

2、外部使用一个唯一的 class 类名包裹

<template>
<a-drawer
:width="width"
:visible="visible"
:title="title"
:closable="true"
:after-visible-change="onAfterClose"
@close="hideModal"
wrap-class-name="drawer-modal-wrap"
>
<a-spin :spinning="loading" class="drawer-modal-content-main">
<a-form-model :model="values" :rules="rules" ref="form" layout="vertical">
<a-row :gutter="48">
<a-col :span="12">
<a-form-model-item label="股票代码" prop="stockCode">
<a-input v-model="values.stockCode" placeholder="请输入" />
</a-form-model-item>
</a-col>
</a-row>
</a-form-model>
</a-spin>
</a-drawer>
</template>
<style lang="less">
.drawer-modal-wrap {
.ant-drawer-wrapper-body {
padding-top: 55px;
}
}
</style>

这种情况不能使用第一种方案,因为节点插入方式不一样,超出了加载器的范围。

表单自定义校验一定要调用 callback 函数

如果不调用,点击提交的时候会出现 this.$refs.form.validate() 函数不触发的问题

img

动态表单联动

场景:a 联动 b、a 联动 b,b 联动 c

<el-form ref="form" :model="values" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol" style="width: 600px">
<el-form-item label="下拉动态切换表单" prop="selectType">
<el-select v-model="values.selectType" placeholder="请选择">
<el-option label="类型一" value="type1"></el-option>
<el-option label="类型一" value="type2"></el-option>
</el-select>
</el-form-item>

// 直接在这里取值进行判断
<template v-if="values.selectType === 'type1'">
<el-form-item el-form-item label="股票代码" prop="code">
<el-input v-model="values.code" placeholder="请输入" />
</el-form-item>
<el-form-item label="涉及证券名称" prop="name">
<el-input v-model="values.name" placeholder="请输入" />
</el-form-item>
</template>
<template v-else>
<el-form-item label="标的金额" prop="money">
<el-input v-model="values.money" placeholder="请输入" />
</el-form-item>
<el-form-item label="涉及投资者交易编码" prop="tradecode">
<el-input v-model="values.tradecode" placeholder="请输入" />
</el-form-item>
</template>

<el-form-item label="中国证监会处罚决定书编号" prop="punishnum">
<el-input v-model="values.punishnum" placeholder="请输入" />
</el-form-item>
<el-form-item label="违法信息披露事实" prop="message">
<el-input v-model="values.message" type="textarea" placeholder="请输入" />
</el-form-item>
<el-form-item :wrapper-col="{span: 14, offset: 7}">
<el-button type="primary" @click="onSubmit" :loading="confirmLoading">
提交
</el-button>
<el-button class="ML10" @click="onReseetForm">
重置
</el-button>
</el-form-item>
</el-form>

data () {
return {
labelCol: { lg: { span: 7 }, sm: { span: 7 } },
wrapperCol: { lg: { span: 10 }, sm: { span: 17 } },
values: {
// 在这里设置默认值
selectType: 'type1',
code: '',
name: '',
money: '',
tradecode: '',
punishnum: '',
message: ''
},
rules: {
code: [{ required: true, message: '不允许为空' }],
name: [{ required: true, message: '不允许为空' }],
money: [{ required: true, message: '不允许为空' }],
tradecode: [{ required: true, message: '不允许为空' }],
punishnum: [{ required: true, message: '不允许为空' }],
message: [{ required: true, message: '不允许为空' }]
},
confirmLoading: false
}
}

forEach

修改原数组,无返回值。

拿到接口数据之后转换成页面需要的数据,或者将数据转换成接口需要的数据结构。

res.data.row.forEach(item => {
// 返回的数据里面有 JSON 字符串,将它转换成 JSON
// "[{fileName: '1.png', filePath: 'a/b/c.png'}]“ =>
// [{fileName: '1.png', filePath: 'a/b/c.png'}]
item.fileInfo = JSON.parse(item.fileInfo)

// 将枚举类型转换成对应的中文
item.statusName = item.status === 0 ? '执行失败' : '执行成功'
})

map

将数据处理成接口想要的格式

返回新数组

// 将时间转化成接口需要的字符串格式
const data = this.values.map(item => ({
...item,
time: $moment(item.time).format('YYYY-MM-DD')
}))
// 输出
// [{name: '张三', time: '2020-05-20'}, {name: '李四', time: '2020-01-01'}]

// 将上传需要的字段取出来
const files = this.files.map(item => {
if (item.response && item.response.code === 0) {
return item.response.data.url
}
return null
}).filter(f => f)
// 输出
// ['a/b/c.png', 'a/c/d.docx']

filter

筛选出符合条件的数据

返回新数组

// 取出所有结果里面成功的数据
const data = this.records.filter(item => item.status === 1)

// 删除
this.records = this.records.filter(item => item.id !== record.id)

find

找出符合条件的数据

返回找到的那条对象数据,没找到返回 null。

// 找出所有数据里面其中符合条件的一条
const item = this.records.find(item => item.status === 1)

// 权限判断
const hasDelete = this.actions.find(action => action === 'delete')

includes

是否包含某个值

返回 true 或 false

// 权限判断
// ['add', 'delete', 'edit']
const hasDelete = this.actions.includes('delete')

// 是否显示某个隐藏的表单
// [1, 2]
this.showName = types.includes(1)

判断是否为空

undefined、null、空字符串、0、NaN 在 if 里面都是 false,如果某个值确定为 false 时一定返回的是空的,可以直接用变量来判断。

if (status) {} // status 要么是 0,要么是 1

if (res.data) {} // res.data 为空的话只可能是 null

if (values.time) {} // 没有设置默认值或者本来就没有值,是 undefined

form中v-model绑定的值无法修改或者设置默认值后表单校验不通过

img

解决办法:

指定 v-model 绑定值的初始值

如果出现调用了 this.***$refs***.***form***.***resetFields***() 方法之后表单无法编辑时,多半也是没有设置默认值造成的。

el-select 获取 label

总是有需要获取下拉列表里面显示的 label 的情况。

// 标签定义
<el-select ref="select"></el-select>

// js 获取
this.$refs.select.selectedLabel

使用 const 和 let 替换 var

区别:

1、var 全局作用域,let、const 块级作用域

2、let、const 不存在变量提升

3、let、const 不会挂载在window下面

const:

1、声明之后必须马上赋值,否则会报错

2、简单类型一旦声明就不能再更改,引用类型(数组、对象)内部数据可以更改。

// 枚举常量
const status = ['成功', '失败', '审批中', '被驳回']
// 设置默认值
const defaultValues = {
type: '自然人'
}

// 根据不同的业务类型有不同的值
let tips = '报错了'
if (res.code === 601) {
tips = '请先登录后再操作'
} else if (res.code === 501) {
tips = '服务器异常'
}
this.$message.error(tips)

尽量使用语义化的命名

js、class 等都遵循这个原则,同时尽量避免通用名称的命名。

<div class="box"></div>

// 尽量加上业务属性
<div class="case-info-box"></div>

<el-dialog ref="modal"></el-dialog>

// 尽量加上业务属性
<el-dialog ref="modalCaseInfo"></el-dialog>

vant 里面去除将 placeholder 转成错误提示的行为

https://github.com/youzan/vant/issues/6378

通过 Form 的 show-error 属性禁用所有 Field 的 error 提示。

computed 的使用

计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。多次访问计算属性会立即返回之前的计算结果,而不必再次执行函数。

表单联动的时候,根据另外一个表单的切换显示不同的下拉选项

客户名称切换的时候,去获取不同的产品名称

img

computed: {
searchProduct () {
if (this.searchValue.company) {
return this.selectValues.productList.filter(item => {
return this.searchValue.company === item.group
})
}
return []
}
}

根据不同的值来进行规则的检测切换

img

computed: {
// 检验规则
searchRules () {
return {
fieldName: [{
required: true, message: '字段不能为空', trigger: 'blur'
}],
operator: [{
required: true, message: '运算符不能为空'
}],
value: [{
required: true, message: '条件不能为空'
}],
min: [{
required: this.searchValues.operator === 'between', message: '最小值不能为空'
}],
max: [{
required: this.searchValues.operator === 'between', message: '最大值不能为空'
}]
}
}
}

refs 的使用

获取组件的实例。

<el-form :model="values" :rules="rules" ref="form"></el-form>

<script>
export default {
methods: {
onClosed () {
this.$refs.form.resetFields()
},
onSubmit () {
const me = this
me.$refs.form.validate(valid => {
if (valid) {

}
})
}
}
</script>

加载数据

<ex-table-auto class="el-table-nowwarp" ref="authListGrid" url='/asama/user/UserAuthInfoRpc/authList.json' autoFill autoPage emptyText="暂无符合条件的审核信息"></ex-table-auto>

<script>
export default {
methods: {
doAuth (status) {
var me = this
var data = {
authId: me.authForm.id,
result: status
}
me.$ajax({
url: '/asama/user/UserAuthInfoRpc/doAuth.json',
data: data,
success (response) {
me.showDetailDialog = false
me.authForm = {}
// 重新刷新数据
me.$refs.authListGrid.loadData(this.searchValue)
}
})
}
}
}
</script>

获取实例里面的属性,例如获取 select 里面的 label

// 标签定义
<el-select ref="select"></el-select>

// js 获取
this.$refs.select.selectedLabel

nextTick 的作用

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

如果发现实际要获取 refs 定义的实例取不到时,可以尝试在 $nextTick 方法里面试试。

// 修改数据
this.msg = 'Hello'
// DOM 还没有更新
this.$nextTick(() => {
// DOM 更新了
})

v-show 和 v-if 的区别

都是用来显示或者隐藏元素的,v-show 使用的是 css 中 display,v-if 是对节点的添加或删除。

// if/else/else-if
<font v-if="scope.row.copies=='one'">原被告共用一份</font>
<font v-else-if="scope.row.copies=='each'">原被告各一份</font>
<font v-else>原被告共用一份</font>


// 存在列表再展示
<el-table v-if="formValue.case_infos.length > 0" :data="formValue.case_infos"></el-table>


// 附件内容展示
<div v-if="item.attachmentShow">
<label>附件:</label>
<div v-for="(file,i) in item.attachmentShow" :key="i"><a>{{file.fileName}}</a></div>
</div>


// 组件之间的切换
<step1 v-show="currentTab === 0" :values="values" :on-next-step="() => currentTab = 1" />
<step2 v-show="currentTab === 1" :values="values" :on-next-step="() => currentTab = 2" :on-prev-step="() => currentTab = 1" />
<step3 v-show="currentTab === 2" :values="values" />

vue 中冒号和艾特符号的区别

冒号传递属性,@ 符号传递方法

<template>
<a-modal
:width="width"
:visible="visible"
:title="title"
:after-close="onClosed"
@cancel="onHideModal"
@ok="onHideModal"
>
内容
</a-modal>
</template>

<script>
export default {
data () {
return {
/**
* 1、宽度可选值:720、560、320
* 2、操作以简单为主,需要内滚动或是长时间的操作,或是展开内容信息较大且和当前页面的场景联系不大时
* 建议使用一般的页面代替
* 3、主要按钮居右,次要按钮居左
*/
width: 560,
visible: false,
title: '弹框标题'
}
},
methods: {
showModal () {
this.visible = true
},
onHideModal () {
this.visible = false
},
onClosed () {
console.log('完全关闭之后的回调处理,一般用于清空表单的操作')
}
}
}
</script>

冒号传递的,通过 this.xxx 访问,艾特符号传递的,通过 this.$emit(‘xxx’) 调用

// el-dialog.vue

console.log(this.title)
this.$emit('cancel')

== 和 ===(非全等和全等)

项目中涉及的判断尽量都用 === 替换 ==,尽量遵循严格模式。

=== 表示类型和值相等, == 仅表示值相等。

// 如果实际返回的是字符串类型的 1,但是用了数字类型的 1 做了判断
'1' == 1 // true
'1' === 1 // false

通过 this.xxx 设置数据偶发的不生效

按照官方写法或者复制其他地方的代码过来总是有问题怎么办

对照写法检查属性是不是遗漏了什么

复制过来的代码,出现某个控件显示异常时,去官方文档对照一下 api,看下是不是少了什么属性,比如:a-select(el-select) 组件,子组件是靠 value 属性来匹配选中值的,而有些地方的写法就少了这个属性,导致无选中的奇怪 bug。

下拉 value 数据类型问题

value 字段如果是变量前面是需要添加冒号的,如果是静态数据且值是数字,务必注意取值的判断。

<el-select v-model="value" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>

a-tree 的 select 里面如何获取父级数据

select 里面的回调 keys

template 绑定 data 中的数据不需要用 this 指定

<span :id="this.id">错误写法</span> // 错误写法
<span :id="id">正确写法</span> // 正确写法
data(){
return {
id:1
}
}

form model 绑定的值不要删除,会导致出现未知的渲染错误

<el-form :model="values">

</el-form>

data () {
return {
// 绑定到 el-form 的 model 属性变量
values: {
files: []
}
}
}

onSubmit () {
// ...
// 错误示例
this.values.fileName = this.values.files[0].fileName
this.values.filePath = this.values.files[0].filePath
delete this.values.files
this.$ajax({
...,
data: this.values
})

// 如果接口需要的数据需要进行处理,用一个变量来进行接收,而不是直接改动使用到的绑定给 model 的变量
const data = {}
const { files } = this.values
data.fileName = files[0].fileName
data.filePath = files[0].filePath
this.$ajax({
...,
data
})
}

输入框删除字符鼠标聚焦位置自动跳转到最后

升级 ant-design-vue 到 1.7.2 版本。

时间字段发送到后端对不上

已提供 ex-date-picker 组件,请优先使用。

1、如果是 element-ui(具体格式看这里),给组件设置 value-format 字段:

<el-date-picker v-model="formValue.planOnlineTime" value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker>

2、在调用接口之前,调用 moment 转换:

const data = {
...this.formValue
}
// 如果没有 $moment,自己 import 一下,像这样:
// import moment from 'moment'
// data.planOnlineTime = moment(data.planOnlineTime).format('YYYY-MM-DD HH:mm:ss')
data.planOnlineTime = this.$moment(data.planOnlineTime).format('YYYY-MM-DD HH:mm:ss')
this.$ajax({
url: '/apollo/app/BugRpc/saveUpdate.json',
method: 'post',
data,
success: (rs) => {

}
})

注意:转换之后后端通过字符串形式接收

table + upload 使用时,upload 失效问题

hg-table + upload 组件一起使用时,upload 组件绑定的值不会更新,因为 hg-table 里面维护了 data-source,这个值并没有双向绑定返回。

js 里面写法:

data () {
dataSource: []
}

loadData () {
this.$http()
.then(res => {
this.dataSource = res.data.rows
return res
})
}

vue 里面写法:

<hg-table :load-data="loadData">
<template slot-scope="text, record, indedx">
<hg-upload v-model="dataSource[index].files"></hg-upload>
</template>
</hg-table>

前端通过文件流下载文件问题

某些场景下,需要前端读取到文件流自己下载文件。比如 https 协议网页要下载不同域下的 http 协议文件,通过 window.open 会被拦截,而 form 方式有些文件又会变成预览,比如说 pdf 文件

基于 antd 的脚手架里面,使用 $http,指定 headerType 是 download 即可。

$http({
// 防止出现协议问题,这里不要写具体的 http 或者 https
url: '//haigui-static.oss-cn-hangzhou.aliyuncs.com/test-dedicated/10.pdf',
headerType: 'download',
method: 'get',
// 不指定默认从 content-disposition 头读取,后端接口也必须要设置 content-disposition 才能读取到
fileName: 'download.docx'
})
.then(res => {

})

项目中如果没有 http.js,可通过 hg init 初始化脚手架之后,去 utils/http.js 里面复制代码。

使用 a-upload 如何使用 form-model-item 自带的错误校验

form-model-item 里面加一个 input,type 是 hidden,change 的时候更新 values.fileList(处理成上传需要的数据格式),同时更新 fileList(用于显示,直接赋值)

<a-form-model-item label="中国证监会处罚决定书编号" prop="fileList">
<a-upload-dragger action="https://www.mocky.io/v2/5cc8019d300000980a055e76" list-type="picture-card" :file-list="fileList" @change="onFileChange">
<div>
<a-icon type="plus" />
<div class="ant-upload-text">
上传
</div>
</div>
</a-upload-dragger>
<a-input type="hidden" :value="JSON.stringify(values.fileList)"></a-input>
</a-form-model-item>
<script>
export default {
data () {
return {
values: {
fileList: []
},
fileList: [],
rules: {
fileList: [{
required: true,
message: '文件不允许为空',
trigger: 'blur'
}]
}
}
},
methods: {
onFileChange (info) {
const status = info.file.status
this.fileList = info.fileList
if (status === 'done') {
this.$message.success(`${info.file.name} file uploaded successfully.`)
this.values.fileList = this.fileList.map(item => item.response.data ? item.response.data : item)
} else if (status === 'error') {
this.$message.error(`${info.file.name} file upload failed.`)
}
}
}
}
</script>

element-ui 里面的 el-upload 写法参照:https://blog.csdn.net/HTingJing/article/details/107960921

JS中循环(forEach,forIn,forOf)总结

  • forEach 遍历列表值,不能使用 break 语句或使用 return 语句
  • for in 遍历对象键值(key),或者数组下标,不推荐循环一个数组
  • for of 遍历列表值,允许遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代的数据结构等.在 ES6 中引入的 for of 循环,以替代 for in 和 forEach() ,并支持新的迭代协议。
  • for in循环出的是key,for of循环出的是value;
  • for of是ES6新引入的特性。修复了ES5的for in的不足;
  • for of不能循环普通的对象,需要通过和Object.keys()搭配使用。