小菜前端的webpack學習日記 -- 基礎篇

安裝

在使用webpack以前咱們要安裝webpack,安裝命令以下:css

# 建立webpack-demo文件夾
mkdir webpack-demo
# 生成package.json文件,對項目依賴進行管理
yarn init -y
# 安裝webpack相關依賴
yarn add webpack webpack-cli -D
複製代碼

這裏咱們安裝webpack-cli的緣由是由於它可讓咱們能夠在命令行中運行webpack,不然webpack命令將沒法運行html

生成打包文件

接下來咱們創建src目錄來管理咱們的源代碼,並創建main.js文件,寫入第一行代碼:vue

console.log('Hello webpack!')
複製代碼

接下來咱們在項目根目錄創建webpack的配置文件webpack.config.js並進行最簡單的配置:node

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    main: './src/main.js'
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'main.js'
  }
}
複製代碼

以後咱們經過package.json文件中的script命令來爲webpack添加快捷命令:
react

配置好後經過命令行執行:yarn build,會發現根目錄出現了dist目錄,裏邊有咱們的代碼main.jswebpack

接下來咱們在根目錄新建index.html,經過script標籤將打包後的代碼引入並在瀏覽器中打開:
es6

或者能夠經過node命令來運行打包文件:
web

到這裏咱們成功打包好了咱們的第一個文件,感受仍是有些小激動的。chrome

入口和出口

在配置以前,咱們先簡單理解下入口和出口的概念:npm

  • 入口(entry): 整個項目的起始模塊,用來做爲構建其內部依賴圖的開始
  • 出口(output): 告訴webpack打包後的文件所存放的目錄

這裏是一個打包截圖:

瞭解了基本概念以後,咱們來看一下經常使用的相關配置:

entry: {
  main: './src/main.js' // 入口文件對應的源代碼位置,key值爲打包生成後的chunkNames
},
output: {
  path: path.resolve(__dirname, './dist'), // 打包生成文件存放的位置
  // 使用每次構建過程當中,惟一的hash生成
  filename: '[name]_[hash].js', // 每一個打包輸出文件的名稱
  // publicPath: 'https://cdn.example.com/assets/', // 會在引入的資源前加入該路徑,例:將資源託管到cnd
}
複製代碼

在配置文件中咱們使用了[name]這種符號,這是webpack中的佔位符(placeholder),這裏介紹下配置中使用到的,若是小夥伴們感興趣能夠本身查閱官方文檔:

  • [name]: 模塊名稱
  • [hash]: 模塊標識符的hash(每次打包都會生成對應惟一的hash值)

打包模式

webpack中,咱們能夠經過mode選項,來分別爲生成環境打包和開發環境打包使用相應的內置優化:

mode: 'development' 
複製代碼

目前咱們的配置是開發環境: development,當咱們使用production的時候,webpack會將我node中的全局變量改成: process.env.NODE_ENV的值設置爲production,而且也會啓用代碼壓縮等功能,有助於幫咱們減小代碼體積,提高用戶體驗。

而當咱們使用development模式時,webpack會將process.env.NODE_ENV的值設置爲development,並取消代碼壓縮等功能,提高開發體驗

使用插件

webpack中,咱們可使用各類各樣的插件來自定義webpack構建過程,方便咱們的開發。

這裏咱們介紹2個經常使用的plugin

  • HtmlWebpackPlugin: 自動建立一個html文件來幫咱們引入打包文件,這對咱們每次打包都經過hash值來生成不一樣的打包文件的狀況特別有用
  • CleanWebpackPlugin: 在打包以前刪除output.path指定的位置中的文件,保證每次打包都是最新的文件

首先咱們來安裝這倆個插件:

yarn add --dev html-webpack-plugin clean-webpack-plugin
複製代碼

接下來咱們再webpack中使用這2個插件:

// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

plugins: [
  new HtmlWebpackPlugin({
    filename: 'index.html', // 生成html文件的文件名
    template: './index.html' // 使用的html模板
  }),
  new CleanWebpackPlugin()
]
複製代碼

