webpack学习笔记-搭建vue项目

355 阅读3分钟

项目结构示意图

项目结构示意图

webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')   //这里引入插件
const vueLoaderPlugin = require('vue-loader/lib/plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')      // 复制文件
const webpack = require('webpack')
const HappyPack = require('happypack')     //单进程转多进程
const os = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })

function resolve(dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: path.resolve(__dirname, '../src/main.js'),
  output: {
    filename: 'js/[name].[hash:8].js',
    path: path.resolve(__dirname, '../dist'),
    chunkFilename: 'js/[name].[hash:8].js',  //异步加载模块
    publicPath: './'
  },
  externals: {},
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: [{
          loader: 'vue-loader',
          options: {
            compilerOptions: {
              preserveWhitespace: false
            }
          }
        }]
      },
      {
        test: /\.js$/,
        use: ['happypack/loader?id=happyBabel'],
        exclude: /node_modules/,
        include: [resolve('src'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.(jpe?g|png|gif)$/i, //图片文件
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10240,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'img/[name].[hash:8].[ext]',
                  publicPath: '../'
                }
              }
            }
          }
        ]
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10240,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'media/[name].[hash:8].[ext]',
                  publicPath: '../'
                }
              }
            }
          }
        ]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10240,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'font/[name].[hash:8].[ext]',
                  publicPath: '../'
                }
              }
            }
          }
        ]
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    },
    extensions: ['.js', '.vue', '.json'],
  },
  //插件注入
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../public/index.html')
    }),
    new HappyPack({
      id: 'happyBabel',
      loaders: ['babel-loader?cacheDirectory'],
      threadPool: happyThreadPool
    }),
    new vueLoaderPlugin(),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, '../public'),
          to: path.resolve(__dirname, '../dist')
        }
      ]
    }),
   
  ]
}

webpack.dev.js

const merge = require('webpack-merge')
const webpack = require('webpack')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')  //更好的看到webpack警告和错误

const devConfig = merge(webpackConfig, {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  devServer: {
    compress: true,
    host: "192.168.31.71",
    port: 8080,
    quiet:true,
    hot: true,
    inline: true,
    hotOnly: true,  //当编译失败时,不刷新页面
    overlay: true,  //用来在编译出错的时候,在浏览器页面上显示错误
    publicPath: '/',  //一定要加
    open: true,
    proxy: {
        // '/api': 'http://localhost:8080'
    },
    watchOptions: {
      // 不监听的文件或文件夹,支持正则匹配
      ignored: /node_modules/,
      // 监听到变化后等1s再去执行动作
      aggregateTimeout: 1000,
      // 默认每秒询问1000次
      poll: 1000
    }
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['vue-style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.scss$/,
        use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new FriendlyErrorsPlugin({
      compilationSuccessInfo: {
        messages: [`Your application is running here: http://localhost:8080`],
      }
    }),
    new webpack.DefinePlugin({
      'env':JSON.stringify(process.env.NODE_ENV)
    })
  ]
})

module.exports = devConfig

webpack.prod.js

const webpackConfig = require('./webpack.config')
const merge = require('webpack-merge')
const webpack = require('webpack')
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')     //清除dist
const MiniCssExtractPlugin = require("mini-css-extract-plugin")   //提取css
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')    //缓存第三方模块
const optimizeCss = require('optimize-css-assets-webpack-plugin');


function resolve(dir) {
  return path.join(__dirname, '..', dir)
}

const prodConfig = merge(webpackConfig, {
  mode: "production",
  devtool: 'none',
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          name: 'vendors',
          test: /[\\\/]node_modules[\\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        common: {
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    },
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
            }
          }, 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.scss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
            }
          }, 'css-loader', 'postcss-loader', 'sass-loader'],
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: 'css/[name].[hash].css',
      chunkFilename: 'css/[name].[hash].css',
    }),
    new HardSourceWebpackPlugin(),
    new optimizeCss({
      cssProcessor: require('cssnano'), //引入cssnano配置压缩选项
      cssProcessorOptions: {
        discardComments: { removeAll: true }
      },
      canPrint: true //是否将插件信息打印到控制台
    }),
    new webpack.DefinePlugin({
      'env':JSON.stringify(process.env.NODE_ENV)
    })
  ]
})

module.exports = prodConfig

zip.js

var fs = require("fs");
var archiver = require("archiver");
var path = require("path");

var output = fs.createWriteStream("./dist.zip");
var output_path = path.normalize(__dirname + '/../dist.zip');
var archive = archiver('zip',{
    zlib:{level:9}
})

archive.pipe(output);
archive.directory('dist/',false);
archive.finalize();

.babelrc

{
  "presets": [
    "@babel/preset-env"
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "helpers": true,
        "regenerator": true,
        "useESModules": false,
        "corejs": 2
      }
    ]
  ]
}

.browserslistrc

> 1%
last 2 versions
not ie <= 8

package.json

{
  "name": "webpack-vue",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.js",
    "build:dev": "cross-env NODE_ENV=production webpack --config  build/webpack.prod.js",
    "build:prod": "cross-env NODE_ENV=production webpack --config  build/webpack.prod.js",
    "pkg": "node build/zip.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.10.2",
    "@babel/plugin-transform-runtime": "^7.10.1",
    "@babel/preset-env": "^7.10.2",
    "@vue/test-utils": "^1.0.3",
    "archiver": "^5.1.0",
    "autoprefixer": "^9.8.0",
    "babel-core": "7.0.0-bridge.0",
    "babel-jest": "^26.0.1",
    "babel-loader": "^8.1.0",
    "clean-webpack-plugin": "^3.0.0",
    "compression-webpack-plugin": "^4.0.0",
    "copy-webpack-plugin": "^6.0.2",
    "cross-env": "^7.0.3",
    "css-loader": "^3.5.3",
    "cssnano": "^4.1.10",
    "file-loader": "^6.0.0",
    "friendly-errors-webpack-plugin": "^1.7.0",
    "happypack": "^5.0.1",
    "hard-source-webpack-plugin": "^0.13.1",
    "html-webpack-plugin": "^4.3.0",
    "jest": "^26.0.1",
    "mini-css-extract-plugin": "^0.9.0",
    "node-sass": "^4.14.1",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.2",
    "sass-resources-loader": "^2.0.3",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "vue-jest": "^3.0.5",
    "vue-loader": "^15.9.2",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.43.0",
    "webpack-bundle-analyzer": "^3.8.0",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.11.0",
    "webpack-merge": "^4.2.2"
  },
  "dependencies": {
    "@babel/polyfill": "^7.10.1",
    "@babel/runtime": "^7.10.2",
    "@babel/runtime-corejs2": "^7.10.2",
    "core-js": "^2.6.11",
    "element-ui": "^2.14.1",
    "vue": "^2.6.11",
    "vue-router": "^3.3.2"
  }
}

postcss.config.js

module.exports = {
  plugins: [require('autoprefixer')]  // 引用该插件即可了
}