手动搭建自己的ts-react-webpack项目

141 阅读2分钟

node -v 14.17.5 npm -v 6.14.14

  • 一) 初始化依赖

npm init -y
npm install webpack@4.46.0 --save-dev
npm install webpack-cli@3.3.11 --save-dev

  • 二) 初始化文件目录

├─ node_modules
├─ package-lock.json
├─ package.json
├─ index.html
├─ webpack.config.js
├─ README.md
└─ src
├─├─ index.js

  • 三) 文件代码补充

webpack.config.js

+ const path = require('path');
+ module.exports = {
+   entry: './src/index.js',
+   output: {
+     filename: 'main.js',
+     path: path.resolve(__dirname, 'dist')
+   }
+ };

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
+       <script src="src/index.js"></script>
</body>
</html>

package.json

{
    "name": "lv-strike-game",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
-      "test": "echo "Error: no test specified" && exit 1",
+      "build": "webpack"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "engines": {
        "node": "14.17.5",
        "npm": "6.14.14"
    },
    "devDependencies": {
        "webpack": "^4.46.0",
        "webpack-cli": "^ 3.3.11"
    }
}

此时运行 npm run buildnpx webpack --config webpack.config.js 可生成构建文件

如果 webpack.config.js 存在,则 webpack 命令将默认选择使用它。我们在这里使用 --config 选项只是向你表明,可以传递任何名称的配置文件。这对于需要拆分成多个文件的复杂配置是非常有用。

  • 四) 添加插件&解析器

npm install --save-dev style-loader@2.0.0
npm install --save-dev css-loader@5.2.7
npm install --save-dev file-loader@6.2.0
npm install --save-dev html-webpack-plugin@4.5.2
npm install --save-dev clean-webpack-plugin@4.0.0-alpha.0
npm install --save-dev webpack-dev-server@3.11.2

修改webpack.config.js配置文件
  const path = require('path');
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const {CleanWebpackPlugin} = require('clean-webpack-plugin');
+ const absolutePath = path.resolve(__dirname, './');

  module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
+  plugins: [
+    new CleanWebpackPlugin(),
+    new HtmlWebpackPlugin({
+      title: '靓仔',
+      template: `${absolutePath}/index.html`,
+      filename: 'index.html',
+      inject: false,
+      hash: true,
+      chunks: ['main'],
+      minify: {
+        collapseWhitespace: true,
+        removeComments: true,
+      }
+    })
+  ],
+  module: {
+    rules: [{
+      test: /.css$/,
+      use: [
+        'style-loader',
+        'css-loader'
+      ]
+    }, {
+      test: /.(png|svg|jpg|gif)$/,
+      use: [
+        'file-loader'
+      ]
+    }]
+  },
+  devServer: {
+    disableHostCheck: true, // 当将此项配置设置为 true 时,将会跳过 host 检查。
+      contentBase: path.resolve(absolutePath + '/public'),
+      host: getLocalhost(),
+      compress: true,
+      hot: true,
+      port: 8080, // 默认8080 引用后可修改
+      // open: true
+  }
};

+ /** 获取本地IP地址 */
+ function getLocalhost() {
+   const interfaces = require('os').networkInterfaces();
+   for (let devName in interfaces) {
+     const iface = interfaces[devName];
+     for (let i = 0; i < iface.length; i++) {
+       const alias = iface[i];
+       if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
+         return alias.address;
+       }
+     }
+   }
+ }
package.json添加新的script指令
 {
   ...
   "scripts": {
+    "start": "webpack-dev-server --open",
-    "build": "webpack"
   },
   ....
 }

此时运行npm run start即可成功启动开发环境项目

  • 五) 开启热更新

修改webpack.config.js配置文件
  ...
+ const webpack = require('webpack');
  ...
  devServer: {
  ...
+   hot: true
  ...
  },
  plugins: [
  ...
+   new webpack.HotModuleReplacementPlugin()
  ...
  ]
  • 六) 生产&开发环境配置分离

npm install --save-dev webpack-merge@5.8.0
npm install --save-dev @webpack-cli/serve@1.5.2

调整目录结构

├─ node_modules
├─ package-lock.json
├─ package.json
├─ index.html
-├─ webpack.config.js
+└─ config
+    ├─ webpack.common.js
+    ├─ webpack.dev.js
+    ├─ webpack.prod.js
├─ README.md
└─ src
├─ index.js

新增webpack.common.js公共配置
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const absolutePath = path.resolve(__dirname, './../');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(absolutePath, './dist')
    },
    resolve: {
        // 模块路径别名
        alias: {
            '@':'../src'
        }
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: '靓仔',
            template: `${absolutePath}/index.html`,
            filename: 'index.html',
            inject: false,
            hash: true,
            chunks: ['main'],
            minify: {
                collapseWhitespace: true,
                removeComments: true,
            }
        }),
    ],
    module: {
        rules: [{
            test: /.css$/,
            use: [
                'style-loader',
                'css-loader'
            ]
        }, {
            test: /.(png|svg|jpg|gif)$/,
            use: [
                'file-loader'
            ]
        }]
    }
};
新增webpack.dev.js开发配置
const path = require('path');
const webpack = require('webpack');
const {merge} = require('webpack-merge');
const common = require('./webpack.common.js');
const absolutePath = path.resolve(__dirname, './../');

