webpack3.0學(xué)習(xí)筆記(一)

—— 踩坑計(jì)劃第一步

目錄一覽

webpack3.0學(xué)習(xí)筆記(一)
webpack3.0學(xué)習(xí)筆記(二)
webpack3.0學(xué)習(xí)筆記(三)

項(xiàng)目源碼地址


前情概要

記錄最近 webpack 的學(xué)習(xí)進(jìn)展,及使用中的注意事項(xiàng)和踩過(guò)的坑,非新手教程歡迎指正錯(cuò)誤。
PS. 當(dāng)前 webpack版本 3.6.0。


webpack

現(xiàn)今許多類(lèi)庫(kù)都支持 webpack 進(jìn)行打包管理,vue react 等主流 MVVM 框架為其瘋狂打call。

  • 模塊化,將大型項(xiàng)目進(jìn)行分割;
  • 支持 TypeScriptCoffeeScript、ES6等語(yǔ)言特性開(kāi)發(fā)的程序運(yùn)行在當(dāng)下的主流瀏覽器上;
  • scss、less、stylusCSS 預(yù)處理語(yǔ)言;
  • ...

webpck配置概覽

const path = require('path');
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
    // devtool: 'source-map',
    entry: {
        main: path.resolve(__dirname,'src/script/main.js'),
        aa: path.resolve(__dirname,'src/script/aa.js'),
        test: path.resolve(__dirname,'src/script/test.js')
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'js/[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: 'babel-loader',
                exclude: /node_modules/
            },
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: [
                        'css-loader',
                        'postcss-loader',
                        'less-loader'
                    ]
                }),
                exclude: [/aa.less$/]
            },
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: [
                        'css-loader',
                        'postcss-loader',
                        'less-loader'
                    ]
                }),
                include: [/aa.less$/]
            },
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1
                        }
                    },
                    'postcss-loader',
                ]
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(['dist']),
        new ExtractTextPlugin('css/[name].css'),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src/index.html'),
            filename: 'index.html',
            chunks:['main']
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src/aa.html'),
            filename: 'aa.html',
            chunks: ['aa'],
            inlineSource: '.css$'
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src/test.html'),
            filename: 'test.html',
            chunks: ['test'],
            inlineSource: '.(js|css)$'
        }),
        new HtmlWebpackInlineSourcePlugin(),
        new webpack.HotModuleReplacementPlugin()
    ],
    devServer: {
        contentBase: './dist',
        historyApiFallback: true,
        hot: true,
        inline: true
    }
}

以上呈現(xiàn)了一個(gè)多頁(yè)面基本配置,當(dāng)然圖片資源還沒(méi)有引入,后續(xù)會(huì)進(jìn)行添加,暫時(shí)先分析一下現(xiàn)有的配置。

入口 entry 及輸出 output 配置

  1. 單入口可配置為:
entry: 'path.resolve(__dirname,'src/script/main.js')'

entry: 'path.resolve([__dirname,'src/script/main.js' , __dirname,'src/script/aa.js'])'
  1. 而多入口配置為:
entry: {
    main: path.resolve(__dirname,'src/script/main.js'),
    aa: path.resolve(__dirname,'src/script/aa.js'),
    test: path.resolve(__dirname,'src/script/test.js')
},

其中 main aa test 都表示為chunk name,即可將多個(gè)入口文件合并成一個(gè) chunk 進(jìn)行合并打包。

  1. 多入口對(duì)應(yīng)的輸出 output 為:
output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/[name].js'
},

filename 若是單入口可設(shè)為 js/bundle.js ,多入口則需配置為 js/[name].js 保留原有文件名,也可加入hash及chunkhash進(jìn)行命名: js/[name]-[hash].js 、js/[name]-[hash].js ,方便生產(chǎn)線打包上線是對(duì)用戶(hù)文件進(jìn)行緩存更新。

以下是可配置參數(shù):

