记一次webpack5从0到1改造老项目的过程

记一次老项目添加webpack的过程

目录结构

  1. 改造前
    在这里插入图片描述

  2. 改造后
    在这里插入图片描述

为什么要改造?老项目痛点在哪里?

  1. 老项目技术栈使用纯原生,需要兼容IE10,没有引入帮助插件
  2. 老项目css与js没有通过postcss与babel进行转化 容易产生兼容错误
  3. 老项目特殊需求静态资源如图片、图标、等需要打包到js文件中,每次新增都要自己转化,很麻烦
  4. 老项目JS代码全冗杂在一起,不模块化,不够清晰。
  5. 老项目与自动化打包构建工具合作不太方便
  6. 老项目没有使用scss等css工具
  7. 老项目没有使用eslint 也没有格式化代码 乱飞你懂得
  8. ……

改造之后的package.json

{
  "name": "aiews_js",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    // dev环境 mode作为环境标识
    "serve": "webpack serve --mode=development",
    // 生产环境
    "build": "webpack --mode=production"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.13.8",
    "@babel/plugin-transform-runtime": "^7.13.9",
    "@babel/preset-env": "^7.13.9",
    "@commitlint/cli": "^12.0.1",
    "@commitlint/config-conventional": "^12.0.1",
    "autoprefixer": "^10.2.4",
    "babel-eslint": "^10.1.0",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^5.1.1",
    "eslint": "^7.21.0",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^7.7.0",
    "eslint-webpack-plugin": "^2.5.2",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^5.2.0",
    "husky": "^5.1.3",
    "lint-staged": "^10.5.4",
    "node-sass": "^4.14.1",
    "postcss-loader": "^5.0.0",
    "postcss-nested": "^5.0.5",
    "prettier": "^2.2.1",
    "sass-loader": "^11.0.1",
    "style-loader": "^2.0.0",
    "terser-webpack-plugin": "^5.1.1",
    "url-loader": "^4.1.1",
    "webpack": "^5.24.3",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^3.11.2"
  },
  "dependencies": {
    "@babel/runtime": "^7.13.9"
  },
  // git提交的验证
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "lint-staged": {
    "*.{js,html}": [
      "npm run lint",
      "git add"
    ]
  }
}

webpack.config.js详解

// 需求暂时就这么多 其实为了精简 模块化 可以采用merge合并配置 

// html提取插件 直接影响pro和dev环境的的html模板
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 访问内置的插件
const path = require('path');
// 每次编译、打包清除dist目录
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// 压缩插件
const TerserPlugin = require('terser-webpack-plugin');
// eslint
const ESLintPlugin = require('eslint-webpack-plugin');
const resolve = function (dir) {
  return path.join(__dirname, dir);
};
// app目录
const APP_PATH = './src/app';

const config = {
  // 构建目标 默认是node 要改成web 热更新才有用 
  // 而且target影响着输出代码的语法格式 如果项目里有browerslistrc 建议使用这个
  // 发现browserslist貌似不能触发热更新 所以在下面通过环境来动态使用target了
  // 如果设置成web  打包之后的webpack相关代码还是会有es6语法的 所以生产环境要使用browserslist作为target

  // target: 'browserslist',

  // 项目入口文件
  // 一开始的需求只有一个入口 后来需要演示 又搞了个演示的入口
  // entry 的 key 就是不同入口的chunk 名字 
  // 这个名字和HtmlWebpackPlugin插件中的chunks对应上就可以将每个入口引入的资源打包到对应的目录中
  entry: {
    // 入口1
    render: `${APP_PATH}/main/index.js`,
    // 入口2
    demonstration: `${APP_PATH}/demonstration/index.js`,
  },
  // 输出 就是构建之后的配置
  output: {
    // [name] 是个变量 对应entry中的key 
    // 例如入口1的key是 `render` 那么他的输出就是 render.js 
    // 可以配置[hash]让每次生成的包名都不一样 避免缓存 我这里需求使然 需要固定的名字
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
    scriptType: 'text/javascript',
  },
  module: {
  	// 需要的一些loader
    rules: [
      {
      	// js使用babelloader 将代码转化为目录中 `.browserslistrc` 文件中的版本
      	// 目录中有个 `babel.config.js` 这个使用按需引入的写法就行了 如果有特殊需求可以显示引入 避免babel-polyfill完整引入 导致包过大 这个下面会介绍按需引入的写啊
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: ['babel-loader'],
      },
      //  图片资源的loader  因为项目特殊需求 我们需要将图片打包进script中 还好图片比较小 
      //  
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
      // sass-loader的配置就可以让我们写scss了  项目需求使然 不需要提取css成文件 都放入到js中 
      {
        test: /\.scss$/,
        use: [
          // 将 JS 字符串生成为 style 节点
          {
            loader: 'style-loader',
          },
          // 将 CSS 转化成 CommonJS 模块
          {
            loader: 'css-loader',
          },
          // 这个就是添加兼容的插件  依赖于 postcss.config.js 还有 .browserslistrc
          'postcss-loader',
          // 将 Sass 编译成 CSS
          {
            loader: 'sass-loader',
          },
        ],
      },
    ],
  },
  // 这个配置是项目路径的别名
  resolve: {
    alias: {
      '@': resolve('src'),
      '@js': resolve('src/js'),
      '@scss': resolve('src/scss'),
    },
  },
  // 使用的插件
  plugins: [
    // 打包过程的输出状态显示插件
    new webpack.ProgressPlugin(),
    // 每次打包前清空目录插件
    new CleanWebpackPlugin(),
    // eslint插件  fix true  自动更正格式
    new ESLintPlugin({ fix: true }),
    
    // 入口1的html提取插件
    new HtmlWebpackPlugin({
      // html模板 
      template: `${APP_PATH}/main/index.html`,
      // 输出的文件名
      filename: 'index.html',
      // chunks 就是要打包进来的js  render对应entry中的render
      chunks: ['render'],
      // 项目需求使然 如果不加这个 script 会加上defer 影响执行顺序 
      scriptLoading: 'blocking',
      // script插入在html中的哪个位置
      inject: 'head',
    }),
    // 入口2 配置同上
    new HtmlWebpackPlugin({
      template: `${APP_PATH}/demonstration/index.html`,
      filename: 'demonstration.html',
      chunks: ['demonstration'],
      scriptLoading: 'blocking',
      inject: 'head',
    }),
  ],
  devServer: {
    host: '0.0.0.0',
    // 静态资源文件夹
    contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
    // 热更新
    hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
    // 不显示info信息
    noInfo: true, // only errors & warns on hot reload
    // 启动后是否自动打开浏览器访问该项目
    open: true,
    // 如果打开了 访问哪个页面
    openPage: 'index.html',
    compress: true,
    overlay: true,
    inline: true,
    // ...
  },
};

