webpack(1)之基本配置

簡介

  webpack是一個模塊打包機,它會從指定入口文件開始,遞歸的尋找JavaScript模塊以及其餘一些瀏覽器沒法直接運行的擴展語言(Sass, TypeScript)等,將其打包成合適的格式以供瀏覽器使用。
  它的做用有代碼轉換(利用各類loader將瀏覽器沒法識別的語言轉換成合適的格式),文件優化(好比說打包時壓縮體積),代碼分割,模塊合併,自動刷新(熱更新),代碼校驗,自動發佈。 引用了網上的一張圖來大體看一下webpack的運行機制: css

webpack的一些基本配置

安裝webpack以及默認配置

  首先是安裝webpack,在本地項目文件夾下npm init初始化以後,下載webpack以及webpack-cli:html

npm init -y
npm i webpack webpack-cli -D
複製代碼

  此時在文件夾下創建一個src文件夾,用於放置項目代碼。webpack此時能夠進行0配置打包,在命令行輸入npx webpack能夠打包出一個dist文件夾,下面有一個main.js就是打包後的文件。這個打包後的文件內容,就是使用遞歸的方式解析src中的js模塊,遞歸的方法名爲__webpack_require__,它支持咱們在瀏覽器中使用CommonJs規範。
  默認打包的配置很弱,它只能識別js模塊,在沒有配置的狀況下,webpack就至關於一個js模塊打包機。固然咱們不可能直接就0配置打包一個項目,下面我總結一下webpack中經常使用的一些基本配置。前端

webpack.config.js

  webpack中默認的配置文件名爲webpack.config.js,在根目錄下創建一個名爲webpack.config.js的文件,就能夠在這個文件中寫配置項。它的內容遵循CommonJs規範,webpack提供給咱們修改這個文件名的一些方法:
  (1)打包時輸入命令npx webpack --config webpack.config.my.js
  (2)爲了避免用每次都在命令行輸出一串這麼長的命令,在package.json中配置scripts,"build" : "webpack --config webpack.config.my.js"
  這兩種配置方法均可以修改默認配置文件名。先在webpack.config.my.js寫一段基本的配置:node

//webpack是用node寫的
let path = require('path');
module.exports = {
    mode: 'development', //模式 生產環境production 開發模式development
    entry: './src/index.js', //入口
    output: { //出口
        filename: 'bundle.[hash:8].js', //打包後的文件名,[hash]每次打包生成新的文件
        //__dirname以當前目錄解析成絕對路徑
        path: path.resolve(__dirname, 'dist'), ///path字段只接受絕對路徑,所以須要一個node模塊來輔助配置 path.resolve把相對路徑解析成絕對路徑 
        publicPath: 'http://www.help.com'//公共路徑,打包出的資源文件會帶着這個公共路徑。
    }
} 
複製代碼

Loader

  loader幫助咱們告訴瀏覽器遇到不能識別的模塊應該怎麼處理,前面咱們說過webpack默認配置只是別js模塊,那麼解析圖片、css、less這些模塊就須要引入loader。react

打包圖片(file-loader,url-loader)

  圖片引入有三種方式:jquery

  1. 在js中建立圖片來引入,打包時要用到file-loader來解析圖片地址加hash戳。
const logo = require('./01.jpg') //把圖片引入,返回的結構是一個新的圖片地址
const image = new Image();
console.log(logo); //用到file-loader 默認會在內部生成一張圖片,到build目錄下 把生成的圖片的名字返回回來
image.src = logo;
document.body.appendChild(image)
複製代碼

  配置以下:webpack

  • 使用file-loader:它解析靜態資源,把原文件原封不動的拷貝一份放到打包後的文件夾下,而且把位置返回,上述logo的值就是該資源在打包後文件夾下的路徑。通常可直接展現的文件用file-loader解析,好比圖片,excel圖表之類的。
{
    test: /\.(png|jpe?g|gif)$/,
    use: {
        loader: 'file-loader',
        options: {
            name:[name].[ext],
            outputPath: '/img/' //打包時另外生成一個img文件夾
        }
    }
},
複製代碼
  • 使用url-loader:它是file-loader的增強版,咱們在它的配置項中能夠設置一個limit值。若是文件體積小於該值,則文件被編碼成Base64字符串直接引用,而不打包成一個新文件。這樣能夠減小一次http請求。若是文件大於該值,它的功能就至關於file-loader。這種方式會使打包後的文件體積變大,所以二者性能須要權衡。
{
    test: /\.(png|jpe?g|gif)$/,
    use: {
        loader: 'url-loader',
        options: {
            limit: 200 * 1024,//小於200k使用url-loader,大於200k使用file-loader
            outputPath: '/img/'
        }
    }
},
複製代碼
  1. 在css中添加圖片,打包時css-loader會將url('./01.jpg')解析爲url(require('./01.jpg'))。
