幽丶墨

幽丶墨 查看完整档案

济南编辑江南大学  |  自动化 编辑  |  填写所在公司/组织 juzkai.github.io/ 编辑
编辑

个人动态

幽丶墨 回答了问题 · 11月25日

封装了el-form, 如何在el-form-item上做v-if判断

可以用template

<template v-for="(item, index) in items" :key="index">
    <el-form-item>
        .....
    </el-form-item>
</template>

关注 4 回答 4

幽丶墨 回答了问题 · 8月18日

解决vue在IE里运行,打包都报语法错误

引入个babel-polyfill吧,可能是有ES6没有转化,在IE上不支持导致的。

关注 2 回答 2

幽丶墨 发布了文章 · 5月6日

移动iOS端软键盘弹起空白和滚动穿透问题

在做h5移动端项目的时候,给用户一个十分友好的体验是很必要的。最近抽空整理了下移动端(iOS端)项目中经常碰到的两个问题

键盘弹起空白

在我们点击input等弹出手机键盘,在点击完成后经常会在底部出现跟键盘同大小的空白,但是当我们滚动下页面发现又好了,这个在iOS端可以说很常见的问题了(应该是布局定位造成的,具体原因没仔细研究),解决方法就是在结束输入的时候控制滚动条偏移下就好。
下面是相关代码:

.inTouch {
  -webkit-overflow-scrolling: auto;
}
.noTouch {
  -webkit-overflow-scrolling: touch;
}
import Vue from 'vue'
Vue.directive('resetPage', {
  inserted (el) {
    document.body.addEventListener('focusin', () => {
      if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
        // 软键盘收起的事件处理
        setTimeout(() => {
          document.getElementsByTagName('body')[0].className = 'inTouch'
        }, 100)
      }
    })
    document.body.addEventListener('focusout', () => {
      if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
        // 软键盘收起的事件处理
        setTimeout(() => {
          const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0
          window.scrollTo(0, Math.max(scrollHeight - 1, 0))
          document.getElementsByTagName('body')[0].className = 'noTouch'
        }, 100)
      }
    })
  }
})

在iOS端,当元素获得焦点的时候,我们把-webkit-overflow-scrolling的值设为auto,防止滚动穿透。当元素失去焦点的时候,我们把值恢复为touch,这样页面的滚动效果不会丢失,同时我们控制滚动条偏移了1像素,解决软键盘弹起空白的问题。
关于我设置的-webkit-overflow-scrolling属性可多说两句:

// -webkit-overflow-scrolling这个样式相信大家都很熟悉了,有auto、touch两个可选值
// auto:使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止。
// touch:使用具有回弹效果的滚动, 当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果。继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。同时也会创建一个新的堆栈上下文。
// 为了增加滚动的流畅性,做iOS移动端适配的时候都会增加这个样式适配的
-webkit-overflow-scrolling: touch;

查看兼容性的事后素我们发现只在iOS端可用,但是出现的问题也很多,我这里加上主要原因是当input失去焦点的时候,有的时候整个页面会卡顿住(这个问题在webview中碰到的),我发现加上这个之后会解决这个问题,就把这两个放到一起了,不需要的可以不加上这个相关的。
我用的是mint-ui开发的移动端,所以在输入框的时候就可以这样使用

<mt-field class="l-modal-body-input" v-reset-page :attr="{ maxlength: 15 }" v-model="name" label="姓名:"></mt-field>
滚动穿透问题

这个可以说也是很常见的问题了,在我们做弹框滚动,如地址或时间picker选择器的时候,我们在滚动选择时候,底部的页面也会跟着一起滚动,一定程度上影响了用户的体验。经查找研究,当我们弹框的时候移除body的touchmove事件,是可以解决这个问题的:

// 锁定body滚动条--主要解决用户弹框滚动时的穿透
Vue.prototype.closeTouch = function () {
  document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, {passive: false}) // 阻止默认事件
}
Vue.prototype.openTouch = function () {
  document.getElementsByTagName('body')[0].removeEventListener('touchmove', this.handler, {passive: false}) // 打开默认事件
}
handler: (e) => {
    e.preventDefault()
}

当我们打开picker等弹框的时候,调用this.closeTouch(),锁定滚动,阻止默认事件。关闭弹框的时候调用this.openTouch()打开默认事件。过程虽然比较麻烦,但能实际解决问题!

以上是我做iOS移动项目时遇到的比较常见的问题,希望能对您有所帮助,如有更好的方法可以互相学习!


