Git Product home page Git Product logo

reactspa's Introduction

GitHub views Build Status LICENSE MIT

该项目是对 React 技术栈以及前端工程化的相关实践。

愿景:沉淀出一套针对中台系统的开发相对完善的使用方案。

效果展示,建议本地打开。

  • 部分模块展示:

  • redux 实现一个 todoList:

Usage

本地运行
yarn install || npm install
yarn start || npm start

打包
yarn build || npm run build

发布
yarn deploy || npm run deploy

Tech Stack

  • 编译打包: Babel Webpack(4.x)
  • 热更新: webpack-dev-server
  • UI 库: React & React-Dom(^16.11.0)
  • UI 组件: Antd(3.x)
  • 路由: react-router(4.x)、react-router-redux
  • 状态管理: redux (TodoList: Graghql + Relay)
  • JS: ES6、ES7
  • 样式: less
  • Ajax: Fetch
  • 跨域: CORS
  • 代码校验: Eslint(Airbnb 规范)
  • 网关: demo
  • 表单: daForm

articles

Module

  • 音乐模块
    • 音乐列表
  • 工具模块
    • 工资、房租、身体指数、年龄的智能计算器
    • 富文本编辑器
    • 待办事项
  • 画廊模块
  • 搜索模块
    • 搜索引擎(集成百度、360、搜狗搜索)
  • 其它
    • one for all 表单方案 —— daForm

Project Structure

├── build.js                   项目打包后的文件
├── config                     webpack配置文件
│   ├──...
│   ├──webpack.config.dev.js   开发环境配置(启动速度优化)
│   ├──webpack.config.prod.js  生产环境配置(打包体积优化)
├── node_modules               node模块目录
├── public
│   └──index.html
├── scripts
│   ├── build.js               打包项目文件
│   ├── start.js               启动项目文件
│   └── test.js                测试项目文件
├── src
│   ├── client
│   │   ├── store              redux中的store
│   │   ├── devTools.js        开发者工具
│   ├── common                 核心目录
│   │   ├── api                请求api层
│   │   ├── actions            redux中的action
│   │   ├── components         通用功能组件
│   │   ├── container          通用样式组件
│   │   ├── images
│   │   ├── pages              页面模块
│   │   ├── reducers           redux中的reducer
│   │   ├── utils              工具类
│   │   │   ├── index.js
│   │   │   ├── config.js      通用配置
│   │   │   ├── menu.js        菜单配置
│   │   │   └── ajax.js        ajax模块
│   │   └── routes.js          前端路由
│   └── server                 服务端目录(日后用到)
│       └── controller
├── .gitignore
├── package.json
├── README.md
└── yarn.lock

todoList

  • 使用 suspense 替代 react-loadable
  • 状态管理库迁移至 graghql

reactspa's People

Contributors

dependabot[bot] avatar muyunyun avatar zouyifeng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactspa's Issues

hello,world

牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?
牧云云大神太厉害了,约不约?

注册页面

我第一次打开输入错误的帐密点击按钮提示我“XXXX”,再次刷新页面会自动又提示我“XXXXX”

日期选择问题

在“”工具模块“”的日期中,如果我单独选择年份的话(例如1999),刚选择完毕,弹出框消失,但是输入框并没有显示我选择的结果。如果是用“<<”选择年份然后在上面选择日期的话,是OK的。

Npm start

npm start后 响应速度太慢了,半天才能刷出页面,有什么地方可以optimizing一下的?

css 模块实践

css-loader

来看下面这一组 webpack 的配置项,

{
  test: /\.less$/,
  use: [
    require.resolve('style-loader'),
    {
      loader: require.resolve('css-loader'),
      options: {
        modules: true,  // 开启 css 模块化
        importLoaders: 2,
      },
    },
    {
      loader: require.resolve('postcss-loader'),
      options: {...},
    },
    {
      loader: require.resolve('less-loader'),
      options: {...},
    },
  ],
},

它们执行顺序是 less-loaderpostcss-loadercss-loaderstyle-loader。我们来理一下各 module 的作用:

  • less-loader: 将 less 解析为 css
  • postcss-loader: 将 css 兼容各浏览器加上相应前缀
  • css-loader: 将 css 转化为 common.js 模块
  • style-loader: 将 common.js 模块注入 style 标签中

实践代码如下所示

import 'styles' from 'index.less'

<Layout className={styles.containAll}>...</Layout>
.containAll {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  .github {...}
  .author {...}
  .white {...}
  .menu {...}
  .switch {...}
}
}

