Webpack 之常用配置(一)

作者:余韻之

webpack目前是前端常用的工程化工具了。它可以幫助我們自動化構建打包各類的資源,極大的提高了我們打包代碼的效率。在webpack看來,所有的資源文件都是模塊(module),只是處理的方式不同。

一、初探webpack

1、安裝webpack

建議不要全局安裝webpack,因為不同的項目webpack的版本號是不一樣的。這樣多個項目來回切換是很不方便的。

npm install wepack webpack-cli -g

在項目內(nèi)安裝webpack

npm install wepack webpack-cli -D

注意:webpack-cli的作用是我們可以在命令行里直接使用webpack

檢查版本

npx webpack -v

查看 webpack 所有可以安裝的版本號

npm info webpack

2、最簡單的webpack.config.js的配置

const path = require('path');

module.exports = {
    entry: {
        main:'./src/index.js'
    },
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
    }
}

這段配置是告訴我們:

  • 我們需要打包入口文件是./src/index.js,
  • 最后輸出的打包文件是在當前目錄下dist/main.js
  • 假如存在bundle/index.html,就可以通過script引入main.js文件了。當然這個也可以通過webpack自動化構建。

3、兩種打包方式

1) npx

npx webpack

npx 表示會在當前目錄里尋找依賴變量 webpack

2) script

在package.json里配置

"scripts": {
    "bundle": "webpack"
},

于是可以運行

npm run bundle

運行 npx webpack / npm run bundle 會先檢查是否有配置文件webpack.config.js,如果沒有就走默認配置,如果有就走配置文件

4、打包的參數(shù)詳解

  • Hash:表示這一次打包的唯一標識值
  • Version:表示這一次打包的使用版本
  • Time:表示當前打包整體耗時
  • Asset:表示此次打包出現(xiàn)了bundle.js
  • Size:表示該文件的大小
  • Chunk Names 表示的是 entry里入口的key,默認為main,也可以任意改為 xxx,yyy
  • Entrypoint main = bundle.js 表示入口文件 以及依次打包的文件[0][1][2]……

二、使用Loader打包資源

1、什么是loader?

webpack不能識別非js的模塊,需要對于不同的模塊提供不同的打包方案,于是要求助于loader。如:css-loader、sass-loader、file-loader、vue-loader、postcss-loader等等

2、打包圖片

file-loader實現(xiàn)原理思路: 當發(fā)現(xiàn)代碼引入圖片模塊,首先把圖片移動到dist目錄下并改了名字,得到了相對于dist的地址,作為返回值給到我們引入的變量之中

module.exports = {
    entry: {
        main: './src/index.js'
    },
    module: {
        rules: [{
          test: /\.(jpg|png)$/,
          use: {
            loader: 'file-loader',
            options: {
              name: '[name]_[hash].[ext]', // [name] [hash] [ext] 均為占位符
              outputPath: 'images/', // 打包出的結果放在images/目錄下
              limit: 10240
            }
          }
        }]
    },
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
    }
}

3、打包CSS 或 SASS

  • css-loader:會幫我們分析幾個css的關系(互相引入)合并成一個css
  • style-loader:當css-loader合并成了一個css,style-loader會把內(nèi)容掛載到head部分
  • sass-loader:解析sass成css
  • postcss-loader:自動添加-webkit等前綴,兼容不同瀏覽器樣式

使用了postcss-loader,需要在根目錄創(chuàng)建postcss.config.js

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

webpack.config.js中module配置

module: {
    rules: [
      {
        test: /\.(jpg|png|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
            limit: 10240
          }
        }
      },
      {
        test: /\.(eot|ttf|svg)$/,
        use: {
          loader: 'file-loader'
        }
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      }]
  },

Loader解析是有先后順序的:從下到上,從右到左

4、CSS modules

{
  test: /\.scss$/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options:{
        importLoaders: 2,// 表示scss文件導入了scss文件依然走postcss-loader sass-loader
        modules:true // 開啟css模塊化
      }
    },
    'sass-loader',
    'postcss-loader'
  ]
}

modules開啟為true后,就可以使用模塊化CSS互不干擾。否則引入的CSS或者SASS的代碼會造成全局污染

使用方式如下

import style from  './index.scss’;