[hash]:模塊標(biāo)識(shí)符(module identifier)的 hash
[chunkhash]:chunk 內(nèi)容的 hash
[name]:模塊名稱(chēng)
[id]:模塊標(biāo)識(shí)符(module identifier)
[query]:模塊的 query,例如文件名 ? 后面的字符串

ps.單獨(dú)說(shuō)一下 entryoutput 這兩個(gè)設(shè)置中的 path 屬性,官方推薦設(shè)置為絕對(duì)路徑,相對(duì)路徑的寫(xiě)法容易引發(fā)問(wèn)題,__dirname 這個(gè)參數(shù)是 nodejs 里自帶的,表示當(dāng)前運(yùn)行環(huán)境的絕對(duì)路徑,利用 path.resolve() 轉(zhuǎn)化成一個(gè)完整的路徑,這里使用 path.resolve 還是 path.join 看個(gè)人了。

模塊熱替換 Hot Module Replacement 配置

用webpack不就為了它 npm上有個(gè) live-server 模塊也能實(shí)現(xiàn)文件的熱更新,挺適合非 webpack 項(xiàng)目使用的(不是打廣告)。

這里需要兩個(gè)步驟開(kāi)啟模塊熱替換功能:

  1. 第一步
    增加 webpack.config.js 配置,配置本地服務(wù)器:
module.exports = {
    ...
    devServer: {
        contentBase: './dist',
        historyApiFallback: true,
        hot: true,
        inline: true
    }
}

contentBase: 告訴服務(wù)器從哪里提供內(nèi)容。只有在你想要提供靜態(tài)文件時(shí)才需要。devServer.publicPath
將用于確定應(yīng)該從哪里提供 bundle,并且此選項(xiàng)優(yōu)先。
historyApiFallback: 當(dāng)使用 HTML5 History API 時(shí),任意的 404 響應(yīng)都可能需要被替代為 index.html。還可傳入對(duì)象進(jìn)一步控制,不多贅述。
hot: 是否開(kāi)啟熱替換,毋庸置疑質(zhì)疑的 true。
inline: 將消息輸出至控制臺(tái)還是iframe的選項(xiàng),開(kāi)啟熱替換推薦 true。

  1. 第二步
    webpack.config.js中配置熱更新插件:
    const webpack = require('webpack')

    module.exports = {
        ...
        plugins: {
            ...
            new webpack.HotModuleReplacementPlugin()
        }
    }

再到 package.json 中配置:

{
    "scripts": {
        "server": "webpack-dev-server --config webpack.config.js --open"
    },
}

在項(xiàng)目根目錄執(zhí)行 npm-run-server即可,輸出如下圖并無(wú)紅字報(bào)錯(cuò)即成功了。

成功運(yùn)行

此時(shí)對(duì)項(xiàng)目?jī)?nèi)任何文件進(jìn)行修改均會(huì)執(zhí)行熱替換,實(shí)時(shí)展示到頁(yè)面中。

瀏覽器執(zhí)行熱替換

ps. 不推薦在 GitBash 中使用 live-server webpack熱替換 等,結(jié)束后node進(jìn)程不會(huì)自動(dòng)關(guān)閉,一直占用端口還特別卡,得手動(dòng)到進(jìn)程管理器里關(guān)閉 換macOS啊。


loader

webpack 可以使用 loader 來(lái)預(yù)處理文件。這允許你打包除 JavaScript 之外的任何靜態(tài)資源。

babel-loader

Babel 是一個(gè) JavaScript 編譯器,能將 ES2015ES2016、ES2017 的語(yǔ)法轉(zhuǎn)義為現(xiàn)代瀏覽器可執(zhí)行的代碼,所以盡情使用最新的語(yǔ)法特性書(shū)寫(xiě)你的代碼吧!

首先需要安裝babel相關(guān)依賴(lài),這里推薦使用 yarn 進(jìn)行包依賴(lài)管理:

yarn add -D babel-loader babel-core babel-preset-env

或使用NPM進(jìn)行安裝:

npm i -D babel-loader babel-core babel-preset-env

