1. 程式人生 > >人人都是webpack使用者,小白從入門到精通秒變大神

人人都是webpack使用者,小白從入門到精通秒變大神

什麼是WebPack,為什麼要使用它?

因為別人都在用啊,我不會用怎麼跟他們一起裝逼?

別人說的這些是什麼?我根本不懂:

類似gulp把自己定位為stream building tools一樣,webpack把自己定位為module building system。
在webpack看來,所以的檔案都是模組,只是處理的方式依賴不同的工具而已。

webpack同時也把node的IO和module system發揮的淋漓盡致。 webpack在配合babel(ES6/7)和tsc(typescript)等類似DSL語言預編譯工具的時候,駕輕就熟,為開發者帶來了幾乎完美的體驗。

weback在web構建工具的激烈競爭中逐漸脫引而出。 無論是編譯速度、報錯提示、可擴充套件性等都給前端開發者耳目一新的感覺。

畢竟小白,無所謂別人說什麼高大上的從gulp到grunt再到webpack的威水史,我們的目的是學會執行一個webpack demo然後跟別人吹吹:“其實webpack這東西也就那樣,百度都有!”

好!廢話不多說我們go

準備:

node環境
npm(cnpm)環境

開始:

1.新建一個資料夾,命名為webpack
2.開啟終端(黑視窗),cd到這個資料夾
3.使用npm init命令自動(所謂的自動就是一直按enter鍵,畢竟是練習所以全部預設)建立package.json檔案
這裡寫圖片描述
4.執行npm install webpack -g 安裝全域性的webpack 和它的腳手架 npm install webpack-cli --g


5.將webpack這個元件寫到我們專案的package.json裡面,–save就是寫進package.json中的意思-dev是放到package.json裡面的devDependencies物件中,這裡的東西都是打包的時候都不會放進線上程式碼中去的,dependencies中的才是線上真正要的東西

// 安裝Webpack
npm install --save-dev webpack

媽的,這程式碼執行的時候竟然報錯了?咩咩咩?
這裡寫圖片描述
大概就是說,你package中的專案名叫做webpack了,你不能再安裝這個模組了;講究。。。
好吧,我的錯,我們將package中的”name”: “webpack”改個更加low的名字”name”: “webpack1”

再執行

npm install --save-dev webpack

於是我們的檔案變成了這個鬼樣!

這裡寫圖片描述
然後在webpack資料夾下建立一個webpack.config.js檔案
並在裡面建立兩個資料夾,app資料夾和public資料夾,app資料夾用來存放原始資料和我們將寫的JavaScript模組,public資料夾用來存放之後供瀏覽器讀取的檔案(包括使用webpack打包生成的js檔案以及一個index.html檔案)。接下來我們再建立檔案:
index.html –放在public資料夾中;
entry.js– 放在app資料夾中;
這裡寫圖片描述

//webpack.config.js
module.exports = {
    entry : './app/entry.js',//入口檔案
    output : {//輸出檔案
        filename : 'index.js',//輸出檔名
        path :  __dirname+'/public'//輸出檔案路徑
    },
}
//entry.js
console.log('你好,老鐵!');
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>ss</title>
</head>
<body>
老王,你好!
<script src="index.js"></script>
</body>
</html>

然後直接執行webpack命令
發現打包成功了,public資料夾裡多了一個index.js,用瀏覽器開啟index.html也發現console.log出了:你好,老鐵!

這裡寫圖片描述
我們再試試將css進行打包吧!先按照對應的解析器,主要用到了css-loader style-loader這裡兩個解析器,後面兩個是給圖片檔案用的。

npm install css-loader style-loader url-loader file-loader --save-dev 

真是專案中還有許多要用的解析器,你們自個去豔遇她們吧!
然後再app裡面多加一個style.css檔案

//style.css
body {
    font-size: 40px;
    background: peachpuff;
    color: green;
}

在entry.js裡面引入

