前端常见开发问题记录


1. input输入框保存对象格式的数据

有时候需要将JavaScript对象(或数组)格式的数据利用表单保存起来,但是输入框会将输入的对象数据当作字符串,这样存到数据库中的数据格式也是字符串。
例如:

// input输入框中输入的是 { name: 'snail', age: 20 }
// 表单提交时,该条数据就是 "{name: 'snail', age: 20 }"

针对这种使用场景,开发时可以从两个方面入手:

  1. 利用input保存数据前,将要保存的数据转为标准的JSON格式,再复制到输入框中,这样在使用保存的该条数据时,可以直接使用JSON.parse()转换为JavaScript对象或数组。
    // JSON字符串
    let o1 = '{ "name": "snail", "age": 20 }'
    console.log(JSON.parse(o1)); 
    // 可以被转成JavaScript对象 
    {
        name: 'snail', 
        age: 20
    }
    
    // “数组”的JSON数据
    let a1 = "[1234, 4324, 432]"
    console.log(JSON.parse(a1));  
    // 可以被转成JS数组 
    [1234, 4324, 432]
    
    
    // “数组”的JSON数据
    let a2 = '[{"name": "jack", "age": 13 }, { "name": "mike", "age": 15 }]'
    console.log(JSON.parse(a2)) 
    //  可以被转成数组 
    [{name: 'jack', age: 13}, {name: 'mike', age: 15}]
    
    
    // 多层JSON数据
    let o2 = '{ "name": "snail", "age": 20, "other": { "sex": "male", "address": "上海市黄浦区人民广场" }}'
    console.log(JSON.parse(o2)); 
    // 可以被转成对象
    {
        age: 20,
        name: "snail",
        other: {
            sex: 'male', 
            address: '上海市黄浦区人民广场'
        }
    }
  2. 一些字符串数据格式不符合标准的JSON格式,直接使用JSON.parse()会报错,这时可以使用eval()来进行格式转换
    let o3 = "{ name: 'snail', age: 20, other: { sex: 'male', address: '上海市黄浦区人民广场' }}"
    console.log(JSON.parse(o3)); // 报错:Uncaught SyntaxError: Unexpected token n in JSON at position 2
    
    // 使用eval(),为了避免eval()解析对象时将其当做语句执行,需要在对象字符串外使用()包裹,转换数组时,无需加()
    console.log(eval('(' + o3 + ')')) // 可以正常转换

2. 苹果IOS中Date的兼容性问题

2001-01-01 12:00:00这种格式使用new Date()时,会报错,需要转变为2001/01/01 12:00:00格式

let date = '2001-01-01 12:00:00';
console.log(new Date(date));  // IOS系统中报错 invalid Date

date = date.replace(/-/g, '/')
console.log(new Date(date)); // IOS中正常

3. 生成海报图片发生错位/偏移问题

原来使用的是html-2-canvas库,版本较旧,改用新库html-to-image完美解决问题。文档地址

4. Vue项目过渡行为和滚动行为的冲突问题

项目中使用transition组件来实现组件的过渡行为,同时也使用vue-router中的scrollBehavior定义了滚动行为。

<transition name="fade-transform">
    <keep-alive>
        <router-view :key="key" />
    </keep-alive>
</transition>
const createRouter = () =>
    new Router({
        mode: 'hash',
        scrollBehavior: (to, from, savedPosition) => {
            if (savedPosition) {
                return new Promise(resolve => {
                    setTimout(()=>{
                        resolve(savedPosition)
                    }, 500)
                })
            } else {
                return { y: 0 }
            }
        },
        routes
    })

export default createRouter()

实测下来,在已发生滚动的页面切换至别的页面时,滚动条首先会被置顶,之后出现页面过渡效果,然后进入新页面。返回旧页面时,没有自动回退到之前的滚动位置。
禁用过渡组件后,返回页面时是可以自动到达之前的滚动位置的。
这应该是过渡组件transition导致组件加载被延迟,滚动行为没有正确作用到组件上,所以没有达到滚动位置被记录的效果。

