2020 前端生涯第一年遇到的小问题

1,183 阅读6分钟

2020 步入工作的第一年,有很多不懂的地方,一开始不懂的很多,经常看一下同事的代码思考怎么写,基本上有点照抄模式;做不一样的部分,自己就会写的有点复杂化和低复用,当然慢慢写着也有点小领悟。在工作的过程中遇到了大大小小的问题,大多数的问题最后都得到了解决,我都把这些点都记起来了,在这里做一个总结。

小程序

1.微信小程序的wx.request()的PUT方法请求,后端接收不到任何数据

由于接触小程序不深,写wx.request()还是遇到了一些问题的就是关于请求的header的content-type的值。 官方文档是这样写,但是我遇到了另一种情况,就是请求PUT的时候,后端一直和我说,他收不到任何我前端传送过去的数据data,其实我看到network已经请求成功的,由于小白问了,他也是说真的没收到,这样就尴尬了,我也不太懂后端。

data 参数说明
最终发送给服务器的数据是 String 类型,如果传入的 data 不是 String 类型,会被转换成 String 。转换规则如下:

对于 GET 方法的数据,会将数据转换成 query string(encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)...)
对于 POST 方法且 header['content-type'] 为 application/json 的数据,会对数据进行 JSON 序列化
对于 POST 方法且 header['content-type'] 为 application/x-www-form-urlencoded 的数据,会将数据转换成 query string (encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)...)

本来我一直用的是一个封装好的request请求方法,但是PUT方法的接口对接不成功,后来我就只一直查文档,官方文档还是没提到这个问题,只提到了POST, 最后只能随便试试了,我把header['content-type'] 为 application/json换成了header['content-type'] 为 application/x-www-form-urlencoded. 这只是简化版,逻辑并不严密,使用的时候还是要逻辑严密点。这里这个问题就这样解决了,但是问题我也不知道是不是真的是前端问题,有清楚的XDM可以评论指点一下。


// data: {
//    isPass: "0",// 通过1,驳回0
//    Id: this.data.id,
//    remark: this.data.inputValueNo
// },
// method : "PUT"
let link = 'http://**.**.***.***:8081';
http(url, method, params) {
let data = { ...params.data }
wx.request({
      url: link + url,
      method: method,
      data,
      header: {
        'content-type': 'application/x-www-form-urlencoded',
        'Authorization': token
      },
      success(res) {
        params.success && params.success(res.data)
      },
      fail(err) {
        params.fail && params.fail(err)
      }
    })
  }

2.微信小程序调用指纹验证

这个是简单的本地验证,是调用设备的指纹接口。官方提供了三个接口文档
检查当前设备是否支持生物识别 wx.checkIsSupportSoterAuthentication,
获取设备内是否录入如指纹等生物信息的接口 wx.checkIsSoterEnrolledInDevice,
开始 SOTER 生物认证 wx.startSoterAuthentication

这个指纹验证只是适用简单的安全的验证,因为设备本地的,假如客户端被破解,就GG。想要安全性高的,还是要把获取的到的生物信息返回给小程序后台,进一步验证,我只是一个小白前端,后端不懂,就不写怎么进一步验证了了。但是这个验证已经在大部分场景适用了,只要不是金融关于钱交易的场景应用,都没有什么问题的,可以方便了不用在输入密码二次验证

  checkedFingerPrin: function() {
    let _this = this;
    if (wx.canIUse('checkIsSupportSoterAuthentication')) { 
      // 获取本机支持的 SOTER 生物认证方式
      wx.checkIsSupportSoterAuthentication({
        success(res) {
          // res.supportMode : ['fingerPrint', 'facial'] 支持指纹识别和人脸识别
          if(res.supportMode.indexOf("fingerPrint") > -1 ){
            wx.checkIsSoterEnrolledInDevice({
              checkAuthMode: 'fingerPrint',
              success(res) {
                console.log(res.isEnrolled);// boolean	是否已录入信息
                // 开始 SOTER 生物认证
                wx.startSoterAuthentication({
                  requestAuthModes: ['fingerPrint'],
                  challenge: "123456",
                  authContent: '请用指纹解锁',
                  success(res) {
                    // res 中包含授权数据,需要进一步验证正确性
                    console.log('指纹解锁', res);
                    _this.getInfo();//验证成功,获取****(自己小程序)信息
                  },
                  fail(res){
                    console.log('用户取消了指纹识别,或调用出现错误');
                    _this.getCaptchaImage();
                  }
                })
              }
            })
          }else{
            console.log('当前设备不支持指纹识别')
          }
        }
      })
    } else {
      console.log("当前设备不支持指纹识别")
    }
  },