babel 的配置寫(xiě)在 webpack.config.js 中:

    module.exports = {
        ...
        module: {
            rules: [
            {
                test: /\.js$/,
                use: 'babel-loader',
                exclude: /node_modules/
            }
        }
    }

采用 loader 的普遍配置,寫(xiě)在 rules 里。

test: 書(shū)寫(xiě)匹配文件的正則表達(dá)式。
use: 對(duì)匹配到的文件使用的loader。
exclude: 需要排除不處理的文件,首要排除的就是 /node_modules/ 文件夾,加快打包效率。
include: 指定匹配的文件或目錄。

babel 的配置可能會(huì)很多,通常都是分離出一個(gè)單文件進(jìn)行配置存儲(chǔ),不寫(xiě) cli 相關(guān)的內(nèi)容了。
根目錄下新建 .babelrc文件,內(nèi)容如下:

{
    "presets": ["es2015"]
}

簡(jiǎn)單的配置一下兼容 es2015,當(dāng)然也可以加入 es2016、es2017的支持。

ES6
轉(zhuǎn)義ES5后

這樣 ES6 語(yǔ)法就被轉(zhuǎn)為 ES5 了, ES6import 模塊引入已經(jīng)被 webpack 原生支持了,運(yùn)行在瀏覽器端的可以安心使用 ES6 module 模塊方法。nodejs 尚未正式支持該語(yǔ)法,僅在8.5版中測(cè)試使用。

postcss-loader

PostCSS 是一個(gè)使用JavaScript插件來(lái)轉(zhuǎn)換CSS的工具,是個(gè)CSS的插件集合,暫時(shí)只用到 Autoprefixer解決CSS3樣式兼容性問(wèn)題。

安裝命令,順手把要用到的 css-loader style-loader 一起裝了:

yarn add -D postcss-loader css-loader style-loader

或使用NPM進(jìn)行安裝:

npm i -D postcss-loader css-loader style-loader

postcss 的配置可能會(huì)很多,通常都是分離出一個(gè)單文件進(jìn)行配置存儲(chǔ),不寫(xiě) cli 相關(guān)的內(nèi)容了。
根目錄下新建 postcss.config.js文件,內(nèi)容如下:

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

需要注意的是在相關(guān)的 js 文件中引用 css。

import '../style/test.css'

webpack.config.js 配置中添加:

module.exports={
    ...
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            }
        ]
    }
}

loader 的加載順序是從右至左,依次 postcss-loader 添加瀏覽器前綴、css-loader轉(zhuǎn)義CSS文件 、style-loader轉(zhuǎn)義成js代碼。

html,body{
    background-color: pink;
}

.div {
    display: flex;
}

通過(guò)將以上三個(gè) loader 將css源碼轉(zhuǎn)化為js代碼。

css轉(zhuǎn)義為js

js代碼執(zhí)行后將css樣式包裹在 style 標(biāo)簽內(nèi)插入至頁(yè)面中。

html頁(yè)面加載效果

less-loader

less-loader 能將通過(guò) less 語(yǔ)法寫(xiě)的樣式轉(zhuǎn)化成瀏覽器可讀取的樣式表,sass stylus 都有其對(duì)應(yīng)的 loader。

安裝命令,畫(huà)一下重點(diǎn),把 less 也一塊安裝,否則依然無(wú)法解析:

yarn add -D less-loader less

或使用NPM進(jìn)行安裝:

npm i -D less-loader less

webpack.config.js 配置中添加:

module.exports={
    ...
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1
                        }    
                    }, 
                    'postcss-loader',
                    'less-loader'
                ]
            }
        ]
    }
}

css-loaderimportLoaders 參數(shù)方法解釋是:用于配置「css-loader 作用于 @import 的資源之前」有多少個(gè) loader。在模塊系統(tǒng)(即 webpack)支持原始 loader 匹配后,此功能可能在將來(lái)會(huì)發(fā)生變化,這段翻譯很難讓人理解。

