webpack4 正確的配置方式

不懂程式設計發表於2018-10-23

原文轉載於 www.rails365.net

原文: A tale of Webpack 4 and how to finally configure it in the right way

基本構建

開始新的專案

mkdir webpack-4-tutorial
cd webpack-4-tutorial

npm init
複製程式碼

上下會初始化一個專案,接下來新增webpack4.

npm install webpack webpack-cli --save-dev
複製程式碼

確定裝的webpack版本是4.

接下來修改package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack"
 },
複製程式碼

儲存之後,npm run dev會出現一個錯誤.

Insufficient number of arguments or no entry found.
Alternatively, run 'webpack(-cli) --help' for usage info.

Hash: 4442e3d9b2e071bd19f6
Version: webpack 4.12.0
Time: 62ms
Built at: 2018-06-22 14:44:34

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/

ERROR in Entry module not found: Error: Can't resolve './src' in '~/workspace/webpack-4-tutorial'
複製程式碼

兩個問題,第一個出錯的意思是說,沒有設定mode,預設會認為是production模式。第二個是說src下沒有找到入口模組。

我們來改下,再次開啟package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack --mode development"
  },
複製程式碼

在專案下新建src資料夾,然後src下新建index.js.

console.log(“hello, world”);
複製程式碼

儲存後再次執行npm run dev.

這時候成功打包,發現專案裡多了一個dist資料夾. 這是因為webpack4是號稱0配置,所以很多東西都給你配置好了,比如預設的入口檔案,預設的輸出檔案。 在webpack中配置環境,基本都是兩個配置檔案(development, production).在webpack4中,有這兩個模式來區分。

"scripts": {
  "dev": "webpack --mode development",
  "build": "webpack --mode production"
}
複製程式碼

儲存後執行npm run build,你會發現main.js檔案小了很多。

上面很多東西都是通過預設配置來的,嘗試去重寫配置,在不使用配置檔案的情況下。

"scripts": {
     "dev": "webpack --mode development ./src/index.js --output ./dist/main.js",
   "build": "webpack --mode production ./src/index.js --output ./dist/main.js"
 },

複製程式碼

編譯轉換你的程式碼

es6就是未來,可是現在的瀏覽器對他的支援不是很好,我們需要babel來轉換他。

npm install babel-core babel-loader babel-preset-env --save-dev
複製程式碼

bable的配置,需要建立一個檔案去配置。 新建.babelrc檔案,寫入如下內容

{
  "presets": [
    "env"
  ]
}
複製程式碼

接下來需要配置babel-loader,可以在package.json檔案配置,但是還是獨立出去好,比較好維護。專案下新建一個webpack.config.js.加入一些基礎配置。

// webpack v4
const path = require('path');
module.exports = {
  entry: { main: './src/index.js' },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};
複製程式碼

然後package.json的指令碼可以去除一些配置.

"scripts": {
    "dev": "webpack --mode development",
  "build": "webpack --mode production"
  },
複製程式碼

然後再執行編譯,也是可以正常編譯。

Html && Css

dist資料夾下新建一個index.html的檔案.

<html>
  <head>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div>Hello, world!</div>
    <script src="main.js"></script>
  </body>
</html>
複製程式碼

上面這段程式碼,使用到了style.css. 接來下去處理css的問題. src資料夾下新建style.css

div {
  color: red;
}
複製程式碼

然後在js檔案中引入css.,開啟index.js檔案

import "./style.css";
console.log("hello, world");
複製程式碼

解析來我們就需要配置css的規則,開啟webpack.config.js

// webpack v4
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); //新加入

module.exports = {
  entry: { main: './src/index.js' },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract(
          {
            fallback: 'style-loader',
            use: ['css-loader']
          })
      }// 新加的css 規則
    ]
  }
};

複製程式碼

上面我們用了一些外掛和載入器.需要去安裝.

npm install extract-text-webpack-plugin --save-dev
npm install style-loader css-loader --save-dev
複製程式碼

上面我們提出css來編譯,你可以看到他的規則, 規則可以這樣認為

