三、webpack环境搭建(下)(搭建react移动端组件库全套路)

509 阅读7分钟

React 移动端组件库搭建---项目搭建

上一篇文章中我们已经实现了一个最简单的框架,接下来我们接着上一篇的成果,继续添加瓦。

配置tslint, eslint

接下来我们开始配置tslint。查看typescript官网,我们会发现,在2019年1月TS官方决定全面采用Eslint作为代码检查的工具。

所以接下来我们只需要在项目中配置eslint就好了,需要注意的是eslint中会搭配typescript-eslint

在项目中安装,eslint的扩展推荐使用alloyTeam的 AlloyTeam的eslint规则

yarn add eslint babel-eslint eslint-config-alloy eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser --dev

在项目中创建.eslintrc.js

module.exports = {
  extends: [
      'alloy',
      'alloy/react',
      'alloy/typescript',
  ],
  plugins: [
    "@typescript-eslint",
    "react-hooks"
  ],
  env: {
      // 这里填入你的项目用到的环境
      // 它们预定义了不同环境的全局变量,比如:
      //
      // browser: true,
      // node: true,
      // mocha: true,
      // jest: true,
      // jquery: true
  },
  globals: {
      // 这里填入你的项目需要的全局变量
      // false 表示这个全局变量不允许被重新赋值,比如:
      //
      // myGlobal: false
  },
  rules: {
    semi: ['error', 'never'],
    "no-debugger": 'off',
    'no-console': 'off',
    'no-unused-vars': 'off',
    'max-nested-callbacks': 'off',
    'react/no-children-prop': 'off',
    'typescript/member-ordering': 'off',
    'typescript/member-delimiter-style': 'off',
    'react/jsx-indent-props': 'off',
    'react/no-did-update-set-state': 'off',
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": 'off',
    indent: [
      'off',
      2,
      {
        SwitchCase: 1,
        flatTernaryExpressions: true
      }
    ]
  }
}

这样,我们就配置好了代码检测,

在package.json添加新的命令:"lint:page": "eslint src --ext .ts,.tsx"

配置prettier

eslint是我们用来做代码检查的,但是它不包括代码自动格式化我们的错误代码

社区有这么一个工具:prettier,按照教程一步一步配置就好了。

yarn add prettier -D

然后创建prettier.config.js文件

module.exports = {
  // 一行最多 100 字符
  printWidth: 100,
  // 使用 2 个空格缩进
  tabWidth: 2,
  // 不使用缩进符,而使用空格
  useTabs: false,
  // 行尾需要有分号
  semi: false,
  // 使用单引号
  singleQuote: true,
  // jsx 不使用单引号,而使用双引号
  jsxSingleQuote: false,
  // 末尾不需要逗号
  trailingComma: 'none',
  // 大括号内的首尾需要空格
  bracketSpacing: true,
  // jsx 标签的反尖括号需要换行
  jsxBracketSameLine: false,
  // 箭头函数,只有一个参数的时候,也需要括号
  arrowParens: 'always',
  // 每个文件格式化的范围是文件的全部内容
  rangeStart: 0,
  rangeEnd: Infinity,
  // 不需要写文件开头的 @prettier
  requirePragma: false,
  // 不需要自动在文件开头插入 @prettier
  insertPragma: false,
  // 使用默认的折行标准
  proseWrap: 'preserve',
  // 根据显示样式决定 html 要不要折行
  htmlWhitespaceSensitivity: 'css',
  // 换行符使用 lf
  endOfLine: 'lf'
}

在package.json中新增命令:"prettier:page": "prettier --write "src/**/*.{ts,tsx}""

测试方式很简单:在某一个ts文件中写一行超过100字符的字符串,然后运行prettier:page看这个字符串会不会折行就ok了

配置lint-stage 在git commit的时候会执行hook钩子函数,对代码做强制检查

安装husky 与lint-staged yarn add husky lint-staged --dev

更新package.json中的配置

"husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
},
"lint-staged": {
    "src/**/*.{ts,tsx}": [
      "npm run prettier:page",
      "npm run lint:page",
      "git add"
    ]
}

然后在git commit 时就能看到

我们的配置已经生效了。

webpack配置优化

到目前为止,一个能用的项目框架已经搭建起来了,但是在正式的项目中,我们会有一套生产环境,一套开发环境。对应的webpack配置也会分为开发环境配置,和生产环境配置。

接下来我们优化下现有的webpack配置,将它拆分一套基础配置,一套开发环境配置,一套生产环境配置。

先来webpack.base.config.js

const baseConfig = {
  entry: [
    resolve('src/index.tsx')
  ],
  output: {
    filename: '[name].[hash:8].js',
    path: resolve('dist')
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        loader: [
          'babel-loader',
          'awesome-typescript-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: resolve('src/index.html')
    }),
    new webpack.HotModuleReplacementPlugin()
  ]
}