var img = new Image();
img.src = avatar;
img.classList.add(style.avatar);

5、打包字體

在webpack.config.js配置

{
    test: /\.(eot|ttf|svg)$/,
    use: {
        loader: 'file-loader'
    } 
},

三、使用Plugins打包便捷

1、什么是Plugins?

plugins是在某個時刻(剛打包、打包結束,打包中間)做一件事

2、html-webpack-plugin

在打包結束時,在dist自動生成html,并且把打包的main.js自動引入html的script的標簽

3、clean-webpack-plugin

在每次打包生成的dist文件前,先刪除里面的內(nèi)容

4、安裝及配置兩個plugin

npm install html-webpack-plugin clean-webpack-plugin -D

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin’);

plugins: [new HtmlWebpackPlugin({
  template: 'src/index.html' // 參考模板為 src/index.html
}), new CleanWebpackPlugin(['dist'])],

還有其他的plugins,比如熱更新 HotModuleReplacementPlugin ……
后面慢慢加上和介紹

四、Entry 和 Output 配置

前面的都是單入口,單出口的配置。

1、多入口,多出口

entry: {
     main: './src/index.js’,
     sub: ‘./src/index.js'
},
plugins: [new HtmlWebpackPlugin({
     template: 'src/index.html'
}), new CleanWebpackPlugin(['dist'])
],
output: {
     filename: '[name].js',
     path: path.resolve(__dirname, 'dist')
}

最后的結果是html 里會引入兩個js文件

<script scr=“./main.js"></script>
<script scr=“./sub.js"></script>

2、把打包的JS上傳到CDN

entry: {
    main: './src/index.js’,
    sub: ‘./src/index.js'
},
plugins: [new HtmlWebpackPlugin({
     template: 'src/index.html'
}), new CleanWebpackPlugin(['dist'])
],
output: {
     publicPath: 'http://cdn.com.cn', // 最后打包出來是http://cdn.com.cn、main.js
     filename: '[name].js',
     path: path.resolve(__dirname, 'dist')
}

html引入的script 會自動加上publicPath路徑

<script scr=“http://cdn.com.cn/main.js"></script>
<script scr=“http://cdn.com.cn/sub.js"></script>

五、SourceMap

1、什么是SourceMap

先舉個例子:
打開瀏覽器發(fā)現(xiàn)代碼報錯了。。?!,F(xiàn)在知道dist目錄下main.js 文件 96行出錯。
用了sourceMap之后(它是一個映射關系),于是知道dist目錄下main.js文件96行實際上對應的是src目錄下index.js文件中的第1行

使用了sourceMap 打包速度是會變慢的。同時dist里多了一個xx.js.map文件,原理一個vlq集合

2、配置SourceMap

在webpack.config.js 里 devtool

devtool: 'cheap-module-eval-source-map',

常用的幾個source-map的前綴

  • Inline:inline-source-map 是把xx.js.map內(nèi)容直接打包到xx.js里,用data url形式的方式放在末尾,會告訴你第幾行第幾列除了問題,很耗費性能
  • Cheap:添加cheap-inline-source-map 可以精確到每一行不精確到每一列出錯 ,可以降低打包時間,提高性能
  • Module:如果要管第三方模塊代碼的映射可以加上module,可以加上cheap-module-inline-source-map
  • Eval:eval是打包最快的方式 ,通過eval是執(zhí)行效率最快、性能最好的方式,但是如果代碼復雜的話,提示的內(nèi)容可能不夠全面,用eval的方式執(zhí)行JS代碼

3、最佳實踐

開發(fā)環(huán)境:提示全,打包速度快

mode: 'development',
devtool: 'cheap-module-eval-source-map',

生產(chǎn)環(huán)境:提示效果會更好

mode: 'production',
devtool: 'cheap-module-source-map',

六、webpackDevServer

1、如何解決每次手動打包,手動啟動瀏覽器刷新頁面更新代碼?

  • webpack —watch

在package.json 里 script 添加

"watch": "webpack --watch"

優(yōu)點:監(jiān)聽到源代碼改變,會自動打包
缺點:需要手動刷新頁面

  • webpack-dev-server

在package.json 里 script 添加

"start": "webpack-dev-server",

