ssr

  1. 服务端不能识别前端的window。特别是首屏渲染的数据需要用到window对象(比如href += location.search);
  2. 服务端不能加载图片,css文件。
require.extensions['.less'] = function () {
    return null;
};
global.__DEV__ = !bytedEnv.isProd();
global.__SERVER__ = true; // 代码服务端环境
global.window = {
    location: {
        search: ''
    }
};
//做了个中间件事实修改window的属性。
module.exports = () => async (ctx, next) => {
  global.window = {
    location: {
      search: ctx.search,
      host: ctx.host,
      protocol: ctx.protocol
    }
  };
  await next();
};

客户端需要全局变量来表示是否是客户端环境来选择加载某些库。

  1. 首屏从localstorage取数据,并不能服务端渲染
    isSsr ? <div className="channel-bar">: <ChannelBar />

这里用了hydrate而不是render渲染react组件,导致首刷的dom被尽可能的复用。
导致的问题是ChannelBar的最外层dom被复用。如果服务端渲染为空div占位的话,上面的class channel-bar就会丢失。

所以首屏渲染最好还是要保证首刷客户端和服务端一样才行。
这种问题官方推荐的做法是在react生命周期里渲染首刷不同的部分。不过这种做法必然会导致组件rerender。

解决方案:ChannelBar的useEffect里去localstorage拿数据,这样保证首刷一致。

react 16 hooks问题

function OnePxBorder({ color }) {

  const style = {
    backgroundColor: color,
    height: '1px'
  };
  return <div className="one-px" style={style} />;
}

对于这样一个组件,const style语句等语句(定义函数等)会在组件rerender的时候重新执行。这样会导致定义在组件内的函数多次定义。

解决方案:无关state的函数定义在react组件外。

function Channel() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(1);
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect会在每次render的时候重新执行,以上代码,count再点也是1;
useEffect第二个参数传空数组可保证函数只执行一遍。

 useEffect(() => {
    setCount(1);
  }, []);

这样实现初始化为1的效果。

function Channel() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('bind')
    const timer = setInterval(() => {
     setCount(count + 1);
    }, 1000);

    return () => {
      console.log('re bind')
      clearInterval(timer);
    };
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

如前,可以看到setInterval被触发,被解绑,被触发,再解绑。
如果加上useEffect(() => {}, []);
会导致count 永远是1。
原因是useEffect如果不重新执行,拿不到最新的state。拿到的state总是最早的state也就是0。

暂时没有好的方法。现在写为useEffect(() => {}, [count]);
如果遇到只能初始化一次的,比如某些插件。可以用事件系统每次拿最新的函数去执行:

import { EventEmitter } from 'events';
const event = new EventEmitter();

 new xxx({ // 此插件只运行一次。回调总执行新的doSomething
    onFinish: () => {
      event.emit('some_event');
    }
  });

function Components({ doSomething }) {
 useEffect(() => {
      event.on('some_event', doSomething);
      return () => {
        event.removeListener('some_event', doSomething);
      };
  });
}

render函数被重复执行

有这样一个组件,loading是个state,每次setloadding会导致list重新执行。
listEl需要数据处理,渲染。每次setloadding,就会处理一遍数据。

{ listEl }
{
   loading && <div>'loading.....'</div>
}

处理方案:useMemo Hooks
useMemo能让我们保存一段dom。

const createMemo = function() {
     return useMemo(() => {
        // handle data
     }, [data.length])
}

这样使得data.length变了之后useMemo hooks里的代码才会重新执行。
useMemo的第二个参数是用Object.is判断的,不能直接写个Object,react不会帮你deep assert。

react 16

如官网所说:react16需要set和map环境。而import的包不会被babel编译,所以需要手动引入map/set

import 'core-js/es6/map';
import 'core-js/es6/set';

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);

React 16 depends on the collection types Map and Set. If you support older browsers and devices which may not yet provide these natively (e.g. IE < 11) or which have non-compliant implementations (e.g. IE 11), consider including a global polyfill in your bundled application, such as core-js or babel-polyfill.

服务端http请求

服务端请求不带header。需要把header带上。
首次请求set到客户端的cookie(用户标识),在服务端首刷请求的时候并不能取得。需要手动修改设置cookie。

 if (!reqestHeader.cookie) {
    reqestHeader.cookie = `uid=${ctx.u_id}; utm_source=${utmSource}`;
}

css module问题

css全局作用域。不想用插件。
靠规范解决。

编译

gulu要求目录文件是app,需要编译要按gulu目录来。
prod环境找问题比较麻烦,对gulu这种框架,需要看他源码来找,没得文档。

