美文网首页
开始做全栈---前端的碎碎念2.0

开始做全栈---前端的碎碎念2.0

作者: cc_daily | 来源:发表于2020-03-27 17:57 被阅读0次

本次写一下我在开发过程中遇到的使用频率高点的部分以及小技巧。或者基础知识。

1.上传下载走过的坑

首先参看elementUI官方关于<el-upload>组件的文档 https://full-ui.fast-daily.tuya-inc.cn/#/zh-CN/component/upload

开发中参考文档如下:https://www.cnblogs.com/sinosaurus/p/9266060.html

image

一、上传文件实现

两种实现方式:

1、直接action


<el-upload

  class="upload-file"

  drag

  :action="doUpload"

  :data="pppss">

  <i class="el-icon-upload"></i>

  <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>

</el-upload>

:action,必选参数,上传的地址,String类型。data()需要使用代理转发,要不然会有跨域的问题

:data,上传时附带的额外参数,object类型。用于传递其他的需要携带的参数,比如下面的srid


data(){

    return {

        ,doUpload:'/api/up/file'

        ,pppss:{

            srid:''

        }

    }

},

2、利用before-upload属性

此种方式有个弊端,就是action是必选的参数,那么action如果和post的url一致,总会请求2次,所以一般把action随便写一个url,虽然不影响最终效果,但是这样会在控制台总有404错误报出


<el-upload

  class="upload-file"

  drag

  :action="doUpload"

  :before-upload="beforeUpload"

  ref="newupload"

  multiple

  :auto-upload="false">

  <i class="el-icon-upload"></i>

  <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>

</el-upload>

beforeUpload(file){

    let fd = new FormData();

    fd.append('file',file);//传文件

    fd.append('srid',this.aqForm.srid);//传其他参数

    axios.post('/api/up/file',fd).then(function(res){

            alert('成功');

    })

},

newSubmitForm(){//确定上传

    this.$refs.newupload.submit();

}

二、常用方法介绍

1、动态改变action地址

action是一个必填参数,且其类型为string,我们把action写成:action,然后后面跟着一个方法名,调用方法,返回你想要的地址,实现动态的去修改上传地址


//html 代码

<el-upload  :action="UploadUrl()"  :on-success="UploadSuccess" :file-list="fileList">

    <el-button size="small" type="primary" >点击上传</el-button>

</el-upload>

// js 代码在 methods中写入需要调用的方法

methods:{

    UploadUrl:function(){

        return "返回需要上传的地址";   

    } 

}

2、在文件上传前做类型大小等限制

(1)一种方式是,加accpet属性

image

(2)另一种方式是在上传前的触发函数里面去判断


beforeAvatarUpload(file) {

    const isJPG = file.type === 'image/jpeg';

    const isGIF = file.type === 'image/gif';

    const isPNG = file.type === 'image/png';

    const isBMP = file.type === 'image/bmp';

    const isLt2M = file.size / 1024 / 1024 < 2;

    if (!isJPG && !isGIF && !isPNG && !isBMP) {

        this.common.errorTip('上传图片必须是JPG/GIF/PNG/BMP 格式!');

    }

    if (!isLt2M) {

        this.common.errorTip('上传图片大小不能超过 2MB!');

    }

    return (isJPG || isBMP || isGIF || isPNG) && isLt2M;

},

3、同时传递form表单及有多个upload文件该如何传递


newSubmitForm () {

  this.$refs['newform'].validate((valid) => {

    if (valid) {

      //表单的数据

      this.uploadForm.append('expName', this.newform.expName)

      this.uploadForm.append('expSn', this.newform.expSn)

      this.uploadForm.append('groupId', this.newgroupId)

      this.uploadForm.append('subGroupId', this.newsubgroupId)

      this.uploadForm.append('expvmDifficulty', this.newform.expvmDifficulty)

      newExp(this.uploadForm).then(res => {

        if (res.code === 400) {

          this.$message.error(res.error)

        } else if (res.code === 200) {

          this.$message.success('上传成功!')

        }

      })

      this.$refs.uploadhtml.submit()  // 提交时分别触发各上传组件的before-upload函数

      this.$refs.uploadfile.submit()

      this.$refs.uploadvideo.submit() 

    } else {

      console.log('error submit!!')

      return false

    }

  })

},