此贴仅供参考学习,转载请注明出处!

查看原文

赞 1 收藏 0 评论 0

幽丶墨 回答了问题 · 4月23日

解决谷歌浏览器输入若干相同长度字符串,输出长度不一致

'‭15312345678'

这个前面是有一个空格的。。不信自己删除试试!

'‭15319317862‬'

至于这个。。前后各有一个空格!

关注 5 回答 4

幽丶墨 回答了问题 · 3月6日

使用html2canvas截取屏幕图片时图片显示不全

对于超过屏幕高度的内容html2canvas无法截取,目前知道的比较好的有两个方法
1、通过复制DOM节点,插入到body下可解决,不过会出现当多个图片层叠的时候元素会出现混乱闪过的情况,不是很好用
2、通过canvas画布解决(推荐)

// 此方法可在图片load完成之后调用
drawToPic () {
    // box -- 需要截取的屏幕的可视区域
    const clientHeight = this.$refs.box.clientHeight
    const width = this.$refs.box.clientWidth
    const cs = document.createElement('canvas')
    cs.width = width * scale
    cs.height = clientHeight * scale
    const content = cs.getContext('2d')
    content.scale(scale, scale)
    const rect = this.$refs.box.getBoundingClientRect()
    content.translate(-rect.left, -rect.top)
    const that = this
    this.$nextTick(() => {
      html2canvas(this.$refs.box, {
        allowTaint: true,
        taintTest: true,
        useCORS: true,
        scale: scale / 2,
        canvas: cs,
        width: width,
        height: clientHeight
      }).then((canvas) => {
          const url = canvas.toDataURL()
          // 微信浏览器下,可长按图片保存
          if (browserType() === 1) {
            this.imgUrl = url
          } else {
            this.imgUrl = URL.createObjectURL(that.base64ToBlob(canvas.toDataURL()))
          }
      })
    }
}
// base64转blob
base64ToBlob (code) {
  let parts = code.split(';base64,')
  let contentType = parts[0].split(':')[1]
  let raw = window.atob(parts[1])
  let rawLength = raw.length
  let uInt8Array = new Uint8Array(rawLength)
  for (let i = 0; i < rawLength; ++i) {
    uInt8Array[i] = raw.charCodeAt(i)
  }
  return new Blob([uInt8Array], {type: contentType})
}

关注 4 回答 3

幽丶墨 回答了问题 · 1月22日

vue中怎么动态控制keep-alive缓存

使用keep-alive中的 include 结合store使用,代码我在这个帖子中回答过。
你的要求其实就是从A -> B 的时候B不缓存,从C -> B的时候B缓存
可以利用beforeRouteLeave操作B页面

beforeRouteLeave (to, from, next) {
    if (to.name === 'C') {
        // 跳转到C页面的时候缓存B页面
        this.$store.dispatch('addCacheView', 'B')   
    } else if (to.name === 'A') {
        // 返回到A页面的时候B页面不再缓存
        this.$store.dispatch('deleteCacheView', 'B')
    }
    next()
}

keep-alive的地方结合mapGetters使用

<keep-alive  :include="cacheView">
    <router-view  class="page-content"/>
</keep-alive>

computed: {
    ...mapGetters(['cacheView'])
}

关注 4 回答 3

幽丶墨 回答了问题 · 1月20日

vue怎么动态缓存组件

使用keep-alive中的 include 结合store使用,我现在用的你可以参考下

// 添加缓存组件:this.$store.dispatch('addCacheView', 'TargetRouterName')
// 清除缓存组件:this.$store.dispatch('deleteCacheView', 'TargetRouterName')
const  view  = {
    state: {
        cacheView: []
    },
    mutations: {
        ADD_CACHE_VIEW: (state, view) => {
            state.cacheView.push(view)
        },
        DELETE_CACHE_VIEW: (state, index) \=> {
            state.cacheView.splice(index, 1)
        }
    },
    actions: {
        addCacheView ({ commit, state }, view) {
            if (!state.cacheView.includes(view)) {
                commit('ADD_CACHE_VIEW', view)
            }
        },
        deleteCacheView ({ commit, state }, view) {
            const  index  =  state.cacheView.indexOf(view)
            if (index  >  -1) {
             commit('DELETE_CACHE_VIEW', index)
            }
        }
    }
}
export  default  view

关注 3 回答 2

幽丶墨 回答了问题 · 1月18日

解决每个vue界面都有获取数据的相同方案,应该如何复用

可以了解下mixins

关注 2 回答 1

幽丶墨 回答了问题 · 1月7日

解决vue 注释问题

标签上的属性没法注释,至少我是没见过。

关注 4 回答 3

幽丶墨 赞了文章 · 2019-12-25

【TypeScript 演化史 -- 10】更好的空值检查 和 混合类

作者:Marius Schulz
译者:前端小智
来源:https://mariusschulz.com/
点赞再看,养成习惯

本文 GitHubhttps://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。

更好地检查表达式的操作数中的 null/undefined

在TypeScript 2.2中,空检查得到了进一步的改进。TypeScript 现在将带有可空操作数的表达式标记为编译时错误。

具体来说,下面这些会被标记为错误:

  • 如果+运算符的任何一个操作数是可空的,并且两个操作数都不是anystring类型。
  • 如果-***/%<<>>>>>, &, |^运算符的任何一个操作数是可空的。
  • 如果 <><=>=in 运算符的任何一个操作数是可空的。
  • 如果 instanceof 运算符的右操作数是可空的。
  • 如果一元运算符+-~++或者--的操作数是可空的。

来看看如果咱们不小心,可空表达式操作数就会坑下咱们的情况。在 TypeScript 2.2 之前,下面这个函数是可以很好地编译通过的:

function isValidPasswordLength(
  password: string,
  min: number,
  max?: number
) {
  return password.length >= min && password.length <= max;
}

注意max参数是可选的。这意味着咱们可以使用两个或三个参数来调用isValidPasswordLength

isValidPasswordLength("open sesame", 6, 128); // true
isValidPasswordLength("open sesame", 6, 8); // false

密码 "open sesame"的长度为10个字符。因此,对于长度范围 [6,128] 返回 true,对于长度范围[6,8]返回false,到目前为止,一切 ok。

如果调用isValidPasswordLength且不提供max参数值,那么当密码长度超过 min 值时,咱们可能希望返回 true。然而,事实并非如此:

isValidPasswordLength("open sesame", 6); // false

这里的问题在于 <= max 比较。如果maxundefined,那么 <= max 的值永远都为false。在这种情况下,isValidPasswordLength将永远不会返回true

在 TypeScript 2.2 中,表达式password.length <= max不正确的类型,如果你的应用程序正在严格的null检查模式下运行:

function isValidPasswordLength(
  password: string,
  min: number,
  max?: number
) {
  return password.length >= min  && password.length <= max; // Error: 对象可能为“未定义”.
}
如果操作数的类型是nullundefined或者包含nullundefined的联合类型,则操作数视为可空的。

注意:包含nullundefined的联合类型只会出现在--strictNullChecks模式中,因为常规类型检查模式下nullundefined在联合类型中是不存在的。

那么要怎么修正这个问题呢?一种的解决方案是为max参数提供一个默认值,它只在传递undefined 时起作用。这样,该参数仍然是可选的,但始终包含类型为number的值

function isValidPasswordLength(
  password: string,
  min: number,
  max: number = Number.MAX_VALUE
) {
  return password.length >= min && password.length <= max;
}

当然咱们也可以选择其他的方法,但是我觉得这个方法很好。只要不再将maxundefined 的值进行比较,就可以了

混合类

TypeScript 的一个目的是支持不同框架和库中使用的通用 JS 模式。从TypeScript 2.2开始,增加了对 ES6 混合类(mixin class)模式。接下来讲讲 mixin 是什么,然后举例说明了如何在 TypeScript 中使用它们。

JavaScript/TypeScript中的 mixin

混合类是实现不同功能方面的类。其他类可以包含 mixin 并访问它的方法和属性。这样,mixin 提供了一种基于组合行为的代码重用形式。

混合类指一个extends(扩展)了类型参数类型的表达式的类声明或表达式. 以下规则对混合类声明适用:

  • extends表达式的类型参数类型必须是混合构造函数.
  • 混合类的构造函数 (如果有) 必须有且仅有一个类型为any[]的变长参数, 并且必须使用展开运算符在super(...args)调用中将这些参数传递。

定义完成之后,来研究一些代码。下面是一个 Timestamped 函数,它在timestamp 属性中跟踪对象的创建日期:

type Constructor<T = {}> = new (..args: any[]) => T;

function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now()
  }
}