重新解釋一下 importLoaders,這里設(shè)為 2
表明在某個(gè) less 文件中 @import進(jìn)來(lái)的資源(其它的 less 文件)會(huì)被使用 postcssless 這兩個(gè) loader 解析,解析正確。
設(shè)為 1
表明在某個(gè) less 文件中 @import 進(jìn)來(lái)的資源(其它的 less 文件)只會(huì)被使用 postcss 這一個(gè) loader 解析,便報(bào)錯(cuò)。

@green: green;

body,html{
    background-color: green;
}

.flex {
    display: flex;
}

less 文件經(jīng)過(guò) loader 轉(zhuǎn)義后:

less轉(zhuǎn)義后

plugins

plugins 插件可以以各種方式定制 webpack 構(gòu)建過(guò)程。

clean-webpack-plugin

每次構(gòu)建時(shí)清理文件,避免文件名帶hash時(shí)重復(fù)生成多個(gè)文件。
神圣的 rm -r XX 連接著我們

yarn add -D clean-webpack-plugin
npm i -D clean-webpack-plugin

webpack.config.js配置中增加:

const CleanWebpackPlugin = require('clean-webpack-plugin')

module.exports = {
    ...
    plugins: {
        ...
        new CleanWebpackPlugin(['dist'])
    }
}

傳遞的參數(shù)是需要清理的文件路徑,每次 webpack 運(yùn)行都會(huì)支持清理操作。

html-webpack-plugin

這是一個(gè)可以動(dòng)態(tài)生成 html 文件的插件,配合各種 loader 可以將處理好的 css js image 引入到 html 中。

yarn add -D html-webpack-plugin
npm i -D html-webpack-plugin

webpack.config.js配置中增加:

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    ...
    plugins: {
        ...
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src/index.html'),
            filename: 'index.html',
            chunks:['main'],
            title: 'this is main.html'
        }),
    }
}

template: 所需讀取的 index 模板文件。
filename: 動(dòng)態(tài)生成后的文件名,路徑以 outputpath 的地址為參照。
chunks: entry 中多入口項(xiàng)目可以指定本次 html 所需依賴(lài)的 js 文件,如果依賴(lài)的 chunk 較多可配置 excludeChunks 來(lái)進(jìn)行排除。
title: 綁定在 htmlWebpackPlugin.options 上的參數(shù),可在模板內(nèi)使用。

index.html 模板文件

<!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><%= htmlWebpackPlugin.options.title %></title>   
</head>
<body>    
</body>
</html>

<%= htmlWebpackPlugin.options.title %>ejs 模板語(yǔ)法,可以使用 htmlWebpackPlugin 上的各種屬性定制自己的 html 模板。

<!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>this is main.html</title>
</head>
<body>
<script type="text/javascript" src="js/main.js"></script></body>
</html>

title 變?yōu)榱藗魅氲闹担?code>js 也被動(dòng)態(tài)引入了,依賴(lài)的 css 也被打包進(jìn) js 內(nèi)。

html-webpack-inline-source-plugin

這是一個(gè)依賴(lài)于 html-webpack-plugin 的插件,目的是將 jscss 文件內(nèi)聯(lián)至 html,

yarn add -D html-webpack-inline-source-plugin
npm i -D html-webpack-inline-source-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('html-webpack-inline-source-plugin')

module.exports = {
    ...
    plugins: {
        ...
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src/index.html'),
            filename: 'index.html',
            chunks:['main'],
            inlineSource: '.(js|css)$',
            title: 'this is main.html'
        }),
        new HtmlWebpackInlineSourcePlugin()
    }
}

HtmlWebpackPlugin 配置中插入 inlineSource: '.(js|css)$' 配置,將 jscss 都內(nèi)聯(lián)。

js css 內(nèi)聯(lián)

注意:本以為這個(gè)插件可以單獨(dú)將 css<style></style> 的形式打包完寫(xiě)入頁(yè)面。然而并不可以,結(jié)合之前的幾個(gè)處理 cssloader 可知,只能將其內(nèi)聯(lián)至 <script></script> 中,頁(yè)面加載時(shí)動(dòng)態(tài)插入至 html 中。

extract-text-webpack-plugin