body {
    background: red;
    background: url('./01.jpg')
}
複製代碼
  1. 在html中經過圖片的img標籤引入圖片,打包時須要用到html-withimg-loader來解析圖片地址。
<img src="./01.jpg" alt="">
複製代碼

  配置以下:ios

{
    test: /\.html$/,
    use: 'html-withimg-loader'
}
複製代碼
樣式設置(css-loader,style-loader,less-loader)

  打包CSS文件,咱們須要用到兩個loader,一個是style-loader,它負責處理css文件中的import、url()語法。style-loader之內聯<style>的形式將樣式都寫到模版html的<head>頭部中。打包LESS文件一樣的套路,less-loader現將less轉換成css。
  配置以下:nginx

module: {
    rules: [ 
        //loader的用法。字符串只用一個loader 多個loader須要用數組 loader的順序 默認從右向左 從下往上執行
        {
            test: /\.css$/,
            use: [{
                loader: 'style-loader',
                options: {
                    insertAt: 'top'//插在最上面,讓本身寫在模版html<style>標籤中的樣式優先級較高
                }
            }, 'css-loader']
        },
        {
            test: /\.less$/,
            use: [{
                loader: 'style-loader',
                options: {
                    insertAt: 'top'
                }
            }, 'css-loader', 'less-loader']
        }
    ]
}
複製代碼

  以下圖所示,head標籤下面的三個樣式是分別在.css和.less文件中定義的樣式,而<head>標籤上面的一個樣式是在模版html中本身設定的。 es6

CSS3爲兼容自動加瀏覽器前綴(postcss-loader)

  用一個autoprefixer包和一個postcss-loader自動添加瀏覽器前綴,且這個插件是會更新的,之前transform在須要加上webkit前綴,但Chrome支持後postcss-loader就不會再給這個屬性加上前綴了。

npm i postcss-loader autoprefixer -D
//在根目錄建立postcss.config.js文件
module.exports = {
    plugins: [require('autoprefixer')]
}
//在rules css配置中新加入postcss-loader 
module: {
    rules: [ 
        //loader的用法。字符串只用一個loader 多個loader須要用數組 loader的順序 默認從右向左 從下往上執行
        {
            test: /\.css$/,
            use: [{
                loader: 'style-loader',
                options: {
                    insertAt: 'top'//插在最上面,讓本身寫在模版html<style>標籤中的樣式優先級較高
                }
            }, 'css-loader']
        }
    ]
}
複製代碼

Plugin

  Plugin能夠在webpack運行到某個階段的時候,幫助咱們作某些事情,相似於生命週期的概念。在某個時間點,須要某個機制完成一些事情。

生成模版html(HtmlWebpackPlugin插件)

  在咱們打包文件後,該插件會生成一個html模版,而且把打包後的其餘文件在該模版中引用。生成的html模版的內容是咱們能夠本身定義的。

const HtmlWebpackPlugin = require('html-webpack-plugin');  
plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html', //模版文件的位置
        filename: 'index.html', //打包出來html文件的名稱
        minify: {
            removeAttributeQuotes: true, // 去除雙引號
            collapseWhitespace: true, //變成一行
        },
        hash: true //添加一個hash戳
    })
],
複製代碼
抽離CSS(MiniCssExtractPlugin插件)

  咱們打包時,把全部樣式抽離出來生成一個CSS文件,在模版html文件中以link形式引入。

