Code Monkey home page Code Monkey logo

hsingyin.site's People

Stargazers

 avatar

Watchers

 avatar  avatar

hsingyin.site's Issues

iOS滚动穿透问题

背景

写某些弹窗组件,会出现这样的问题,安卓上没有问题,iOS就会出现滚动穿透的问题。

思路

比较简单的解决方式,参考vant的popup组件写法,可以在弹窗显示的时候在body上加样式。

overflow: hidden !important;

弹窗关闭的时候,去除这个样式,就可以了。

动态修改微信浏览器title

很直接的想法 路由守卫里取路由名字然后赋值给title

Vuejs 单页应用在iOS系统下部分APP的webview中 标题不能通过 document.title = xxx 的方式修改

这里参考了vue-wechat-title 源码修改了一下写法,引入一个iframe 触发微信浏览器title更新,然后移除iframe

router.js中

{
  path: '/userCenter',
  name: 'userCenter',
  component: userCenter,
  meta: {
    title: '我的'
  }
}

路由守卫中

  document.title = to.meta.title || ''
  var mobile = navigator.userAgent.toLowerCase()
  if (/iphone|ipad|ipod/.test(mobile)) {
    var iframe = document.createElement('iframe')
    iframe.style.display = 'none'
    var iframeCallback = function () {
      setTimeout(function () {
        iframe.removeEventListener('load', iframeCallback)
        document.body.removeChild(iframe)
      }, 0)
    }
    iframe.addEventListener('load', iframeCallback)
    document.body.appendChild(iframe)
  }
})

实现图片前端JS压缩并上传

你肯定遇到上传图片的业务场景,你会需要对用户上传的图片做限制,文件类型,文件大小,但是如果限制死了文件大小,必然会带来糟糕的用户体验,现在通常的做法是对用户上传的原图做一次压缩减小体积后再上传。

  • 方法1:利用canvas
    比较简单的处理逻辑,缺点就是会丢失画质,处理流程为图片 -> canvas压缩 -> 图片
// 入参就是需要压缩的图片文件(file类型)
compressImg(file) {
  // 读取上传的图片文件
  let reader = new FileReader()
  let img = new Image()
  reader.readAsDataURL(file)
  reader.onload = e => {
    img.src = e.target.result
  }
  // 准备好canvas画布
  let canvas = document.createElement('canvas')
  let context = canvas.getContext('2d')
  let res = ''
  // img加载好的回调方法
  img.onload = () => {
    const originWidth = img.width
    const originHeight = img.height
    // 最大尺寸限制
    const maxWidth = 400, maxHeight = 400
    // 目标尺寸
    let targetWidth = originWidth, targetHeight = originHeight
    // 图片尺寸超过400x400的限制
    if (originWidth > maxWidth || originHeight > maxHeight) {
        if (originWidth / originHeight > maxWidth / maxHeight) {
            // 更宽,按照宽度限定尺寸
            targetWidth = maxWidth
            targetHeight = Math.round(maxWidth * (originHeight / originWidth))
        } else {
            targetHeight = maxHeight
            targetWidth = Math.round(maxHeight * (originWidth / originHeight))
        }
    }
    // canvas对图片进行缩放
    canvas.width = targetWidth;
    canvas.height = targetHeight;
    // 清除画布
    context.clearRect(0, 0, targetWidth, targetHeight);
    // 核心JS就这个
    context.drawImage(img, 0, 0, 400, 300)
    // 转base64
    // res = canvas.toDataURL("image/png")
    // this.filesArr.push(res)
    // 转blob
    canvas.toBlob((blob) => {
      // blob转回file
      let _file = new File([blob], file.name, {type: file.type, lastModified: Date.now()})
      // 加入文件预上传列表(这里加你的处理逻辑)
      this.filesArr.push(_file)
    })
  }
}

qrcodejs2生成二维码自适应

展码这样的需求场景十分常见,在二维码生成之后需要根据移动设备的宽高写自适应。但是问题来了,使用到的qrcodejs2,构造函数传入的宽高不是rem这样的单位。canvas的画布大小是固定的。为此使用曲线救国在生成二维码的节点下加入important样式。

 img,
 canvas {
     width: 3.8rem !important;
     height: 3.8rem !important;
 }

