liangbus / blogging

Blog to go
10 stars 0 forks source link

从零开始搭建 webpack + React + TypeScript 环境 #19

Open liangbus opened 4 years ago

liangbus commented 4 years ago

其实在这这前,自己搭过蛮多次的了,但每次都是网上东拼西凑,于是就自己记录一下这个完整的过程,也细看下每个环节所代表的意义

node 环境就不用多说了 Node 下载

我使用的包安装工具是 yarn,个人认为比 npm 好用太多,速度又快 Yarn 官网

上述是环境的准备工作,完成之后我们就可以开始真正的搭建我们的项目框架

这里我选择的是都是目前行业上比较流行的技术

webpack, webpack-dev-server, React, TypeScript, Babel...

贴一下我 package.json 里面的基本依赖

  "dependencies": {
    "@babel/plugin-proposal-class-properties": "^7.7.4",
    "@babel/plugin-proposal-object-rest-spread": "^7.7.4",
    "@babel/preset-env": "^7.7.4",
    "@babel/preset-typescript": "^7.7.4",
    "@types/react": "^16.9.15", // @types 包下的都是相关类型的声明
    "@types/react-dom": "^16.9.4",
    "@typescript-eslint/eslint-plugin": "^2.10.0", // 语法检查
    "@typescript-eslint/parser": "^2.10.0",
    "awesome-typescript-loader": "^5.2.1",
    "babel": "^6.23.0",
    "babel-loader": "^8.0.6",
    "babel-polyfill": "^6.26.0",
    "clean-webpack-plugin": "^3.0.0",
    "cross-env": "^6.0.3", // 用来区分开发环境与生产环境
    "eslint": "^6.7.2",
    "html-webpack-plugin": "^3.2.0", // 模板文件入口
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "sass-loader": "^8.0.0",
    "node-sass": "^4.13.0",
    "css-loader": "^3.2.1",
    "style-loader": "^1.0.1",
    "ts-loader": "^6.2.1",
    "typescript": "^3.7.3",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.9.0"
  }

cross-env 可以指定参数,用以区分开发测试环境,还是生产环境,从而读取不同的配置文件,示例

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

这里可能需要后面再找个时间对各个包的作用做一翻解释

webpack 默认只能识别固定的配置名 webpack.config.js,但是可以通过 --config 参数指定特定的 webpack 配置文件,比如我这里写的

webpack-dev-server --config config/webpack.dev.conf.js

一般项目里面也会根据环境的不同,创建不同的 webpack 配置文件

webpack.base.config.js
webpack.dev.config.js
webpack.prod.config.js

webpack.base.config.js

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

console.log('in webpack base config')

module.exports = {
  entry: './src/index.tsx',
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, '../dist'),
    publicPath: '/'
  },
  devtool: 'inline-source-map',
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
        exclude: /node_modules/
      },
      {
        test: /\.(tsx|ts)?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    // 增加解析后缀,ts, tsx,默认是 js, json
    extensions: [ '.tsx', '.ts', '.js' ]
  },
  plugins: [
    // 热更新模块
    new webpack.HotModuleReplacementPlugin(),
    // 模板插件
    new HtmlWebpackPlugin({
      // 指定入口模板
      template: './src/index.html',
      hash: true,
      minify: {
        removeComments: false, // 改为false
        collapseWhitespace: false, // 改为false
        removeAttributeQuotes: false // 改为false
      }
    }),
    // 每次都会清除之前留下的打包文件,避免文件过多
    // 最新版本不需要传目标目录,自动读取 output 下的 path
    new CleanWebpackPlugin()
  ]
}

webpack.dev.config.js

const baseConfig = require('./webpack.base.conf');
const devServerConfig = require('./webpack.dev.server.conf');

console.log('===== Envirement ====== ', process.env.NODE_ENV)
// console.log('~~~~~~~~~~~~~\n', baseConfig)
const webpackDevConfig = {
  ...baseConfig,
  ...{
    optimization: {
      // 打包压缩配置
      minimize: false
    },
    devServer: devServerConfig
  }
}
module.exports = webpackDevConfig;

我们用 webpack-dev-server 作为微型前端服务 webpack.dev.server.config.js

const path = require('path')
/**
 * webpack-dev-server 配置项
 */
module.exports = {
  port: 9000,
  index: 'index.html',
  proxy: {
    // proxy URLs to backend development server
    '/api': 'http://localhost:1001'
  },
  contentBase: path.join(__dirname, 'dist'), // boolean | string | array, static file location
  compress: false, // enable gzip compression
  historyApiFallback: true, // true for index.html upon 404, object for multiple paths
  hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
  https: false, // true for self-signed, object for cert authority
  noInfo: true, // only errors & warns on hot reload
  after: () => {
    console.info(`webpack server running, see http://localhost:${9000}/ `)
  }
}

在 package 添加命令

"scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --config config/webpack.dev.conf.js",
    "build-dev": "cross-env NODE_ENV=development webpack --config config/webpack.dev.conf.js"
  },

.babelrc 文件

{
  "presets": [
    "@babel/preset-typescript",
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/proposal-class-properties",
    "@babel/proposal-object-rest-spread"
  ]
}

再根据 webpack 配置中填写的入口文件,去创建相应的目录和文件 由于使用的是 typescript,因此还需要创建 tsconfig.json 文件,否则会报错 tsconfig.js

{
  "compilerOptions": {
    "outDir": "./dist/",
    "module": "es2015",
    // Target latest version of ECMAScript.
    "target": "es5",
    // Search under node_modules for non-relative imports.
    "moduleResolution": "node",
    "sourceMap": true,
    // Process & infer types from .js files.
    "allowJs": true,
    // Don't emit; allow Babel to transform files.
    // "noEmit": true,
    // Enable strictest settings like strictNullChecks & noImplicitAny.
    "strict": true,
    // Disallow features that require cross-file information for emit.
    // "isolatedModules": true,
    // Import non-ES modules as default imports.
    "esModuleInterop": true,
    "jsx": "react"
  },
  "include": [
    "src"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

到了这里基本上已经完成环境的搭建了

最后是自己根据这套流程搭起来的框架,然后弄了个 todo-list-app todo-list-app