在devDependencies安裝

"webpack-dev-server": "^3.1.10"

優(yōu)點:監(jiān)聽到源代碼改變,會自動打包,自動啟動服務器,自動更新瀏覽器

注意使用了webpack-dev-server打包后就不會目錄里有dist了,而是放在電腦某個內(nèi)存里,可以提高效率。

2、如何啟動時自動打開瀏覽器

在webpack.config.js配置

devServer: {
     contentBase: './dist',
     open: true, // 默認啟動開啟瀏覽器
     port: 8080, // 端口設置為8080
},

七、熱更新 HMR(HOT MODULE REPLACE)

如果只是純粹使用了webpack-dev-server那么當改變了代碼,瀏覽器會自動刷新初始化數(shù)據(jù)。有時候比較麻煩。

有沒有什么辦法可以做到:

當修改了css只改變css的樣式,js新增的數(shù)據(jù)不變。

此時把背景色改成紅色

或者當修改了某一個模塊的數(shù)據(jù),另一個模塊的數(shù)據(jù)不改變

配置如下

webpack.config.js

const webpack = require('webpack’);


devServer: {
     contentBase: './dist',
     open: true,
     port: 8080,
     hot: true, // 開啟熱更新的功能
     hotOnly: true // 即使HMR不生效,也不更新瀏覽器    
},

plugins: [
     new HtmlWebpackPlugin({
      template: 'src/index.html'
     }), 
     new CleanWebpackPlugin(['dist']),
     new webpack.HotModuleReplacementPlugin() // 配置熱更新plugins 
],

同時必須添加模塊更新的代碼。例如有兩個模塊,當改變了number模塊,就更新number,counter模塊不改變

index.js

import counter from './counter';
import number from './number';


counter();
number();


if(module.hot) {
 module.hot.accept('./number', () => {
  document.body.removeChild(document.getElementById('number'));
  number();
 })
}

css可以不寫 module.hot判斷 是因為css-loader已經(jīng)實現(xiàn)了。如果一些特別的模塊,就需要自己寫一個module.hot來判斷

八、Babel處理ES6語法

1、安裝依賴

npm install —save-dev babel-loader @babel/core @babel/preset-env
  • babel-loader : 提供識別模塊的打包工具
  • @babel/core : 識別js代碼轉化為AST抽象語法樹,編譯轉化成新的語法
  • @babel/preset-env : 把ES6代碼轉化為ES5語法,提供了翻譯規(guī)則

2、打包方式

二選一

1)安裝@babel/profill

npm install -save-dev @babel/profill

缺點:這個會出現(xiàn)全局污染

配置規(guī)則

rules:[{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: 'babel-loader',
  options:{
    presets: [['@babel/preset-env', {
      targets: {
        chrome: "67",
      },
      useBuiltIns: 'usage' // 表示做polyfill 根據(jù)業(yè)務代碼來加對應的代碼,可以減少打包的體積
    }]]
  }
}],

注意:useBuiltIns: 'usage' 表示按需引入

2)安裝 @babel/plugin-transform-runtime

npm install -save-dev @babel/plugin-transform-runtime @babel/runtime @babel/runtime-corejs2

優(yōu)點:可是使用閉包的方式不影響其他環(huán)境變量

配置 創(chuàng)建.babelrc

{
 "plugins": [["@babel/plugin-transform-runtime", {
  "corejs": 2, // 使用了2 就需要安裝 @babel/runtime-corejs2
    "helpers": true,
    "regenerator": true,
    "useESModules": false
 }]]
}

webpack.config.js

{
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader'
}

九、打包React代碼

1、安裝依賴

@babel/preset-react : 可以解析JSX

npm install --save-dev @babel/preset-react

webpack.config.js

{
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
}

創(chuàng)建.babelrc

{
  "presets": [
    [
      "@babel/preset-env", {
        "targets": {
          "chrome": "67"
        },
        "useBuiltIns": "usage"
      }
    ],
    "@babel/preset-react"
  ]
}

注意:先解析react的語法,然后再把ES6語法解析為ES5。presets是自下而上,自右邊而左來解析的

我們是曉黑板前端,歡迎關注我們的知乎Segmentfault、CSDN、簡書、開源中國、博客園賬號。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容