这看起来有点复杂,咱们一行一行来看看:

type Constructor<T = {}> = new (..args: any[]) => T;

type Constructor <T>是构造签名的别名,该签名描述了可以构造通用类型T的对象的类型,并且其构造函数接受任意数量的任何类型的参数。

接下来,让我们看一下mixin函数本身:

function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now();
  };
}

Timestamped 函数接受一个名为Base的参数,该参数属于泛型类型 TBase注意TBase 必须与Constructor兼容,即类型必须能够构造某些东西。

在函数体中,咱们创建并返回一个派生自Base的新类。这种语法乍一看可能有点奇怪。咱们创建的是类表达式,而不是类声明,后者是定义类的更常用方法。咱们的新类定义了一个timestamp的属性,并立即分配自UNIX时代以来经过的毫秒数。

注意,从mixin函数返回的类表达式是一个未命名的类表达式,因为class关键字后面没有名称。与类声明不同,类表达式不必命名。咱们可以选择添加一个名称,它将是类主体的本地名称,并允许类引用自己

function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class Timestamped extends Base {
    timestamp = Date.now();
  };
}

现在已经介绍了两个类型别名和mixin函数的声明,接下来看看如何在另一个类中使用 mixin:

class User {
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

// 通过将`"Timestamped"`混合到"User"中创建一个新类
const TimestampedUser = Timestamped(User);

// 实例化新的 "TimestampedUser" 类
const user = new TimestampedUser("前端小智")

// 现在,咱们可以同时从User 类中访问属性
// 也可以从 Timestamped 类中访问属性
console.log(user.name);
console.log(user.timestamp);

TypeScript 编译器知道我们在这里创建并使用了一个mixin,一切都是完全静态类型的,并且会自动完成和重构。

混合构造函数

现在,看看一个稍微高级一点的 mixin,类中定义一个构造函数

function Tagged<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    tag: string | null;

