1. 程式人生 > >webpack4構建前端專案

webpack4構建前端專案

在使用webpack 4.x版本構建前端專案的時候,遇到了一些坑點,這裡做一下記錄,詳細內容見註釋。

1、專案目錄:

 

 

2、基本配置內容

webpack.base.config.js

'use strict';
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
//用於在構建前清除dist目錄中的內容
const CleanWebpackPlugin = require('clean-webpack-plugin');

let baseWebpackConfig = {
  // 指定找入口檔案所在資料夾
  context: path.resolve(__dirname, '../src/test'), // 這裡是我做test的一個單獨資料夾
  entry: './index.js',
  output: {
    // 打包出的檔名
    filename: '[name].bundle.[hash:8].js',
    // 打包出的檔案輸出路徑
    path: path.resolve(__dirname, '../dist'),
    publicPath: "/" // 編譯後模板檔案地址
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/i,
        // use: ['babel-loader'],
        loader: 'babel-loader',
        exclude: /(node_modules|bower_components)/,
        options: {
          // This is a feature of `babel-loader` for webpack (not Babel itself).
          // It enables caching results in ./node_modules/.cache/babel-loader/
          // directory for faster rebuilds.
          cacheDirectory: true,
          plugins: ['react-hot-loader/babel'],
        }
      },
      {
        test: /\.(less|css)$/i,
        use:['css-hot-loader',MiniCssExtractPlugin.loader,"css-loader","less-loader",{
          loader: "postcss-loader",
          options: {
            ident: 'postcss',
            plugins: () => [
              require('autoprefixer')("last 100 versions")
            ]
          }
        }]
      },
      {
        test: /\.(png|jpe?g|gif|svg|ico)(\?.*)?$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 10000, // 大於就用檔案形式,小於就壓縮成base64
            name: 'img/[name].[hash:7].[ext]'
          }
        }]
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 10000,
            name: 'media/[name].[hash:7].[ext]'
          }
        }]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 10000,
            name: 'fonts/[name].[hash:7].[ext]'
          }
        }]
      }
    ]
  },
  plugins: [
    //用於在構建前清除dist目錄中的內容
    new CleanWebpackPlugin(['dist'], {
      root: path.resolve(__dirname, '../'),       // 根目錄
      verbose: true,                  // 開啟在控制檯輸出資訊
    }),
    // dist目錄下生成html模板檔案
    new HtmlWebpackPlugin({
      template: './index.html'
    }),
    // 樣式程式碼分離
    // new ExtractTextWebpackPlugin('styles.css'), // (webpack4廢棄)
    new MiniCssExtractPlugin({
      filename: "[name].css", // **注意**這裡不能使用hash,否則無法實現熱跟新,如果有hash需要,可以開發環境和生產環境分開配置成[name].[chunkhash:8].css
      chunkFilename: "[id].css" // 構建時生成的檔名
    })
  ],
  resolve: {
    // 設定自動解析的擴充套件
    extensions: ['.web.js', '.js', '.json', '.jsx', '.less', '.css'],
    // 設定路徑別名
    alias: {
      '@': path.resolve(__dirname, '../src/')
    }
  }
};

module.exports = baseWebpackConfig;

3、本地開發環境dev伺服器配置

webpack.dev.config.js

'use strict';
const PORT = 3000;
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.config');
const OpenBrowserWebpackPlugin = require('open-browser-webpack-plugin');

let webpackDevServerConfig = {
  mode: 'development',
  // 入口配置
  entry: [
    'babel-polyfill', // 原生支援
    'react-hot-loader/patch', // 區域性更新
    './index.js'
  ],
  devtool: 'cheap-module-source-map',
  plugins: [
    // 美化 console 輸出
    new webpack.NamedModulesPlugin(),
    // 開啟全域性的模組熱替換(HMR)
    new webpack.HotModuleReplacementPlugin(),
    // 編譯完成在再瀏覽器開啟專案
    new OpenBrowserWebpackPlugin({url: `http://0.0.0.0:${PORT}`}), // {url: `http://localhost:${PORT}`}
    // 編譯時定義全域性變數,用於判斷執行環境 | 配合cross_env外掛使用,它是執行跨平臺設定和使用環境變數的指令碼
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      }
    })
  ],
  devServer: {
    // hot: true,
    hotOnly: true, // 只熱替換,不自動重新整理
    port: PORT,
    inline: true, // 實時重新整理
    publicPath: baseWebpackConfig.output.publicPath,
    host: '0.0.0.0',
    // 是否關閉用於DNS重繫結的HTTP請求的host檢查,它通常用於搭配 --host 0.0.0.0 使用,
    // 因為你想要其它裝置訪問你本地的服務,但訪問時是直接通過 IP 地址訪問而不是 HOST 訪問,所以需要關閉 HOST 檢查。
    disableHostCheck: true,
    // 把專案根目錄下的src目錄設定成DevServer伺服器的檔案根目錄
    contentBase: path.resolve(__dirname, 'src'),
    // 可以保證類似http://localhost:8080/aa的請求返回跟http://localhost:8080/一樣的頁面,
    // 這樣才能用同一個js根據路徑的不同去往不同的路由
    historyApiFallback: true
  },
  // dev環境打包的單個js檔案可能過大,通過該方式取消警告
  performance: {
    hints: process.env.NODE_ENV === 'development' ? false : 'warning'
  }
};
module.exports = merge(baseWebpackConfig, webpackDevServerConfig)

4、生產打包配置

webpack.build.config.js

'use strict';
const merge = require('webpack-merge');
const webpack = require('webpack');
const baseWebpackConfig = require('./webpack.base.config.js');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

let webpackBuildConfig = {
  mode: 'production',
  // 入口配置
  entry: [
    'babel-polyfill', // 瀏覽器原生支援
    'react-hot-loader/patch', // 區域性更新
    './index.js'
  ],
  plugins: [
    new UglifyJSPlugin({
      sourceMap: true
   }),
    // 編譯時定義全域性變數,用於判斷執行環境
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      }
    })
  ]
};

module.exports = merge(baseWebpackConfig, webpackBuildConfig);

5、package.json對scripts屬性配置

核心程式碼:

"test": "echo \"Error: no test specified\" && exit 1",
"start": "npm run dev",
"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.config.js",
"build": "cross-env NODE_ENV=production webpack -p --config build/webpack.build.config.js"

package.json

 

6、test目錄下的一些測試檔案

<1>、 index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>test-html</title>
</head>
<body>
  <div id="test"></div>

</body>
</html>

<2>、index.js

import React from 'react'
import ReactDom from 'react-dom'
import { AppContainer } from 'react-hot-loader';
import './style.less'
import bigImg from './imgs/big-test.png'
import smallImg from './imgs/small-test.png'

let html = <div className='wrapper'>
  <h1 className='test'>hello webpack!</h1>
  <span>16.4kb---></span><img src={bigImg} />
  <span>6.09kb---></span><img src={smallImg} />
</div>

ReactDom.render(
  <AppContainer>
    {html}
  </AppContainer>,
  document.getElementById('test')
)

// HMR 介面
if (module.hot) {
  // 實現熱更新
  module.hot.accept()
}

<3>、style.less

.wrapper{
  display: flex;
  flex-direction: column;
  align-items: center;
  .test{
    border-radius: 5px;
    flex: 1;
    color: black
  }
}

7、.babelrc的配置

{
  "presets": [
    [
      "es2015",
      {
        "modules": false
      }
    ],
    "react",
    "stage-0"
  ],
  "plugins": [
    "react-hot-loader/babel",
    "transform-runtime"
  ]
}