newHtml (file) {  // before-upload

  this.uploadForm.append('html', file)

  return false

},

newFiles (file) {

  this.uploadForm.append('file[]', file)

  return false

},

newVideo (file) {

  this.uploadForm.append('video', file)

  return false

}

export function newExp (data) {

  return axios({

    method: 'post',  // 方式一定是post

    url: '你的后台接收函数路径',

    timeout: 20000,

    data: data        // 参数需要是单一的formData形式

  })

}

注意:(1)对于多个上传组件来说,需要分别触发,去给FormData append数据

(2)接收多文件一定要是数组形式的file[],this.uploadForm.append('file[]', file)

4、如何传递文件和其他参数

就像第一节那样,如果不使用action实现上传,而使用before-upload属性也能实现上传的效果。

before-upload属性,这是一个function类型的属性,默认参数是当前文件,只要能传递这个文件也能实现效果

要传递这个方法就需要new一个formdata对象,然后对这个对象追加key和value,类似于postman测试时那样。

另外注意:传递formdata和data不能一起传递,要传递formdata就不能有data,所以对于其他参数的传递,也要改为


beforeUpload (file,id) {

    let fd = new FormData()

    fd.append('file', file)

    fd.append('id',id)//其他参数

    axios.post(url, fd, {

    })

},

而不能使用这种又有FormData,又有data的模式


beforeUpload (file,id) {

        let fd = new FormData()

        fd.append('key', file, fileName)

        axios.post(url, fd,{

          data:{

          id:id

          },

          headers: {

          'Content-Type': 'multipart/form-data'

          }

        })

    },

我开发中的案例如下:


<el-form :model="cVO" :rules="uploadRules" ref="uploadRulesRef">

      <el-form-item

        label="名称"

        :label-width="120px"

        prop="xxx"

      >

        <el-input

          v-model="cVO.xxx"

          autocomplete="off"

          placeholder="请填写名称"

          class="form-item"

        ></el-input>

      </el-form-item>

<el-form-item

        label="上传"

        prop="url"

        :label-width="120px"

      >

        <el-upload

          class="form-item"

          drag //是否启用拖拽上传

          action="https://jsonplaceholder.typicode.com/posts/" //上传的路径

          :before-upload="beforeUpload"

          :file-list="fileList"

          ref="uploadRef"

        >

          <div>

            <i class="el-icon-upload"></i>

            <div class="el-upload__text">

              将文件拖到此处,或<em>点击上传</em>

            </div>

          </div>

        </el-upload>

      </el-form-item>

</el-form>


beforeUpload(zipFile) {

      this.$refs['uploadRulesRef'].validate((valid) => {

        if (!valid) {

          this.fileList = []

          return false

        }

        let fd = new FormData()

        fd.append('zipFile', zipFile)

        fd.append('xxx', this.cVO.xxx)

        import(fd)

          .then(() => {

            this.$message.success('上传成功!')

          })

          .catch(() => {

            return false

          })

      })

    },


@RequestMapping(value ="import", method = { RequestMethod.POST })

@ResponseBody

public import( @RequestParam(value ="zipFile", required =false) MultipartFile multipartZipFile,

                       @RequestParam(value ="xxx", required =false) String xxx,

                        HttpServletRequest request)

2.解构赋值(es6)

在ES5中,开发者们为了从对象和数组中获取特定数据并赋值给变量,编写了许多看起来同质化的代码,如下例子。

let options = {
    repeat: true,
    save: false
};
// 从对象中提取数据
let repeat = options.repeat,
save = options.save;

这段代码从options对象中提取repeat和save的值,并将其存储为同名局部变量,提取的过程极为相似,如果要提取更多变量,则必须依次编写类似的代码来为变量赋值,如果其中还包含嵌套结构,只靠遍历是找不到真实信息的,必须要深入挖掘整个数据结构才能找到所需数据,所以ES6添加了解构功能,将数据结构打散的过程变得更加简单,可以从打散后更小的部分中获取所需信息。

对象解构