这样就可以实现二维码的自适应。

iOS10、iOS9白屏BUG

前一阵子,开发了某个vue项目在iOS10的H5表现和预期的不一致,出现了白屏的情况。打开vconsole查看发现报了个错误:SyntaxError: Cannot declare a let variable twice: 'e'.重复定义了变量?打开查看代码估计会有这么一段

let e = e => {
    console.log(e);
    for (let e of [1, 2, 3])
        console.log(e);
};

很显然这个e不是人为定义变量,是打包的时候做的混淆和替换,这个怎么就出问题了呢?搜索引擎给出了答案,这是一个iOS10的BUG

We incorrectly throw a syntax error when declaring a top level for-loop iteration variable the same as a parameter
当你定义一个与参数同名的for循环迭代变量时,我们错误地认为这是一个语法错误。

加之我用的是旧版本的vue-cli,才出现的问题。

解决方法如下:

  1. 进入build文件夹;
  2. 找到webpack.prod.conf.js文件;
  3. 在UglifyPlugin的定义里添加关于mangle的选项,使它变成下面这个样子
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false
        },
        mangle: {
          safari10: true
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),

再次build就不会有问题了。

感谢张京写的文章《假如测试说你的网站在iOS 10有问题》

不要混用v-for和v-if

假定需求是列表中只能包含10块钱以下的食物。
当你开心的写一个下拉列表并且想做一个条件渲染的时候你可能会有这么直接的写法:在v-for中加v-if

// 你的数据
options: [
  {
    name: '子母粉丝',
    value: 18
  },{
    name: '红烧牛肉面',
    value: 28
  },{
    name: '瓦罐小炒',
    value: 13
  },{
    name: '炒面',
    value: 8
  }
]
<!-- 你的下拉组件 -->
<el-select v-model="value" placeholder="请选择">
  <el-option
    v-for="item in options"
    :key="item.name"
    :label="item.name"
    v-if="item.value < 10"
    :value="item.value">
  </el-option>
</el-select>

到这里你实现了这个需求,不过其实很大概率你的eslint会警告你,这也是Vue官方不推荐的写法,参考链接
v-ifv-for 一起使用时,v-for 具有比 v-if 更高的优先级,实际上我们浪费了一些性能在不必要渲染的数据项上,我们可以通过计算属性例如 optionsIsCheap 来代替原本的数据源 options ,计算属性返回一个过滤后的列表。

optionsIsCheap() {
  return this.options.filter(item => item.value < 10)
}

改写之后大概是这个样子

<!-- 你的下拉组件 -->
<el-select v-model="value" placeholder="请选择">
  <el-option
    v-for="item in optionsIsCheap"
    :key="item.name"
    :label="item.name"
    :value="item.value">
  </el-option>
</el-select>

mpvue小程序开发踩坑

微信小程序开发暂时采用了是mpvue框架,开发时遇到了一些坑,记录一下防止遗忘。

  • data数据

当你跳转(使用navigateTo)的时候页面上的data会被缓存,所以在onShow或者onLoad钩子函数最好做个初始化。特别是你写了定时器等。

  • 添加页面

必须npm run dev 一下,否则微信开发者工具会报错

  • template

模板里面的写法不支持过滤器,函数表达式,用计算属性或者别的代替

  • 小程序图片

这个只能说你不能用特别大的背景图写在css里的background里,取而代之写image标签,注意压缩一下图片节省空间

  • 函数调用

和微信小程序原生支持的一些API,比如获取用户手机号信息,需要一个button去调用

<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>

你要改写成以下这种,用@代替bind

<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber"></button>
  • class绑定

不支持对象写法

  • solt插槽

不支持插槽传递变量的写法,建议props代替

移动端展示PDF方案

背景

手机预览PDF经常被产品提出,H5、小程序均需要展示PDF的需求,稍微整理一下。

H5

在不依赖插件的情况下,用个iframe大法。iOS可以打开PDF的链接直接预览查看,安卓则会跳到浏览器拉起下载。两端的体验不太一致。