3.小程序的过滤器怎么写

第一,在小程序目录下新建这样的目录,已有就不用新建了,dict.wxs、、toThousandFilter.wxs就是我们需要的过滤器,这个过滤器制作我还不是很熟悉,所以我把它们一个方法一个文件的分开建立。以toThousandFilter.wxs为例:

第二,在toThousandFilter.wxs中,用js实现过滤方法,这个方法内容在PC部分的《vue金额的格式化----过滤器》中,就不粘贴了。这个很重要啊:WXS文件es6语法不能使用

var bill = {
  toThousandFilter: function(val) {
    // 因为不能用es6 所以第一行改成这样
    var reg = getRegExp('/\-|\,/','g');
    val = ((+val || 0) / 100).toString().replace(reg, '');
    // 。。。
    // 在PC部分的《vue金额的格式化----过滤器》中
  }
}

module.exports = {
  toThousandFilter: bill.toThousandFilter
}

第三,使用
在业务页面wxml中引用wxs,然后使用filter

<view class="feeNumb"><wxs module="bill" src="../../../utils/filter/toThousandFilter.wxs"></wxs>
	<text>{{bill.toThousandFilter(item.fee) || ''}}</text>
</view>

4.微信小程序设置open-data的样式

open-data组件是微信小程序的默认授权展示用户信息的组件。但是它的样式固定的,然而需求是要圆的,一开始以为很简单,改个样式圆角就好,然后发现这个加class不行,我还以为我编译器出问题了,试了很多次都是改变不了样式,只能另辟蹊径,于是想着我在这个组件上层加多两个view改一下样式覆盖,居然这样样式又成功了。我就奇怪了,为啥这个元素不能改样式宽高不能改圆边框?突然想起行内元素就是这样的,然后我去一查发现设置了display:inline,是没有办法设置宽高的,所以只要把 open-data 的display设置为 inline-block 或 block 后才能设置它的宽高,然后就可以随便改了,但是在这里说一嘴,微信这样做可能是为了尽最大可能去兼容不一样的open-data类型type的,还是不要整体改变,css有一个属性选择器,

open-data[type="userAvatarUrl"] {
  display: block;
  overflow: hidden;
}

PC

1.Vue跳转页面打开新窗口

这个就用到了$router.push,本来是这样写得this.$router.push({ path: '/home' }),查了一下vue好像没有给定一个参数去打开新页面,而打开新网站都可以,但是都是在这个当前窗口操作更换url,后面思考发现想要打开新的页面其实是在浏览器上操作的,然后一查发现了window.open,对js理解不深,基本只在面试的时候看了一边红宝书,这个在2021年要主攻的地方,所以就可以这样写了:

let route = this.$router.resolve({ path: '/home' });
window.open(route.href, '_blank');

2.获取url中的参数

有时候url中是带参数的,我们需要用他的值来做一些判断,但是又没有用框架的时候就需要自己去截取url了。 例如:url:http://localhost:8080/index.html?id=965&date=1024&name=chenxuyuan

function getURLParam(paramName)
{
   //构造一个含有目标参数的正则表达式的对象
   var reg = new RegExp("(^|&)" + paramName + "=([^&]*)(&|$)");
   //匹配目标参数
   var url = window.location.search.substr(1).match(reg);
  //返回参数值
  if(url != null)
   return unescape(url[2]);
  return null;
}

var type = getURLParam(name); // chenxuyuan

3.vue的@click.native 原生点击事件、阻止默认事件和冒泡

原生点击事件
给vue组件绑定事件时候,必须加上native ,不然不会生效(监听根元素的原生事件,使用 .native 修饰符)
等同于在自组件中:子组件内部处理click事件然后向外发送click事件:$emit("click".fn)

//子组件照常写,不作任何事件绑定,删掉methods
<svg :class="svgClass" aria-hidden="true">
  <use :xlink:href="iconName"></use>
</svg>
 
//父组件
<svg-icon icon-class="user" @click.native="svgClick"></svg-icon>

阻止默认事件和冒泡
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
这个是在vue的官网看到的,很有道理,至于这几个的使用去看文档,很具体了

