前端工程化探索学习之模块打包工具webpack(2)

本文档详细记录了一位开发者在搭建webpack项目时的思考过程和配置步骤,包括从理解官方文档、设定目标、选择合适的版本,到安装依赖、配置打包文件、设置插件等各个环节。开发者在配置中考虑了模块加载、环境区分、热更新和代码压缩等功能,并通过编写build.js文件进行打包操作。目前实现了React和TypeScript支持,图片和代码文件打包,但还需完善生产环境的代码压缩和开发环境的热更新功能。
摘要由CSDN通过智能技术生成

目录

一、待解决的问题

二、思考相关问题的历程

2.1 看官方文档看的头大

2.2  初步搭建webpack实现的目标分析和思考

2.3   关于对应版本的问题

三、搭建过程记录

  3.1 整理一部分相关依赖的安装

3.2 文件夹作用和命名初步确认

3.3  打包文件文件命名及作用

3.4  打包相关配置文件配置

3.5 插件相关配置文件

3.6 开始打包文件配置

四、测试打包情况和打包后的文件

五.现有配置实际实现的需求和待实现的需求


一、待解决的问题

上次初步了解webpack后待解决的问题

1.webpack相对成熟和常用的module加载和plugins的搭配组合写法?在处理较为庞大的项目中时,webpack对应的组合和写法。

2.webpack动态加载模块的实现方式和对应的基本原理?

        这些在网上面有很多种解答,很庞大,还是梳理作用和需求,逐步搭建一遍吧。不然无法分辨网上大量的说法的正确与否。

二、思考相关问题的历程

2.1 看官方文档看的头大

       涉及到配置的说明文字相当庞大,除了基本结构在这个过程中更清晰了一些。其它的内容,越看越头大。有很多的配置属性只是一笔带过,具体的配置内容需要到相应的loader和plugin里面去看。有种一眼望不到边的感觉,更别谈看着这玩意配置了。

      直到看到由浅入深的官方文档的配置指南具体配置,才开始有了一定的头绪。

2.2  初步搭建webpack实现的目标分析和思考

     (1)能够打包对应的指定文件路口的文件:包括了图片、代码文件、css文件

     (2)分环境:划分开发,生产两个环境

     (3)在开发环境下,可以热更新,发生改变后,自动刷新界面。在生产环境下,需要能够压缩文件,能够缓存未变更的文件。只变更已经变更了的文件。

     (4)在开发环境下可以统一规范代码编写方式。明显格式错误时会报错

      暂时只能想到这些,后面参考搭建好的较打项目的需求,考虑webpack相应的需要满足的新的需求。

          大致的需求整理如下

2.3   关于对应版本的问题

       webpack5是最新的版本,但是很多版本特性可能不稳定,所以暂时使用webpack4。从稳定性角度考虑,框架和对应版本选型均低于最新版本的1到2个版本。

        React最新版本是17,现在用16.9

        对应配置版本如下:

  • webpack v4
  • react v16.9
  • typescript v3.5
  • babel v7
  • eslint v6.2