<!--PDF查看器-->
<template>
  <iframe :src="src" class="pdf-viewer"></iframe>
</template>

<script>
export default {
  data() {
    return {
      src: this.$route.query.pdfUrl
    }
  }
}
</script>

<style lang="less">
.pdf-viewer {
  border: 0;
  position: relative;
  min-height: 100vh;
  width: 100%;
}
</style>

使用插件可以尝试pdfh5vue-pdf,可以获得iOS和安卓两端体验一致的良好体验情况。

微信小程序(mpvue框架)

小程序里查看PDF也和H5上打开PDF的情况一样,iOS和安卓需要区别对待。iOS可以直接在webview组件预览,安卓则下载后打开。iOS你同样可以下载再打开,不过直接webview组件预览体验更好。

// 这边在onLoad(options)调用init方法
init(options) {
  this.viewPdf(options)
},
async viewPdf(options) {
  // 这边对平台的相关api做了封装并挂载到原型上
  this.$api.showLoading()
  // 获取系统信息,这里是用promise包了一层微信的wx.getSystemInfo
  const res = await this.$api.getSystemInfo()
  this.systemInfo = res.system
  const isAndroid = /android/i
  if (!options.webUrl) {
    console.log('未携带参数')
    this.$api.hideLoading()
    return false
  }
  if (isAndroid.test(this.systemInfo)) {
    console.log('Android')
    wx.downloadFile({
      url: options && options.webUrl,
      success: (res) => {
        console.log(res)
        const Path = res.tempFilePath // 返回的文件临时地址,用于后面打开本地预览所用
        this.$api.hideLoading()
        wx.openDocument({
          filePath: Path,
          success: function (res) {
            console.log('打开成功')
          }
        })
      },
      fail: (res) => {
        this.$api.hideLoading()
        this.$api.showToast({
          title: '下载失败'
        })
        console.log(res)
      }
    })
  } else {
    console.log('iOS')
    let _webUrl = options && options.webUrl
    // 传递参数的时候需要encodeURIComponent一下防止参数截断
    _webUrl = decodeURIComponent(_webUrl)
    if (_webUrl) {
      this.webUrl = _webUrl
    }
    this.$api.hideLoading()
    console.log(this.webUrl)
  }
}

vue切换路由控制台报错,Uncaught (in promise)

背景

升级了vue-router v3.1.6,加了路由守卫,判断没有携带token就指向登录页,强迫症患者看着控制台打出了一堆Uncaught (in promise) undefined十分难受。

原因

在vue-router v3.0.7之后push和replace返回的是promise不是回调,如果遇到路由守卫的next指定到某个页面(例如登录页面),需要主动catch这个promisereject。[issue2881] (vuejs/vue-router#2881 (comment)) 里posva给出了比较详细的回答和解决方案。

处理方案

  1. 每次push都去主动写一个空的回调(或者显式指定onComplete和onAbort回调函数)。
      this.$router.push({
        name: `${name}`
      }, () => {})

      // or
      this.$router.push({
        name: `${name}`
      },
      onComplete => {
        console.log('完成')
      },
      onAbort => {
        console.log('哦打断了')
      })
  1. 在引入router之前重写push方法。
const originalPush = VueRouter.prototype.push

VueRouter.prototype.push = function push(location, onResolve, onReject) {
  if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
  return originalPush.call(this, location).catch(err => err)
}

Vscode 代码格式化风格指南

  1. 安装Vetur
  2. 安装Prettier
    3.根目录增加.prettierrc.json
{
  "singleQuote": true,
  "semi": false,
  "tabWidth": 2
}
  1. 安装EditorConfig
  2. 根目录增加.editorconfig
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
format_on_save = true

Vue兼容低版本Android、iOS设备实践

背景

在低版本的Android或者iOS设备下,H5会出现白屏的现象,打开控制台发现报错了许多错误。(这里针对vue-cli2.X项目)

原因

原因是Android 6.0iOS 10以下对ES6新语法不兼容,现在低版本的系统在市场上仍有一部分占比,虽然不想兼容但是莫得办法

解决方案

1. 引入babel-polyfill

  • 安装babel-polyfilles6-promise
  • main.js中引用babel-polyfilles6-promise
import 'babel-polyfill';
import Es6Promise from 'es6-promise';
require('es6-promise').polyfill();
Es6Promise.polyfill()

2. 配置项目的.babelrc文件

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime",["import", {
    "libraryName": "vant",
    "libraryDirectory": "es",
    "style": true
  }]],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node",["import", {
        "libraryName": "vant",
        "libraryDirectory": "es",
        "style": true
      }]]
    }
  }
}

