同构渲染SPA框架搭建过程

合并了4月19与5月15的搭建记录, 主要记录了当时前端框架搭建的过程,虽然最后没有使用,但为了以后再次搭建,发布出来。

借鉴

项目

  1. components与containers这种分离
    containers来包含components,然后被routers所依赖,router被client与server所调用

  2. bin中通过global方式处理server或者client的处理

  3. helpers中ApiClient的处理
  4. server、router、client;
    client与server都依赖于ApiClient,ApiClient是与redux依赖的一个通信的接口,在它其中也对SERVER进行了判断,这里有个superagent,超级代理,来完成对api的调用,客户端为什么会调用这里?

  5. config,js没有它灵活是因为在它里边可以做判断

  6. client中通过ReactDOM.render()将路由刷进content所在的dom
  7. server通过helpers中的Html,执行less预编译,使其可以在服务端进行渲染css model
  8. 重定向

问题

  • 异构时client 如何渲染的
    应该在Html中将client作为script传递过去才对
    按这个思路来实现一下吧
    一直没有找到client.js如何放进浏览器的,感觉上要么是打包时候,要么是在redux做了一些处理

    同构原理
    同构应用

    入口不同的意思是,server端是staticRouter, 浏览器是browserRouter,所以从服务端刷新时候使用是sever端的入口,在浏览器端时候的client端的入口。

  • product与develop的差别?

设计

  • components来做组件
  • container来组建componets
  • router来组建containers,借鉴了react-router官网,将这一部分改为了<App>
  • Html类似于模板,根据传入数据来渲染不同的内容
  • 在sever中将routers传送给Html来做服务端的渲染,反之不传router,完全client渲染
  • 服务端的入口是server,客户端的入口是client,客户端需要打包,并通过server端传递到浏览器

搭建记录

文件介绍

  • server
    服务的创建
    服务端渲染
    路由的代理[先不移植]
    可以以模板的写,但太过于硬编码,于是抽象成Html来做,Html是一个mponent,通过RenderToString的方式来完成body体中字符串的拼接
  • Html
    Html有一个componet的接口,可以渲染任意的传入的组件,并返回一个无DOCTYPE html>的页面
    在这里需要有对client.js的引入,以及client render的位置。
    Html最后只对server负责
    Server中传入的Component,是

    1
    2
    3
    <StaticRouter>
    <App />
    </StaticRouter>
  • client
    根据路由再次渲染,渲染的位置是Html指定的挂载点
    内容应该是

    1
    2
    3
    <BrowserRoute>
    <App />
    </BrowserRoute>
  • route
    route应该是App,来聚合各种的Route,为server与browser提供支持

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    const App = () => {
    const someVariable = true;

    return (
    <Switch>
    {/* these are good */}
    <Route exact path="/" component={Home} />
    <Route
    path="/about"
    render={props => <About {...props} extra={someVariable} />}
    />
    {/* do not do this */}
    <Route
    path="/contact"
    component={props => <Contact {...props} extra={someVariable} />}
    />
    </Switch>
    );
    };
  • containers
    containers中用来装各个组装的组件,在MPA中,是页面,用来被App引用,其内部也可以再做转发

  • components
    这个是common的组件,是被container所共用的组件