三、搭建过程记录

  3.1 整理一部分相关依赖的安装

     这部分依赖安装,要是分步安装的话,会很容易出现版本问题。参考官方脚手架及git项目中的依赖。直接写入package.json中,然后使用npm install 完成初步的依赖安装会更快些。 后面有新的需要再单个依赖安装,package.json会自动更新。

  "dependencies": {
    "@loadable/component": "^5.12.0",
    "core-js": "^2.6.9",
    "react": "^16.13.0",
    "react-dom": "^16.13.0",
    "react-router-dom": "^5.1.2"
  },
  "devDependencies": {
    "@babel/core": "^7.8.7",
    "@babel/plugin-proposal-class-properties": "^7.8.3",
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/polyfill": "^7.8.7",
    "@babel/preset-env": "^7.8.7",
    "@babel/preset-react": "^7.8.3",
    "@commitlint/cli": "^8.3.5",
    "@svgr/webpack": "^5.2.0",
    "@types/jest": "^25.1.4",
    "@types/node": "^13.9.0",
    "@types/react": "^16.9.23",
    "@types/react-dom": "^16.9.5",
    "@types/react-router-dom": "^5.1.3",
    "@typescript-eslint/eslint-plugin": "^2.22.0",
    "@typescript-eslint/parser": "^2.22.0",
    "autoprefixer": "^9.7.4",
    "awesome-typescript-loader": "^5.2.1",
    "babel-eslint": "^10.1.0",
    "babel-jest": "^25.1.0",
    "babel-loader": "^8.0.6",
    "chalk": "^3.0.0",
    "clean-webpack-plugin": "^3.0.0",
    "compression-webpack-plugin": "^3.1.0",
    "copy-webpack-plugin": "^5.1.1",
    "cross-env": "^7.0.2",
    "css-loader": "^3.4.2",
    "cssnano": "^4.1.10",
    "cssnano-preset-advanced": "^4.0.7",
    "dotenv": "^8.2.0",
    "dotenv-expand": "^5.1.0",
    "eslint": "^6.8.0",
    "eslint-config-prettier": "^6.10.0",
    "eslint-loader": "^3.0.3",
    "eslint-plugin-jsx-control-statements": "^2.2.1",
    "eslint-plugin-prettier": "^3.1.2",
    "eslint-plugin-react": "^7.19.0",
    "file-loader": "^5.1.0",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "husky": "^4.2.3",
    "interpolate-html-plugin": "^3.0.0",
    "jest": "^25.1.0",
    "koa": "^2.11.0",
    "koa-router": "^8.0.8",
    "less": "^3.11.1",
    "less-loader": "^5.0.0",
    "lint-staged": "^10.0.8",
    "mini-css-extract-plugin": "^0.9.0",
    "mockjs": "^1.1.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "ora": "^4.0.3",
    "portfinder": "^1.0.25",
    "postcss-aspect-ratio-mini": "^1.0.1",
    "postcss-loader": "^3.0.0",
    "postcss-px-to-viewport": "^1.1.1",
    "postcss-write-svg": "^3.0.1",
    "preload-webpack-plugin": "^2.3.0",
    "prettier": "^1.19.1",
    "style-loader": "^1.1.3",
    "terser-webpack-plugin": "^2.3.5",
    "ts-jest": "^25.2.1",
    "typescript": "^3.8.3",
    "url-loader": "^3.0.0",
    "webpack": "^4.42.0",
    "webpack-bundle-analyzer": "^3.6.0",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.10.3",
    "webpack-merge": "^4.2.2"
  },

    dependencies:代表的是生产环境用的依赖     –save   (对应的安装依赖的后缀)

    devDependencies:代表的是开发环境相关的依赖   –save  -dev   (对应的开发环境安装文件的后缀)

3.2 文件夹作用和命名初步确认

(1)项目根目录

  • config 打包配置
  • public 静态文件夹
    • index.html
    • favicon.ico
  • src 源码目录

(2)初始化命令

$ git init
$ npm init

初始化后生成初始化文件

3.3  打包文件文件命名及作用

  • config 打包配置
    • webpack.base.js  //公共配置文件
    • webpack.dev.js   //开发环境文件
    • webpack.prod.js //生产环境配置文件
    • build.js  //执行构建的相关命令文件
    • config.js  //用于提取可能会变动的环境配置文件

3.4  打包相关配置文件配置

webpack.base.js

const path = require('path');
const webpack = require('webpack');
const config = require('./config');
const APP_PATH = path.resolve(__dirname, '../src');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin }=require('clean-webpack-plugin');
module.exports = {
  entry: {   //入口
    app: './src/index.tsx',
  },
  output: {  //输出文件
    filename: 'js/[name].bundle.js',
    path: config.assetsRoot,
    publicPath: config.publicPath
  },
  module: {  //loader
    rules: [
        {
          oneOf: [
            {
              test: /\.(html)$/,
              loader: 'html-loader'
            },
            {
              test: /\.(j|t)sx?$/,
              include: APP_PATH,
              use: [
                {
                  loader: 'babel-loader',
                  options: {
                    presets: [
                      '@babel/preset-react',  // jsx支持
                      ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 2 }] // 按需使用polyfill
                    ],
                    plugins: [
                      ['@babel/plugin-proposal-class-properties', { 'loose': true }] // class中的箭头函数中的this指向组件
                    ],
                    cacheDirectory: true // 加快编译速度
                  }
                },
                {
                  loader: 'awesome-typescript-loader'
                }
              ]
            },
            {
              test: /\.svg$/,
              use: ['@svgr/webpack']
            },
            {
              test: /\.(jpg|jpeg|bmp|png|webp|gif)$/,
              loader: 'url-loader',
              options: {
                limit: 8 * 1024, // 小于这个大小的图片,会自动base64编码后插入到代码中
                name: 'img/[name].[hash:8].[ext]',
                outputPath: config.assetsDirectory,
                publicPath: config.assetsRoot
              }
            },
            {//加载处理文件
              exclude: [/\.(js|mjs|ts|tsx|less|css|jsx)$/, /\.html$/, /\.json$/],
              loader: 'file-loader',
              options: {
                name: 'media/[path][name].[hash:8].[ext]',
                outputPath: config.assetsDirectory,
                publicPath: config.assetsRoot
              }
            }
            
          ]
        }
      ]
    },
  resolve: { //加载时
    extensions: ['.js', '.json', '.jsx', '.ts', '.tsx'], // 自动判断后缀名,引入时可以不带后缀
    alias: {
        '@': path.resolve(__dirname, '../src/') // 以 @ 表示src目录
       }
  },
  plugins: [
    new HtmlWebpackPlugin({  
      inject: true,
      template: config.indexPath,
      showErrors: true
    }),
    new CleanWebpackPlugin(),//用于自动删除上次打包文件
  ],
  optimization: {}
};