4.vue-cli3打包去掉console.log

通过npm包 terser-webpack-plugin 来实现

第一步:安装

npm install terser-webpack-plugin --save-dev

第二步:配置  (vue.config.js)

module.export = {
  configureWebpack: (config)=>{
    if(process.env.NODE_ENV === 'production'){
      config.optimization.minimizer[0].options.terserOptions.compress = {
      	// warnings: false,// 移除警告
        // drop_debugger: true,// 发布时去除debugger
        drop_console: true,// 发布时去除console
        dead_code: true,// 移除没被引用的代码
        pure_funcs: ['console.log'],// 发布时不被打包的函数
      }
    }
  }
}

5.vue实现checkbox点击选择控制element-ui table表单动态列展示与隐藏

这个有分开一个篇幅写文章链接, 类似这个iview的,功能点是一样的,但是风格不一样

6.js中的offsetWidth、clientWidth、scrollWidth

元素视图属性

  • offsetWidth 水平方向 width + 左右padding + 左右border-width

  • offsetHeight 垂直方向 height + 上下padding + 上下border-width

  • clientWidth 水平方向 width + 左右padding

  • clientHeight 垂直方向 height + 上下padding

  • offsetTop 获取当前元素到 定位父节点 的top方向的距离

  • offsetLeft 获取当前元素到 定位父节点 的left方向的距离

  • scrollWidth 元素内容真实的宽度,内容不超出盒子高度时为盒子的clientWidth

  • scrollHeight 元素内容真实的高度,内容不超出盒子高度时为盒子的clientHeight

Window视图属性(低版本IE浏览器[<IE9]不支持) 【自测包含滚动条,但网络教程都说不包含???】

  • innerWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏)
  • innerHeight 浏览器窗口可视区高度(不包括浏览器控制台、菜单栏、工具栏)

Document文档视图
(低版本IE的innerWidth、innerHeight的代替方案)

  • document.documentElement.clientWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏、滚动条)

  • document.documentElement.clientHeight 浏览器窗口可视区高度(不包括浏览器控制台、菜单栏、工具栏、滚动条)

  • document.documentElement.offsetHeight 获取整个文档的高度(包含body的margin)

  • document.body.offsetHeight 获取整个文档的高度(不包含body的margin)

  • document.documentElement.scrollTop 返回文档的滚动top方向的距离(当窗口发生滚动时值改变)

  • document.documentElement.scrollLeft 返回文档的滚动left方向的距离(当窗口发生滚动时值改变)

元素方法

  1. getBoundingClientRect() 获取元素到body

    • bottom: 元素底边(包括border)到可视区最顶部的距离
    • left: 元素最左边(不包括border)到可视区最左边的距离
    • right: 元素最右边(包括border)到可视区最左边的距离
    • top: 元素顶边(不包括border)到可视区最顶部的距离
    • height: 元素的offsetHeight
    • width: 元素的offsetWidth
    • x: 元素左上角的x坐标
    • y: 元素左上角的y坐标
  2. scrollIntoView() 让元素滚动到可视区

7.vue金额的格式化----过滤器

src\filters\index.js

(1).10000 => "10,000"


export function toThousandFilter(num) {
  return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
}

(2).分转元并格式化1234567 => "12,345.67"

export function toThousandFilter1(val) {
  val = ((+val || 0) / 100).toString().replace(/\$|\,/g, '')
  if (isNaN(val)) {
    val = '0'
  }
  let sign = (val == (val = Math.abs(val)))
  val = Math.floor(val * 100 + 0.50000000001)
  let cents = val % 100
  val = Math.floor(val / 100).toString()
  if (cents < 10) {
    cents = '0' + cents
  }
  for (let i = 0; i < Math.floor((val.length - (1 + i)) / 3); i++) {
    val = val.substring(0, val.length - (4 * i + 3)) + ',' + val.substring(val.length - (4 * i + 3))
  }

  return (((sign) ? '' : '-') + val + '.' + cents)
}

8.Vue判断用户首次进入页面还是浏览器刷新页面

在首次进入页面时我们给window.name设置一个固定值(noFirstEnterPage)

mounted(){
    if(window.name == ""){
         console.log("首次被加载");
         window.name = "noFirstEnterPage"; 
    } else if (window.name == "noFirstEnterPage") {
         console.log("页面被刷新");
    }
}