module.exports = (env, argv) => {
  // 不同的环境使用不同的配置 这个是开发环境 可以通过插件给项目中注入环境变量
  if (argv.mode === 'development') {
    config.devtool = 'source-map';
    config.target = 'web';
  }
  // 这是生产模式
  if (argv.mode === 'production') {
    //... 生产模式启动压缩
    config.optimization = {
      minimize: true,
      minimizer: [new TerserPlugin()],
    };
    config.target = 'browserslist';
  }
  return config;
};

.browserslistrc

> 1%,
last 2 versions,
not ie <= 8,
safari >= 7

.eslintrc.js

module.exports = {
  // 使用哪个插件
  plugins: ['prettier'],
  // 使用的核心
  parser: 'babel-eslint',
  // 自定义规则
  rules: {
    quotes: ['error', 'single'],
    'prettier/prettier': [
      'error',
      { singleQuote: true },
      { trailingComma: 'none' },
    ],
  },
};

babel.config.js

module.exports = {
  presets: [['@babel/preset-env']],
  plugins: ['@babel/plugin-transform-runtime'],
};

postcss.config.js

module.exports = {
  plugins: [require('autoprefixer'), require('postcss-nested')],
};

commitlint.config.js

这个就是git提交钩子插件 需要eslint的配合去做验证

module.exports = {
  extends: ['@commitlint/config-conventional'],
};

单入口的webpack配置栗子

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 访问内置的插件
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');

const config = {
  // 构建目标 默认是node  要改成web 热更新才有用
  // 而且target影响着输出代码的语法格式 如果项目里有browerslistrc 建议使用这个
  target: 'browserslist',
  entry: './src/main.js',
  output: {
    filename: 'render.js',
    path: path.resolve(__dirname, 'dist'),
    scriptType: 'text/javascript',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: ['babel-loader'],
      },
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
      {
        test: /\.scss$/,
        use: [
          // 将 JS 字符串生成为 style 节点
          {
            loader: 'style-loader',
          },
          // 将 CSS 转化成 CommonJS 模块
          {
            loader: 'css-loader',
          },
          'postcss-loader',
          // 将 Sass 编译成 CSS
          {
            loader: 'sass-loader',
          },
        ],
      },
    ],
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      scriptLoading: 'blocking',
      inject: 'head',
    }),
    new ESLintPlugin({ fix: true }),
  ],
  devServer: {
    host: '0.0.0.0',
    contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
    hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
    noInfo: true, // only errors & warns on hot reload
    open: true,
    compress: true,
    overlay: true,
    inline: true,
    // ...
  },
};

module.exports = (env, argv) => {
  if (argv.mode === 'development') {
    config.devtool = 'source-map';
    // config.target = 'browserslist';
  }
  if (argv.mode === 'production') {
    //...
    config.optimization = {
      minimize: true,
      minimizer: [new TerserPlugin()],
    };
    // config.target = 'browserslist';
  }
  return config;
};

总结

  1. webpack的集成在前端自动化构建上真的省心省力,而且新版本编译速度很快,上手也比较简单,非常好用!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值