let node = {
    type: "Identifier",
    name: "foo"
},
type = "Literal",
name = 5;
function outputInfo(value) {
    console.log(value === node); // true
}
outputInfo({ type, name } = node);
console.log(type); // "Identifier"
console.log(name); // "foo"

在这个示例中,声明变量type和name时初始化了一个值,在后面几行中,通过解构赋值的方法,从node对象读取相应的值重新为这两个变量赋值。

[注意]1.一定要用一对小括号包裹解构赋值语句,JS引擎将一对开放的花括号视为一个代码块。语法规定,代码块语句不允许出现在赋值语句左侧,添加小括号后可以将块语句转化为一个表达式,从而实现整个解构赋值过程。
2.解构赋值表达式(也就是=右侧的表达式)如果为null或undefined会导致程序抛出错误。也就是说,任何尝试读取null或undefined的属性的行为都会触发运行时错误。

解构赋值表达式的值与表达式右侧(也就是=右侧)的值相等,如此一来,在任何可以使用值的地方都可以使用解构赋值表达式。
当出现嵌套对象时,node.loc.start被存储在了新的局部变量localStart中。解构模式可以应用于任意层级深度的对象,且每一层都具备同等的功能。

let node = {
    type: "Identifier",
    name: "foo",
    loc: {
        start: {
            line: 1,
            column: 1
        },
    end: {
        line: 1,
        column: 4
    }
}
};
let { loc: { start }} = node;
console.log(start.line); // 1
console.log(start.column); // 1
let { loc: { start: localStart }} = node;
console.log(localStart.line); // 1
console.log(localStart.column); // 1

数组解构

与对象解构的语法相比,数组解构就简单多了,它使用的是数组字面量,且解构操作全部在数组内完成,而不是像对象字面量语法一样使用对象的命名属性。我们通过值在数组中的位置进行选取,且可以将其存储在任意变量中,未显式声明的元素都会直接被忽略。