问题记录

  • 不认识Styl模块问题

    需要增加一个lib.d.ts

    1
    2
    3
    4
    5
    6
    7
    declare module "*.styl" {
    interface IClassNames {
    [className: string]: string
    }
    const classNames: IClassNames;
    export = classNames;
    }
  • 后端React引用问题
    “React”指 UMD 全局,但当前文件是模块。请考虑改为添加导入。ts(2686)
    routers.tsx中没有直接使用React,但使用着Route、Switch等,没有引入React,结果显示问题。

    原因是:b.tsx does directly rely on React. The <SomeComponent /> line will be turned into a React.createComponent call.

  • ts检验的问题
    Import sources within a group must be alphabetized.:模块的导入须排序
    未命名函数只能是箭头函数
    不能使用console.log等问题

    1
    2
    3
    4
    5
    6
      "rules": {
    "ordered-imports": [false],
    "object-literal-sort-keys": [false],
    "only-arrow-functions": false,
    "no-console": false
    }
  • js中调用ts运行
    需要增加ts-node来在js中直接调用ts,否则不会认识模块

    1
    2
    3
    require('ts-node').register({
    project: require('../tsconfig.json')
    })
  • js运行不识别import问题
    运行bin中的server.js,不认识import,用require就不认识ts中的imports
    SyntaxError: Unexpected token import

    感觉还是ts的问题,试着修改了tsconfig.json
    module”: “esnext”, => “module”: “commonjs”, 的时候问题解决

  • [*]客户端刷新没成功
    客户端刷新的形式几个例子都是通过直接挂载在模板上过去的,这时候他们的webpack都是分开打包的,从server中去访问dist中相应的js文件来完成的
    但我们主要的同构的不是这样玩的,它是通过webpack的同构工具完成的:<script src={assets.javascript.main} charSet="UTF-8"/>,把这条注释掉,发现再点击的时候,每次点击都会走路由,若不注释,只第一次刷新时候走的路由。考虑移植webpackIsomorphicTools来试试

  • webpack客户端的只打包成main.xxxxxx.js的原因
    webpack在编译的时候entry可以是对象、数组、字符串,对象的key值是编译后的文件名,value值是路径,若直接放字符串,则默认是main
    在output中指明了[name].js, 这样编译出来的就是main.js了

  • [*]debug模式如何完成客户端渲染
    确实去拿了,确实拿到了,原因是express把所有的路由都返回了服务端的那个渲染
    研究一下express.static,express可以托管静态文静

    1
    app.use('/dist', Express.static('dist/browser'));

    前边还是路由,后边是Express托管的静态文件的地址,这样修改后,浏览器中即可获取到客户端入口文件了

  • 浏览器上显示React引用问题
    React is not defined
    在这里又返回了我们第一问题,即要想用BrowserRouter,就必须引入React,因为它是通过React.createElement()的方式来创建的对象,像是一个语法糖一样
    找了了React is not defined处理文章,去查了一下webpack的配置,发现已经存在了 ‘react’:’React’ 了
    从问题描述中看到react.min.js等需要引入,于是引入,问题解决。

  • [*]ts中运行成功,但编译成dist之后运行不成功

    • 从编译后的server的js中引用browser中js失败
      引用的函数返回undefined

    • 采用js来写这段代码
      import 问题,修改import成 require, 编译问题
      js中没有经过babel,无法识别其中的React标签,然后通过
      React与Babel,安装babel-loader,@babel/core,@babel/preset-env,@babel/preset-react之后,问题解决,一运行,问题回到原点,及没法引出编译之后的模块
      然后发现编译之后的模块没有导出任何东西,编译之后的js自成模块,无对外的引用与导出,…
      然后考虑在server中增加对它的连接

    • 链接失败
      因为入口设成server.ts之后,会去递归连接.express\net等模块,出现错误
      对比了借鉴项目,webpack并不去处理server的入口…
      有一种方式,就是跟server端的连接一样,将tsx也都翻译成js,链接一份,非链接一份,这样运行即可。但这样对styl等的处理不好
      这样有两种选择:1. 用模板,2. 用ts直接运行

      ts-node for production
      ts-node for production

      以上两个问题的回到,ts-node可以直接用与生产,只不过会比js直接运行多一点内存。
      那就先用这种方式吧。

  • 后端渲染stylus的坑
    需要使用webpack-isomorphic-tools来辅助,步骤见官网
    大体的过程是,先配置一个webpack-isomorphic-tools注入到webpack中,并进行配置,然后在服务端启动的入口,将webpack-isomorphic-tools注入到server中,然后在Html中即可使用这个工具。
    工具本身已经在js构建工具中记录过了,这里不再详谈了

  • 后端渲染的browser dom的坑
    描述:
    在Html渲染时,使用的是 react-dom/server引出的ReactDOM.renderToString()函数,并且组建也是用staticRouter包裹的<App />。但却依旧报了browser dom渲染的错。

    解决:
    问题的解决是发现App组件中,又增加了一个router,去掉这个router之后,问题解决了

  • 后端渲染material-ui组件的坑
    描述:
    后台在渲染material-ui组件时,总是会报错,包括 <Divider /> <IconButton>等组件

    方案:
    借鉴,material-ui ssr
    这个问题没有真正解决,原因在于react-jss安装不成功,怀疑是node与npm版本问题