react 16 ssr的重构踩坑的更多相关文章

  1. React Native Android配置部署踩坑日记

    万事开头难 作为一只进入ECMAScript世界不久的菜鸟,已经被React Native的名气惊到了,开源一周数万星勾起了我浓烈的兴趣.新年新气象,来个HellWorld压压惊吧^_^(故意少打个' ...

  2. ant.design React使用Echarts,实力踩坑

    最近项目用到Echarts(以下用ec代替),于是照猫画虎得引入到团队的antd项目中,但是遇到2个棘手问题: 1. ec对dom不渲染,检查后发现,原来是全局存在id重复,所以使用React时,最好 ...

  3. React 16 升级时遇到的一个坑,分享一下

    遇到的坑 今天在跟着dva.js官网上面的一个教程写东西的时候,照着教程上面的代码写之后,运行总是报错:TypeError: Cannot read property 'object' of unde ...

  4. react中的路由配置踩坑记

    react 路由配置中,如果根路由(/)匹配一个组件,另一个路由(/list)在进行匹配的时候也会匹配到根路由(/),即在 /list 页面展示的时候 / 页面总是展示在上方. 此时如果想进行严格匹配 ...

  5. 使用 jest 测试 react component 的配置,踩坑。

    首先安装依赖 npm i jest -g npm i jest babel-jest identity-obj-proxy enzyme enzyme-adapter-react-15.4 react ...

  6. 利用vue-router和compoment重构代码--踩坑(一)

    业务主要功能 获取所有的数据库列表 点击某一个数据库列表的时候,右侧分页展示数据 点击右侧某一条数据的时候,现实数据详情 以下是之前的页面,存在以下问题: 前段开发没有工程化(webpack) 主要功 ...

  7. React Native踩坑日记 —— tailwind-rn

    项目背景 在项目的初始阶段,我们需要建立自己的design system,我们spike了一些方案,tailwind-rn就是其中一种,如果有用到或者即将用到tailwind-rn的,可以进来看一看, ...

  8. React Native踩坑Tip

    最近在使用React Native(以下简称RN)中踩了个坑,RN只能异步调用原生方法,所以在原生方法直接调用UI刷新操作需要将任务递交到主线程才可以. RCT_EXPORT_METHOD(finis ...

  9. Ubuntu 16.04 安装Mysql 5.7 踩坑小记

    title:Ubuntu 16.04 安装Mysql 5.7 踩坑小记 date: 2018.02.03 安装mysql sudo apt-get install mysql-server mysql ...

随机推荐

  1. jenkins实现以gitlab为代码仓库的构建

    简介 前一篇随笔是安装jenkins的过程,比较简单,这一次说一下用jenkins配置以gitlab为代码管理仓库的maven项目的完整个构建过程,以及我碰到的一些问题.由于是maven项目,所以我们 ...

  2. mysql获取某个表中除了某个字段名外的所有字段名

    现一个表中有很多字段,但我不想要其中某一个字段,手打出来字段太多,不方便. SELECT GROUP_CONCAT( column_name SEPARATOR ',' ) FROM informat ...

  3. 二分查找BinarySearch(Java)

    二分查找(折半查找)BinarySearch 二分查找  一组排好顺序的数,查找其中的一个数(value)的位置,按照数组(int[] a)存放这组数据,数组的索引所指的位置就是需要查找的数,用三个变 ...

  4. Spring注解IOC/DI(4)

    2019-03-08/11:10:17 演示:使用注解的方式完成注入对象中的效果 注解参考链接:https://www.cnblogs.com/szlbm/p/5512931.html Spring中 ...

  5. 【土旦】vue项目中 使用 pako.js 解密 gzip加密字符串

    前言 今天跟后台对接一个接口,接受到一个加密的值,说是通过gzip加密过的,然后就蒙蔽了, 赶紧上百度找了一下资料,通过一篇文章(原文在底部)发现有个js库可以解密,就下载轻松解密了 实现代码 pok ...

  6. wordpress 图片上传冲突

    网上常见的wordpress图片上传 jQuery('#upload_image_button').click(function() { //formfield并未用上,可能代码遗漏了一段,怀疑和类的 ...

  7. IM多类型holder封装

    如标题,这是一个在列表多类型视图时的一个简化封装方法,减少多余代码,提高复用性,更好迭代扩展,先看视图列表效果图 GitHub:https://github.com/1024477951/Fragme ...

  8. 简单易懂的单元测试框架-gtest(二)

    简介     事件机制用于在案例运行前后添加一些操作(相当于挂钩函数).目前,gtest提供了三种等级的事件,分别: 全局级,所有案例执行的前后 TestSuite级,某一个案例集的前后 TestCa ...

  9. 各种raid对比

    级别 最少单元 特征 冗余 性能 空间利用率 综合评价 RAID0 1 分片分散存入 否 读写2倍 100% 分散存储,任何一块坏掉数据则不完整 RAID1 2 相同数据存入2个磁盘 是 写不变,读快 ...

  10. Exchange 2010邮件服务器的搭建和部署

    Exchange主要是针对内部网或者企业网用户进行搭建的邮件服务器软件,利用它能够很快地搭建安全性较高的内部网邮件系统. 本次搭建在个人环境中实践,纯属爱好折腾,分四步骤,1.搭建windows 20 ...