let colors = [ "red", "green", "blue" ];
let [ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
let [ , , thirdColor ] = colors;
console.log(thirdColor); // "blue"
firstColor = "black",
secondColor = "purple";
[ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"

这段代码使用解构赋值语法从colors中获取第3个元素,thirdColor前的逗号是前方元素的占位符,无论数组中的元素有多少个,都可以通过这种方法提取想要的元素,不需要为每一个元素都指定变量名。
数组解构语法还有一个独特的用例:交换两个变量的值。在排序算法中,值交换是一个非常常见的操作,如果要在ES5中交换两个变量的值,则须引入第三个临时变量。如果使用数组解构赋值语法,就不再需要额外的变量了。

// 在 ES5 中互换值
let a = 1,
  b = 2,
  tmp;
tmp = a;
a = b;
b = tmp;
console.log(a); // 2
console.log(b); // 1
// 在 ES6 中互换值
let a = 1,
    b = 2;
[ a, b ] = [ b, a ];
console.log(a); // 2
console.log(b); // 1

在ES5中,开发者们经常使用concat()方法来克隆数组,在ES6中,可以通过不定元素的语法来实现相同的目标 。

// 在 ES5 中克隆数组
var colors = [ "red", "green", "blue" ];
var clonedColors = colors.concat();
console.log(clonedColors); //"[red,green,blue]"
// 在 ES6 中克隆数组
let colors = [ "red", "green", "blue" ];
let [ ...clonedColors ] = colors;
console.log(clonedColors); //"[red,green,blue]"

函数具有不定参数,而在数组解构语法中有一个相似的概念——不定元素。在数组中,可以通过...语法将数组中的其余元素赋值给一个特定的变量

let colors = [ "red", "green", "blue" ];
let [ firstColor, ...restColors ] = colors;
console.log(firstColor); // "red"
console.log(restColors.length); // 2
console.log(restColors[0]); // "green"
console.log(restColors[1]); // "blue"

其他解构

字符串也可以解构赋值。这是因为,字符串被转换成了一个类似数组的对象。

const [a, b, c, d, e] = 'hello';
console.log(a);//"h"
console.log(b);//"e"
console.log(c);//"l"
console.log(d);//"l"
console.log(e);//"o"
const {length} = 'hello';
console.log(length);//5

数值和布尔值解构也可以结构赋值,如果等号右边是数值和布尔值,则会先转为对象解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

let {toString:s1} = 123;
console.log(s1 === Number.prototype.toString);//true
let {toString:s2} = true;
console.log(s2 === Boolean.prototype.toString);//true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

3.枚举

在后端为了一些固定值得字段重复使用,有着枚举的概念。使用枚举能够提高代码维护性,确保变量合法,提高代码可读性。后端的枚举类大多是为了各个模块的交互使用,或者是可读性等等,前端试用期使用枚举不仅仅可以有这些作用,同时可以用作前端下拉框,单选框,复选框的使用,更加直观。
在common或者个人模块下创建单独枚举js,如enums.js。然后导入到页面@/common/Enum.js并添加一个字面量对象。

logType: new Enum()
    .add('INFO', '普通级别', 1)
    .add('WARN', '警告级别', 2)
    .add('ERROR', '错误级别', 3)

如需要表单选择可如下使用:

<el-select v-model="query.status" clearable placeholder="请选择">
  <--  传统做法   -->
   <el-option value="1" :label="普通级别 "></el-option>
   <el-option value="2" :label="警告级别 "></el-option>
   <el-option value="3" :label="错误级别 "></el-option>

  <--  基于枚举类的方法 -->
   <el-option v-for="item in enums.logType" 
             :key="item.value" :value="item.value" :label="item.label">
   </el-option>
</el-select>

如需页面以及table显示枚举lable可如下使用:

//可以避免为每个枚举值进行判断后,再取其label,后端返回staus:1
<el-table-column prop="status" label="状态">
   <template slot-scope="scope">
       <--  传统做法:定义function通过switch或者if判断并返回label   -->
      {{ switch(scope.row.status) case 0:... case 1... case 2.... }}

       <--  基于枚举类的方法 -->
      {{ enums.logType.getLabelByValue(scope.row.status) }}
   </template>
</el-table-column>

4.组件的提取复用

作为一个好的前端,许需要具备的技能就是合理的抽取可复用写作自己的组件。既可以提高代码简洁性,如果后期需要修改功能又可以方便后期维护。我也在逐步的提高自己这方面的能力。提取组件的前提是能很好的学会父子组件的传值。
4.1搜索选择框
一般都是用在模糊搜索匹配上。比如很多公司内部项目需要关联某些人员,比如增加数据管理员,操作人,负责人之类的操作。就可以单独抽取一个支持模糊搜索的下拉框作为通用组件重复使用。

<el-select
    v-model="userInfo"
    filterable
    remote  //远程搜索
    multiple  //多选
    :loading="this.load"
    placeholder="请输入姓名或者工号"
    :remote-method="getUsers"
    @clear="clear" //可清除
    @change="changeValue" //有需要自己定义
  >
    <el-option
      v-for="item in userList"
      :key="item.username"
      :label="item.nick"
      :value="item.username"
    >
    </el-option>
  </el-select>

//js部分
clear() {
      this.userName = ''
    },
    getUsers(value) {
      setTimeout(() => {
        this.load = false
        if (value != '') {
          this.getUserApi(value)
        }
      }, 200)   //防抖
    },
    getUserApi(value) {
      searchUser(value)
        .then((res) => {
          this.userList = res
        })
        .catch((error) => {
          this.$message(error, 'error')
        })
    }

4.2验证校验框
很多时候我们在调用一些接口需要校验身份,或者先去确认一下数据权限之类的,就需要做这个啦。

<el-dialog
    :visible.sync="showCheckDialog"
    @close="closeImport"
    append-to-body
    title="校验身份"
  >
    <el-form :model="checkInfo">
      <el-form-item label="验证码" :label-width="formLabelWidth">
        <el-input
          class="form-item-class"
          placeholder="请输入验证码"
          v-model="checkInfo.pwd"
          clearable
        >
        </el-input>
      </el-form-item>
      <el-form-item :label-width="formLabelWidth">
        <el-button
          plain
          type="primary"
          icon="el-icon-download"
          @click="updown()"
          >下载</el-button
        >
      </el-form-item>
    </el-form>
  </el-dialog>
</template>

//js部分
closeImport() {
      this.showCheckDialog = false
      this.$emit('closeCheckDialog')
    },
updownCertificateCrt(request) {
      download(request)
        .then((response) => {
          if (response) {
            const content = response.data
            const blob = new Blob([content])
            const fileName = response.fileName
            if ('download' in document.createElement('a')) {
              // 非IE下载
              const elink = document.createElement('a')
              elink.download = fileName
              elink.style.display = 'none'
              elink.href = URL.createObjectURL(blob)
              document.body.appendChild(elink)
              elink.click()
              URL.revokeObjectURL(elink.href) // 释放URL 对象
              document.body.removeChild(elink)
            } else {
              // IE10+下载
              navigator.msSaveBlob(blob, fileName)
            }
            this.$message({
              message: '下载成功',
              type: 'success'
            })
            this.closeImport()
          }
        })
        .catch((error) => {
          this.$message(error, 'error')
        })
    }

5.开发中方便实用的小技巧

1.去重

//2.选择某个作为主键去重 
function unique(arr) {
      const res = new Map()
      return arr.filter(
        (arr) => !res.has(arr.id) && res.set(arr.id, 1)
      )
    }
  },

//2.es6set去重
function unique (arr) {
 return Array.from(new Set(arr))
}

//3.类似方法二的简化版
[...new Set(arr)]
//其实就是以下代码的简化版
function distinct(a, b) {
 return Array.from(new Set([...a, ...b]))
}

2.一键复制功能实现

<el-button
          :data-clipboard-text="this.info"
          @click="onCopy"
          >复制</el-button
        >

//js部分
import Clipboard from 'clipboard'  //引入复制组件

onCopy() {
      var clipboard = new Clipboard('.tag-read')
      clipboard.on(this.$message.success('复制成功!'), () => {
        // 释放内存
        clipboard.destroy()
      })
    }

相关文章

  • 开始做全栈---前端的碎碎念2.0

    本次写一下我在开发过程中遇到的使用频率高点的部分以及小技巧。或者基础知识。 1.上传下载走过的坑 首先参看elem...

  • 开始做全栈---前端的碎碎念1.0

    1.开发背景 最近有一个新的前后端分离的项目需求,之前虽然有维护前后端分离的项目,但是自己没有从0到1的经验。从框...

  • 前端碎碎念 之 nextTick, setTimeout 以及

    转载自https://segmentfault.com/a/1190000008595101 『前端碎碎念』系列会...

  • 碎碎念2.0

    去年的这个时候是最忙的时候,忙着暑假的团,虽然忙碌却又充实。今年的6月应该是上班4年以来最轻松的。当然,也是...

  • 碎碎念2.0

    我想不到这个月的文章主题是什么。 分享生活?分享美食?还是分享心情?好像都有些无聊。转念想想,本来就是记录生活的一...

  • 碎碎念2.0

    你是我最后悔认识的人。 那年冬天还是一如既往的冷,寒风刺骨,你的爱穿越​七百多公里的距离温暖我,让我倍感幸福。大人...

  • 关于找到发力点的“三环模型”

    美力帮的姑娘们,大家晚上好呀 我们开始今晚的碎碎念时刻,昨天给大家碎碎念了我杜撰的概念"全覆盖级穿衣"。其实就是让...

  • 主题式碎碎念清单(预习)

    主题式碎碎念是日常碎碎念的升级版。 这几天开始尝试做的是日常的碎碎念记录,我目前能写出蛮多的碎碎念,但是有一大部分...

  • 心若明镜

    昨晚和孩子躺在床上,我们开始聊天, “外公外婆都喜欢碎碎念,妈妈有没有碎碎念呢?” “妈妈一样碎碎念。” “那妈...

  • 今天下雪了

    从今天开始,重新出发。 学习碎碎念,其实也一直想碎碎念,可能碎碎念能排空我心里的垃圾,也能让我重新找自己吧! 看了...

网友评论

      本文标题:开始做全栈---前端的碎碎念2.0

      本文链接:https://www.haomeiwen.com/subject/flrruhtx.html