手写 Webpack 实现 Vue 项目打包任务

775 阅读2分钟

首先是vue项目结构目录

app.vue中引用组件helloworld.vue

初始化需要的模块

1.安装webpack webpack-cli

npm install --save-dev webpacak webpack-cli 

yarn add webpack webapck-cli --dev 

此时编译启动会报错 需安装其他模块,具体如下:

npm install vue vue-loader vue-template-compiler less less-loader css css-loader style-loader file-loader --save-dev

yarn add vue vue-loader vue-template-compiler less less-loader css css-loader style-loader file-loader --dev

2.在webpack.config.js中使用CommonJs规范配置入口及输出位置,即本文中的webpack.common.js:

const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')
const vueLoaderPlugin = require('vue-loader/lib/plugin-webpack5')

// 使用commonjs规范编写
module.exports = {
  mode: 'none',
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          {
            loader: 'css-loader',
            options: {
              // enable CSS Modules
              modules: false,
            }
          }
        ]
      },
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.less$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader'
          },
          {
            loader: 'less-loader',
            options: {
              lessOptions: {
                strictMath: true,
              },
            }
          }
        ]
      },
      {
        test: /\.(png|jpe?g|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            outputPath: 'img',
            name: '[name].[ext]'
          }
        }
      },
      
      {
        test: /\.vue$/,
        use: [
          'vue-loader'
        ]
      }

    ]
  },
  plugins: [
    new HtmlWebpackPlugin(),
    new vueLoaderPlugin()
  ]
}

3.其中遇到的问题:

  • 3.1 package.json中配置
// package.json 中部分代码
"scripts": {
	"serve": "webpack serve --config webpack.config.js"
}

目的是不用每次都使用yarn webpck --config webpack.config.js执行编译,因为这里设置了分环境使用不同的配置文件,所以每次打包运行都需要指定配置文件.

  • 3.2 报错Invalid configuration object. Webpack has been initialised using a configura

    原因:

    • package.json文件中配置的webpack版本与所使用版本不一致
    • 检查代码中是否存在module写成了modules 解决方法:
    • 重新安装webpack版本: yarn add webpack --dev || npm install webpack --save-dev
  • 3.3 报错ERROR in Entry module not found: Error: Can't resolve 'babel-loader'

    原因:

    • 这类错误未能找到这个模块,安装即可: yarn add babel-loader --dev || npm install babel-loader --save-dev
  • 3.4 报错You may need an additional loader to handle the result of these loaders.

    原因: 需要一个loaders来加载一些运行后的结果

    解决方法: 安装对应模块: vue-loader vue-template-compiler

    再次执行扔报错: webpack.common.js中导入对应的plugin:

  const vueLoaderPlugin = require('vue-loader/lib/plugin-webpack5')
  
  .... // 省略代码
  plugins: [
    new HtmlWebpackPlugin(),
    new vueLoaderPlugin()
  ]

4. webpack.prod.js中:

需求分析:

  • 需要使用common中的部分
  • 需要单独压缩代码, 并且打包public中的文件:new CopyWebpackPlugin(['public']) 所以可以得到以下代码:
const common = require('./webpack.common')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = Object.assign({}, common, {
  mode: "production",
  plugins: [
    new CleanWebpackPlugin(),
    new CopyWebpackPlugin(['public'])
  ]
})

此时,assign方法后面拼接的对象会把前面对象的覆盖掉,所以不适合,使用webpack-merge模块提供的方法去合并common中的配置. 安装及引入:

yarn add webpack-merge --dev 
// 或
npm install webpack-merge --save-dev

// 引入

const commonConfig = require('./webpack.common')
const {merge} = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = merge(commonConfig, {
  mode: 'production',
  devtool: 'nosources-source-map',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          {
            loader: 'css-loader',
            options: {
              // enable CSS Modules
              modules: false,
            }
          }
        ]
      }
    ]
  },
  optimization: {
    usedExports: true,// 标记未引用代码
    minimize: true,//移除未使用代码
    splitChunks: {
      chunks: 'all'
    }
  },
  plugins: [
    new CleanWebpackPlugin(),
  ]
})

  • 4.1 编译出现:Multiple chunks emit assets to the same filename bundle.js报错
    • 原因: 在打包多文件入口时,只指定了一个输出文件名,应该每个需要打包的文件根据文件名打包输出
    • 解决方案: 在webpack.common.js中:
output: {
    filename: './js/[name].js',
    path: resolve(__dirname, 'build')
}

5.配置webpack.dev.js

需求分析:

  • 需要使用common中的部分
  • 需要使用eslint在开发阶段杜绝格式及语法错误
  • vue中集成eslint,需要通过loader加载,并在.eslintrc.jsplugin项配置 所以可以得到以下代码:
const  commonConfig =  require('./webpack.common')
const {merge} = require('webpack-merge')

const path = require('path')


module.exports = merge(commonConfig,{
  mode:'development',
  devtool:'eval-cheap-module-source-map',
  module:{
    rules:[
      {
        test: /\.js$/, 
        exclude: /node_modules/, 
        use: 'eslint-loader',
        enforce:'pre'
      },
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    port: 9000,
    // open: true,
    hot: true
  }
})

  • 5.1 需安装eslint eslint-loader
npm install eslint eslint-loader --save-dev
// 或
yarn add eslint eslint-loader --dev
  • 5.2 初始化.eslintrc.js文件:
npx eslint --init
// 或
yarn eslint --init
  • 5.3 得到.eslintrc.js文件,并配置:
module.exports = {
  env: {
    browser: true,
    es2021: true
  },
  extends: [
    'plugin:vue/essential',
    'standard'
  ],
  parserOptions: {
    ecmaVersion: 12
  },
  plugins: [
    'vue'
  ],
  rules: {
    
  }
}

6 在package.json中配置相关命令

"scripts": {
    "build": "webpack --config webpack.prod.js",
    "serve": "webpack serve --config webpack.dev.js",
    "lint": "npm run serve"
  },

一些列操作后,package.json如下:

{
  "name": "vue-app-base",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "serve": "webpack serve --config webpack.dev.js",
    "lint": "npm run serve"
  },
  "dependencies": {
    "@babel/core": "^7.12.16",
    "@babel/preset-env": "^7.12.16",
    "babel-loader": "^8.2.2",
    "core-js": "^3.6.5",
    "vue": "^2.6.12"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^4.5.11",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^7.0.0",
    "css": "^3.0.0",
    "css-loader": "^5.0.2",
    "eslint": "^7.20.0",
    "eslint-config-standard": "^16.0.2",
    "eslint-loader": "^4.0.2",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.3.1",
    "eslint-plugin-vue": "^7.6.0",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^5.1.0",
    "less": "^4.1.1",
    "less-loader": "^8.0.0",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "vue-loader": "^15.9.6",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.6.12",
    "webpack": "^5.23.0",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^3.9.0",
    "webpack-merge": "^5.7.3"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}