分类
CSS

ios兼容性问题收集

文字垂直排版时,设置text-orientation:upright失效

产生原因:因为根据常规思路设置了属性

.x{
   -o-text-orientation: upright;
  -moz-text-orientation: upright;
  -webkit-text-orientation: upright;
  text-orientation: upright;
}

正确的做法

.x{
  -o-text-orientation: upright;
  -moz-text-orientation: upright;
  text-orientation: upright;
  // 增加ios兼容性,这个要放后面,因为ios放前面不没有这个属性了
  -webkit-text-orientation: upright;
}

ios16版本,父容器宽或高为0,子内容是宽高为100%的SVG元素,SVG会完整显示。

ios就是以前的ie…

解决方案:若宽或高为0,则给父元素添加overflow:hidden,隐藏子元素。

阻止ios长按菜单事件

.x{
  -webkit-touch-callout:none;
}

父容器加的border-radius,然后加上overflow:hidden。Swiper-item子元素是图片,没有被裁剪。

加上transform:translateZ(0)使用GPU渲染

GPU渲染的元素突破层叠限制,显示在最上方。

通常的形成一个层叠上下文需要position为非static,并且有z-index不为auto。GPU渲染通常是使用了transform:translateZ

在ios中使用html2canvas页面卡死

看看文字的font-family是否使用了san-self。如果有则改掉

分类
javascript vue 性能 算法 解决方案

做虚拟列表时所遇到的坑

页面跳动

出现这种情况的原因是虚拟列表所渲染出的Dom是有限的,向下滚动时需要将上方的Dom移除,向上滚动时需要添加上方的Dom

解决方案

降低Dom渲染的频率

  1. 我有一个当前所处位置的检测值命名为topIndex,以3的倍数进行检测,当我所处位置为1值为0,所处位置为4值为3
  2. 我有一个判断当前滚动方向的检测值,命名为slideArrow,当向下滑动屏幕是置为down,向上滑动屏幕时值为up
  3. 我有一个由topIndex和slideArrow组合而成的计算值,命名为topAndArrowChange
  4. 监听topAndArrowChange,当发生改变时判断滑动方向,改变虚拟列表的值
  5. 当快要到底部时会触发查找列表,数据会放进源列表recommendList中
  6. 当滑动方向为up向上滑动,看下面的内容。或者滑动方向为down向下滑动,看上面的内容。那么虚拟列表为源数据的截取值。
topAndArrowChange(newVal) {
      const circle = 30
      const circleNum = parseInt(this.topIndex / circle, 10)
      console.log(newVal)
      if (this.slideArrow === 'up') {
        this.virtualRecommendList = this.recommendList.slice(circleNum * circle, circleNum * circle + 60)
      } else {
        const start = Math.max((Math.round(this.topIndex / circle) - 1) * circle, 0)
        const end = Math.round(this.topIndex / circle) * circle + circle
        this.virtualRecommendList = this.recommendList.slice(start, end)
      }
    },

看下面的内容,截取的部分

如果当前的topIndex为40,则截取30至90,最大截取60条

看上面的内容,截取的部分

如果当前的topIndex是0,则截取0-30

如果当前的topIndex是36,则截取0-60

如果当前的topIndex是119,则截取90-150

如果当前的topIndex是129,则截取90-150

无限触发查找数据

出现原因是初始时虚拟列表没有赋值,滚动过快触底,虚拟列表来不急赋值

解决方案

  1. 添加源数据列表监听,发现源数据列表更新的同时修改虚拟列表的值
  2. 只有在查找数据时才会触发源列表数据更新,因此虚拟列表的截取值为
 // 防止无限触发查找列表
    recommendList() {
      const circle = 30
      const circleNum = parseInt(this.topIndex / circle, 10)
      this.virtualRecommendList = this.recommendList.slice(circleNum * circle)
    },

当还是以30为一个循环,最多截取不超过60个Dom

总结

综上,此方案虚拟列表最多渲染60条数据

分类
react

React mobx Store 分拆

一个较大的项目如果所有内容都写在一个store里,就显得太多。将一个store拆散,分为一个主store和多个副store。副store可以用到主store的数据。主store有副store的引用便于提供React.createContext。和保持只有一个Provider包裹在主UI组件中

创建主Store

// store.ts
import PageEffectStore from '../Home/stores/PageEffectStore'
import HybridStore from './stores/HybridStore'

class Store {
  /** 副store1 */
  pageEffectStore: PageEffectStore
  /** 副store2 */
  hybridStore: HybridStore
  pageData = ''
  constructor() {
    this.hybridStore = new HybridStore({ mainStore: this })
    this.pageEffectStore = new PageEffectStore({ mainStore: this })
    makeAutoObservable(this)
  }
}

创建副Store

// hybridStore
interface HybridStoreConstructor {
  mainStore: MainStore
}

class HybridStore {
  mainStore: MainStore
  constructor({ mainStore }: HybridStoreConstructor) {
    this.mainStore = mainStore
    makeAutoObservable(this)
  }
}
分类
react