编译后结果为

<div class="_2xsOSdvdJbkVjqgjHOE58l">...</div>
<style type='text/css'>
._2xsOSdvdJbkVjqgjHOE58l {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
}
</style>

通过 _2xsOSdvdJbkVjqgjHOE58l 锁定作用域

但是这种方式有一个缺陷,因为 JavaScript 是不识别连接符的,所以如果类名必须写成驼峰式的。如下这样写它就会编译报错:

import 'styles' from 'index.less'

<Layout className={styles.contain-all}>...</Layout>

为了更大的普适性,我们来看下面一种方案:

react-css-modules

它的本质是一个高阶组件,目前文档上已经说明不再维护(因为影响了运行时的性能)。

babel-plugin-react-css-modules

目前在 less 中使用有 bug,参见 https://github.com/gajus/babel-plugin-react-css-modules/issues/165,正在跟进。

使用动态引入语法 import()

可以参考 压缩打包优化实践,使用这种方案,也做到了当前模块只展示相应引入的 css(本质也是 style)

@connect 的问题

你好啊大神 ,我 在 拜读 你的 https://github.com/MuYunyun/reactSPA 项目, 看到用 了
image @connect
语法, 我项目里也是 yarn eject 之后 , 也用了这个语法 ,但是 我 npm run build 时就报错了,
image
然后 我 npm i react-scripts --save-dev 重新装回 react-scripts 这个库后, package.json里的 build 脚本 改成 "build": "react-scripts build",
这下 就 看到
image@connect 的错误, 你的项目 是 怎么配置的啊, 我看你的项目 可以 build 成功啊

mock 方案调研

当前痛点

  • 前端必须依赖后端接口返回的数据以及格式
  • 后端同学接口文档变更,无法及时响应
  • 本地造数据的成本比较大, 而且 mock 数据污染代码

当下急切寻找一个平台级的 mock 方案,在各个系统间使用无差别利于推广。调研开始。

easy-mock

大搜车推出的 mock 方案。

优势:前端团队间合作较为容易;支持部署到公司内网;支持 swager;
缺点:需掌握 mock.js 的语法,有一定上手成本;还是得依赖后端定好相应的数据格式;

zan-api

有赞前端推出的 mock 方案。目前感觉这个好玩的点比较多,但是并没有开源。

难点:

  • 后端生成 javadoc 的时候如何在平台上生成一份额外的 api;
  • 根据这份参数(以及参数类型)自动生成 mock 数据;
  • 前端调用平台数据的方式;

hi

前排挤一挤,拜拜牧大神!
前排挤一挤,拜拜牧大神!
前排挤一挤,拜拜牧大神!
前排挤一挤,拜拜牧大神!
前排挤一挤,拜拜牧大神!
前排挤一挤,拜拜牧大神!

疑问

你这个右侧的调试工具是什么插件,本地跑起来,但是不知道怎么关,新手

完全不支持IE吗?

IE11控制台都会报错“ 对象不支持“startsWith”属性或方法”,页面无法显示

menu 模块

我在选择某个模块,比如#/album,重新刷新页面。页面还是#/album的页面,但是左侧的tab并没有选中

压缩打包实践

能还配置优化或是分块打包,目前打包出来文件太大了

页面的分页机制

我看了一下这个页面的分页机制,实际上是进行内存分页,并不是使用异步加载分页机制。这里是有开关控制吗

路由问题

image
我是使用BrowserRouter写的路由,标签下的两个中path="/"的那个必须放在第一位么?

若path="/"放在第一位,访问login时根本跳转不到登录页面,图中所截取的存放方式却可以实现,为什么?是我配路由的方式不对是么?

脚手架

这个项目用的脚手架,然后eject了?还是webpack全是自己配的

npm start