module.exports = merge(common, {
    mode: 'development',
    devtool: "eval-source-map",
    plugins: [
        new webpack.HotModuleReplacementPlugin(), // webpack内置的热更新
    ],
    devServer: {
        disableHostCheck: true, // 当将此项配置设置为 true 时,将会跳过 host 检查。
        contentBase: path.resolve(absolutePath + '/public'),
        host: getLocalhost(),
        compress: true,
        hot: true,
        port: 8080, // 默认8080 引用后可修改
        // open: true
    },
});

/** 获取本地IP地址 */
function getLocalhost() {
    const interfaces = require('os').networkInterfaces();
    for (let devName in interfaces) {
        const iface = interfaces[devName];
        for (let i = 0; i < iface.length; i++) {
            const alias = iface[i];
            if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
                return alias.address;
            }
        }
    }
}
新增webpack.prod.js生成配置
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
    mode: 'production',
});
修改package.json
 "scripts": {
+  "start": "webpack-dev-server --open --config config/webpack.dev.js",
+  "build": "webpack --config config/webpack.prod.js"
 }
  • 七) 引入使用TS

npm install --save-dev ts-loader@8.3.0
npm install --save-dev typescript@4.3.5

调整目录结构

 ├─ node_modules
├─ package-lock.json
├─ package.json
├─ index.html
├─ webpack.config.js
└─ config
├─ webpack.common.js
├─ webpack.dev.js
├─ webpack.prod.js
├─ README.md
└─ src
-    ├─ index.js
+   ├─ index.ts
+├─ tsconfig.json

新增tsconfig.json
{
    "compilerOptions": {
        "target": "es5",
        "lib": [
            "dom",
            "dom.iterable",
            "esnext"
        ],
        "typeRoots": [
            "src/types"
        ],
        "paths": {
            "@/*": [
                "./src/*"
            ]
        },
        "allowJs": true,
        "skipLibCheck": true,
        "esModuleInterop": true,
        "experimentalDecorators": true,
        "allowSyntheticDefaultImports": true,
        "sourceMap": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "noEmit": false,
        "baseUrl": ".",
        "jsx": "react",
        "types": [
            "node"
        ]
    },
    "include": [
        "src"
    ],
    "exclude": [
        "./node_modules"
    ]
}
修改webpack.common.js文件
 module.exports = {
+  entry: './src/index.ts',
-  entry: './src/index.js',
 ...
 resolve: {
  // 模块路径别名
  alias: {
   '@':'../src'
  },
+  // 指定要解析的文件扩展名
+  extensions: ['.tsx', '.ts', '.js'],
 },
 module: { 
 ...
+  rules: [{
+    test: /.tsx?$/,
+    use: 'ts-loader',
+    exclude: /node_modules/,
+  }]
 ...
 }
}

八) 引入使用React

初始化依赖加载

npm install --save-dev react@17.0.2
npm install --save-dev react-dom@17.0.2
npm install --save-dev @types/react@17.0.19
npm install --save-dev @types/react-dom@17.0.19

初始化babel依赖

npm install --save-dev babel-loader@8.2.2
npm install --save-dev @babel/core@7.15.0
npm install --save-dev @babel/preset-env@7.15.0
npm install --save-dev @babel/preset-react@7.14.5
npm install --save-dev @babel/preset-typescript@7.15.0

文件目录调整

 ├─ node_modules
├─ package-lock.json
├─ package.json
├─ index.html
├─ webpack.config.js
└─ config
├─ webpack.common.js
├─ webpack.dev.js
├─ webpack.prod.js
├─ README.md
└─ src
+  ├─ index.tsx
-   ├─ index.ts
├─ tsconfig.json
+├─ .babelrc

.babelrc

{
    "presets": [
        "@babel/env",
        "@babel/preset-react",
        "@babel/preset-typescript"
    ]
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
    <% for (var css of htmlWebpackPlugin.files.css) { %>
    <link rel="stylesheet" href="<%=css %>">
    <% } %>
</head>
<body>
+   <div id="app"></div>

    <% for (var js of htmlWebpackPlugin.files.js) { %>
        <script type="text/javascript" src="<%=js %>"></script>
    <% } %>
</body>
</html>

webpack.common.js

 module.exports = {
-  entry: './src/index.js',
+  entry: './src/index.tsx',
  module: {
+    rules: [{
+    test: /.m?js$/,
+    exclude: /(node_modules|bower_components)/,
+    use: {
+      loader: 'babel-loader',
+      options: {
+        presets: ['@babel/preset-env']
+      }
+    }
+  }]
  }
 }

react热更新插件 => react-refresh

安装依赖

npm install --save-dev @pmmmwh/react-refresh-webpack-plugin@0.4.3
npm install --save-dev react-refresh@0.10.0
npm install --save-dev type-fest@2.0.0
webpack.dev.js

const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
...
module.exports = merge(common, {
...
plugins: [
+   new ReactRefreshPlugin()
]
...
});

webpack.common.js

...
module.exports = {
    module: {
        rules: [{
            test: /.m?js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env'],
+                   plugins: ['react-refresh/babel']
                }
            }
        }]
    }
}
...
兼容ts-loader

npm install --save-dev react-refresh-typescript@2.0.2
webpack.common.js

...
module.exports = {
    module: {
        rules: [{
            test: /.tsx?$/,
            exclude: /node_modules/,
-           use: 'ts-loader',
+           use: [{
+             loader: 'ts-loader',
+             options: {
+             getCustomTransformers: () => ({
+               before: [ReactRefreshTypeScript()],
+             }),
+             // `ts-loader` does not work with HMR unless `transpileOnly` is used.
+             // If you need type checking, `ForkTsCheckerWebpackPlugin` is an alternative.
+             transpileOnly: true,
+           },
+         }]
       }]
    }
}
...