React mobx共享全局变量

mobx持久性变量实现方式。会有一下几个步骤:1.定义store。2.暴露store。3.引入在.tsx引入store。4.在.tsx内使用store

定义Store

// store.ts
import { makeAutoObservable } from 'mobx'

class Store {
  visible = false
  constructor() {
    makeAutoObservable(this)
  }
}

export default Store

暴露Store

// context.ts
import React from 'react'
import Store from './store'

const GlobalContext = React.createContext<Store>(new Store())

export default GlobalContext

引入Store

// index.tsx
import React, { FC } from 'react'
import Store from './store'
import GlobalContext from './context'
import { observer, useLocalObservable } from 'mobx-react'

const Home:FC = () => {
  const store = useLocalObservable(() => new Store())
  return (<GlobalContext.Provider value={store}>...</GlobalContext.Provider>)
}

export default observer(Home)

使用Store

import GlobalContext from '../context'
import React, { FC, useContext } from 'react'

const LongPage:FC<...> = () => {
  const globalStore = useContext(GlobalContext)
}
export default observer(LongPage)

分类
CSS

对层叠上下文内部显示顺序的理解

这篇文章不讲述层叠上下文之间的显示顺序,而是探究在一个父层叠上下文上,子元素的显示顺序(子元素不会产生新的层叠上下文)。相同的显示等级需要考虑先来后到原则。因此本次只考虑不同的显示等级,以背景色的方式。查看覆盖情况得出显示等级。

第一等

position为非static

第二等

display:inline/inline-block

flex容器的子项

第三等

float:left/right

第四等

display:block

冲突情况

当display:inline-block和float:left/right同时存在时,显示等级为第三等,是由于inline-block失效造成的。

父元素与子元素等级不同时的显示顺序

第一个:父元素第一等,子元素第四等。第二个父元素第四等,子元素第一等

第二个的子元素显示在第一个的子元素上方。

第一个:父元素第二等,子元素第四等。第二个父元素第四等,子元素第一等

第二个的子元素显示在第一个的子元素上方

第一个:父元素第二等,子元素第四等。第二个父元素第四等,子元素第三等

第一个的子元素显示在第二个的子元素上方

第一个:父元素第四等,子元素第一等。第二个父元素第一等,子元素第四等。

第二个的子元素显示在第二个子元素的上方

结论:子元素的显示等级由子元素本身和父元素的显示等级决定,如果子元素的显示等级比父元素的显示等级高,则子元素的显示等级为自身的显示等级,如果子元素的显示等级比父元素的显示等级低,则子元素的显示等级为该层叠上下文下,等级最高的父元素的显示等级。

分类
javascript regex 常识

用户环境判断

在开发过程中,我们可能会针对不同的环境做不同的跳转等操作,因此判断环境也是一个比较必要的技术。

const matchEquipment = reg => reg.test(window.navigator.userAgent)

export const isAndroid = () => matchEquipment(/Android/i)

export const isIOS = () => matchEquipment(/ip(hone|od|ad)/i)

// 企业微信
export const isWorkWeiXin = () => matchEquipment(/wxwork/i)

export const isWeiXin = () => matchEquipment(/MicroMessenger/i)

export const isQQ = () => matchEquipment(/QQ/i)

export const isWeiBo = () => matchEquipment(/WeiBo/i)

export const isWindows = () => matchEquipment(/IEMobile/i)

export const isMobile = () => matchEquipment(/Android|webOS|iPhone|iPod|BlackBerry/i)

export const isWeiXinMiniPrograme = () => return matchEquipment(/miniProgram/i)
分类
svg

APP与H5互相调用方案

APP调用H5

/**
 * 这里注入一些全局方法供app端使用
 */
export function injectGlobalProps(name: string | Record<string, any>, callback?: Function) {
  // if (isApp() || __MODE__ === 'start') {
  if (!window.INVITATION_CARD) {
    window.INVITATION_CARD = {}
  }
  if (isString(name)) {
    window.INVITATION_CARD[name] = callback
  } else if (isPlainObj(name)) {
    Object.keys(name).forEach((key) => {
      window.INVITATION_CARD[key] = name[key]
    })
  }
  // }
}

H5调用APP

/**
 * 调用app端注入的方法
 * onEditElement(element: 元素数据) 编辑元素 点击元素,将元素数据传过app
 * onEditImage({ element: ImageElement, allImageElements: ImageElement[] }) 编辑图片 allImageElements 当前请帖所有图片数据
 */
export function callAppBridge(name: string, params?: any) {
  try {
    if (name) {
      console.log(name, params)
      if (isAndroid() && window.messageHandlers && window.messageHandlers[name]) {
        window.messageHandlers[name](JSON.stringify(params))
      } else if (window.webkit?.messageHandlers?.[name]) {
        window.webkit.messageHandlers[name].postMessage(params)
      } else {
        console.log('不存在的方法' + name)
      }
    }
  } catch (e) {
    console.log(e)
  }
}

需要APP提前将方法写入的messageHandlers中

