webpack3.0學習筆記(一)

—— 踩坑計劃第一步

目錄一覽

webpack3.0學習筆記(一)
webpack3.0學習筆記(二)
webpack3.0學習筆記(三)

項目源碼地址


前情概要

記錄最近 webpack 的學習進展,及使用中的注意事項和踩過的坑,非新手教程歡迎指正錯誤。
PS. 當前 webpack版本 3.6.0


webpack

現今許多類庫都支持 webpack 進行打包管理,vue react 等主流 MVVM 框架為其瘋狂打call。

  • 模塊化,將大型項目進行分割;
  • 支持 TypeScriptCoffeeScriptES6等語言特性開發的程序運行在當下的主流瀏覽器上;
  • scsslessstylusCSS 預處理語言;
  • ...

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
    }
}

以上呈現了一個多頁面基本配置,當然圖片資源還沒有引入,后續會進行添加,暫時先分析一下現有的配置。

入口 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,即可將多個入口文件合并成一個 chunk 進行合并打包。

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

filename 若是單入口可設為 js/bundle.js ,多入口則需配置為 js/[name].js 保留原有文件名,也可加入hash及chunkhash進行命名: js/[name]-[hash].jsjs/[name]-[hash].js ,方便生產線打包上線是對用戶文件進行緩存更新。

以下是可配置參數:

[hash]:模塊標識符(module identifier)的 hash
[chunkhash]:chunk 內容的 hash
[name]:模塊名稱
[id]:模塊標識符(module identifier)
[query]:模塊的 query,例如文件名 ? 后面的字符串

ps.單獨說一下 entryoutput 這兩個設置中的 path 屬性,官方推薦設置為絕對路徑,相對路徑的寫法容易引發問題,__dirname 這個參數是 nodejs 里自帶的,表示當前運行環境的絕對路徑,利用 path.resolve() 轉化成一個完整的路徑,這里使用 path.resolve 還是 path.join 看個人了。

模塊熱替換 Hot Module Replacement 配置

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

這里需要兩個步驟開啟模塊熱替換功能:

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

contentBase: 告訴服務器從哪里提供內容。只有在你想要提供靜態文件時才需要。devServer.publicPath
將用于確定應該從哪里提供 bundle,并且此選項優先。
historyApiFallback: 當使用 HTML5 History API 時,任意的 404 響應都可能需要被替代為 index.html。還可傳入對象進一步控制,不多贅述。
hot: 是否開啟熱替換,毋庸置疑質疑的 true
inline: 將消息輸出至控制臺還是iframe的選項,開啟熱替換推薦 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"
    },
}

在項目根目錄執行 npm-run-server即可,輸出如下圖并無紅字報錯即成功了。

成功運行

此時對項目內任何文件進行修改均會執行熱替換,實時展示到頁面中。

瀏覽器執行熱替換

ps. 不推薦在 GitBash 中使用 live-server webpack熱替換 等,結束后node進程不會自動關閉,一直占用端口還特別卡,得手動到進程管理器里關閉 換macOS啊


loader

webpack 可以使用 loader 來預處理文件。這允許你打包除 JavaScript 之外的任何靜態資源。

babel-loader

Babel 是一個 JavaScript 編譯器,能將 ES2015ES2016ES2017 的語法轉義為現代瀏覽器可執行的代碼,所以盡情使用最新的語法特性書寫你的代碼吧!

首先需要安裝babel相關依賴,這里推薦使用 yarn 進行包依賴管理:

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

或使用NPM進行安裝:

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

babel 的配置寫在 webpack.config.js 中:

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

采用 loader 的普遍配置,寫在 rules 里。

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

babel 的配置可能會很多,通常都是分離出一個單文件進行配置存儲,不寫 cli 相關的內容了。
根目錄下新建 .babelrc文件,內容如下:

{
    "presets": ["es2015"]
}