    constructor(...args: any[]) {
      super(...args);
      this.tag = null;
    }
  };
}

如果在混合类中定义构造函数,那么它必须有一个类型为any[]rest参数。这样做的原因是,mixin不应该绑定到具有已知构造函数参数的特定类;因此,mixin应该接受任意数量的任意值作为构造函数参数。所有参数都传递给Base的构造函数,然后mixin执行它的任务。在咱们的例子中,它初始化 tag 属性。

混合构造函数类型指仅有单个构造函数签名,且该签名仅有一个类型为 any[] 的变长参数,返回值为对象类型. 比如, 有 X 为对象类型, new (...args: any[]) => X 是一个实例类型为 X 的混合构造函数类型。

以前面使用Timestamped的相同方式来使用混合Tagged

// 通过 User 作为混合 Tagged 来创建一个新类
const TaggedUser = Tagged(User);

// 实例化 "TaggedUser" 类
const user = new TaggedUser("John Doe");

// 现在,可以从 User 类访问属性和 Tagged 中的属性

user.name = "Jane Doe";
user.tag = "janedoe";

mixin 与方法

到目前为止,咱们只在mixin中添加了数据属性。现在来看看另一个 mixin,它额外实现了两个方法:

fucntion Activatable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    isActivated = false;
    
    activate() {
      this.isActivated = true;
    }
    
    deactivate() {
      this.isActivated = false;
    }
  }
}

咱们从mixin函数返回一个常规的类。这意味着咱们可以使用所有受支持的类功能,例如构造函数,属性,方法,getter/setter,静态成员等。

如何所示,咱们如何在 User 类中使用混合的 Activatable

const ActivatableUser = Activatable(User);

// 实例化新的"ActivatableUser"类
const user = new ActivatableUser("John Doe");

//初始化,isActivated 的值为 false
console.log(user.isActivated);

user.activate();

console.log(user.isActivated); // true

组合多个mixin

组合的mixin,可以让它更加灵活。一个类可以包含任意多的mixin,为了演示这点,咱们把上面提到的所有mixin 代码组合在一起。

const SpecialUser = Activatable(Tagged(Timestamped(User)));
const user = new SpecialUser("John Doe");

当然 SpecialUser类不一定非常有用,但关键是,TypeScript静态地理解这种mixin组合。编译器可以类型检查所有的使用,并在自动完成列表中建议可用的成员:
clipboard.png

与类继承进行对比,有个区别:一个类只能有一个基类。继承多个基类在 JS 中不行的,因此在 TypeScript中也不行。


原文:
https://mariusschulz.com/blog...
https://mariusschulz.com/blog...

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug


交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 2 收藏 1 评论 7

认证与成就

  • 获得 51 次点赞
  • 获得 11 枚徽章 获得 1 枚金徽章, 获得 2 枚银徽章, 获得 8 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-02-01
个人主页被 574 人浏览