分类
svg

无耦合弹窗(Vue)

在body中挂载一个组件

import Vue from 'vue'

/**
 * 创建一个根组件
 * @param Component 组件
 * @param props 组件属性
 */
const createComponent = (Component, props) => {
  // vue.extend()获取创建实例
  const Ctor = Vue.extend(Component)
  // 创建组件实例,这时得到的是虚拟dom,如何转化为真实dom
  const comp = new Ctor({
    options: {}
  })
  // 前面说到$mount就是这个作用
  comp.$mount()
  // 挂在在body上,这时是comp的dom
  document.body.appendChild(comp.$el)
  comp.remove = () => {
    // 移除本身
    document.body.removeChild(comp.$el)
    // 释放自己所占资源
    comp.$destroy()
  }
  return comp
}

export default createComponent

创建弹窗实例

import Info from './Info'
import createComponent from './createComponent'

/**
 * 喜糖弹窗组件
 */
const Modal = {}

// 弹窗组件实例
const modalInstance = {}
const openModal = (name, Component, options) => {
  if (!modalInstance[name]) {
    modalInstance[name] = createComponent(Component)
  }
  // 在渲染完成打开,否则首次动画播放有问题
  setTimeout(() => {
    modalInstance[name].open(options)
  })
}
// 弹窗预加载 (可在项目入口调用该函数,实现弹窗预加载)
Modal.prefetch = () => {
  modalInstance.info = createComponent(Info)
}
/**
 * 信息弹窗
 *  {
 *    title?: String // 弹窗标题
 *    content?: String // 弹窗内容
 *    onOk?: () => void  // 确认点击回调
 *    okText?: String // ok按钮文本。 默认: 确认
 *    cancelText?: String // cancel按钮文本 默认: 取消
 *    containerTracker: 弹窗容器tracker(可用于弹窗曝光) 支持传递v-tracker埋点数据
 *    okTracker: 确认按钮tracker (可用于确认按钮点击埋点) 支持传递v-tracker埋点数据
 *  }
 * @param options
 */
Modal.info = (options = {}) => {
  openModal('info', Info, options)
}
export default Modal

onOk函数如何实现

data() {
    return {
      isShow: false,
      options: { title: '', content: '', onOK: () => {}, onCancel: () => {} },
    }
  },
  methods: {
    // 调用show方法展示
    open(options) {
      this.options = { ...this.options, ...options }
      this.isShow = true
    },
    handleOkClick() {
      this.options.onOK()
      this.isShow = false
    },
    // 消失后移除自身所占资源
    hide() {
      this.isShow = false
      this.options.onCancel()
    },
  },

如何使用

Modal.info({
            title: 'xxxxx:',
            okText: '我知道了',
            content:
              'xxxx',
            onOK: () => {
              
            },
            
          })
分类
svg

自定义list-style

list-style是使用在ul标签,和ol标签中,通常我们会让list-style的值为none,但其实我们也可以自定义list-style的样式和规则,只是使用场景有限。而且它的同行有选择器,couter-reset等,因此是一个不常使用的技术。(不想讲了)

@couter-style

制订一个自定义计数规则

 @counter-style triangle {
        ...
      }
      ul {
        list-style: triangle;
      }

couter-style下的三个属性

suffix

后缀,suffix:’ >’; 相当于把原有的’ .’替换了

symbols

符号,如果是原来默认的ol计数则它的符号是

symbols: ‘0’ ‘1’ ‘2’ ‘3’ ‘4’ ‘5’ ‘6’ ‘7’ ‘8’ ‘9’

system

计数规则

system: cyclic

循环计数

system: fixed

如果是有序计数,则只计数前有符号的,其余的按常规的有序计数来

system: numeric

默认的有序计数规则

system: additive

相加规则,比较有趣

@counter-style triangle {
        system: additive;
        additive-symbols: 6 ⚅, 5 ⚄, 4 ⚃, 3 ⚂, 2 ⚁, 1 ⚀;
        suffix: ' ';
      }
image - 自定义list-style
分类
svg

css语法解析系列~animation

animation: <single-animation>#

single-animation

single-animation=<time> || <easing-function> || <time> || <single-animation-iteration-count> || <single-animation-direction> || <single-animation-fill-mode> || <single-animation-play-state> || [none | <keyframes-name>]

single-animation-direction

single-animation-direction = normal | reverse | alternate | alternate-reverse

normal:默认值正常顺序

reverse:动画按相反的方向播放

alternate:奇数动画按顺序播放,偶数动画按相反的方向播放

alternate-reverse:奇数动画按相反方向播放,偶数动画按顺序播放

single-animation-fill-mode

single-animation-fill-mode= none | forwards | backwards | both

none: 默认值,播放完后就当没有动画

forwards:播放完后元素处于最后一帧状态

backwards:播放前动画处于第一帧的状态

both:播放前处于第一帧,播放后处于最后一帧

single-animation-play-state

single-animation-play-state=running | paused

running:正常播放动画

paused:动画暂停