执行npm start 报错,什么原因呢?
F:\app\reactSPA-master\scripts\start.js:23
const {
^

SyntaxError: Unexpected token {

iconfont本地化

由于antd使用公网的iconfont,导致内网环境无法使用iconfont,有什么解决办法可以使用本地的iconfont吗?

react 性能优化实践

SearchBar组件中的input类型疑问

当我使用 type='input'时,页面上的文本框只能输入一个字符,但是我看了源码好像并没有限制maxlength

{
            title: '歌曲名称',
            key: ['songName'],
            type: 'input',
 }

image

eslint 风格规范

试用 airbnb 代码规范。

// .eslintrc
"extends": "airbnb",
// package.json
"eslint": "^5.5.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-react": "^7.11.1",

路由问题

路由的设计思路是参照你的项目设计的,但是登录成功之后无法跳转到首页,页面显示一片空白,原因应该是没有进入container

_20171211172042
image

标签里的exact又不能去掉,去掉的话就全渲染了,有无法跳转到登录页面了

“Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `TableCell`.”

打开音乐列表页面。会有警告提示“Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of TableCell.”
in Tooltip (created by TableCell)
in TableCell (created by TableRow)
in tr (created by TableRow)
in TableRow (created by Table)
in tbody (created by Table)
in table (created by Table)
in div (created by Table)
in div (created by Table)
in div (created by Table)
in div (created by Table)
in Table (created by Table)
in div (created by Spin)
in AnimateChild (created by Animate)
in div (created by Animate)
in Animate (created by Spin)
in Spin (created by Table)
in div (created by Table)
in Table (created by Table)
in div (created by Table)
in Table (created by Music)
in div (created by Music)
in div (created by Music)
in div (created by Music)
in Music (created by Connect(Music))
in Connect(Music) (created by Route)
in Route (created by Contents)
in div (created by Basic)
in Basic (created by Adapter)
in Adapter (created by Contents)
in Contents (created by Container)
in div (created by BasicLayout)
in BasicLayout (created by Adapter)
in Adapter (created by Container)
in div (created by BasicLayout)
in BasicLayout (created by Adapter)
in Adapter (created by Container)
in Container (created by Route)
in Route
in Switch
in div
in Router (created by HashRouter)
in HashRouter
in Provider

请问如何实现的路由跳转呢?

作者你好!我刚开始接触React,我看了下你的项目中路由是在routers.js中去配置的,但是里面只有首页与登录页的配置,那么点击侧边栏的时候让页面去跳转是如何实现的呢?这些是在哪里配置的呢?

webpack 打包优化实践

使用 webpack 插件找出占用空间较大的包

开发环境中可使用 analyze-webpack-plugin 观察各模块的占用情况。以该项目为例:浏览器中输入 http://localhost:3000/analyze.html 可以看到如下效果:

按需加载

  • 对模块结合 babel 进行按需加载;
  • 测试 day.js 替代 moment.js. 实际上 moment.js 也使用按需加载 了(实验减少了 40KB+),所以最终结果相差不大;

code-spliting

使用 MiniCssExtractPlugin 插件分离 JavaScript 和 Css 文件:

  823.94 KB           build / static / js / main.496a38b7.js
  8.2 KB              build / static / css / main.css

code-spliting 官方给出三种方案,分别如下:

方案一:在 entry 处增加打包入口

方案一的缺点如下:

  • 如果多个文件引人了相同的包(比如 lodash),引用的包会被分别打包两次;
  • 这种方案不够灵活,无法根据逻辑动态分割代码;

所以方案一通常会结合方案二、方案三一起使用,方案一的配置大致如下:

entry: [require.resolve('./polyfills'), paths.appIndexJs],

// 也可以写成

entry: {
  polyfill: require.resolve('./polyfills'),
  IndexJs: paths.appIndexJs,
},

方案二:使用插件 SplitChunkPlugin

  optimization: {
    runtimeChunk: false,
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: 'all',
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          maxAsyncRequests: 5,
          priority: 10,
          enforce: true,
        },
      },
    },
  },

打包效果如下:

  723.96 KB  build/static/js/vendor.a9289a29.chunk.js // node-modules 模块
  98.72 KB   build/static/js/main.7bcaca24.js
  8.2 KB     build/static/css/1.css

此时将 node-modules 里的包打包成了一个大块头,这样对加载仍然是不友好的。解决方案为:将核心的框架单独打包出来,剩余模块异步加载,比如可以使用 bundle-loader)。

  optimization: {
    runtimeChunk: false,
    splitChunks: {
      cacheGroups: {
        vendor1: { // 主要模块
          chunks: 'all',
          test: /[\\/]node_modules[\\/](react|react-dom|antd)[\\/]/,
          name: 'vendor1',
          maxAsyncRequests: 5,
          priority: 10,
          enforce: true,
        },
        vendor2: { // 次要模块
          chunks: 'all',
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor2',
          maxAsyncRequests: 5,
          priority: 9,
          enforce: true,
          reuseExistingChunk: true,
        },
      },
    },
  }