3. 检查依赖、静态资源js是否用了高级语法(因为babel会忽略node_modules,导致依赖包中的ES6语法无法被转换)

  • 用到了vantsm-crypto依赖包有高级语法需要转换向后兼容,配置build/webpack.base.conf.js
//  build/webpack.base.conf.js
  entry: {
    app: ["babel-polyfill", "./src/main.js"]
  },
...
{
        test: /\.js$/,
        loader: 'babel-loader',
        include: [
          resolve('src'), 
          resolve('test'),  
          // 将需要转译的模块包括进来
          resolve('node_modules/sm-crypto'), 
          resolve('node_modules/vant'), 
          resolve('node_modules/webpack-dev-server/client')
        ]
}
  • 个人项目中有使用到静态资源文件static/js/serviceConfig.js(因为不会被编译)里面的一些变量使用的模板字符串要改成通常的写法,letconst也得换成var

mpvue+微信小程序auto-fill属性踩坑经历

前言

好久没写issue了,目前淹没在搬砖业务中,想想还是要整理一下遇到的问题和解决方法。我们公司目前用mpvue来构建小程序,taro+ts的方案目前还没大面积采用。为了项目的“稳定”,mpvue大法先上。

问题

微信小程序的实名接口申请比较繁琐和复杂,且不一定能申请下来。所以目前获取用户信息我们通常取巧的使用微信的auto-fill自动填充)能力快速填写用户的三要素。

使用mpvue写页面逻辑时,如果我们单纯只是在input标签增加auto-fill属性,会遇到视图有回填数据,但是在输入框上v-model的data没有成功赋值绑定的神奇效果。

方案

我们观察一下官方示例:

<form bindsubmit="submit">
  <input class="weui-input" placeholder="姓名" auto-fill="base_info.name"  />
  <input class="weui-input" placeholder="手机" auto-fill="phone_info.phone" />
  <input class="weui-input" placeholder="身份证" auto-fill="base_info.id_card_num" />
  <picker placeholder="住址" auto-fill="address_info.address" />
  <button form-type="submit">submit</button>
</form>

项目针对input做了组件封装,通常的想法是增加props传入auto-fill属性,但是实践发现这样无法拉起自动填充。form内部的button也不能加click,转由formbindsubmit(mpuve里面写法变成@submit)去调用函数。最后采用了单独封装带有auto-fill属性的input组件解决了问题,简单粗暴的解决方案。

随着之而来的是打脸,虽然可以正常使用项目封装的input组件拉起自动填充,但是点击button提交的时候,并没有获取到form内部input的值,而且就算通过change事件监听输入框的值,也只会监听到当前输入框的变化,但是自动填充是点击之后会把当前页面所有带自动填充属性的input都赋值,所以还需要外层formsubmit回调函数在点击button的时候获取表单内的数据,通过一番检查后发现input组件中input标签缺少了name属性,导致了外层form无法拿到数据,添加之后就喜闻乐见的可以拿到data数据

总结

  1. 需要有个form表单包在外层,且用带有form-typesubmitbutton去触发formsubmit事件,button自身不写click事件
  2. input组件直接传入props动态渲染auto-fill属性无法拉起自动填充,需要单独封装带有auto-fill属性的input组件,或者直接使用input标签。同样无法动态渲染的还有type属性。
  3. input组件必须添加name属性,否则在外层formsubmit方法无法拿到表单内input输入框的值
  4. form表单的submit回调函数可以拿到input输入框的值进行赋值操作

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.