Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

用webpack 5從頭建置本機開發環境 #1

Open
lienweb opened this issue Jun 29, 2022 · 0 comments
Open

用webpack 5從頭建置本機開發環境 #1

lienweb opened this issue Jun 29, 2022 · 0 comments
Labels
env setup 環境設定、部署流程 webpack

Comments

@lienweb
Copy link
Owner

lienweb commented Jun 29, 2022

目錄

  • 建置重點
  • 問題點紀錄
    • 如何編譯檔案
    • 如何管理舊檔
    • dev tool:自動compile/部署/hot reload
  • what's still missing in this note
  • debug webpack可參考的資源
  • 本篇筆記reference

導讀

剛開始學webpack也不是很理解為什麼entry point會是js,以及什麼叫做所有的檔案都是透過bundle的方式打包,因此推薦先看完胡立大寫的此篇文後會對webpack工具有個基礎概念
webpack 新手教學之淺談模組化與 snowpack


起手式

💡 建置要點

  1. 程式碼可以被正確轉譯成靜態檔,如scss->css
  2. 瀏覽器可向local server取得編譯後的前端靜態檔
  3. 支援HMR(hot module replacement)

這篇筆記主要是紀錄按照webpack官網Guide建置時,可能會遇到的問題

問題1: 如何編譯檔案-loaders

SCSS->CSS: sass-loader、css loader、MiniCssExtractPlugin

  • sass-loader
    要處理SCSS檔案就需要SASS預處理器,才能成功編譯SCSS檔。而webpack透過loaders預處理檔案,因此需要安裝sass-loader
    至於SASS的目錄架構,採用的是7-1 pattern

  • css-loader
    css-loader則是為了讓CSS檔案在JS中可用module方式引入,而需要安裝的loader

  • MiniCssExtractPlugin
    MiniCssExtractPlugin的概念我認為是相對於style-loader的。

    比較早期的開發可能會看到的狀況可能會看到用<style>撰寫CSS,而style-loader就是將遇到的CSS注入<style>中,見w3c internal css說明

    但現代網頁開發比較常使用的方式是將CSS寫在獨立的檔案進行管理,因此MiniCssExtractPlugin會將CSS抽取出並生成獨立的CSS檔案

圖片

節錄自webpack官網對於asset management的教學

webpack 5後使用asset module管理圖片或是icon類的靜態資源,因此只要在設定檔中制定檔名相對應的處理方式,就可以透過webpack 5自動在build時把圖片加到dist/中。

這麼做的好處是當在CSS或是HTML中引用圖片(如 src或是background-image屬性),圖片經過asset module處理後會生成一個url。在src/中的scss檔案中引用圖片時,圖片原本的相對路徑經過css-loader處理後,會自動將原本的圖片路徑轉換為dist中圖片的路徑,就不需要再去手動處理最終靜態檔圖片路徑的問題。同理,html-loader也會自動轉換圖片路徑

  • 在webpack config檔中設定圖片處理規則
//webpack.config.js
 const path = require('path');

 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
   module: {
     rules: [
+      {
+        test: /\.(png|svg|jpg|jpeg|gif)$/i,  //regex
+        type: 'asset/resource',
+      },
     ],
   },
 };

html-loader

剛開始的時候我也覺得奇怪,為何需要html-loader解析HTML檔,但實際上這個loader可以幫助我們自動解決html中圖檔的路徑。除此之外,也可以自動引入JS/CSS檔,省去手動修改的時間

Loaders執行順序: 由下到上/由右至左

  • 撰寫規則時,要注意loader的執行順序
    image

問題2: 檔案管理

當專案功能越多,原本手動將bundle.js及preprocess後的main.css引入index.html的作法可能並不實用,因為此時為了管理方便可能會使用以下機制:

  1. 對靜態檔名使用hash命名(以c的概念來說就是compile後的執行檔): 因為瀏覽器的cache機制,production中若使用相同檔名,即便code有改動,瀏覽器卻不一定會抓取最新的檔案。
    因此通常會在每次build後,利用hash生成unique檔名,以確保瀏覽器所取得的靜態檔是最新的

  2. 可能有多個entry point

因此會衍伸下列議題 :

舊檔案的清理

每次build後都會產生unique檔名,但如果不斷的下npm run build指令,dist/裡的檔案會不斷增加。因此會希望在產生最終靜態檔前,先清除舊檔案再build。webpack 4以前是透過安裝clean-webpack-plugin實現,不過webpack 5後可透過在設定檔中設置clean屬性的方式達成

//webpack.config.js
 const path = require('path');

 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
+    clean: true,
   },
 };