插件能將 js 中引入的 css 文件分離出來(lái),不再以 <style></style> 的形式插入頁(yè)面,單獨(dú)以 <link> 的方式引入。

yarn add -D extract-text-webpack-plugin
npm i -D extract-text-webpack-plugin

webpack.config.js配置中增加:

const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: [
                        'css-loader',
                        'postcss-loader',
                        'less-loader'
                    ]
                })
            } 
        ]
    },
    plugins: {
        ...
        new ExtractTextPlugin('css/[name].css'),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src/index.html'),
            filename: 'index.html',
            chunks:['main'],
            title: 'this is main.html'
        })        
    }
}

重新打包后 main.css 被引入頁(yè)面。

<!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>this is main.html</title>
<link href="css/main.css" rel="stylesheet"></head>
<body>
<script type="text/javascript" src="js/main.js"></script></body>
</html>

注意:這個(gè)插件存在問(wèn)題,chunk 內(nèi)引入了多個(gè) css 文件,分離時(shí)會(huì)合并為一個(gè) css 文件。

extract-text-webpack-pluginhtml-webpack-inline-source-plugin 聯(lián)合使用可以將 css<style></style> 的形式打包完寫(xiě)入頁(yè)面。

const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: [
                        'css-loader',
                        'postcss-loader',
                        'less-loader'
                    ]
                })
            } 
        ]
    },
    plugins: {
        ...
        new ExtractTextPlugin('css/[name].css'),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'src/index.html'),
            filename: 'aa.html',
            chunks: ['aa'],
            inlineSource: '.css$',
            title: 'this is aa.html'
        })    
    }
}

inlineSource 設(shè)為 .css$。

<!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>this is aa.html</title> 
<style type="text/css">body,
html {
  background-color: gray;
}
</style></head>
<body>
<script type="text/javascript" src="js/aa.js"></script></body>
</html>

注意: 此方法不會(huì)因?yàn)?css 的內(nèi)聯(lián)將分離生成的 .css文件清除,依然會(huì)保留,只是不會(huì)引入頁(yè)面內(nèi)。


總結(jié)

webpack 對(duì)于 css 文件的處理不太友好,分離 css 的做法也有些違背 webpack 模塊化打包的初衷,有人傾向于使用 gulp 進(jìn)行 圖片 css 的資源文件的出來(lái),而使 webpack 專(zhuān)注于 js 模塊化,日后會(huì)著重研究這塊。

為了寫(xiě)博客翻了下有稍顯雜亂的 webpack 文檔,發(fā)現(xiàn) extra-loaderhtml-loader 之前被我選擇性忽略了,可以嘗試換新的方式處理 css 文件打包問(wèn)題。


尾巴

第一篇技術(shù)博客磕磕絆絆的寫(xiě)完了,愿自己越來(lái)越上進(jìn)吧!

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

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

  • 無(wú)意中看到zhangwnag大佬分享的webpack教程感覺(jué)受益匪淺,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,383評(píng)論 7 35
  • GitChat技術(shù)雜談 前言 本文較長(zhǎng),為了節(jié)省你的閱讀時(shí)間,在文前列寫(xiě)作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,916評(píng)論 7 110
  • 最近在學(xué)習(xí) Webpack,網(wǎng)上大多數(shù)入門(mén)教程都是基于 Webpack 1.x 版本的,我學(xué)習(xí) Webpack 的...
    My_Oh_My閱讀 8,345評(píng)論 40 247
  • Webpack學(xué)習(xí)總結(jié) 此文只是自己學(xué)習(xí)webpack的一些總結(jié),方便自己查閱,閱讀不變,非常抱歉??! 下載安裝:...
    Lxs_597閱讀 1,098評(píng)論 0 0
  • 痛苦而又快樂(lè)的軍訓(xùn)已逝去了五天,雖然痛苦但依然掛念不忘,每次走到曾經(jīng)走過(guò)的地方,心中總會(huì)觸動(dòng)不已,總是情不自禁的懷...
    厚厚的三明治閱讀 241評(píng)論 0 0

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