npm i mini-css-extract-plugin -D
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
plugins: [
    new MiniCssExtractPlugin({
        filename: 'main.css'
    })
]
module: {
    rules: [ 
        {
            test: /\.css$/,
            use: [
            MiniCssExtractPlugin.loader, //把style-loader換成MiniCssExtractPlugin.loader
            'css-loader', 
            'postcss-loader'
            ]
        },
        {
            test: /\.less$/,
            use: [
            MiniCssExtractPlugin.loader, 
            'css-loader', 
            'less-loader', 
            'postcss-loader'
            ]
        }
    ]
}
複製代碼
壓縮CSS和JS(OptimizeCSSAssetsPlugin插件、UglifyJsPlugin插件)

  進行到這一步會發現,生產模式下打包出來的main.css也沒有被壓縮,是由於用了MiniCssExtractPlugin這個插件不會壓縮css,須要本身壓縮。使用OptimizeCSSAssetsPlugin插件配置優化項,可是使用這個插件以後,css確實壓縮了,但js又不會壓縮了,所以還要用到UglifyJsPlugin再來壓縮js。

npm i optimize-css-assets-webpack-plugin -D
npm i uglifyjs-webpack-plugin -D  
const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); //壓縮js
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); //壓縮css
optimization: {
    minimizer: [
        new UglifyJsPlugin({
            cache: true, //緩存
            parallel: true, //併發壓縮
            sourceMap: true // set to true if you want JS source maps
        }),
        new OptimizeCSSAssetsPlugin({})
    ]
}
複製代碼
更新打包目錄(CleanWebpackPlugin插件)

  在沒有使用該插件以前,每次打包上一版本的文件會遺留在dist文件夾下,須要咱們手動刪除。這個插件,在每次打包以前,先把以前的dist文件夾刪除,打包生成新的dist目錄。

npm install --save-dev clean-webpack-plugin
const {CleanWebpackPulgin} = require('clean-webpack-plugin')  
plugins: [
    new CleanWebpackPlugin()
]
複製代碼

SourceMap

  源代碼與打包後的代碼的映射關係,幫助咱們定位錯誤在源代碼中的位置。在devtool字段中配置,推薦的配置以下:

  • cheap :較快,只定位行,不定位列。
  • moudle :定位引入的第三方模塊的錯誤。
  • eval :速度最快,包裹模塊代碼。
  • source-map :生成map,內容是源代碼和打包後的代碼的映射。
devtool:"cheap-module-eval-source-map" //開發環境
devtool:"cheap-module-source-map" //線上生產環境
複製代碼

webpack-dev-server

  一個提高開發效率的利器,每次改完代碼都要從新打包一次,刷新瀏覽器很是的麻煩。用webpack-dev-server搭建一個服務器,使得咱們不用真實的打包,而是在內存中打包,放置到devSever服務器上,以便咱們在開發時調試測試整個項目。
  下載webpack-dev-server:

npm i webpack-dev-server -D  
複製代碼

  以後,先在package.json中配置scripts,"dev" : "webpack-dev-server --config webpack.config.my.js"。而後配置一下devServer中的一些配置項:

devServer: { //開發服務器的配置
    port: 8889, //端口號
    progress: true, //進度條
    contentBase: './dist', //指定了服務器資源的根目錄,可是在開發過程不會真實打包
    compress: true, //啓用 gzip 壓縮
    open: true //自動打開瀏覽器
},
複製代碼
mock

  聯調期間,先後端分離,直接獲取數據會跨域,上線後咱們使⽤用nginx轉發,開發期間,webpack-dev-server就能夠搞定這件事。
  咱們先啓動服務器,mock一個接口:

const koa = require('koa')
const app = new koa()  

const Router = require('koa-router')
const router = new Router()  

router.get('/api/info', async (ctx, next) => {
   ctx.body = {
       username: 'zhunny',
       message: 'hello mock'
   }
   ctx.status = 200
})

app.use(router.routes())

app.listen(3000)
複製代碼

  此時在咱們前端項目中請求該接口的數據,會存在跨域問題,咱們在dev-server中配置服務器代理:

axios.get('http://localhost:3000/api/info').then(res=>{
    console.log(res)
})
複製代碼
devServer: { //開發服務器的配置
    port: 8889, //端口號
    progress: true, //進度條
    contentBase: './dist', //指定了服務器資源的根目錄,可是在開發過程不會真實打包
    compress: true, //啓用 gzip 壓縮
    open: true, //自動打開瀏覽器
    proxy: {
       "/api": {
         target: "http://localhost:3000"
        }
    }
},
複製代碼

  以後修改請求的接口:

axios.get('/api/info').then(res=>{
    console.log(res)
})
複製代碼

