3 React前端工程架构 - eslint & 其他优化

 

相关commit

安装eslint:`npm i eslint -D`

创建eslint配置文件/.eslintrc

{
  "extends": "standard"
}


client目录下,用到了jsx,需要另一套eslint规范

client/.eslintrc

{
  "parser": "babel-eslint",
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "parserOptions": {
    "emacVersion": 6,
    "sourceType": "module"
  },
  "extends": "airbnb",
  "rules": {
    "semi":[0]
  }
}

说明:eslint文档


webpack配置编译前检测 - webpack.config.client.js

...
const config = {
    ...
    module: {
        rules: [
            {
                enforce: "pre",
                test: /.(jsx|js)/,
                loader: 'eslint-loader',
                exclude: [
                    path.join(__dirname, '../node_modules')
                ]
            },
            {
                test: /.jsx$/,
                loader: 'babel-loader',
            },
            {
                test: /.js$/,
                loader: 'babel-loader',
                exclude: [
                    path.join(__dirname, '../node_modules')
                ]
            }
        ]
    },
    ...
}

安装一大堆插件:

$ npm i babel-eslint eslint-config-airbnb eslint-config-standard eslint-loader eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-node eslint-plugin-promise eslint-plugin-react eslint-plugin-standard -D

编译的时候会报一大堆错,没关系,先配置下格式化文件

.editorconfig

文档

root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_new_line = true
trim_trailing_whitespace = true

禁止eslint检测的方法

import {AppContainer} from 'react-hot-loader' // eslint-disable-line
"rules": {
  "semi":[0],
  "react/jsx-filename-extension":[0]
}


git-commit之前检测

工具:哈士奇

安装:`npm i husky -D`

定义npm script

package.json

{
...
  "scripts": {
    ...
    "lint": "eslint --ext .js --ext .jsx client/",
    "precommit": "npm run lint"
  },
...
}

说明:

优化

相关commit

webpack.config优化

client和server两个config中module有一些配置内容是一样,我们抽取baseConfig解决代码复用问题

说明:

build/webpack.config.base.js

const path = require('path');

const config = {
  output: {
    filename: '[name].[hash].js',
    path: path.join(__dirname, '../dist'),
    publicPath: '/public/'
  },
  module: {
    rules: [
      {
        enforce: "pre",
        test: /.(jsx|js)/,
        loader: 'eslint-loader',
        exclude: [
          path.join(__dirname, '../node_modules')
        ]
      },
      {
        test: /.jsx$/,
        loader: 'babel-loader',
      },
      {
        test: /.js$/,
        loader: 'babel-loader',
        exclude: [
          path.join(__dirname, '../node_modules')
        ]
      }
    ]
  }
};

module.exports = config;

build/webpack.config.client.js

const path = require('path');
const HTMLPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');

const isDev = process.env.NODE_ENV === "development";

const config = webpackMerge(baseConfig,{
    target: 'web',
    entry: {
        app: path.join(__dirname, '../client/app.js')
    },
    output: {
        filename: '[name].[hash].js',
    },
    plugins: [
        new HTMLPlugin({
            template: path.join(__dirname, '../client/template.html')
        })
    ],
    mode: 'production'
});

if (isDev) {
    config.entry = {
        app: [
            'react-hot-loader/patch',
            path.join(__dirname, '../client/app.js')
        ]
    };

    config.devServer = {
        host: '0.0.0.0',
        port: '8787',
        contentBase: path.join(__dirname, '../dist'),
        hot: true,
        overlay: {
            error: true
        },
        publicPath: '/public/',
        historyApiFallback: {
            index: '/public/index.html'
        },
    };

    config.mode = 'development';

    config.plugins.push(new webpack.HotModuleReplacementPlugin())
}

module.exports = config;

build/webpack.config.server.js

const path = require('path');
const webpackMerge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');

const config = webpackMerge(baseConfig, {
    target: 'node',
    entry: {
        app: path.join(__dirname, '../client/server-entry.js')
    },
    output: {
        filename: 'server-entry.js',
        libraryTarget: 'commonjs2'
    }
});

module.exports = config;

站点图标的正确渲染

工具:`npm i serve-favicon -S`

创建图标:https://tool.lu/favicon

server.js

...
const favicon = require('serve-favicon');
...
app.use(favicon(path.join(__dirname, '../favicon.ico')));
...

web server实时更新

问题:服务端代码每次更新需要重启服务

解决方案:`npm i nodemon -D`

配置文件:nodemon.json

{
  "restartable":"rs",
  "ignore": [
    ".git",
    "node_modules/**/node_modules",
    ".eslintrc",
    "client",
    "build"
  ],
  "env": {
    "NODE_ENV": "development"
  },
  "verbose": true,
  "ext": "js"
}

更新npm-script

"dev:web:start": "nodemon server/server.js"