我们拆出来的baseConfig,其中只包含了基础的entry和output,而module的rules中包括es-loader和awesome-typescript-loader,当然仅仅这两个loader是肯定不够的,我们项目中经常会用到的file-laoder url-loader等,会在用到的时候再添加到这个配置里。

接下来我们开始配置我们的开发环境webpack配置

const devConfig = merge(baseConfig, {
  mode: mode,
  devtool:"cheap-module-eval-source-map",
  entry: [
    'react-hot-loader/patch',
    resolve('src/index.tsx')
  ],
  resolve: {
    alias: {
      'react-dom': '@hot-loader/react-dom'
    }
  },
  module: {
    rules: [
      {
        test: /\.less/,
        include: [
          resolve('src')
        ],
        use: [
          { loader: 'style-loader' },
          { loader: 'css-loader' },
          { loader: 'less-loader' }
        ]
      }
    ]
  },
  devServer: {
    port: process.env.PORT || 5678,
    publicPath: '',
    historyApiFallback: true,
    overlay: {
      errors: true
    },
    open: true,
    contentBase: resolve('dist'),
    hot: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
})

在devConfig的配置中,可以看到,我们在入口处添加react hot reload,引入了开发环境的source-map方便查找问题,在alias中添加了react-dom hot reload 的支持,在rules中添加了.less文件的支持,这是应为在开发环境中我们不需要将css代码抽离成不同的文件,但是在实际的项目中,通常我们会根据不同的页面打出不同的css包,再加载到这个页面时才会去请求对应的css。这样做的好处是可以加快首页的加载的速度,减少首页包的体积。

修改package.json的scripts,添加新的命令:

cross-env NODE_ENV=development webpack-dev-server --config ./config/webpack.dev.js --color

然后运行npm run dev

能看到编译成功了,但是我们还是会发现一些问题:

  1. 每次编译需要的是时间太长,从上图我们看到,我们的第一次编译花费了6835ms,这还是在只引入了react的情况下。这么长的编译时间是需要优化的。
  2. 编译输出的无用信息太多。

接下来我们针对这两点继续做优化。首先我们来优化输出不友好的问题:

webpack提供了一个配置项叫stats(统计信息)用来输出编译时的信息。

它有一个选项:minimal 只在发生错误或有新的编译时输出。

更新webpack配置:

stats: 'minimal'

然后webpack还有一些在控制输出的插件,我们使用Friendly-errors-webpack-plugin和webpack-dashboard。

Friendly-errors-webpack-plugin 是聚合错误信息,更友好的展示错误信息的不二之选。

webpack-dashboard 则是将更加模块化的展示编译界面。

将这两个插件配置在webpack的plugins中:

new DashboardPlugin(PORT),
new FriendlyErrorsWebpackPlugin({
    compilationSuccessInfo: {
    messages: [`your application successful start`]
    },
    clearConsole: true
}),

更新package.json的dev命令: cross-env NODE_ENV=development webpack-dashboard -- webpack-dev-server --config ./config/webpack.dev.js --color

重新运行 npm run dev

现在的输出就比之前友好多了😝。

然后我们再来考虑下第一次编译时间很长的问题。

首先安装 yarn add speed-measure-webpack-plugin 这个插件能让我们观测到每个loader,每个plugin执行过程中花费的时间

它的使用方法也很简单

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin")
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap(devConfig)

重新npm run dev

上图发现我们在node_modules文件和ts文件编译过程中花费了较多的时间

接下来我们一步一步优化

resolve: {
    extensions: ['.js', '.ts', '.tsx'],
    modules: [
      resolve('node_modules'),
      resolve('src')
    ]
  },

在resolve中配置modules的路径,让webpack减少寻找时间

观察上图我们发现 babel-loader, and awesome-typescript-loader took 3.061 secs 这一步花费了很多了时间,尝试只用一个loader,我们只用awesome-typescript-loader,重新编译,awesome-typescript-loader took 2.64 secs 减少了0.4秒

接下来我们再尝试用thread-loader做一下多核编译尝试

悲催的发现号线awesome-typescript-loader支支持tread-loader

然后我将awesome-typescript-loader 换成了ts-loader。。结果发现ts-loader took 2.071 secs 减少了足足一秒🤣。。。。

整体的时间也从7秒减少到了3.5秒

接下来我们重新配置thread-loader

{
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        include: [
          resolve('src')
        ],
        use: [
          { loader: 'cache-loader' },
          {
            loader: 'thread-loader',
            options: {
              works: require('os').cpus().length - 1,
              poolTimeout: Infinity
            }
          },
          {
            loader: 'ts-loader',
            options: {
              happyPackMode: true
            }
          }
        ]
      }

然后重新运行:

到这一步我们的优化基本已经差不多了,当然我们还有很多其他的常规的优化可以做,比如:dll或者externals去掉不需要的编译的包,将我们的react变为通过CDN引入。

完成了开发环境的配置,接下来就是生产环境的配置了

生产环境相对于开发环境就是多了代码压缩,按需引入,已经css抽离。

代码就不贴了。

想要源码的同学可以等我后面的组件开发贴上github地址。