zoukankan      html  css  js  c++  java
  • react分享

    后台项目应用分享

    后台项目应用分享

    webpack + react + redux + antd

    策略篇

    框架选择

    兼容性IE9+

    • 组件化:React
    • 状态管理:React Redux
    • 前端路由: React Router
    • Ajax请求: Axios
    • UI库:Ant Design
    • 构建工具:Webpack

    组件化开发

    组件?组件!

    (讨论)

    CSS in JS下的样式开发思路

    • 样式跟着组件走
    button-group.js
    button-group.less
    • 使用工具方法
    @import (reference) "~BaseLess";
    .username{
    display: inline-block;
    max-width: 200px;
    .text-overflow() //使用单行溢出隐藏方法
    }

    注:在less文件中引用alias定义或node _modules下的less文件,需要在路径前加~

    • 使用compose实现样式复用
    /* components/Button.css */
    .base { /* 所有通用的样式 */ }

    .normal {
    composes: base;
    /* normal 其它样式 */
    }

    .disabled {
    composes: base;
    /* disabled 其它样式 */
    }
    imp
    • 重置全局样式
    .collapsed {
    //anticon类原样输出
    :global(.anticon) {
    font-size: 16px;
    margin-left: 8px;
    }
    :global(.anticon+span),
    :global(.ant-menu-submenu-vertical > .ant-menu-submenu-title:after) {
    display: none;
    }
    }

    扩展阅读:CSS Modules 详解及 React 中实践

    展示组件 VS 容器组件

    我们先来看一下Redux官方文档中的定义:

     展示组件容器组件
    作用 描述如何展现(骨架、样式) 描述如何运行(数据获取、状态更新)
    直接使用 Redux
    数据来源 props 监听 Redux state
    数据修改 从props调用回调函数 向 Redux 派发 actions
    调用方式 手动 通常由 React Redux 生成

    结合官方定义,我们把组件分为三个层次:

    • 应用(App): 整个管理系统是一个应用(单页模式一般只有一个应用,多页模式可能有多个应用)
    • 容器(Container):可以从路由访问得到的组件叫做容器,类似传统开发模式中的后端页面
    • 组件(Component):容器以外的组件都叫组件

    Action/Reducer 应该和组件绑定吗

    (讨论)

    调试Redux

    (演示)

    构建一个调试工具配置文件devtool.js

    /**
    * redux调试工具
    */


    import React from 'react';
    import { createDevTools } from 'redux-devtools';
    import LogMonitor from 'redux-devtools-log-monitor';
    import DockMonitor from 'redux-devtools-dock-monitor';

    export default createDevTools(
    <DockMonitor defaultIsVisible={false}
    toggleVisibilityKey="alt-h"
    changePositionKey="alt-q">

    <LogMonitor />
    </DockMonitor>

    );

    开发环境,在容器root.dev.js中引入调试工具

    import React from 'react';
    import DevTools from './devtools';
    import Layout from 'components/layout';
    import style from './style.less'

    export default class Root extends React.Component {

    render () {
    return (
    <div className={style.root}>
    <Layout>{this.props.children}</Layout>
    <DevTools />
    </div>

    )
    }
    }

    开发环境,在store.dev.js中引入调试工具

    import DevTools from 'containers/root/devtools'

    export default function configureStore(initialState, reducers) {
    const store = createStore(
    ...
    compose(
    ...
    //redux调试工具
    DevTools.instrument()
    )
    );
    ...
    return store
    }

    规范建议

    命名规范

    • 变量名以驼峰方式,如:
    const userInfo = {}
    • 类名以大写字母开头,如:
    Class UserManager extend from React.Component{
    ...
    }
    • 文件(夹)名一律小写,以下划线_或中杠线-作为分隔符
    • 文件(夹)名以下划线_区分类型,如:common_action.js,表示Action
    • 文件(夹)名以圆点.区分环境,如:root.dev.js,表示开发环境

    模块规范

    • 同一目录下的模块之间以相对路径的方式引用
    • 不同目录下的模块之间以绝对路径的方式引用
    • 常用模块以alias的方式引用(推荐alias以大写字母开头,以区分模块路径引用)

    思考:这样设计的好处是什么?

    目录划分

    build                构建工具及配置
    dist 目标目录
    src 源码目录
    mock mock数据
    public 开发环境临时目录
    node_modules npm包目录
    node_shrinkwrap npm离线包目录

    延伸阅读:为什么要有npm离线包?

    build:构建工具及配置

    build/
    lib/ 工具库
    *.js
    shell/ 部署脚本
    *.sh
    webpack.config.common.js webpack公共配置
    webpack.config.dev.js webpack开发环境配置
    webpack.config.prod.js webpack生产环境配置
    webpack.dll.config.js webpack.dllPlugin配置
    config.js 构建配置

    config.js示例:

    const fs = require('fs');
    const path = require('path');

    //提取多文件共用配置、项目可定制的配置

    const pkg = require('../package.json');
    const src = path.resolve(__dirname, '../src');
    const dist = path.resolve(__dirname, '../dist/resource')

    module.exports = {

    /*
    * 以下配置在项目中通常不需要变动
    */


    //package.json
    pkg,
    //源文件路径,使用绝对路径
    src,
    //导出路径,使用绝对路径
    dist,
    //静态资源目录,使用绝对路径
    contentBase: path.resolve(__dirname, '../public'),
    //导出资源映射表路径,使用绝对路径
    manifest: path.resolve(__dirname, '../dist/manifest'),
    //资源映射表名称,如下配置将根据当前日期生成对应的资源映射表
    manifestFileName: function() {
    return `${new Date().getFullYear()}-${new Date().getMonth()+1}-${new Date().getDate()}.json`
    },
    //dll生成路径
    dllPath: {
    development: path.resolve(src, 'vendor'),
    production: dist
    },
    //dll资源映射
    dllManifest: {
    development: path.resolve(src, 'vendor/dll-manifest.json'),
    production: path.resolve(dist, 'dll-manifest.json')
    },
    //js压缩配置
    UglifyJsOptions: {
    compress: {
    //不输出警告
    warnings: false
    },
    //不输出注释
    comments: false
    },

    /*
    * 以下配置在项目中通常需要定制
    */


    //生产环境中前端资源路径(需要与nginx配置保持一致),可以为域名url
    publicPath: '/Public/',
    //模块别名,相对于conf.src路径配置
    //- 推荐以大写字母开头,以区分非别名
    alias: {
    // 起别名:"module" -> "new-module" 和 "module/path/file" -> "new-module/path/file"
    // "module": "new-module",
    // 起别名 "only-module" -> "new-module",但不匹配 "module/path/file" -> "new-module/path/file"
    // "only-module$": "new-module",
    // 起别名 "module" -> "./app/third/module.js" 和 "module/file" 会导致错误
    // 模块别名相对于当前上下文导入
    // "module": "./app/third/module.js"
    "Coms": "components/common",
    "ActionTypes$": "utils/action_types.js",
    "Api$": "utils/api.js",
    "Helper$": "utils/helper.js",
    "Ajax$": "utils/ajax.js",
    "BaseLess$":"utils/baseless/baseless.less"
    },
    //代理配置
    proxy: {
    "/": {
    target: "http://test-matrix-v2.yileyoo.com",
    changeOrigin: true
    }
    },
    //mock配置
    mock: {
    //mock目录,使用绝对路径
    mockPath: path.resolve(__dirname, '../mock'),
    //支持设置统一接口后缀,如:.do
    apiExt: ''
    },
    //html模板配置
    template: {
    all:{
    title:'Matrix管理平台'
    },
    development:{
    serverOutput:'<script src="/server/output"></script>'
    },
    production:{
    serverOutput:'<script>window.REDUX_STATE = {!! $jsData !!};</script>'
    }
    },
    theme: path.resolve(src, 'theme/red.less')
    }

    dist:存放构建结果

    dist/
    resource
    index_xxx.html
    app_xxx.js
    vendor_xxx.js
    app
    manifest
    2017-x-x.json

    src:源码目录

    src/
    ----------------------------------------------------
    actions/ Redux Action目录
    *_action.js
    reducers/ Redux Reducer目录
    *_reducer.js
    store/ Redux Store目录
    index.js
    store.dev.js
    store.prod.js
    ----------------------------------------------------
    routes/ React路由目录
    index.js
    ----------------------------------------------------
    containers/ React容器目录
    root/
    index.js
    root.dev.js
    root.prod.js
    devtools.js
    components/ React组件目录
    common/ React公共组件目录(alias:Coms)
    page_1/
    index.js
    *.js
    *.less
    ... React页面组件
    page_n/
    ----------------------------------------------------
    vendor/ 第三方库目录
    vendor.js
    verdor.js.map
    dll-manifest.json
    static/ 静态资源目录
    favicon.ico
    utils/ 工具目录
    ajax.js ajax工具(alias:Ajax)
    api.js api工具(alias:Api)
    action_types.js action类型工具(alias:ActionTypes)
    helper.js 常用工具方法(alias:Helper)
    baseless/*.less 常用less方法(alias:BaseLess)
    theme/ 主题目录
    *.less
    ----------------------------------------------------
    config/ 配置目录
    *_config.js
    ----------------------------------------------------

    mock:存放mock数据的目录

    mock/
    *.js
    *.json

    public:开发环境临时目录

    public/
    index.html

    npm package:npm包

    node_modules                npm包目录
    node_shrinkwrap npm离线包目录

    other files:根目录下其他文件

    .gitignore                  git忽略配置
    .gitlab-ci.yml gitlab-ci配置
    npm-shrinkwrap.json npm包版本锁定配置
    package.json npm包配置
    webpack.config.js webpack配置入口
    README.md 说明文档

    UI篇

    如何引入ant

    // .babelrc or babel-loader option
    {
    "plugins": [
    ["import", { libraryName: "antd", style: "css" }] // `style: true` 会加载 less 文件
    ]
    }

    然后只需从 antd 引入模块即可,无需单独引入样式。等同于下面手动引入的方式。

    // babel-plugin-import 会帮助你加载 JS 和 CSS
    import { DatePicker } from 'antd';

    如何定制antd

    antd通过less变量提供了较灵活的主题定制功能。(参考:修改 Ant Design 的样式变量

    需要修改babel-loader配置:

    {
    "plugins": [
    ["import", {
    libraryName: "antd",
    style: true // 这里需要修改为`style: true`以实现主题配置
    }]
    ]
    }

    这里我们做了简单的封装:

    1)在src/theme目录建一个主题文件,如:red.less(参考:antd默认主题文件

    @primary-color: #f00;

    2)在build/config.js文件配置主题文件的路径

    {
    ...
    theme: path.resolve(src, 'theme/red.less')
    }

    后续我们还将推出主题配置监听多主题切换等功能,敬请期待~

    动手写一个antd组件

    任意在项目中可复用的组件,都可以通过antd组件组合成一个通用(业务)组件。
    注意,这里的组件是Component(见前面的定义)类型。

    (演示)

    工具篇

    devServer什么鬼

    • 一个node express应用
    • 提供静态资源服务
    • 支持文件监听
    • 支持热加载
    • 支持路由代理
    • 支持接口转发

    调用方式非常简单,在命令行执行:

    webpack-dev-server --env development --port 3000 --hot --inline --progress --open

    webpack中的相关配置:

    //开发服务器配置
    devServer: {
    //告诉服务器从哪里提供内容。只有在你想要提供静态文件时才需要。
    //devServer.publicPath 将用于确定应该从哪里提供 bundle,并且此选项优先。
    contentBase: [
    conf.contentBase,
    path.join(conf.src, 'static'),
    path.join(conf.src, 'vendor')
    ],
    //信息显示配置
    stats: "normal",
    //是否显示全屏遮罩
    overlay: true,
    //watch配置
    watchOptions: {
    ignored: /node_modules/,
    aggregateTimeout: 300,
    poll: 100
    },
    //联调模式下,使用数据代理
    proxy: isDebug ? conf.proxy : {},
    //开启浏览器历史
    historyApiFallback: true,
    //扩展devServer
    setup(app) {
    //非联调模式下,使用mock数据
    !isDebug && makeMock(app, conf.mock)
    }
    }

    不容忽视的HtmlWebpackPlugin

    • 自动引用webpack生成的资源,支持过滤
    • 默认支持ejs模板,可以注入变量,语法亲和
    • 社区生态好,一众扩展插件可以满足各种需求(参考

    dllPlugin教程没有教你

    教程很多,随便找一篇:怎样令webpack的构建加快十倍、DllPlugin的用法

    不实用!!!

    get√到打开方式后,我们来针对实际情况做个总结:

    • 区分开发和生产环境
     开发环境生产环境
    警告信息
    内容压缩
    文件hash
    SourceMap 可选
    • 怎么在页面引用

    使用add-asset-html-webpack-plugin插件

    {
    plugins:[
    ...
    // 在入口页面中引入静态资源
    new AddAssetHtmlPlugin({
    //通过dllManifest读取dll文件名
    filepath: path.resolve(conf.dllPath[NODE_ENV], `${dllManifest.name}.js`)
    })
    ]
    }
    • 加一个库就必须手写一下entry不需要!!!
     entry: {
    //读取package.json中的依赖
    vendor: Object.keys(conf.pkg.dependencies)
    }
    • 完整的webpack.dll.config.js
    const path = require('path');
    const webpack = require('webpack');
    const conf = require('./config');

    module.exports = function( /*通过命令行参数--env传入*/ NODE_ENV) {
    //是否生产环境
    const isProd = NODE_ENV === 'production';
    //文件名(不带后缀)
    const name = `[name]${isProd?"_[chunkhash:8]":""}`;
    //输出文件路径
    const filePath = conf.dllPath[NODE_ENV];
    //输出manifest路径
    const manifest = conf.dllManifest[NODE_ENV];
    //sourcemap配置
    const devtool = isProd ? '' : 'source-map';
    //插件
    let plugins = [
    new webpack.DllPlugin({
    //解析包路径的上下文,这个要跟接下来配置的 webpack.config.js 一致。
    context: __dirname,
    //manifest.json文件的输出路径,这个文件会用于后续的业务代码打包
    path: manifest,
    //dll暴露的对象名,要跟output.library保持一致
    name: name
    })
    ];
    //生产环境使用压缩版
    if (isProd) {
    plugins = plugins.concat([
    //变量定义
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(NODE_ENV)
    }),
    // js压缩配置
    new webpack.optimize.UglifyJsPlugin(conf.UglifyJsOptions)
    ])
    }
    return {
    entry: {
    //读取package.json中的依赖
    vendor: Object.keys(conf.pkg.dependencies)
    },
    output: {
    path: filePath,
    filename: name + '.js',
    //需要与filename保持一致,用于页面引用
    library: name
    },
    devtool,
    plugins
    }
    };

    扩展阅读:webpack 构建性能优化策略小结
    思考:相比其他方案,dllPlugin的优势在哪里?

    SourceMap全方案

    • loader中启用SourceMap
    {
    loader: 'xxx-loader',
    options: {
    ...
    sourceMap: true
    }
    }
    • devtools中配置SourceMap类型

    开发环境:唯快不破,最大化满足调试需求

    {   
    ...
    devtool: 'cheap-module-eval-source-map'
    }

    生产环境:需要考虑安全性和性能

    {    
    ...
    devtool: 'source-map'
    }

    扩展阅读:Webpack devtool source map

    拆分配置文件

    webpack.config.common.js       公共配置
    webpack.config.dev.js 开发环境配置
    webpack.config.prod.js 生产环境配置

    拆分原则:

    • moduleresolve在开发和生产环境中的配置差异性相对较小,非常适合抽取到公共配置中
    • entryoutputplugins 相对来说开发和生产环境有不同的配置,因此放到devprod各自配置中
    • devtooldevServer 等仅出现在开发环境的配置直接放入dev配置中

    协同篇

    承载页

    纯静态 vs 服务端渲染

    • 纯静态:服务端仅提供数据接口,彻底不需要后端维护,缺点是无法做资源回溯和切换
    • 服务端渲染:服务端提供数据接口,并将部分数据或状态渲染到页面,缺点是耦合了前后端部署逻辑

    对服务端渲染的改进:

    • 将后端模板指向前端部署目录的html文件,如: index.html
    • 使用固定的后端模板,将数据或状态以JSON对象的方式输出
    • 使用HtmlWebpackPlugin,将后端模板注入到自动生成的页面中

    build/config中的配置:

    //html模板配置
    template: {
    all:{
    title:'Matrix管理平台'
    },
    development:{
    serverOutput:'<script src="/server/output"></script>'
    },
    production:{
    serverOutput:'<script>window.REDUX_STATE = {!! $jsData !!};</script>'
    }
    }

    webpack中的配置:

    {
    plugins:[
    ...
    // 根据模板创建入口页面
    new HtmlWebpackPlugin(Object.assign({
    template: path.resolve(conf.src, 'index.ejs'),
    filename: path.resolve(conf.contentBase, 'index.html')
    }, /*全环境模板配置*/conf.template.all, /*当前环境模板配置*/conf.template[NODE_ENV]))
    ]
    }

    index.ejs模板中引用

    <!DOCTYPE html>
    <html>

    <head>
    <meta charset="utf-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    </head>

    <body>
    <div id="root" style="height: 100%"></div>
    <%= htmlWebpackPlugin.options.serverOutput %>
    </body>

    </html>

    资源灰度 & 回溯

    使用 assets-webpack-plugin插件,在webpack中生成资源映射json文件

    {
    plugins:[
    ...
    //生成资源映射表
    new AssetsPlugin({
    path: conf.manifest,
    filename: conf.manifestFileName(),
    processOutput: function (assets) {
    //注入dll依赖信息
    assets.vendor = {
    'js': conf.publicPath + dllManifest.name + '.js'
    };
    return JSON.stringify(assets);
    }
    })
    ]
    }

    两种方案:

    • 同一个html模板,仅切换资源。需提供资源映射表
    • 不同html模板,切换模板。需要提供模板映射表

    数据接口

    文档

    ——接口定义,告诉我们有哪些接口,接口支持哪些http方法,每个接口字段的含义是什么等

    (演示)

    mock

    ——接口没有开发完成,前端根据接口文档模拟的数据

    (演示)

    proxy

    ——接口已经开发完成,使用代理的方式实现本地接口联调

    webpack中的相关配置:

    devServer:{
    ...
    proxy: {
    "/api": "http://localhost:3000"
    }
    }

    权限控制

    页面权限

    前端:

    • react-router中定义所有页面的路由
    • 页面初始化时,请求后端接口获取菜单权限
    • 仅显示具备权限的菜单

    后端:

    • 提供获取菜单权限的接口
    • 将接受到的路由请求作过滤,具备权限的则转向前端页面
    • 将404/500等错误路由转到前端页面

    操作权限

    前端:

    • 请求后端接口获取操作权限
    • 根据操作权限控制操作按钮是否显示

    后端:

    • 提供获取操作权限的接口
  • 相关阅读:
    MySql cmd下的学习笔记 —— 引擎和事务(engine,transaction)
    MySql cmd下的学习笔记 —— 有关视图的操作(algorithm)
    MySql cmd下的学习笔记 —— 有关视图的操作(建立表)
    MySql cmd下的学习笔记 —— 有关常用函数的介绍(数学函数,聚合函数等等)
    MySql cmd下的学习笔记 —— 有关多表查询的操作(多表查询练习题及union操作)
    MySql 在cmd下的学习笔记 —— 有关多表查询的操作(内连接,外连接,交叉连接)
    MySql cmd下的学习笔记 —— 有关子查询的操作(where型,from型,exists型子查询)
    MySql cmd下的学习笔记 —— 有关select的操作(order by,limit)
    剑指Offer--第21题 调整数组顺序使奇数位于偶数前面;
    剑指Offer--和为s的连续正数序列
  • 原文地址:https://www.cnblogs.com/MonaSong/p/7126033.html
Copyright © 2011-2022 走看看