簡單的配置一下兼容 es2015,當然也可以加入 es2016es2017的支持。

ES6
轉義ES5后

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

postcss-loader

PostCSS 是一個使用JavaScript插件來轉換CSS的工具,是個CSS的插件集合,暫時只用到 Autoprefixer解決CSS3樣式兼容性問題。

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

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

或使用NPM進行安裝:

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

postcss 的配置可能會很多,通常都是分離出一個單文件進行配置存儲,不寫 cli 相關的內容了。
根目錄下新建 postcss.config.js文件,內容如下:

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

需要注意的是在相關的 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轉義CSS文件 、style-loader轉義成js代碼。

html,body{
    background-color: pink;
}

.div {
    display: flex;
}

通過將以上三個 loader 將css源碼轉化為js代碼。

css轉義為js

js代碼執行后將css樣式包裹在 style 標簽內插入至頁面中。

html頁面加載效果

less-loader

less-loader 能將通過 less 語法寫的樣式轉化成瀏覽器可讀取的樣式表,sass stylus 都有其對應的 loader

安裝命令,畫一下重點,把 less 也一塊安裝,否則依然無法解析:

yarn add -D less-loader less

或使用NPM進行安裝:

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 參數方法解釋是:用于配置「css-loader 作用于 @import 的資源之前」有多少個 loader。在模塊系統(即 webpack)支持原始 loader 匹配后,此功能可能在將來會發生變化,這段翻譯很難讓人理解。

重新解釋一下 importLoaders,這里設為 2
表明在某個 less 文件中 @import進來的資源(其它的 less 文件)會被使用 postcssless 這兩個 loader 解析,解析正確。
設為 1
表明在某個 less 文件中 @import 進來的資源(其它的 less 文件)只會被使用 postcss 這一個 loader 解析,便報錯。

@green: green;

body,html{
    background-color: green;
}

.flex {
    display: flex;
}

less 文件經過 loader 轉義后:

less轉義后

plugins

plugins 插件可以以各種方式定制 webpack 構建過程。

clean-webpack-plugin

每次構建時清理文件,避免文件名帶hash時重復生成多個文件。
神圣的 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'])
    }
}

傳遞的參數是需要清理的文件路徑,每次 webpack 運行都會支持清理操作。

html-webpack-plugin

這是一個可以動態生成 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: 動態生成后的文件名,路徑以 outputpath 的地址為參照。
chunks: entry 中多入口項目可以指定本次 html 所需依賴的 js 文件,如果依賴的 chunk 較多可配置 excludeChunks 來進行排除。
title: 綁定在 htmlWebpackPlugin.options 上的參數,可在模板內使用。

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 模板語法,可以使用 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 變為了傳入的值,js 也被動態引入了,依賴的 css 也被打包進 js 內。

html-webpack-inline-source-plugin

這是一個依賴于 html-webpack-plugin 的插件,目的是將 jscss 文件內聯至 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 都內聯。

js css 內聯

注意:本以為這個插件可以單獨將 css<style></style> 的形式打包完寫入頁面。然而并不可以,結合之前的幾個處理 cssloader 可知,只能將其內聯至 <script></script> 中,頁面加載時動態插入至 html 中。

extract-text-webpack-plugin

插件能將 js 中引入的 css 文件分離出來,不再以 <style></style> 的形式插入頁面,單獨以 <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 被引入頁面。

<!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>

注意:這個插件存在問題,chunk 內引入了多個 css 文件,分離時會合并為一個 css 文件。

extract-text-webpack-pluginhtml-webpack-inline-source-plugin 聯合使用可以將 css<style></style> 的形式打包完寫入頁面。

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 設為 .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>

注意: 此方法不會因為 css 的內聯將分離生成的 .css文件清除,依然會保留,只是不會引入頁面內。


總結

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

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


尾巴

第一篇技術博客磕磕絆絆的寫完了,愿自己越來越上進吧!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

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