轉換es6語法及校驗

  webpack自己能夠處理ES6語法,可是有些瀏覽器對es六、es7或者是es8的語法還不能識別。出於兼容性的考慮,咱們會使用Babel來將ES6轉換成ES5語法。

  • babel-loader: 是Babel於webpack通訊的橋樑。
  • @babel/core: Babel工具的核心代碼庫。
  • @babel/preset-env: ES6語法的轉換規則。
npm i babel-loader @babel/core @babel/preset-env -D
{
    test: /\.js$/,
    exclude: /node_modules/, //排除該文件夾下的內容
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                '@babel/preset-env'
            ]
        }
    }
}
複製代碼
@babel/polyfill

  固然只配置上述字段是不夠的,到此步爲止,一些es六、七、8新增的方法和類依然不能被識別。咱們還須要下載@babel/polyfill,它將es六、七、8中的語法特性打包放到瀏覽器中,至關於一個補丁包。
  它的基本使用方法是在入口js文件中引用:import '@babel/polyfill'。可是這種方法是引入整個補丁包,使得webpack打包後的體積變大。咱們能夠對這點進行優化。移除在js文件中引用的@babel/polyfill,配置文件中添加useBuiltIns字段,對使用到的es六、七、8語法特性按需加載。

presets: [
    [
        '@babel/preset-env',
        {
            useBuiltIns: 'usage',//按需加載
            corejs: 2 //指定core的版本
        }
    ]
],
複製代碼
@babel/plugin-transform-runtime

  當咱們開發組件庫、工具庫這些場景時,在js文件中引用@babel/polyfill就不合適了。由於@babel/polyfill以全局變量的方式注入,會形成全局污染。上面用的按需加載usage的方法也不會形成全局污染,可是這個字段還在試驗階段。咱們可使用閉包方式@babel/plugin-transform-runtime來代替。可是這種方式就不會對原型鏈上的某些方法進行轉義,所以開發正常的業務場景就比較適合用polyfill,無所謂全局變量的影響,咱們不須要擔憂某些原型鏈上的方法沒有被轉義。

npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S  
{
    test: /\.js$/,
    exclude: /node_modules/, //排除該文件夾下的內容
    use: {
        loader: 'babel-loader',
        options: {
            plugins: [
                [
                '@babel/plugin-transform-runtime',
                {
                    absoluteRuntime:false,
                    corejs:2,
                    helpers:true,
                    regenerator:true,
                    useESMoudules:false
                }
                ]
            ]
        }
    }
}
複製代碼
.babelrc

  Babel配置可能內容較多,咱們能夠把options內容放到.babelrc中。

//.babelrc
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage", //按需加載 實驗性的功能
        "corejs": 2
      }
    ],
    "@babel/preset-react"
  ]
}
複製代碼
一些提案語法的補丁包

  一些es7的提案如class,則還須要用到@babel/plugin-proposal-class-properties,裝飾器則須要用到@babel/plugin-proposal-decorators:

npm i @babel/plugin-proposal-class-properties -D npm i @babel/plugin-proposal-decorators -D {
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                '@babel/preset-env'
            ],
            plugins: [
                ['@babel/plugin-proposal-decorators', { "legacy": true }],
                ['@babel/plugin-proposal-class-properties', { "loose": true }],
                ['@babel/plugin-transform-runtime'] //generator
            ]
        }
    }
}
複製代碼
校驗

  js語法的校驗用到了eslint以及eslint-loader,他的官網爲https://eslint.org,eslint在使用時須要配置一個.eslint.json的規則文件放在根目錄,具體配置項見官網。

npm i eslint eslint-loader -D
{
    test: /\.js$/,
    use: {
        loader: 'eslint-loader',
        options: {
            enforce: 'pre' //在普通loader以前執行
        }
    },
}
複製代碼

全局變量的引入

  引入全局變量有三種方式,假如要在全局引入jquery庫:

  1. 使用內聯loader expose-loader將jquery暴露到window屬性上。
import $ from 'jquery'
require(expose-loader)
console.log(window.$)
複製代碼
  1. 使用webpack.providePlugin插件將$注入到每一個模塊。
new webpack.providePlugin(
    {$:'jquery'}
)
複製代碼
  1. 引入jquery的cdn。

參考

webpack官方網站
Babel官方網站

相關文章
相關標籤/搜索