{
        test: /\.擴充名$/,
        exclude: /不需要執行的資料夾/,
        use: {
          loader: "你的載入器"
        }
}
複製程式碼

我們需要extract-text-webpack-plugin這個提取外掛,因為webpack只會辨別js.這裡需要通過ExtractTextPlugin獲取css文字進行壓縮。

我們再次嘗試編譯,npm run dev發現會有報錯,這個是因為這個提取器在新版本的原因。相關報錯Webpack 4 compatibility 可以解決這個問題,那就是換個版本。

npm install -D extract-text-webpack-plugin@next
複製程式碼

然後再編譯,發現可以來,同時再dist下有一個style.css檔案。

這時候package.json的依賴檔案如下

"devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.4",
    "babel-preset-env": "^1.6.1",
    "css-loader": "^0.28.11",
    "extract-text-webpack-plugin": "^4.0.0-beta.0",
    "style-loader": "^0.20.3",
    "webpack": "^4.4.1",
    "webpack-cli": "^2.0.12"
  }
複製程式碼

下面我們來配置支援scss

npm install node-sass sass-loader --save-dev
複製程式碼

我們在src下新建一個index.html

<html>
  <head>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div>Hello, world!</div>
    <script src="main.js"></script>
  </body>
</html>
複製程式碼

安裝html外掛

npm install html-webpack-plugin --save-dev
複製程式碼

安裝完成之後來修改webpack.config.js

// webpack v4
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');// 新加

module.exports = {
  entry: { main: './src/index.js' },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract(
          {
            fallback: 'style-loader',
            use: ['css-loader']
          })
      }// 新加
    ]
  },
  plugins: [
    new ExtractTextPlugin({filename: 'style.css'}),
    new HtmlWebpackPlugin({
      inject: false,
      hash: true,
      template: './src/index.html',
      filename: 'index.html'
    })// 新加

  ]
};

複製程式碼

這個是最終的html的一個模版. 現在html,css,js基本配置的差不多,我們刪除dist來試試。

rm -rf dist/
npm run dev
複製程式碼

這時候會發現dist下存在js,css,html檔案.

快取

可以檢視關於hash快取的文件來使瀏覽器只請求改變的檔案.

webpack4提供了chunkhash來處理。

來看看:

// webpack v4
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');// 新加

module.exports = {
  entry: { main: './src/index.js' },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js' //changed
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract(
          {
            fallback: 'style-loader',
            use: ['css-loader']
          })
      }// 新加
    ]
  },
  plugins: [
    new ExtractTextPlugin(
      {filename: 'style.[chunkhash].css', disable: false, allChunks: true}
    ), //changed
    new HtmlWebpackPlugin({
      inject: false,
      hash: true,
      template: './src/index.html',
      filename: 'index.html'
    })

  ]
};

複製程式碼

然後src下的html也要修改

<html>
  <head>
    <link rel="stylesheet" href="<%=htmlWebpackPlugin.files.chunks.main.css %>">
  </head>
  <body>
    <div>Hello, world!</div>
    <script src="<%= htmlWebpackPlugin.files.chunks.main.entry %>"></script>
  </body>
</html>
複製程式碼

可以發現這裡改變來,這個是使模版使用hash.

好了,現在可以編譯,會發現dist下的css和js名字是帶有hash指的。

快取帶來的問題,如何解決

如果我們改變程式碼,在編譯的時候,發現並沒有生成創新的hash名檔案。

div {
  color: 'red';
  background-color: 'blue';
}
複製程式碼

再次進行編譯npm run dev,發現並沒有生產新的hash檔名稱,但是如果改變js程式碼,再次編譯,會發現是改變了hash名稱

import "./index.css"
console.log('Hello, world!');
複製程式碼

如何解決?

兩種辦法:

  • 方法1 把css提取器中的[chunkhash]換成[hash].但是這個會與webpack4.3中的contenthash有衝突.可以結合webpack-md5-hash一起使用。

npm install webpack-md5-hash --save-dev

