资讯专栏INFORMATION COLUMN

项目总结 20171226

张金宝 / 2011人阅读

摘要:借助预加载图片详情文档地址项目中为了确保页面显示时,图片已经全部加载完毕,因此需要提前加载图片,加载图片的过程使用进度条显示。第二个参数表示是否搜索其子目录。并在同元素或父级添加了时,元素显示。

1. 借助require.context预加载图片

详情文档地址

项目中为了确保页面显示时,图片已经全部加载完毕,因此需要提前加载图片,加载图片的过程使用进度条显示。

在webpack构建的项目中,可以使用require.context来获取到静态资源的地址。语法如下:

require.context(directory, useSubdirectories = false, regExp = /^.//)

第一个参数表示要搜索的文件夹目录,该目录支持相对路径与在配置文件中定义的路径别名。
第二个参数表示是否搜索其子目录。
第三个参数是一个用来匹配文件的正则表达式。

require.context("modules/App", true, /.(png|jpg|jpeg|gif)$/);
// 创建一个包含App目录下所有图片的上下文模块

可以使用该上下文模块自带的keys方法得到路径组成的数组。

const images = require.context("modules/App/", true, /.(png|jpeg|jpg|gif)$/);
console.log(images.keys());

效果大概如下图所示。

得到图片路径之后,就可以借助Promise.all来完成图片预加载,确保图片加载完成之后再渲染页面。

Promise.all(images.keys().map(path => {
  const image = new Image();
  image.src = path;
  image.onload = image.onerror = () => {
    resolve();
  }
}))

但是在开发中遇到一个问题,本地页面引用的图片是编译过后的图片地址,并不是相对路径,因此如果直接这样的话会因此地址不一致而报错。

解决办法是在设置image对象src属性时,修改如下:

image.src = images(path);
// images 是由require.context 创建的上下文模块

打印出images(path)之后的图片路径如下:

上面的修复方式可以使用如下的知识点来理解。

const ctx = require.context("modules/App", true, /*.js/);
const table = ctx("./table.js");

// 上面的代码等价于
const table = require("modules/App/table.js"); // 使用require引入模块

参考文档

当还需要从服务端提前加载其他资源时,可以使用数组的concat方法一起放入Promise.all中。

Promise.all(images.keys().map(
  // ...
).concat(http.get("/api/v1/summary")))
2. 细节优化

整个页面的显示,一共有15页构成,由于每一页的逻辑与效果都有不少差异,因此将每一页定义为了一个组件,最初在引入这些模块时很糟糕的这样做:

import Page00 from "./Page00";
import Page01 from "./Page01";
import Page02 from "./Page02";
import Page03 from "./Page03";
import Page04 from "./Page04";
import Page05 from "./Page05";
import Page06 from "./Page06";
import Page07 from "./Page07";
import Page08 from "./Page08";
import Page09 from "./Page09";
import Page10 from "./Page10";
import Page11 from "./Page11";
import Page12 from "./Page12";
import Page13 from "./Page13";
import Page14 from "./Page14";

// render里也很复杂

// ...
render() {
  return (
      
        
        
        
        ...
         
      
  )
}

当组件更多时,这样的引入方式自然是不合理的,可以使用循环的方式来引入代码,优化如下:

const allPages = [];

for(let i = 0; i < 15; i++) {
  const id = `0${i}`.slice(-2);
  allPages.push(require(`./Page${id}`).default)
}

这样就将所有的Page组件放在了allPages数组中。

render里也可以使用map来渲染。

render() {
  return (
    
{allPages.map(({ id, Component: Page }) => )}
) }
3. 使用高阶组件处理公共逻辑

每一个Page组件中,都有共同的元素或逻辑,包括logo,分享当前屏幕截图按钮,统计逻辑,判断对应页面是否显示等。可以将这些共用逻辑使用高阶组件来处理以简化代码。

因此定义了withBox组件来处理它们。

import React from "react";
import logo from "./images/logo.png";
import { sendEvent } from "utils/track";
import share from "./share";

export default function(Wrapped, checkProp) {
  return class NewPage extends React.Component {
    shareScreen = () => {
      const id = this.refs.box.getAttribute("data-page-id");
      this.refs.box.classList.add("will-screenshot");
      setTimeout(() => share.shareScreenshot(), 100);
      setTimeout(() => this.refs.box.classList.remove("will-screenshot"), 1500);

      sendEvent("share-click", "page" + id);
      sendEvent("click", "share-btn");
    };

    render() {
      const { id, className, ...props } = this.props;
      const cls = className ? `page${id} ${className}` : `page${id}`;

      if (!checkProp || (props.info[checkProp] !== null && props.info[checkProp] !== "undefined")) {
        return (
          
); } return null; } }; }
4. 通过添加/删除元素的方式统一控制动画

首先定义一个class如下,将会参与动画的元素(或其父级)都添加该class以隐藏。

.aninode {
  visibility: hidden;
}

并在同元素(或父级)添加了animated时,元素显示。

.animated {
  &.aninode, .aninode {
    visibility: visible;
  }
}

并在运动元素的class中添加了animated时,运动生效,因此定义运动css时,应该这样做:

.animated {
  &.flyTopIn, .flyTopIn {
    animation-name: flyTopIn;
    animation-duration: 1s;
  }
  /* more */
}

因此,运动元素在运动开始之前,应该保持这样

需要运动时,在该元素的class中添加animated即可。

// or

使用sass的循环语法定义delay样式

@for $i from 0 through $delay_count {
  .animated .delay#{$i * 100} {
    animation-delay: $i * 100;
    animation-fill-mode: backwards;
  }
}