打包效果如下:

  588.06 KB  build/static/js/vendor2.d63694f4.chunk.js
  133.17 KB  build/static/js/vendor1.0d40234c.chunk.js
  98.72 KB   build/static/js/main.b7a98d03.js
  8.2 KB     build/static/css/2.css

可以看到此时 node_modules 包已经被拆分成了核心模块和非核心模块。

使用动态引入语法 import()

首先使用官网安利的 react-loadable 这个包,它的**是根据路由(代替模块)进行代码的动态分割,异步加载所需要的组件,从而极大地提高页面加载速率。

在路由界面进行如下配置:

const Loading = () => <div>Loading...</div>

const Home = Loadable({
  loader: () => import('../pages/home'),
  loading: Loading,
})

// 类似这样使用路由
<Router>
  <Route path="/home" component={Home} />
  <Route path="/follow" component={Follow} />
  <Route path="/tools" component={Tools} />
  <Route path="/music" component={Music} />
  <Route path="/todo" component={Todo} />
  <Route path="/album" component={Album} />
  <Route path="/editor" component={Editor} />
  <Route path="/todoList" component={TodoList} />
  <Route path="/searchEngine" component={Search} />
  <Route path="/waterfall" component={Waterfall} /
</Router>

我们来看代码分割后的结果:

这里测试结果是去掉方案二的配置后进行的,实验对比后,使用方案三的方式稍优于方案二、三共同使用的方式。

  235.89 KB  build/static/js/IndexJs.57ee1596.js
  225.94 KB  build/static/js/15.c09a5919.chunk.js
  138.18 KB  build/static/js/17.30c26142.chunk.js
  82.71 KB   build/static/js/1.667779a6.chunk.js
  57.55 KB   build/static/js/16.f8fa2302.chunk.js
  16.46 KB   build/static/js/2.e7b77a5d.chunk.js
  14.79 KB   build/static/js/18.cad1f84d.chunk.js
  12.51 KB   build/static/js/0.73df11a7.chunk.js
  11.22 KB   build/static/js/13.19501c58.chunk.js
  8.34 KB    build/static/js/5.33fd1c35.chunk.js
  7 KB       build/static/js/8.9f1d0a47.chunk.js
  5.86 KB    build/static/js/12.24f0a7ec.chunk.js
  5.06 KB    build/static/css/18.css
  4.97 KB    build/static/js/polyfill.1c61a660.js
  3.58 KB    build/static/js/7.dd4976e3.chunk.js
  3.53 KB    build/static/js/14.16f6b811.chunk.js
  3.42 KB    build/static/css/17.css
  2.98 KB    build/static/js/10.464a61e4.chunk.js
  2.02 KB    build/static/js/11.3728d5a9.chunk.js
  1.45 KB    build/static/js/6.92fbac58.chunk.js
  1.13 KB    build/static/js/9.59160a3a.chunk.js

有多少个路由,react-loadable 库就自动帮我们多拆分了多少个包文件。可以想象在越大的项目中,这种动态引人库的好处越明显。

image

而且可以很清晰的看到,当我们在 /home 下,只有 home 组件是被加载的,其他组件并没有被加载!

那么 react-loadable 的神秘之力是如何实现的呢,它本质上是个运用了属性代理的高阶函数,通过在高阶函数里配合 import() 加进各种状态,从而达到异步加载模块的效果。

使用 DefinePlugin 区分环境

其作用是能自定义一些区分生成环境与开发环境的名字,用法如下:

生产环境:

new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify('production'),
  ENABLE_DEVTOOLS: false,
}),

生产环境配置中的 process.env.NODE_ENV: "'production'" 必须要填上,根据这个命令, 有些不必要的 warn 命令不会出现在生产环境。(不过在实践中,发现 mode 为 production 时,webpack 应该自动加上了这个配置)

开发环境:

new DefinePlugin({
  'process.env': { NODE_ENV: JSON.stringify({ 'development' }) }
  ENABLE_DEVTOOLS: true,
}) 

比如只有在开发环境下才使用 redux 分析工具,可以作如下区分:

{ENABLE_DEVTOOLS ? <DevTools /> : ''}

参考文献

code-splitting
Code-Splitting(react)

请教一个问题,展开缩略那个按钮失效

您好,我是个初学者,在使用以下布局形式开发时,引入侧边栏js及标题栏js时,缩小侧边栏按钮功能无法生效该怎么办?
image

`
import SiderNavi from '../SiderNavi';
import HomeHeader from '../HomeHeader';

{//侧边栏js} {//标题栏js} `

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.