// webpack v4
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackMd5Hash = require('webpack-md5-hash'); //新加
module.exports = {
  entry: { main: './src/index.js' },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract(
          {
            fallback: 'style-loader',
            use: ['css-loader']
          })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin(
      {filename: 'style.[hash].css', disable: false, allChunks: true}
    ),//changed
    new HtmlWebpackPlugin({
      inject: false,
      hash: true,
      template: './src/index.html',
      filename: 'index.html'
    }),
    new WebpackMd5Hash() //新加

  ]
};

複製程式碼

然後編譯檢視是否改變。 現在的變化是,改變css檔案,那麼css檔案的hash名字改變。如果改變js檔案,那麼css和js的壓縮hash名稱都將改變。

  • 方法2(推薦)

方法1可能還會存在其他的衝突,所以來嘗試下mini-css-extract-plugin

這個外掛是為了取代extract-plugin, 並帶來相容性的改變

npm install --save-dev mini-css-extract-plugin

嘗試使用

// webpack v4
const path = require('path');
// const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackMd5Hash = require('webpack-md5-hash');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");//新加

module.exports = {
  entry: { main: './src/index.js' },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      // {
      //   test: /\.css$/,
      //   use: ExtractTextPlugin.extract(
      //     {
      //       fallback: 'style-loader',
      //       use: ['css-loader']
      //     })
      // }
      {
        test: /\.(scss|css)$/,
        use:  [  'style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
      } // 新加
    ]
  },
  plugins: [
    // new ExtractTextPlugin(
    //   {filename: 'style.[hash].css', disable: false, allChunks: true}
    // ),
    new MiniCssExtractPlugin({
      filename: 'style.[contenthash].css',
    }),//新加
    new HtmlWebpackPlugin({
      inject: false,
      hash: true,
      template: './src/index.html',
      filename: 'index.html'
    }),
    new WebpackMd5Hash()

  ]
};

複製程式碼

好了,接下來我們嘗試改變檔案會怎麼變化。 改變css,進行編譯發現只有css的檔案改變來。改變js檔案,發現編譯之後只有js的hash名稱改變,不錯,就是這樣。

繼承PostCss

postcss大家應該不會陌生了。

npm install postcss-loader --save-dev
npm i -D autoprefixer
複製程式碼

-D.

建立postcss.config.js

module.exports = {
    plugins: [
      require('autoprefixer')
    ]
}
複製程式碼

然後來配置你的webpack.config.js

// webpack v4
const path = require('path');
// const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackMd5Hash = require('webpack-md5-hash');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: { main: './src/index.js' },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.(scss|css)$/,
        use:  [  'style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader']//changed
      }
    ]
  },
  plugins: [
    // new ExtractTextPlugin(
    //   {filename: 'style.[hash].css', disable: false, allChunks: true}
    // ),
    new MiniCssExtractPlugin({
      filename: 'style.[contenthash].css',
    }),
    new HtmlWebpackPlugin({
      inject: false,
      hash: true,
      template: './src/index.html',
      filename: 'index.html'
    }),
    new WebpackMd5Hash()

  ]
};

複製程式碼

保持dist整潔

這裡使用clean-webpack-plugin外掛.

npm i clean-webpack-plugin --save-dev

// webpack v4
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackMd5Hash = require('webpack-md5-hash');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');//新加

module.exports = {
  entry: { main: './src/index.js' },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.(scss|css)$/,
        use:  [  'style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader']//changed
      }
    ]
  },
  plugins: [
    // new ExtractTextPlugin(
    //   {filename: 'style.[hash].css', disable: false, allChunks: true}
    // ),
    new CleanWebpackPlugin('dist', {} ),//新加
    new MiniCssExtractPlugin({
      filename: 'style.[contenthash].css',
    }),
    new HtmlWebpackPlugin({
      inject: false,
      hash: true,
      template: './src/index.html',
      filename: 'index.html'
    }),
    new WebpackMd5Hash()

  ]
};

複製程式碼

這樣每次編譯的時候,就會清空dist資料夾

最終程式碼

相關文章