這裏咱們經過爲html-webpack-plugin來指定根目錄下的index.html爲模版,生成打包後的index.html。新生成的index.html文件與模板文件不一樣之處在於會自動引入打包生成的文件(包括js文件、css文件、以及以後配置的dll文件),這樣即便咱們使用了[hash]等佔位符,插件也會自動幫咱們動態引入資源,而不用咱們每次手工配置。

使用HtmlWebpackPlugin指定template的緣由是由於咱們能夠在模板文件中本身寫一些咱們本身的代碼,好比引入一些css或者執行一段js腳本亦或者咱們能夠在模板文件中指定項目的根元素:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <!-- 在模板中指定根元素,方便開發時爲其中插入元素 -->
  <div id="root"></div>
</body>

</html>
複製代碼

經過使用插件咱們目前解決了以下問題:

  1. 不用再手動引入打包文件
  2. 不用在從新打包的時候手動去刪除舊的打包文件

使用loader

下面是對webpack官網中loader介紹的一步分摘抄:

loader用於對模塊的源代碼進行轉換
特性(這裏只列出了部分):

  • loader支持鏈式傳遞,鏈中每一個loader會將轉換應用在已處理過的資源上。一組鏈式的loader將按照相反的順序執行。鏈中的第一個loader將其結果(也就是轉換後的資源)傳遞給下一個loader,依次類推
  • loader能夠經過options對象配置
  • 插件能夠爲loader帶來更多特性

我我的以爲其實loader就是讓webpack能夠識別各類資源,而後將資源加工處理成瀏覽器能夠識別的、兼容性更好的、性能更好的代碼。

接下來咱們學習如何經過loader來讓webpack處理各類資源。

項目中使用css

