美文网首页React技术栈React.js
react boilerplate设计&&技术栈

react boilerplate设计&&技术栈

作者: candice2cc | 来源:发表于2017-03-01 16:52 被阅读266次

    为什么需要脚手架?

    工作中经常会基于react开发一些项目,出于以下初衷,搭建react boilerplate显然是非常有意义的。

    • 新项目快速搭建环境
    • 保证项目代码结构一致性和规范
    • 工程化的落地与脚本复用
    • 技术栈的调研与选型,使用优质的npm库但不滥用

    适用场景

    现在还是有很多项目的接口依旧是采用java/php等传统后端语言编写,部署时基本是编译一个前端代码版本,然后把这些静态文件拷贝到java/php等对应的web服务器下。
    为什么还是这种陈旧的状况呢?前端开发同学对于后端开发相关的技术积累(数据库、操作系统、网络等)还不够,业务熟悉度也不够,前后端全栈开发还存在一定难度,但相信这只是一个过渡时期。
    因此,这个脚手架是基于该应用场景而设计,同时通过express提供web server,方便前后端接口联调测试。
    后续会提供一个“前后端同构”、“react+express”的“全栈boilerplate”,方便过渡到“前后端全栈开发”上来。

    安装与使用

    npm install //安装所有的依赖包
    npm run dev //启动开发环境
    npm run build //编译一个产品版本的代码
    npm start //启动产品环境进行测试
    

    技术栈

    react boilerplate中使用到了一些第三方的NPM库,这些库的引入,均是为了满足常见的一些使用场景需求,此处仅对其底层原理或者引入原因进行介绍,具体使用方法参考官方文档即可。

    react

    react是一个前端视图框架。它可以理解为是MVC中的View。

    • Virtual DOM
      痛点:复杂或者频繁的DOM操作通常是Web开发性能瓶颈。
      react引入了Virtual DOM机制,所有的DOM操作都是通过虚拟DOM进行,每当数据变化时,进行DOM diff,然后仅将需要变化的部分进行实际的浏览器DOM更新。
    • Render
      不再关注某个数据的变化如何更新到一个或多个具体的DOM上,只需要关心:在任意一个数据状态上,整个界面是如何Render的,且不像服务端Render,需要整个页面刷新
    • 组件化
      UI功能模块之间的分离:整个UI通过一个个独立的小组件构成大组件,每个组件只关心自己的逻辑,彼此独立。
      特征:可组合、可重用、可维护、可测试

    react-router

    react路由,当然,使用路由方便更新导航栏并使用对应的应用页面。更底层的原因是它提供了一套逻辑机制去拆分代码。
    学习react-router官方文档了解更多

    redux

    当前组件间通信形式有:

    1. 嵌套组件间,上层组件向下层组件传递回调函数,下层组件触发回调来更新上层组件的数据。
    2. 以事件的形式,使用发布订阅的方式来通知数据更新。
    3. Flux/Redux
      当项目越来越大的时候,管理数据的事件或回调函数将越来越多,也将越来越不好管理了。
      Redux 是一个用来管理数据状态和 UI 状态的 JavaScript 应用工具。随着 JavaScript 单页应用(Single Page Applications, SPAs)开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态),Redux 被创造用来让 state 的更易于管理。
    Free-Converter.com-redux-article-3-03-30585450.jpg

    redux更多可以学习:

    react-redux

    react-redux主要是用来帮助react component与redux的“链接”的高阶组件(HOC)

    redux-thunk

    redux-thunk 是一个比较流行的 redux 异步 action 中间件,比如 action 中有 setTimeout 或者通过 fetch 通用远程 API 这些场景,那么就应该使用 redux-thunk 了。
    redux-thunk 帮助统一了异步和同步 action 的调用方式,把异步过程放在 action 级别解决,对 component 没有影响。

    redux-router

    暂未引入

    isomorphic-fetch

    弃用传统的XMLHttpRequest,改用fetch api与后台服务器交换数据(更简单优雅的一套API,在最新版的Firefox和Chrome中已经提供支持)。
    isomorphic-fetch为不兼容fetch api的环境提供支持,同时支持nodejs和浏览器环境(前后端同构)

    es6-promise

    ES6 promise polyfill,部分浏览器环境依旧还不支持Promise

    工程化

    需求

    一个高性能、安全、可用的web网站,前端实践需要考虑:

    • 缓存
    • 压缩与合并
    • 混淆
    • 浏览器兼容
    • 按需加载
    • 首屏加速
    • 语法编译
    • ...

    而为了提高开发效率,考虑以下:

    • 开发服务器
    • 热加载和热替换
    • 自动化脚本
    • ...

    而这些,很多都依托于工程化落地去实践。

    工欲善其事必先利其器

    webpack1.x

    引用webpack官方的一张示意图:


    what-is-webpack
    • 插件
      webpack有丰富的插件接口,可定制化
    • Loaders
      webpack通过loader支持各种文件预处理。允许将不同类型的静态资源进行打包(不仅仅是JavaScript)
    • 代码分块

    代码模块在到达浏览器前,应当事先编译打包。存在两种倾向:
    1. 一个模块一个请求
    2. 所有模块一个请求

    以上两种都是次优解,当项目有一定复杂度和规模时,我们考虑分chunk传输。
    webpack允许将代码分块,通过确定代码分割点,实现按需加载,加速首屏加载。

    • 开发工具
      webpack提供开发中间件和开发服务器,自动重新加载,并且生成SourceUrls和SoureMaps,方便调试。
    • 性能
      webpack使用异步I/O操作,多级缓存和增量编译机制
    • 支持
      webpack同时支持AMD和CommonJs的模块定义方式。因此支持使用绝大部分的已有库。
    • 优化
      webpack有很多优化手段减少静态文件大小,并且通过生成hash值方便浏览器请求缓存
    • HMR
      在不刷新浏览器的条件下,应用最新的代码更新

    webpack配置

    下面是webpack开发环境的配置:

    /**
     * Created by candice on 17/1/26.
     */
    const path = require('path'),
        webpack = require('webpack'),
        HtmlWebpackPlugin = require('html-webpack-plugin'),
        ProgressBarPlugin = require('progress-bar-webpack-plugin');
    
    module.exports = {
        devtool: 'eval-source-map',
        context: path.resolve(__dirname, '..'),
        entry: {
            bundle: [
                'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000',
                './client/index',
            ],
            vendor: [
                'react',
                'react-dom',
                'redux',
                'react-redux',
                'es6-promise',
                'isomorphic-fetch'
            ]
        },
        output: {
            path: path.resolve(__dirname, '../build/client'),
            filename: 'js/[name].js',
            chunkFilename: 'js/chunk.[name]-[hash].js',
            publicPath: '/'
        },
        module: {
            loaders: [{
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel',
                query: {
                    presets: ['es2015', 'react', 'stage-0', 'react-hmre'],
                }
            }, {
                test: /\.scss$/,
                loaders: [
                    'style',
                    'css?modules&camelCase&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:8]',
                    'sass'
                ]
            }, {
                test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)/,
                loader: 'url?limit=8000&name=img/[name]-[hash].[ext]'
            },
                {
                    test: /\.json$/,
                    loader: 'json'
                }, {
                    test: /\.html$/,
                    loader: 'html?minimize=false'
                }]
        },
        resolve: {extensions: ['', '.js', '.json', '.scss', '.html']},
        plugins: [
            new webpack.optimize.OccurenceOrderPlugin(), // 根据模块调用次数,给模块分配ids,常被调用的ids分配更短的id,使得ids可预测,降低文件大小,该模块推荐使用
            new webpack.optimize.DedupePlugin(), //打包的时候删除重复或者相似的文件
            new webpack.optimize.CommonsChunkPlugin({
                names: ['vendor'],
                filename: 'js/[name].js'
            }),
            new webpack.HotModuleReplacementPlugin(),  //热替换
            new webpack.NoErrorsPlugin(),  //报错但不退出webpack进程
            new webpack.DefinePlugin({'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)}), //定义变量
            new HtmlWebpackPlugin({                       //生成html
                filename: './index.html',
                template: './client/index.html',
                favicon: './client/common/assets/favicon.ico',
            }),
            new ProgressBarPlugin({summary: false})
        ],
    };
    

    loader介绍

    • babel-loader:处理JS文件的加载,通过preset完成es2015和JSX到JS的文件转换
    • css-loader:处理css中路径引用等问题
    • css-loader?modules通过modules查询参数启用css-module。
      css-module意义主要在于:局部作用域和模块依赖
    • style-loader:动态把样式写入css
    • sass-loader: sass编译器
    • url-loader:处理图片
    • html-loader:处理html文件

    plugin介绍

    • OccurenceOrderPlugin:webpack为每个模块指定唯一的id,通过该插件,webpack会分析和为模块按优先级排序,为最经常使用的分配一个最小的ID
    • DedupePlugin:打包的时候删除重复或者相似的文件
    • CommonsChunkPlugin:处理公共模块
    • UglifyJsPlugin:JS混淆处理
    • HotModuleReplacementPlugin: 配置代码自动编译和热替换插件

    目录结构

    /build/ compile输出
           /client 前端代码compile输出
           /server 服务端代码compile输出
    
    /client/     前端目录
         /common/ 公共代码
                 /actions/   redux actions
                 /assets/    公共静态资源
                 /components/   公共木偶组件
                 /containers/   公共智能组件
                 /reducers/     redux reducers
                 /sass/         公共样式和公共组件样式
                 /store/        redux store
                 /util/         工具类与函数
                 /config/       配置相关
         /module_a/ a模块
                 /components/
                 /containers/
                 /sass/
         /module_b/ b模块
                 /components/   木偶组件
                 /containers/   智能组件
                 /sass/         样式
         /index.html   入口html文件
         /index.js     入口js文件
         /routes.js    路由文件
    /server/  服务端目录,用来mock服务端接口联调测试
            /routes/  控制层:api服务路由
            /app.dev.js    开发环境下的app.js
            /app.prod.js   产品环境下的app.js
         
         
    /node_modules/ 第三方库
    /tools/
           /webpack.config.dev.js  开发环境下的webpack配置
           /webpack.config.prod.js 产品环境下的webpack配置
    /.babelrc     babel配置文件
    /package.json  
    /readme.md    说明文档
    

    附上:react boilerplate源码

    相关文章

      网友评论

        本文标题:react boilerplate设计&&技术栈

        本文链接:https://www.haomeiwen.com/subject/eiqcgttx.html