webpack.dev.js

const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base');
const config = require('./config');

module.exports = merge.smart(baseWebpackConfig, {
  mode: 'development',
  output: {
    filename: 'js/[name].[hash:8].js',
    publicPath: config.publicPath // 这里可以省略
  },
  module: {
    rules: [
      {
        oneOf: []
      }
    ]
  },
})

webpack.prod.js

const config = require('./config');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const sourceMapsMode = config.productionJsSourceMap ? 'source-map' : 'none';

module.exports = merge.smart(baseWebpackConfig, {
  mode: 'production',
  devtool: sourceMapsMode,//将编译后的代码映射回原始源代码。可以更准确的定位到错误代码的出处
  output: {
    filename: 'js/[name].[contenthash:8].js', // contenthash:只有模块的内容改变,才会改变hash值
  },
  plugins: [
    new CleanWebpackPlugin(),
  ]
})

config.js//抽出部分可能会变化的属性 写入配置文件中,防止变更配置时需要同步修改生产环境和开发环境配置

const path = require('path');

module.exports = {
  assetsRoot: path.resolve(__dirname, '../dist'),
  assetsDirectory: 'static',
  publicPath: '/',
  indexPath: path.resolve(__dirname, '../public/index.html'),
  productionJsSourceMap: false,
  devServer: {
    port: 8080,
    host: 'localhost',
    contentBase: path.join(__dirname, '../public'),
    watchContentBase: true,
    publicPath: '/',
    compress: true,
    historyApiFallback: true,
    hot: true,
    clientLogLevel: 'error',
    open: true,
    overlay: false,
    quiet: false,
    noInfo: false,
    watchOptions: {
      ignored: /node_modules/
    },
    proxy: {}
  }
};

3.5 插件相关配置文件

根目录下创建相关文件,并写入配置

.commitlintrc.js//用于规范git commit命令时候的规范

module.exports = {
    parserPreset: {
      parserOpts: {
        headerPattern: /^(\w*)(?:\((.*)\))?:\s(.*)$/,
        headerCorrespondence: ['type', 'scope', 'subject']
      }
    },
    rules: {
      'type-empty': [2, 'never'],
      'type-case': [2, 'always', 'lower-case'],
      'subject-empty': [2, 'never'],
      'type-enum': [
        2,
        'always',
        ['feat', 'fix', 'update', 'docs', 'style', 'refactor', 'test', 'chore', 'release', 'revert']
      ]
    }
  }

tsconfig.json//Typescript相关配置文件

{
    "compilerOptions": {
      "target": "es5",
      "lib": [
        "dom",
        "dom.iterable",
        "esnext"
      ],
      "allowJs": true,
      "skipLibCheck": true,
      "esModuleInterop": true,
      "experimentalDecorators": true,
      "allowSyntheticDefaultImports": true,
      "strict": true,
      "forceConsistentCasingInFileNames": true,
      "module": "esnext",
      "moduleResolution": "node",
      "resolveJsonModule": true,
      "isolatedModules": true,
      "noEmit": true,
      "jsx": "react",
      "baseUrl": ".",
      "paths": {
        "@/*": ["src/*"]
      }
    },
    "include": [
      "src"
    ],
    "exclude": [
      "./node_modules"
    ]
  }
  

3.6 开始打包文件配置

build.js

const ora = require('ora');
const chalk = require('chalk');
const webpack = require('webpack');
const webpackConfig = require('./webpack.prod');

const spinner = ora('webpack编译开始...\n').start();

webpack(webpackConfig, function (err, stats) {
  if (err) {
    spinner.fail('编译失败');
    console.log(err);
    return;
  }
  spinner.succeed('编译结束!\n');

  process.stdout.write(stats.toString({
    colors: true,
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false
  }) + '\n\n');
});

package.json中添加相应的脚本指令

  "scripts": {
    "build": "cross-env NODE_ENV=production node config/build.js",
  },

四、测试打包情况和打包后的文件

    npm run build 后

生成打包文件 /dist

五.现有配置实际实现的需求和待实现的需求

   (1)   支持react和typescript,支持打包图片文件和代码相关文件。 生产环境下打包存在缓存(contenthash),只有当文件有变更时,才会更新对应的文件。公共配置提取。代码git命令规范化。css文件可以自动插入被打包的文件中

(2)待实现的需求:未支持生产环境打包自动压缩,未支持开发环境下热更新。编写代码格式未规范。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值