要想使用css文件,咱們首先須要安裝style-loadercss-loader`:

yarn add style-loader css-loader -D
複製代碼

webpack.config.js進行配置:

module: {
  rules: [
    {
      test: /.css$/,
      use: ['style-loader', 'css-loader']
    }
  ]
}
複製代碼

可是平常的項目中,有不少css屬性須要添加瀏覽器供應商前綴來確保兼容性, 咱們可使用postcss-loader結合autoprefixer來實現:

yarn add postcss-loader autoprefixer -D
複製代碼

咱們須要爲css文件添加postcss-loader,以後在根目錄新建postcss.config.js配置autoprefixer:

// webpack.config.js
rules: [
  {
    test: /\.css$/,
    use: [
      'style-loader',
      'css-loader',
      'postcss-loader'
    ]
  }
]

// postcss.config.js
module.exports = {
  plugins: [
    // autoprefixer: parse CSS and add vendor prefixes to CSS rule using values from can I use
    // 解析CSS並使用Can I use 中的值 將供應商前綴添加到css規則中
    require('autoprefixer')
  ]
};
複製代碼

日常咱們也會用到css預處理器來方便開發,這裏咱們以sass爲例:

yarn add sass-loader node-sass -D
複製代碼

webpack中添加以下配置:

{
  test: /\.scss$/,
  use: [
    'style-loader',
    'css-loader',
    'postcss-loader',
    'sass-loader'
  ]
}
複製代碼

這裏要特別注意loader的順序問題(反向:從下到上,從右到左):

  • 首先經過sass-loaderscss文件中的語法解析爲css語法
  • 以後經過postcss-loader爲對應的css屬性添加瀏覽器供應商前綴
  • 而後使用css-loader根據import語法將全部的css文件整合到一塊兒
  • 最後將css-loader整合的css文件經過style標籤插入到html中,實現樣式的更改

若是順序書寫錯誤,會致使程序沒法正常運行

最終效果以下:

學習了上面的知識之後,咱們再瞭解幾個css-loader的經常使用配置:

{
  test: /\.scss$/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        // 開啓css模塊化
        modules: true,
        // 在css-loader前應用的loader的數量:確保在使用import語法前先通過sass-loader和postcss-loader的處理
        importLoaders: 2
      }
    },
    'postcss-loader',
    'sass-loader'
  ]
}
複製代碼

項目中使用圖片和字體圖標

要想在項目中使用圖片和字體圖標,咱們須要使用到file-loader:

yarn add file-loader -D
複製代碼

須要在webpack.config.js中添加以下配置:

{
  test: /\.(png|svg|jpg|gif)$/,
  use: ['file-loader']
}
複製代碼

配置成功後webpack會將咱們引入的資源轉換爲路徑字符串,webpack會經過字符串幫咱們找到文件的位置。

接下來咱們嘗試一下file-loader的幾個配置項:

{
  test: /\.(png|svg|jpg|jpeg|gif)$/,
  use: [
    {
      loader: 'file-loader',
      options: {
        // placeholders:
        // [ext]: 資源擴展名
        // [name]: 資源的基本名稱
        // [hash]: 內容hash值
        // [path]: 資源相對於context的路徑
        // 默認值: [hash].[ext]
        name: '[name]_[hash:8].[ext]',
        // 打包文件存放到出口目錄下的images文件中
        outputPath: 'images/'
      }
    }
  ]
}
複製代碼

打包文件以下:

這裏也可使用url-loader來處理靜態資源,url-loader的工做方式和file-loader相同,可是它會在文件小於限制大小(單位byte)的時候,返回base64字符串,當大於限制大小時會返回和file-loader同樣的地址字符串。

這在處理一些小文件的時候不用再進行資源請求,能夠提升性能

{
  test: /\.(png|svg|jpg|jpeg|gif)$/,
  use: [
    {
      loader: 'url-loader',
      options: {
        // placeholders:
        // [ext]: 資源擴展名,默認file.extname
        // [name]: 資源的基本名稱,默認file.basename
        // [hash]: 內容hash值,默認md5
        // [path]: 資源相對於context的路徑,默認值file.dirname
        // 默認值: [hash].[ext]
        name: '[name]_[hash:8].[ext]',
        // 打包文件存放到出口目錄下的images文件中
        outputPath: 'images/',
        limit: 8192, // 單位byte,文件小於8kb時返回base64文件,大於這個限制會返回地址
      }
    }
  ]
}
複製代碼

咱們能夠看一下打包區別:

source map配置

當咱們使用webpack進行代碼打包之後,若是代碼出現錯誤,會很難追蹤到錯誤和警告在源代碼中的原始位置。爲了更容易地追蹤錯誤和警告,JavaScript提供了SourceMap功能,能夠將編譯後的代碼映射回原始源代碼。

webpack中配置以下:

// 此選項控制是否生成,以及如何生成source map
devtool: 'none',
複製代碼

可選項以下:

這裏簡單介紹下各配置項中單詞的一些含義,方便理解:

  • cheap: 不會生成列映射,只會映射行數
  • eval: 每一個模塊都使用eval執行,而且都有//@sourceURL
  • module:會處理來自loadersource map
  • inline: source map轉換爲DataUrl後添加到bundle

平常的開發中咱們會進行以下配置:

  • 開發環境: cheap-module-eval-source-map
  • 生產環境: cheap-module-source-map(生產環境使用source map,方便定位錯誤)

區分不一樣環境的配置文件

在開發環境和生產環境中,咱們要用到的webpack配置是不一樣的,這裏咱們在根目錄下分別新建webpack.dev.jswebpack.prod.js來分別存放開發環境和生產環境所須要的配置,經過原有的webpack.config.js來存放公共的配置文件。

接下來咱們使用webpack-merge來進行配置文件的合併工做:

yarn add webpack-merge -D
複製代碼

爲了方便統一管理,咱們根目錄下新建config文件夾,而後將配置文件放到裏邊,如今的配置文件是這樣的:

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const absPath = (dir) => path.resolve(__dirname, dir)
module.exports = {
  entry: {
    main: absPath('../src/main.js') // 入口文件對應的源代碼位置,key值爲打包生成後的chunkNames
  },
  output: {
    path: absPath('../dist'), // 打包生成文件存放的位置
    // 使用每次構建過程當中,惟一的hash生成
    filename: '[name]_[hash].js', // 每一個打包輸出文件的名稱
    // publicPath: 'https://cdn.example.com/assets/', // 會在引入的資源前加入該路徑,例:將資源託管到cnd
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
        ]
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              // 開啓css模塊化
              modules: true,
              // 在css-loader前應用的loader的數量:確保在使用import語法前先通過sass-loader和postcss-loader的處理
              importLoaders: 2
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              // placeholders:
              // [ext]: 資源擴展名,默認file.extname
              // [name]: 資源的基本名稱,默認file.basename
              // [hash]: 內容hash值,默認md5
              // [path]: 資源相對於context的路徑,默認值file.dirname
              // 默認值: [hash].[ext]
              name: '[name]_[hash:8].[ext]',
              // 打包文件存放到出口目錄下的images文件中
              outputPath: 'images/',
              limit: 8192, // 單位byte,文件小於8kb時返回base64文件,大於這個限制會返回地址
            }
          }
        ]
      }
    ]
  },
  plugins: [
    // 自動引入打包後的文件到html中:
    // 對於每次打包都會從新經過hash值來生成文件名的狀況特別適用
    // 也能夠經過template來生成一個咱們本身定義的html模板,而後幫咱們把打包後生成的文件引入
    new HtmlWebpackPlugin({
      filename: 'index.html', // 生成html文件的文件名
      template: absPath('../index.html') // 使用的html模板
    })
  ]
};
// webpack.dev.js
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config')
module.exports = merge(baseConfig, {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map'
})
// webpack.prod.js
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = merge(baseConfig, {
  mode: 'production',
  devtool: 'cheap-module-source-map',
  plugins: [
    new CleanWebpackPlugin()
  ]
})
複製代碼

而後咱們在package.json中分別爲開發環境和生產環境添加打包快捷命令:

"scripts": {
  "build": "webpack --config ./config/webpack.prod.js",
  "start": "webpack --config ./config/webpack.dev.js"
},
複製代碼

如今目錄結構是這樣的:

接下來咱們就能夠直接使用yarn buildyarn start命令來分別爲開發環境和生產環境進行打包了

webpackDevServer方便開發和調試

如今咱們在每次修改代碼後,都須要再次從新打包文件,並在瀏覽器中打開生成的html文件。並且因爲是本地打開html文件,諸如請求代理、局域網內預覽等功能便沒法使用。

webpack-dev-server爲咱們提供了一個簡單的web服務器,可以實時從新加載(live reloading)修改後的代碼,而且也提供瞭如proxy等功能來支持咱們跨域請求接口,極大的方便了開發。

yarn add webpack-dev-server -D
複製代碼

以後咱們在開發環境的webpack配置中添加以下配置:

devServer: {
  // 告訴服務器從哪裏提供內容。只有在你想要提供靜態文件時才須要
  contentBase: absPath('../dist'),
  // 是否在啓動服務時自動打開瀏覽器
  open: true,
  // 端口在沒有設置的時候默認爲8080
  port: 3000
}
複製代碼

固然,這樣更改以後咱們要經過webpack-dev-server來執行開發時的配置文件:

// "start": "webpack --config ./config/webpack.dev.js"
"start": "webpack-dev-server --config ./config/webpack.dev.js"
複製代碼

執行yarn start:

實現代碼的熱模塊更新

當咱們在開發過程當中修改一些代碼的時候,咱們可能只是想讓咱們當前修改的內容生效,而不是形成整個頁面的刷新,致使咱們每次修改都要從新以前的步驟來驗證咱們的代碼。

webpack中咱們經過以下配置來實現模塊熱替換的功能(hot module replacement),固然這個功能只在開發環境開啓:

// webpack.dev.js
devServer: {
  // 告訴服務器從哪裏提供內容。只有在你想要提供靜態文件時才須要
  contentBase: absPath('../dist'),
  // 是否在啓動服務時自動打開瀏覽器
  open: true,
  // 端口在沒有設置的時候默認爲8080
  port: 3000,
  // 啓用webpack的模塊熱替換特性
  hot: true
},
plugins: [
  new webpack.NamedModulesPlugin(),
  new webpack.HotModuleReplacementPlugin()
]
複製代碼

和以前配置的區別有2個:

  • devServer添加配置項: hot: true
  • 新增2個webpack的插件: webpack.NameModulesPluginwebpack.HotModuleReplacementPlugin

而後這樣只能實現css文件的熱更新,對於js的熱更新,咱們還須要添加一下代碼:

// accept接收2個參數: 1. dependencies: 一個字符串或字符串數組 2. callback: 模塊更新後觸發的函數
if (module.hot) {
  module.hot.accept('./utils/printSomething', () => {
    console.log('update module');
  })
}
複製代碼

頁面中的效果以下:

你們可能會發現咱們在寫css和相似於vuereact框架代碼的時候,並無本身手寫module.hot.accept方法,這是框架和cssloader已經幫咱們進行了自動處理,咱們只須要關注代碼的書寫便可。

使用babel轉義es6語法

在平常工做中,咱們會使用不少es6裏的新語法,這些語法在咱們目前使用的chrome新版瀏覽其中通常均可以很好的支持,可是在一些國產瀏覽器或者低版本瀏覽器中可能會出現兼容性問題。

這裏咱們須要經過babel-loader來進行語法的轉義:

yarn add babel-loader @babel/core @babel/preset-env -D
複製代碼

webpack中進行配置:

// webpack.config.js 中添加一個loader配置項
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      cacheDirectory: true
    }
  }
},
複製代碼

項目根目錄創建.babelrc文件,並加入以下代碼:

{
  "presets": [
    "@babel/preset-env"
  ]
}
複製代碼

文檔地址

這樣配置以後,項目中的Promisemap等語法依舊不會進行轉換,這裏咱們使用@babel/polyfill來轉換這些語法:

yarn add @babel/polyfill -D
複製代碼

.babelrc進行以下配置:

{
  "presets": [
    [
      "@babel/env",
      {
        // 爲低版本引入babel/polyfill
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1",
        },
        // 使用useBuiltIns要指定corejs
        "corejs": "2",
        // 只對項目中用到的`esnext`語法進行polyfill處理
        "useBuiltIns": "usage",
      }
    ]
  ]
}
複製代碼

這樣配置以後就能夠經過babel來轉換esnext的一些語法,而且能夠兼容低版本和國產瀏覽器。因爲使用到了useBuiltIns:usage,只會對咱們使用到的新語法進行轉換,減小了polyfill的體積

配置react開發環境

在學習完以上內容之後,咱們須要搭建一個支持react框架語法的webpack配置。

首先咱們安裝相應的依賴:

yarn add react react-dom
yarn add @babel/preset-react @babel/plugin-proposal-class-properties -D
複製代碼

.babelrc中配置以下:

{
  "presets": [
    [
      "@babel/env",
      {
        // 爲低版本引入babel/polyfill
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1",
        },
        // 使用useBuiltIns要指定corejs
        "corejs": "2",
        // 只對項目中用到的`esnext`語法進行polyfill處理
        "useBuiltIns": "usage",
      }
    ],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties"
  ]
}
複製代碼

以後咱們在入口文件main.js中寫入以下代碼:

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
  state = {
    number: 10
  }
  render() {
    return (
      <div> hello Webapck React <h2>{this.state.number}</h2> </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root')) 複製代碼

執行yarn start命令:

到這裏爲止,咱們已經能夠搭建一個簡單的react開發環境,以後咱們能夠繼續學習一些webpack的高級概念。

相關文章
相關標籤/搜索