解决方案:

  1. 【已测试】scrollBehavior中使用延迟滚动,同时设置一下scroll-behavior样式。

    const createRouter = () =>
        new Router({
            mode: 'hash',
            scrollBehavior: (to, from, savedPosition) => {
                return new Promise((resolve) => {
                    // 这里的延迟时间根据过渡时间来决定,>过渡时间即可
                    setTimeout(() => {
                        resolve(savedPosition ? savedPosition : { y:0 })
                    }, 1000)
                })
            },
            routes
        })
    
    export default createRouter()
    

    平滑滚动,目前除了IE浏览器,主流浏览器都已经支持。

    html {
        scroll-behavior: smooth;
    }
  2. 【待测试】使用vue-router-view-transition组件替换transition组件替换

  3. 【已测试,无效】history路由模式下,设置history.scrollRestoration = 'manual'

  4. 【待测试】自定义过渡动画,弃用transition组件

5. Canvas设置window.innerHeight时出现滚动条的问题

项目中利用Three.js全屏3D模型,分别给canvas设置window.innerWidthwindow.innerHeight作为宽高,发现始终在竖直方向上会出现
滚动条,这就导致水平方向上也会出现滚动条(因为innerWidth是包含滚动条的宽度)。
解决办法:

  1. 确保body元素的margin被清零
  2. canvas元素设置display: block;,或给canvas的父元素设置font-size:0;以确保元素之间的间隙被清零
  3. 电脑屏幕设置了缩放的情况下(我的缩放是125%),window.innerHeight高度是窗口理论缩放高度原始innerHeight/devicePixelRatio四舍五入
    后的结果,所以window.innerHeight有可能大于理论缩放高度,这就导致设置了滚动条的出现。
    我这里进行了一定抹零处理。如果想省事也可以直接window.innerHeight - 0.5
    const { innerWidth, innerHeight, devicePixelRatio } = window;
    
    // 现在电脑的高分辨率屏幕一般都会设置缩放(我的屏幕设置了125%)
    // 此时window.innerHeight = Math.round(理论上屏幕缩放后分辨率)
    // 这时候如果给元素设置的高度 = window.innerHeight,可能就会出现滚动条
    // 这里利用devicePixelRatio计算一下window.innerHeight缩放前的高度(这是一个近似之后的值)
    // 对缩放前的高度抹零后再计算缩放值,这样就能保证永远不会出现滚动条
    const canvasHeight = (Math.floor(innerHeight * devicePixelRatio) / devicePixelRatio).toFixed(2);
    renderer.setSize(innerWidth, canvasHeight); // 设置渲染区域尺寸

6. 长列表优化

  • 时间分片(适用于简单dom)
    • 按照一定的时间间隔来分批渲染数据,避免数据同时一次性插入过多dom导致页面卡顿,常用的实现是通过requestAnimationFrameDocumentFragment来间歇性的渲染dom
  • 虚拟列表(适用于复杂dom)
    • 只对可见区域进行渲染,对非可见区域中的数据利用等高的占位元素来替代,从而减少页面中的DOM节点数量,以提升渲染速度

💡「前端进阶」高性能渲染十万条数据(虚拟列表)
💡「前端进阶」高性能渲染十万条数据(时间分片)

7. Vue兼容IE8及以下版本的策略

目前看来确实无法兼容,可以使用司徒正美的avalon.js, 兼容到IE6

8. 不同浏览器的兼容性问题解决

CSS兼容

  • 使用reset.css重置样式表或Normalize.css,统一各浏览器的基础样式,比如marginpaddingfont-size
  • 使用PostCss插件autoprefixer,自动补全css浏览器前缀,解决部分浏览器中css属性不一致的问题。

js兼容

  • 事件监听绑定和移除的兼容问题(绑定:addEventListener和IE:attachEvent,移除:removeEventListener和IE:detachchEvent),要先判断再使用
  • 事件对象的兼容问题(event),IE8及以下浏览器中,将event放在了window.event属性下,所以要使用event || window.event
  • 键盘的键值码,keyCode和IE的which
  • 阻止事件的默认行为的兼容问题,preventDefault和IE的returnValue
  • 阻止事件冒泡的兼容问题,stopPropagation和IE的cancelBubble
  • new Date()构造函数使用,2019-12-09是有问题的,要转换为2019/12/09
  • document.documentElement兼容,要使用document.documentElement || document.body

9. npm install报错问题

基于uni-app + vue2开发的项目,在使用npm install报错python找不到, npm run dev报错mustache找不到
原因:node版本问题
解决办法:npm install要在node@13.6.0下,npm run dev需要在node@16.13.1下,至于为什么npm installnpm run dev需要在不同的node版本下,有待研究。


文章作者: Snail-Lu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Snail-Lu !
  目录