若以C語言的概念去理解的話,就是每次下make指令生成執行檔前,先刪除舊執行檔再compile,避免目錄size無限增長。

HTML管理:

  • HtmlWebpackPlugin
    若今天有多個entry point,或變更entry point的檔名,都必須手動更改HTML引入的檔名。透過安裝此plugin可以自動將dist/中的CSS及JS引入HTML,就不需要每次手動修改。
    要注意的是HtmlWebpackPlugin預設在dist目錄產生index.html,因此若將安裝plugin前已寫好的index.html放在dist/,則該檔會被覆寫
    ps: 透過設置template參數方式,可將原本的index.html檔放在src/下,就會自動引入CSS及JS並在dist產生新的index.html檔

問題3: 部署到local server並自動編譯、重整頁面

以系統概念來理解的話就是當偵測到code有異動,會自動重新compile並重啟服務。

若以網頁開發來說,希望可以達成自動偵測檔案異動->preprocesser->瀏覽器刷新頁面,這麼做的好處是不需要每次下npm run build後,還要F5刷新頁面才能看到修改後的結果。

webpack提供三種選項,不過最常見的是使用webpack-dev-server這個工具,以下紀錄設置時debug過程:

  • 參數設定: 預設8080 port,可變更的參數可參考這個guide
  • Cannot get / 錯誤發生原因:
    按照webpack教學文使用cli設定指令,自動在瀏覽器開啟http:localhost:8080,會出現cannot get /錯誤
    image
    • 指令設定如下
//package.json
 {
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
   "private": true,
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
+    "start": "webpack serve --open",
     "build": "webpack"
   }
 }

透過觀察啟動時dev server顯示的log,找到發生原因為未載入已設置好參數的webpack.config.js檔
截圖 2022-06-30 上午5 12 41
截圖 2022-06-30 上午5 12 56

由此可見當所有設定被dev server視為未設定時,預設開發模式mode=production,且最終靜態檔目錄為~/public/

因此啟動webpack-dev-server的指令應修正如下

//package.json
{
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
   "private": true,
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
+    "start": "webpack serve -c webpack.config.js --open", //add config
     "build": "webpack"
   }
 }

即可修正此問題

//webpack.config.js
 const path = require('path');

 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
+    clean: true,
   },
    devServer: {
        static: './dist',
+        hot: true,
   },
 };

到目前為止,設定完後應可達成

這邊要注意的是build與start的差異,雖然透過webpack-dev-server可即時看到code修改後的結果,但這不代表最終靜態檔也是修改後的狀態,仍然要下npm run build,才能保證dist/的靜態檔與http:localhost:8080看到的結果一致

最終建置

因此可成功建立本機開發環境,最終的webpack.config.js與package.json檔案應如下

//webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  devtool: 'inline-source-map',
  devServer: {
    static: {
      directory: path.join(__dirname, './dist')
    },
    hot: true,
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    //export images to specific dir
    assetModuleFilename: 'images/[hash][ext][query]',
    //clean up dist/ before each build
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          //extract css into separate files
          MiniCssExtractPlugin.loader,
          // Translates CSS into CommonJS
          "css-loader",
          // Compiles Scss to CSS
          "sass-loader",
        ],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'images/[hash][ext][query]'
        },
      },
      {
        //resolve img src problem
        test: /\.html$/i,
        loader: "html-loader",
        options: {},
      },
    ],
  },
  plugins: [new MiniCssExtractPlugin(),
  //hot reload html
  new HtmlWebpackPlugin(
    {
      template: 'src/template.html',
    }
  ),
  ],
  // add this if have > 1 entry point 
  // optimization: {
  //   runtimeChunk: 'single',
  // },
};
//package.json
{
  "main": "webpack.config.js",
  "scripts": {
    "start": "webpack serve  -c webpack.config.dev.js --open",
    "build": "webpack  --config webpack.config.dev.js --mode development",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.7.1",
    "html-loader": "^3.1.2",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.6.1",
    "node-sass": "^7.0.1",
    "sass-loader": "^13.0.0",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.9.2"
  },
}

what's still missing?

  • 效能優化相關設定: code split / minifying / file compress
  • production相關設定
  • debug相關: 設定source map
  • 前端框架

debug時建議先看的資源

本篇筆記參考資料

@lienweb lienweb added webpack env setup 環境設定、部署流程 labels Jun 29, 2022
@lienweb lienweb changed the title [新手筆記]用webpack 5從頭建置本機開發環境 用webpack 5從頭建置本機開發環境 Jun 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
env setup 環境設定、部署流程 webpack
Projects
None yet
Development

No branches or pull requests

1 participant