具体实现可参考

5. 小数精度问题导致的bug

js的计算中,经常会遇到小数精度的问题,最初没有注意,导致数据显示出了很多问题。例如如下计算结果

1.099 * 100
109.89999999999999

解决方法如下:

(1.099 * 100).toFixed(2)

6. Promise与setTimeout的写法问题

利用setTimeout判断某个对象是否注入成功。

// 错误写法
export const checkSDK = () => {
    var timer = null;
    const start = Date.now();

    return new Promise((resolve, reject) => {
        if (typeof window.TigerBridge === "object") {
            resolve();
            return;
        }
        if (Date.now() - start <= 5 * 1000) {
            clearTimeout(timer);
            timer = setTimeout(checkSDK, 100);
            return;
        }

        reject();
    })
}


// 正确写法

export const checkBridge = () => {
    var timer = null;
    const start = Date.now();

    function check(resolve, reject) {
        if (typeof window.TigerBridge === "object") {
            resolve();
            return true;
        } 
        if (Date.now() - start <= 5 * 1000) {
            clearTimeout(timer);
            timer = setTimeout(check.bind(null, resolve, reject), 100);
            return;
        }
        reject();
        return false;
    }

    return new Promise((resolve, reject) => check(resolve, reject))
}

本地模拟注入过程

if (process.env.NODE_ENV != "production") {
    setTimeout(() => {
        window.TigerBridge = {
            getAccessToken: () => {
                return pkg.token;
            },
            isAccountPermissionLimited: () => false
        };
    }, 1600);
}
7. 图片串行加载优化

一次性加载所有图片会导致浏览器http线程阻塞严重。因此需要稍作优化,让图片一张一张加载。

// 优化前
images.keys().map(path => new Promise(resolve => {
  const image = new Image();
  image.src = images(path);
  image.onload = image.onerror = resolve;
}))

// 优化后
images.keys().reduce((cachePromise, path) => cachePromise.then(() => {
  return new Promise(resolve => {
    const image = new Image();
    const complete = () => {
      clearTimeout(timer);
      resolve();
    }
    const timer = setTimeout(complete, 1000);  // 单张图片最多加载1s
    image.src = images(path);
    image.onload = image.onerror = complete;
  })
}), Promise.resolve());

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90455.html

相关文章

  • 记一次Nodejs安全工单的处理过程_20171226

    摘要:事件原因之前使用开发的一个网站。事件的处理方法在公司是有专门的安全组来做安全这块儿工作的。第一时间对这个接口进行了下线处理,然后评估了安全的解决方案,再次上线该接口。这些机器也肯定是经过做特殊的隔离处理的没有敏感的公司信息资源。 事件原因: 之前使用Nodejs开发的一个网站。在网站上有一个页面有个功能,允许用户上传图片或者粘贴一张图片链接。服务端读取用户上传的图片信息或者是请求用户填...

    gnehc 评论0 收藏0
  • 【Vue项目总结项目nginx部署

    摘要:项目开发完成,接下来是上线,关于项目的部署,我司前端是部署在服务器上,关于的相关文档,请自行查阅本文只记录部署时碰到的一些问题。其他总结文章常规打包优化方案组件通信处理方案后台管理项目总结 项目开发完成,接下来是上线,关于vue项目的部署,我司前端是部署在nginx服务器上,关于nginx的相关文档,请自行查阅;本文只记录部署时碰到的一些问题。 打包 vue项目打包后,是生成一系列的静...

    CntChen 评论0 收藏0
  • 【经验分享】测试人员如何写总结

    摘要:参与者项目经理产品经理开发经理测试经理及其它相关人员。项目上线后问题反馈针对项目客户反馈问题进行分析总结,类似缺陷分析,重点总结遗漏的原因及后需的规避措施。六汇总整理各部门总结并发布基于测试总结过程中的数据分析,我们提出了对部门的建议。 最近参与了几次面试,面试者的简历中都会提及:需求或者...

    Vixb 评论0 收藏0
  • 新疆智营销项目总结

    摘要:需要对本项目做一个总结,提醒自己,记得这些问题。总结自己在这个项目犯得错以及踩的坑,自己在今后的项目将会注意以下事项了解项目整体需求和结构,知道哪些组件会公用。 新疆智营销项目是我来到亚信的第一个项目,项目初期需求分析没有参加,接到设计图直接写,没来得及系统的了解整个项目的结构,表格组件定下来之后,表单form的输入项打算让郭山专职负责,后来情况发生变化导致表单一块前后三个人参与,没有...

    monw3c 评论0 收藏0

发表评论

0条评论

张金宝

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<