• react-starter脚手架搭建过程


    本文献给一些 react 新手,不对,应该说针对想要通过 react 进入前端世界的程序猿们~

    背景

    公司的项目需要使用 react,两三个月前还是一个基本不怎么懂前端,当时的我除了 jquery 什么都不知道,但是公司要求使用 react 开发新的项目,完全不懂前端的我只能去试着学习使用 react 进行开发,然后什么 nodejs/webpack/es5/es6/babel/jsx 一堆堆全部涌过来,讲真,彻底被前端的复杂世界搞懵逼了,从来没想过前端是这么的复杂。然后匆匆忙忙的开始学习,循环往复十来天。接着公司要求写项目了,我擦,我还什么都没学会,没办法,硬着头皮上,去全球最大同性交友网站(#雾)瞅了瞅,找了个 star 很多的脚手架,开始了开发之旅,其中的痛苦真的是不忍赘述,经常为了目标熬夜赶工,结果还是各种拖拖踏踏,一直没能完成。还好一个月后被叫去做其他项目,不然真的是崩崩崩。接下来的时间开始了真正的探索 react 路途…一两个月的工作之余熬夜苦学之后,尝试了很多不同的脚手架之后,开始有了自己对于开发、编译的需求,处于对于想要将一个技术完全征服的欲望,于是开始准备搭建自己的 react 脚手架

    准备工作

    知识准备

    react 自不必说,首先是去了解nodejswebpackbabeles6等等,多学习源生 js,彻底抛弃 jquery 才能真正感受到源生 js 带来的魅力,了解一些 js 规范,比如commonjs之类的。一定要用心去感悟(笑)。

    需求准备

    对于个人的一路走来的感觉来说,前端开发经验少的人,应该还是先使用别人的脚手架,多读读人家的 readme 的说明,了解了解别人对于本项目的一些规范或者需求,这里推荐一个react starter kit,感觉确实很全面,不过里面没有使用 jsx 语法定义路由,而是使用的webpack 的 api 来实现的按需加载,开始可能会让人有点昏,不过多去了解了解也就没什么障碍,官方也给出了很好的解释。多写写,多动手,就会明白自己的一些需求了。这里列出我自己的一些想要的需求。

    开发模式下:

    1. 热加载(没它开发会累哭)
    2. 能与后台交互时的解决跨域问题

    生产模式下:

    1. 能把 react 模块分开
    2. 能自动将生成 js 文件绑定到 html 页面
    3. 压缩 js、css 代码
    4. 想要使用 ant design 开发,但是 antd 样式文件较大,能够按需加载

    通用的:

    1. 使用 es6、jsx 语法
    2. 能生成 map 文件
    3. 能加载各种文件:json,图片,字体,css,scss,less
    4. 找路径不要那么麻烦的使用 '…/…/'之类的,能直接从项目跟路径找。
    5. 命令能够兼容 linux 或者 windows
    6. 使用 eslint 进行代码检查
    7. 使用 esformatter 进行代码格式化
    8. 能自动处理 css 样式兼容性问题

    开始搭建

    虽然我搭建环境有时候在 linux 有时候在 windows,不过写此文的时候处于 windows 环境下,懒得切系统,所以统一使用 windows 环境,一般来说 linux 没什么不同,想起来有不同的时候我会说。

    工具推荐

    强烈推荐 vscode,支持得真的好=-=,虽然我还不是很用的来。

    初始化

    安装 nodejs,我使用 nvm 来管理不同的 nodejs 版本,而且通过 nvm 安装 nodejs 也超级简单,所以推荐用这种方式吧,下载地址。下载安装,配置环境变量,搞定,具体教程自行百度。然后使用 nvm 安装最新的 nodejs 稳定版,目前是 6.10.0。 nvm install 6.10.0,然后等安装完毕,新建一个 react-start 文件夹,cd 进去, 使用命令 nvm use 6.10.0 ,强烈建议安装 cnpm 配置淘宝源,这样安装依赖会快得多,否则等秃(#雾,使用命令 npm install -g cnpm --registry=https://registry.npm.taobao.org,使用cnpm init初始化项目,基本上可以一路回车下去。完成初始化,安装全局 webpack 依赖cnpm install webpack -g

    搭建骨架

    跟路径新建 webpack.config.js,webpack 会默认读取这个文件,先编写个大概,然后慢慢去补

    'use strict'
    
    const path = require('path')
    const webpack = require('webpack')
    const rootPath = path.resolve(__dirname)
    const srcPath = path.join(rootPath, 'src')
    const common = {
      rootPath: rootPath,
      srcPath: srcPath,
      dist: path.join(rootPath, 'dist'),
      indexHtml: path.join(srcPath, 'index.html'),
      staticDir: path.join(rootPath, 'static'),
      entry: path.join(common.srcPath, 'index.js'),
    }
    var webpackConfig = {
      entry: common.entry,
      output: {
        filename: 'bundle.js',
        path: common.dist,
      },
    }
    
    module.exports = webpackConfig
    

    新建 src 文件夹,我们编写的 js 源代码文件就写在这里面了 进入 src 文件夹,新建 index.js

    var render = function() {
      console.log('hello world')
    }
    

    根目录新建 dist 文件夹,这是我们生成编译后的代码文件夹,新建 index.html

    DOCTYPE html>
    <html>
      <head>
        <title>react-startertitle>
      head>
      <body>
        <script src="bundle.js">script>
      body>
    html>
    

    根目录运行webpack,等待编译完成,浏览器打开 index.html,打开控制台就能够看到 hello world 啦。

    按照需求安装依赖编写 webpack.config.js

    为了能够将项目迁移到其他电脑,本地安装 webpackcnpm install webpack --save-dev

    加入环境变量

    为了实现 linux 和 windows 都能设置环境变量,安装 cross-env,cnpm install cross-env --save-dev 修改 config,加入

    const env = process.env.NODE_ENV.trim()
    const isDev = env === 'development'
    

    实现热加载

    安装 webpack-dev-server,cnpm install webpack-dev-server,编写 config,

    'use strict'
    
    const path = require('path')
    const webpack = require('webpack')
    const rootPath = path.resolve(__dirname)
    const srcPath = path.join(rootPath, 'src')
    
    const env = process.env.NODE_ENV.trim()
    const isDev = env === 'development'
    
    const common = {
      rootPath: rootPath,
      srcPath: srcPath,
      dist: path.join(rootPath, 'dist'),
      indexHtml: path.join(srcPath, 'index.html'),
      staticDir: path.join(rootPath, 'static'),
      entry: path.join(common.srcPath, 'index.js'),
    }
    //开发模式下修改入口文件
    if (isDev) {
      common.entry = [
        'react-hot-loader/patch',
        // activate HMR for React
    
        'webpack-dev-server/client?http://localhost:3000',
        // bundle the client for webpack-dev-server and connect to the provided endpoint
    
        'webpack/hot/only-dev-server',
        // bundle the client for hot reloading only- means to only hot reload for
        // successful updates
    
        path.join(common.srcPath, 'index.js'),
      ]
    }
    var webpackConfig = {
      entry: common.entry,
      output: {
        filename: 'bundle.js',
        path: common.dist,
        publicPath: '/', //让HMR知道在哪里加载热更新块所必需的
      },
    }
    //开发模式下添加devServer字段
    //devServer候选字段参考https://webpack.js.org/configuration/dev-server/
    if (isDev) {
      webpackConfig.devServer = {
        historyApiFallback: true,
        hot: true,
        contentBase: path.resolve(__dirname, 'dist'),
        publicPath: '/',
        clientLogLevel: 'none', //日志
        compress: true, //压缩
        port: 3000,
        stats: {
          colors: true,
        },
        proxy: {
          '/api/*': {
            target: 'http://localhost',
            changeOrigin: true,
            secure: false,
          },
        },
      }
    }
    
    module.exports = webpackConfig
    

    添加 es6 语法支持

    webpack2.0 好像默认支持 es6 语法,不过稳妥起见,还是使用 Babel 进行 es2016 转换

    安装 babel 系列,cnpm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-stage-0 支持到 js 标准的 stage 0 阶段, webpackConfig 中添加 module 字段

    module: {
      //webpack1.0中可以省略 '-loader',但是官方说法为了有明确的区分,在webpack2.0中,不能再省略
      rules: [
        {
          test: /\.(js)$/,
          loader: 'babel-loader',
          include: [
            path.join(common.rootPath, 'src'), //转换src路径下的代码
          ],
          exclude: /node_modules/, //忽略node_modules路径代码
        },
      ]
    }
    

    添加 react

    安装 react,cnpm install --save react react-dom react-routercnpm install babel-preset-react react-hot-loader@^3.0.0-beta.6 babel-plugin-transform-runtime babel-preset-react babel-preset-react-optimize --save-dev,继续修改 webpackConfig,在上一个修改的代码中的 test 字段中,修改为:test: /\.(js|jsx)$/, 根目录新建.babelrc

    {
      "presets": ["es2015", "stage-0", "react"],
      "plugins": ["transform-runtime", "react-hot-loader/babel"],
      "env": {
        "production": {
          "presets": ["react-optimize"]
        }
      }
    }
    

    编写 config

    'use strict'
    
    const path = require( 'path' )
    const webpack = require( 'webpack' )
    
    const rootPath = path.resolve( __dirname )
    const srcPath = path.join( rootPath, 'src' )
    
    const env = process
      .env
      .NODE_ENV
      .trim()
    const isDev = (env === 'development')
    
    const common = {
      rootPath: rootPath,
      srcPath: srcPath,
      dist: path.join( rootPath, 'dist' ),
      indexHtml: path.join( srcPath, 'index.html' ),
      staticDir: path.join( rootPath, 'static' )
    }
    if ( isDev ) {
      common.entry = [
        'react-hot-loader/patch',
        // activate HMR for React
    
        'webpack-dev-server/client?http://localhost:3000',
        // bundle the client for webpack-dev-server and connect to the provided endpoint
    
        'webpack/hot/only-dev-server',
        // bundle the client for hot reloading only- means to only hot reload for
        // successful updates
    
        path.join( common.srcPath, 'index.js' )
      ]
    }
    if ( isDev ) {
        new webpack.HotModuleReplacementPlugin(), // HMR全局启用
        new webpack.NamedModulesPlugin(), // 在HMR更新的浏览器控制台中打印更易读的模块名称
      ]
    }
    const webpackConfig = {
      entry: common.entry,
      output: {
        filename: 'bundle.js',
        path: common.dist,
        publicPath: '/', //让HMR知道在哪里加载热更新块所必需的
      },
      context: path.resolve( __dirname, 'src' ),
      module: {
        //webpack1.0中可以省略 '-loader',但是官方说法为了有明确的区分,在webpack2.0中,不能再省略
        rules:[
            {
            test: /\.(js|jsx)$/,
            loader: 'babel-loader',
            include: [
              path.join( common.rootPath, 'src' ), //转换src路径下的代码
            ],
            exclude: /node_modules/, //忽略node_modules路径代码
          }
        ]
      }
    }
    if ( isDev ) {
      webpackConfig.devServer = {
        historyApiFallback:true,
        hot: true,
        contentBase: path.resolve( __dirname, 'dist' ),
        publicPath: '/',
        clientLogLevel: 'none', //日志
        compress: true, //压缩
        port:3000,
        stats: {
          colors: true
        },
        proxy:{
          '/api/*':{
            target: 'http://localhost',
            changeOrigin: true,
            secure: false,
          }
        }
      }
    }
    
    module.exports = webpackConfig
    

    在 html 文件中加入

    新建/src/components 文件夹 新建/src/components/index.js

    	import React,{Component} from 'react'
        export const App = ()=>(
        	
    hello world!
    )

    编写/src/index.js

    import React from 'react'
    import ReactDOM from 'react-dom'
    
    import { AppContainer } from 'react-hot-loader'
    // AppContainer is a necessary wrapper component for HMR
    
    const Root = document.getElementById('root')
    
    const isDev = !(process.env.NODE_ENV === 'development')
    
    let render = () => {
      const Routes = require('./routes/index').default
      ReactDOM.render(
        <AppContainer>
          <Routes />
        </AppContainer>,
        Root
      )
    }
    
    if (isDev) {
      if (window.devToolsExtension) {
        window.devToolsExtension.open()
      }
    }
    
    if (isDev) {
      if (module.hot) {
        // Development render functions
        const renderApp = render
    
        // Wrap render in try/catch
        render = () => {
          renderApp()
        }
    
        // Setup hot module replacement
        module.hot.accept('./components', () =>
          setImmediate(() => {
            ReactDOM.unmountComponentAtNode(Root)
            render()
          })
        )
      }
    }
    
    // Hot Module Replacement API
    // if (module.hot) {
    //   module
    //     .hot
    //     .accept('./', () => {
    //       render(Routers)
    //     })
    // }
    
    render()
    

    运行命令 cross-env NODE_ENV=development webpack-dev-server 打开浏览器,输入 localhost:3000 看看是不是已经有了 hell world 啦?在源文件中将 hello world 修改成其他字样,看看是不是已经能够自动刷新网页了?可喜可贺~

    支持各种文件加载

    在 webpack 概念中,各种文件都可以通过 loader 达到 all in js 的效果,从而让 js 能够做各种操作 所以接下来要安装各种 loader, cnpm install --save css-loader file-loader html-loader json-loader less less-loader node-sass sass-loader style-loader url-loader

    简单说明(一下省略 “-loader”):

    • style:将 css 样式转换成 css in js,也就是说没有单独的 css 文件,全部都在 js 文件中夹杂着,这里引入主要是为了热加载
    • css、file、html、json、less、sass:顾名思义就是将这些类型文件加载到 js 中
    • less、node-sass:因为 less 和 sass 是预处理语言,要使其发挥作用,必须加入这些支持
    • url-loader:file-loader 的加强版,可以使用 limit 参数,主要用来加载图片

    修改 config

    /**
     * webpack2.0 配置文件
     * @authors wuhongxu (wuhongxu1208@gmail.com)
     * @date    2017-03-10 01:52:00
     * @version 1.0.0
     * @link https://zido.site/
     *
     */
    
    'use strict'
    
    const path = require( 'path' )
    const webpack = require( 'webpack' )
    
    const rootPath = path.resolve( __dirname )
    const srcPath = path.join( rootPath, 'src' )
    
    const env = process
      .env
      .NODE_ENV
      .trim()
    const isDev = (env === 'development')
    
    const common = {
      rootPath: rootPath,
      srcPath: srcPath,
      dist: path.join( rootPath, 'dist' ),
      indexHtml: path.join( srcPath, 'index.html' ),
      staticDir: path.join( rootPath, 'static' )
    }
    if ( isDev ) {
      common.entry = [
        'react-hot-loader/patch',
        // activate HMR for React
    
        'webpack-dev-server/client?http://localhost:3000',
        // bundle the client for webpack-dev-server and connect to the provided endpoint
    
        'webpack/hot/only-dev-server',
        // bundle the client for hot reloading only- means to only hot reload for
        // successful updates
    
        path.join( common.srcPath, 'index.js' )
      ]
    } else {
      common.entry = {
        app: path.join( common.srcPath, 'index.js' )
    }
    
    const styleLoaders = {
      style: {
        loader: 'style-loader'
      },
      css: {
        loader: 'css-loader',
        options: {
          //将css进行hash编码,保证模块性,保证单独使用,而不会污染全局
          // modules: true
        }
      }
    }
    
    const webpackConfig = {
      entry: common.entry,
      output: {
        filename: 'bundle.js',
        path: common.dist,
        publicPath: '/', //让HMR知道在哪里加载热更新块所必需的
      },
      context: path.resolve( __dirname, 'src' ),
      module: {
        //webpack1.0中可以省略 '-loader',但是官方说法为了有明确的区分,在webpack2.0中,不能再省略
        rules: [
          {
            test: /\.(js|jsx)$/,
            loader: 'babel-loader',
            include: [
              path.join( common.rootPath, 'src' ), //转换src路径下的代码
            ],
            exclude: /node_modules/, //忽略node_modules路径代码
          },
          {
            test: /\.json$/,
            use: 'json-loader'
          },
          {
            test: /.html$/,
            use: 'html-loader'
          },
          {
            test: /\.(woff2?|eot|ttf|otf)$/,
            use: {
              loader: 'url-loader',
              options: {
                limit: 10240,
                name: '[name]-[hash:6].[ext]'
              }
            }
          },
          {
            test: /\.(png|jpg|gif|svg)$/,
            use: {
              loader: 'url-loader',
              options: {
                limit: 10240,
                name: '[name]-[hash:6].[ext]'
              }
            }
          },
          {
            test: /\.css$/,
            use: ( handleStyle( extractCss, [
              styleLoaders.style,
              styleLoaders.css,
              styleLoaders.postcss
            ] ))
          },
          {
            test: /\.scss$/,
            use: ( handleStyle( extractScss, [
              styleLoaders.style,
              styleLoaders.css,
              styleLoaders.postcss,
              {
                loader: 'sass-loader'
              }
            ] ))
          },
          {
            test: /\.less$/,
            use: ( handleStyle( extractLess, [
              styleLoaders.style,
              styleLoaders.css,
              styleLoaders.postcss,
              {
                loader: 'less-loader'
              }
            ] ))
          }
        ]
      }
    }
    if ( isDev ) {
      webpackConfig.devServer = {
        historyApiFallback:true,
        hot: true,
        contentBase: path.resolve( __dirname, 'dist' ),
        publicPath: '/',
        clientLogLevel: 'none', //日志
        compress: true, //压缩
        port:3000,
        stats: {
          colors: true
        },
        proxy:{
          '/api/*':{
            target: 'http://localhost',
            changeOrigin: true,
            secure: false,
          }
        }
      }
    }
    
    module.exports = webpackConfig
    
    

    处理 css 样式兼容性,开发模式下将 css 单独出来

    此时需要引入 css 后处理器 postcss,然后使用autoprefixer来进行前缀配置,css 文件单独出来需要使用 webpack 的插件 extractPlugin ,安装依赖,cnpm install autoprefixer-loader extract-text-webpack-plugin postcss-loader --save-dev 编写 postcss.config.js

    //处理css前缀,用来更好的兼容各种浏览器
    //在 package.json中 使用 browserslist 字段已经定义好了浏览器适配(官方推荐)
    module.exports = {
      plugins: [require('autoprefixer')],
    }
    

    在 package.json 中添加字段

      "browserslist": [
        "> 1%",
        "last 2 versions"
      ],
    

    编写 webpack.config.js

    'use strict'
    
    const path = require('path')
    const webpack = require('webpack')
    
    //使用插件
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const ExtractTextPlugin = require('extract-text-webpack-plugin')
    const extractCss = new ExtractTextPlugin('style/[name]-css-[hash:6].css')
    const extractScss = new ExtractTextPlugin('style/[name]-scss-[hash:6].css')
    const extractLess = new ExtractTextPlugin('style/[name]-less-[hash:6].css')
    
    const rootPath = path.resolve(__dirname)
    const srcPath = path.join(rootPath, 'src')
    
    const env = process.env.NODE_ENV.trim()
    const isDev = env === 'development'
    
    const common = {
      rootPath: rootPath,
      srcPath: srcPath,
      dist: path.join(rootPath, 'dist'),
      indexHtml: path.join(srcPath, 'index.html'),
      staticDir: path.join(rootPath, 'static'),
    }
    if (isDev) {
      common.entry = [
        'react-hot-loader/patch',
        // activate HMR for React
    
        'webpack-dev-server/client?http://localhost:3000',
        // bundle the client for webpack-dev-server and connect to the provided endpoint
    
        'webpack/hot/only-dev-server',
        // bundle the client for hot reloading only- means to only hot reload for
        // successful updates
    
        path.join(common.srcPath, 'index.js'),
      ]
    } else {
      common.entry = {
        app: path.join(common.srcPath, 'index.js'),
        vendor: ['react'],
      }
    }
    if (isDev) {
      common.plugins = [
        new HtmlWebpackPlugin({
          template: common.indexHtml,
          inject: 'body',
        }),
        new webpack.HotModuleReplacementPlugin(), // HMR全局启用
        new webpack.NamedModulesPlugin(), // 在HMR更新的浏览器控制台中打印更易读的模块名称
      ]
    } else {
      common.plugins = [
        new webpack.optimize.UglifyJsPlugin(),
        new HtmlWebpackPlugin({
          template: common.indexHtml,
          inject: 'body',
        }),
        new webpack.NoEmitOnErrorsPlugin(),
        // stataic目录下静态资源的复制
        new CopyWebpackPlugin([
          {
            context: common.rootPath,
            from: 'static/*',
            ignore: ['*.md'],
          },
        ]),
        new webpack.optimize.CommonsChunkPlugin({
          name: 'vendor',
          filename: 'vendor.bundle.js',
        }),
      ]
    }
    
    common.plugins.push(
      new webpack.DefinePlugin({
        'process.env': {
          NODE_ENV: JSON.stringify(env),
        },
      })
    )
    const styleLoaders = {
      style: {
        loader: 'style-loader',
      },
      css: {
        loader: 'css-loader',
        options: {
          //将css进行hash编码,保证模块性,保证单独使用,而不会污染全局
          // modules: true
        },
      },
      postcss: {
        //css后处理器,这里主要是为了加载 autoprefixer 用来处理css前缀
        loader: 'postcss-loader',
      },
    }
    function handleStyle(plugin, list) {
      //如果不是开发模式,删除数组中的第一个元素,并使用extrat-plugin将样式额外打包
      if (!isDev) {
        list.splice(0, 1)
        return plugin.extract(list)
      }
      return list
    }
    
    const webpackConfig = {
      entry: common.entry,
      output: {
        filename: 'bundle.js',
        path: common.dist,
        publicPath: '/', //让HMR知道在哪里加载热更新块所必需的
      },
      context: path.resolve(__dirname, 'src'),
      devtool: isDev ? 'cheap-module-eval-source-map' : 'cheap-module-source-map',
      module: {
        //webpack1.0中可以省略 '-loader',但是官方说法为了有明确的区分,在webpack2.0中,不能再省略
        rules: [
          {
            test: /\.(js|jsx)$/,
            enforce: 'pre',
            loader: 'eslint-loader',
            exclude: /node_modules/,
            options: {
              emitWarning: true,
              emitError: true,
              //failOnWarning: false, failOnError: true,
              useEslintrc: false,
              // configFile: path.join(__dirname, "eslint_conf.js")
              configFile: path.join(__dirname, '.eslintrc.json'),
            },
          },
          {
            test: /\.(js|jsx)$/,
            loader: 'babel-loader',
            include: [
              path.join(common.rootPath, 'src'), //转换src路径下的代码
            ],
            exclude: /node_modules/, //忽略node_modules路径代码
            options: {
              plugins: [
                ['import', [{ libraryName: 'antd', style: 'css' }]], //按需加载antd 样式,有效小包大小
              ],
            },
          },
          {
            test: /\.json$/,
            use: 'json-loader',
          },
          {
            test: /.html$/,
            use: 'html-loader',
          },
          {
            test: /\.(woff2?|eot|ttf|otf)$/,
            use: {
              loader: 'url-loader',
              options: {
                limit: 10240,
                name: '[name]-[hash:6].[ext]',
              },
            },
          },
          {
            test: /\.(png|jpg|gif|svg)$/,
            use: {
              loader: 'url-loader',
              options: {
                limit: 10240,
                name: '[name]-[hash:6].[ext]',
              },
            },
          },
          {
            test: /\.css$/,
            use: handleStyle(extractCss, [styleLoaders.style, styleLoaders.css, styleLoaders.postcss]),
          },
          {
            test: /\.scss$/,
            use: handleStyle(extractScss, [
              styleLoaders.style,
              styleLoaders.css,
              styleLoaders.postcss,
              {
                loader: 'sass-loader',
              },
            ]),
          },
          {
            test: /\.less$/,
            use: handleStyle(extractLess, [
              styleLoaders.style,
              styleLoaders.css,
              styleLoaders.postcss,
              {
                loader: 'less-loader',
              },
            ]),
          },
        ],
      },
      resolve: {
        extensions: ['.js', '.jsx', '.json'],
        alias: {
          Root: path.resolve(__dirname, 'src'),
          Components: path.resolve(__dirname, 'src/components'),
          Layouts: path.resolve(__dirname, 'src/layouts'),
          Routes: path.resolve(__dirname, 'src/routes'),
        }, //为某些路径设置别名
      },
      plugins: (function() {
        //如果是开发模式不将样式文件进行分离。tip:为了实现热加载
        if (isDev) return common.plugins
        common.plugins.push(extractCss)
        common.plugins.push(extractLess)
        common.plugins.push(extractScss)
        //返回组装完成后的plugins
        return common.plugins
      })(),
    }
    if (isDev) {
      webpackConfig.devServer = {
        historyApiFallback: true,
        hot: true,
        contentBase: path.resolve(__dirname, 'dist'),
        publicPath: '/',
        clientLogLevel: 'none', //日志
        compress: true, //压缩
        port: 3000,
        stats: {
          colors: true,
        },
        proxy: {
          '/api/*': {
            target: 'http://localhost',
            changeOrigin: true,
            secure: false,
          },
        },
      }
    }
    
    module.exports = webpackConfig
    

    使用 eslint 和 esformatter

    这一步的作用其实不是很大,更多的是自定义一个 js 代码的规范,其实不标准照样可以运行 安装 eslint 和 esformatter 的各项依赖,cnpm install --save-dev eslint esformatter es-strip-semicolons eslint-config-airbnb eslint-loader eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react

    根目录新建.esformatter,这里的代码太长且不重要,这里不贴了,github 地址,请自行查看

    根目录新建 .eslintrc.json 同上,github 地址

    编写 webpack.config.js

    在 module.rules 里面新增

    {
            test: /\.(js|jsx)$/,
            enforce: 'pre',
            loader: 'eslint-loader',
            exclude: /node_modules/,
            options: {
              emitWarning: true,
              emitError: true,
              //failOnWarning: false, failOnError: true,
              useEslintrc: false,
              // configFile: path.join(__dirname, "eslint_conf.js")
              configFile: path.join( __dirname, '.eslintrc.json' )
            }
          },
    

    这样会强制在运行前检查 js 代码,非常严格,一旦有错将不能运行,如果觉得没必要,可以去掉这一段代码

    其他细节

    其他小的细节,是一看就懂的,这里就不再赘述步骤。

    项目的github 地址

    聊聊其中的曲折和收获

    其实本来这个项目开始是使用 webpack1.x 搭建的,结果搭建好之后,才知道,喵蛋的,2.x 都出来了,于是果断放弃之前的,将项目速度改进到 2.x 版本。

    很多东西都是根据其他的项目而来,但是那些版本都太老旧了,很多的依赖都是我看了名字之后,转而去 github 或者 npm 查看新版本的用法,这段是最费时间,最费眼睛的了(因为我的英文实在是差劲 QAQ,要不是 chrome 的翻译插件,我估计我已经崩了),对于我这种特别崇尚最新的人来说,用老旧的东西,是我非常非常不能忍受的,所以我用的依赖基本上都是最新的,其中有些除了 github 上面,其他地方基本都么有相关资料。

    说实话,做出来之后,还能很好的运行,我真的是长长的出了一口大气,很舒心的感觉,终于特么的我走通一回了,好气啊~~~

    最大的收获,就是这一份走通的感觉了吧,个人前端后端都在弄,一直依赖都是两边奔波却基本上都没怎么摸透过。感觉暂时前端终于能稍微放一下,继续搞后端,目标分布式和并发,走起!

    写完这篇都特么快五点了,必须得睡觉了。顺便也请各位纠纠错,其实写的时候也没多仔细,难免会有所遗漏与错误。

    最后

    最新最新的 react 脚手架react-starter,算是提供个思路和参考吧,有用请 star~ 也请大佬们能够帮我修改修改 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 相关阅读:
    vue简单案例----小张记事本
    企业如何选择一款高效的ETL工具
    【Java 基础篇】Java transient 关键字详解:对象序列化与非序列化字段
    docker参数大P与小p的区别
    git stash详解
    求解wxid转换成二维码添加方法
    Laravel 下实现 Google 2fa 验证
    HStreamDB Newsletter 2022-07|分区模型优化、数据集成框架进一步完善
    哈工大李治军老师操作系统笔记【3】:操作系统的接口(Learning OS Concepts By Coding Them !)
    MySql中的like和in走不走索引
  • 原文地址:https://blog.csdn.net/Orzak/article/details/139089528