//entry.js
require('./style.css');//引入css檔案
console.log('你好,老鐵!');

增加css檔案的解析規則

//webpack.config.js
module.exports = {
    entry : './app/entry.js',//入口檔案
    output : {//輸出檔案
        filename : 'index.js',//輸出檔名
        path :  __dirname+'/public/'//輸出檔案路徑
    },
    module : {
        rules: [
            {test: /.css$/, use: ['style-loader', 'css-loader']},/*解析css, 並把css新增到html的style標籤裡*/
        ]
    },
}

再次執行webpack命令,執行index.html你會發現老王綠了!
我們又來看看圖片這類的檔案怎麼打包的。放一張圖片到app資料夾中,命名為1.png

//entry.js
require('./style.css');//引入css檔案
console.log('你好,老鐵!');

var img = new Image();
img.src = require('./1.png');//當成模組引入圖片
document.body.appendChild(img);

主要用了url-loader file-loader 這兩個解析器

//webpack.config.js
module.exports = {
    entry : './app/entry.js',//入口檔案
    output : {//輸出檔案
        filename : 'index.js',//輸出檔名
        publicPath: __dirname + '/public/',//新增靜態資源, 否則會出現路徑錯誤
        path :  __dirname+'/public/'//輸出檔案路徑
    },
    module : {
        rules: [
            {test: /.css$/, use: ['style-loader', 'css-loader']},/*解析css, 並把css新增到html的style標籤裡*/
            {test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,use: [{loader: 'url-loader',options: {limit: 10000}}]}
        ]
    },
}

再次執行webpack命令,執行index.html新增的圖片出現了!

加深一點:

1.如果是多檔案

entry : {index1: './app/entry.js', index2: './app/entry2.js'},
output : {
       filename : '[name].js',//這樣就可以生成兩個js檔案, 名字分別為index1.js, 和index2.js
     }

2.別人用npm run build很酷,我們也可以,在package.json裡面修改一下即可。

"scripts": {
    "build": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

3.新手如何在瀏覽器中除錯打包後的專案呢?

加入devtool: ‘source-map’ 什麼是devtool

//webpack.config.js
module.exports = {
    entry : './app/entry.js',//入口檔案
    output : {//輸出檔案
        filename : 'index.js',//輸出檔名
        publicPath: __dirname + '/public/',//新增靜態資源, 否則會出現路徑錯誤
        path :  __dirname+'/public/'//輸出檔案路徑
    },
    module : {
        rules: [
            {test: /.css$/, use: ['style-loader', 'css-loader']},/*解析css, 並把css新增到html的style標籤裡*/
            {test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,use: [{loader: 'url-loader',options: {limit: 10000}}]}
        ]
    },
    devtool: 'source-map'
}

在瀏覽器的sources下可以看到webpack資料夾下有許多專案的檔案,方便除錯;

這裡寫圖片描述

小白教程基本結束,是不是不難,哇咔咔!

webpack整體架構(以webpack.config主要部分進行劃分)

1.entry: 定義整個編譯過程的起點
2.output: 定義整個編譯過程的終點
3.module: 定義模組module的處理方式
4.plugin 對編譯完成後的內容進行二度加工
5.resolve.alias 定義模組的別名

webpack的核心module

無論你是jsx,tsx,html,css,scss,less,png檔案,webpack一視同仁為module。並且每個檔案[module]都會經過相同的編譯工序 loader==> plugin。

關於以上這點,以如下一個簡單的webpack.config檔案為例。看下webpack會做什麼

module.exports =  {
        watch: true,
        entry: './index.js',
        devtool: 'source-map',
        output: {
            path: path.resolve(process.cwd(),'dist/'),
            filename: '[name].js'
        },
        resolve: {
            alias:{ jquery: 'src/lib/jquery.js', }
        },
        plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                _: 'underscore',
                React: 'react'
            }),
            new WebpackNotifierPlugin()
        ],
        module: {
            loaders: [{
                test: /\.js[x]?$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },  {
                test: /\.less$/,
                loaders:['style-loader', 'css-loader','less-loader']
            }, {
                test: /\.(png|jpg|gif|woff|woff2|ttf|eot|svg|swf)$/,
                loader: "file-loader?name=[name]_[sha512:hash:base64:7].[ext]"
            }, {
                test: /\.html/,
                loader: "html-loader?" + JSON.stringify({minimize: false })
            } ]
        }
    };

webpack是如何處理如上webpack.config檔案解析

1.確定webpack編譯上下文context

預設情況下就是node啟動的工作目錄process.cwd(),當然也可以在配置中手動指定context。

webpack在確定webpack.config中entry的路徑依賴時,會根據這個context確定每個要編譯的檔案(assets)的絕對路徑。

2.entry和output 確定webpack的編譯起點和終點

顧名思義,entry定義webpack編譯起點,入口模組。 對應的結果為compolation.assets

output定義webpack編譯的終點,匯出目錄

3.module.loaders 和 module.test 確定模組預編譯處理方式

以babel為例,當webpack發現模組名稱匹配test中的正則/js[x]?的時候。

它會將當前模組作為引數傳入babel函式處理,babel([當前模組資源的引用])。

函式執行的結果將會快取在webpack的compilation物件上,並分配唯一的id 。

以上的這一步,非常非常關鍵。唯一的id值決定了webpack在最後的編譯結果中,是否會存在重複程式碼。
而快取在compilation物件上,則決定了webpack可以在plugin階段直接拿取模組資源進行二度加工。

4.plugin階段貫穿於webpack的整個編譯流程,一般用來做一些優化操作。

比如webpack.ProvidePlugin,它會在對編譯結果再加工的操作過程中進行自定義的變數注入,當模組中碰到比如這個變數的時候,webpack將從快取的module中取出underscore模組載入進引用的檔案(compilation.assets)。
比如WebpackNotifierPlugin,它會在編譯結果ready的時通知開發者,output已經就緒。

5.resolve.alias的作用就是對module模組提供別名,並沒有什麼特殊的。

【副作用】 webpack編譯過程中的電腦卡慢?
在weback經歷以上流程的時候,檢視你的記憶體,你會發現,記憶體飆升!!!

這一般都是loader階段,對DSL進行AST抽象語法樹分析的時候,由於大量應用遞迴,記憶體溢位的情
況也是非常常見。

output目錄不是一個漸進的編譯目錄,只有在最後compilation結果ready的時候,才會寫入,造成開發者等待的時候,output目錄始終為空。

【webpack編譯物件compilation】 webpack將編譯結果匯出到output是怎麼做到的
如上,webpack在plugin結束前,將會在記憶體中生成一個compilation物件檔案模組tree。

這個階段是webpack的done階段 : webpack寫入output目錄的分割點。

這棵樹的枝葉節點就是所有的module[由import或者require為標誌,並配備唯一moduleId],

這棵樹的主枝幹就是所有的assets,也就是我們最後需要寫入到output.path資料夾裡的檔案內容。

最後,這個compilation物件也是所有webpackPlugin的處理的時候的arguments。

總結

對於開發者來說,整體而言webpack的編譯過程細節比較多,但是大體的框架還是比較直觀。

裡面涉及到的類似DSL,AST的概念及模組快取等等,在構建工具中還是比較常見的,配合watch模式,debug模式,對於開發者來說實在是一大利器。

一切檔案皆為模組也和react的一切dom都可以變為JS一樣,對前端世界帶來了新的開發理念。

但是,對於普通前端開發者來說,這些配置實在太繁瑣和困難了,特別是在處理大專案上,必須要有能架構整個前端工程的人把這個東西搭好,然後再讓小前端們敲component;不然就像小公司那樣傻瓜式vue-cli-人人都是架構師。