2020了,你需要懂的webpack知識(shí)[基礎(chǔ)篇]

隨著前端工程化的越來(lái)越瘋狂,不會(huì)點(diǎn)webpack的前端真的吃不香,睡不著。

所謂webpack就是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器。

開胃菜

吃飯前先來(lái)一道開胃菜,看完這遍文章你會(huì)學(xué)到什么?

  • 了解什么是webpack
  • 知道怎么去打包靜態(tài)資源
  • 知道幾個(gè)(entrt,output,module,plugins等)核心的配置作用
  • 不用vue-cli,從0到1跑起自己的vue項(xiàng)目
  • 不用create-react-app,從0到1跑起自己的react項(xiàng)目
  • 搭建一個(gè)既能跑vue項(xiàng)目也能跑react項(xiàng)目的配置

以上這些都是你將會(huì)學(xué)到的。

溫馨提示:建議跟著一起敲,這會(huì)加深你對(duì)每一個(gè)知識(shí)的理解和固定; 還有內(nèi)容有點(diǎn)多,需要你靜下心來(lái)慢慢的品嘗,你會(huì)收獲到你意想不到的知識(shí)。

入門webpack

1.1初始化項(xiàng)目

先我們先在自己舒服的地方創(chuàng)建一個(gè)文件夾,文件夾里面初始化項(xiàng)目。

    mkdir myWebpackPro
    npm init -y

然后我們安裝webpack webpack-cli 到項(xiàng)目

npm i webpack webpack-cli -D

你也可以用cnpm 或者yarn 來(lái)安裝

cnpm i webpack webpack-cli -D / yarn add webpack webpack-cli -D

接著我們?cè)?strong>myWebpack文件夾創(chuàng)建一個(gè)src文件夾,在里面創(chuàng)建一個(gè)index.js文件(這個(gè)將來(lái)將是我們的入口文件)。

mkdir src
cd src
touch index.js

最后我們?cè)谂csrc同級(jí)創(chuàng)建一個(gè)webpack.config.js(配置webpack的文件)文件

touch webpack.config.js

最終的目錄結(jié)構(gòu)是這樣的

image

到此為止,我們初始化項(xiàng)目就完成了。

1.2測(cè)試webpack能否正常打包

首先,我們?cè)?strong>index.js里面隨便console.log()一句

image

然后到package.jsonscript下配置一下build命令,先刪除默認(rèn)的test命令,再添加build命令:

"build":"webpack ./src/index.js"
image

最后我們?cè)俳K端執(zhí)行npm run build命令

npm run build

如果執(zhí)行成功的話在根目錄會(huì)多一個(gè)dist文件夾里面會(huì)有一個(gè)main.js

image

讓我們打開main.js看看有沒有把我們剛剛console.log()的那句話打包進(jìn)去。

image

現(xiàn)在讓我們真正進(jìn)入webpack配置。

1.3更改webpack的入口和默認(rèn)打包的文件名

首先,我們打開webpack.config.js配置一下entryoutput

image

我們?cè)陧攲右M(jìn)path模塊,這是node內(nèi)置的一個(gè)模塊,無(wú)需單獨(dú)安裝,是用來(lái)出來(lái)文件路徑的一個(gè)模塊。我們的打包入口(entry)依舊是剛剛創(chuàng)建的那個(gè)index.js文件,出口(output)也就是打包成功后,輸出的位置(path)和文件名(filename),我們輸出的位置依舊選擇了dist文件下,但是輸出的文件名,我們?cè)摓榱?strong>index.js(之后我們會(huì)將文件名改為動(dòng)態(tài)的)

配置完這些后,我們到package.json改一下打包路徑,將之前的./src/index.js改為./webpack.config.js

image

我們npm run build之后依舊能打包成功。

image

輸出的文件名也變成了index.js,現(xiàn)在你會(huì)看到dist目錄下還有個(gè)main.js,是不是感到很奇怪?不用奇怪,這是我們上一次打包余留下來(lái)的,等下我們將來(lái)說(shuō)如何解決上一次打包余留下來(lái)的文件。

1.4新建一個(gè)index.html模版

首先,我們?cè)?strong>src下新建一個(gè)跑public文件夾,然后在里面新建一個(gè)index.html模版(用于我們查看打包的js文件,之后再用于我們vue或者react項(xiàng)目的唯一html渲染模版)

cd src
mkdir public 
touch index.html

新建完之后我們將剛才打包好的js引進(jìn)去看看,瀏覽器有沒有打印出我們console.log()。

image

但是,當(dāng)我們將outputfilename改成動(dòng)態(tài)之后,每次打包輸出的文件名都不一樣,那么我們每一次打包都要重新引用一次,這樣特別麻煩。

1.5html-webpack-plugin

為了解決上面說(shuō)的那個(gè)問題,我們需要安裝一下html-webpack-plugin插件。

npm run html-webpack-plugin -D

安裝完之后我們?cè)?strong>webpack.config.js配置一下plugins。

image

這里我們配置了html-webpack-plugin,也把輸入文件名改為了動(dòng)態(tài)的了([name].[hash:8].js)。

我們?cè)?strong>npm run build,然后你會(huì)看到再dist下多一個(gè)index.html。接著打開它,發(fā)現(xiàn)控制臺(tái)輸出的跟我們console.log()的值一致就對(duì)了。(記得在打包前,把之前在index.html手機(jī)引進(jìn)的script標(biāo)簽去掉,不如打包會(huì)在控制臺(tái)會(huì)報(bào)一個(gè)找不到文件名的錯(cuò))

接下來(lái),我們來(lái)解決,打包后自動(dòng)清除之前打包的代碼。

1.6 clean-webpack-plugin

現(xiàn)在你打開dist好很多冗余的js文件,每次手動(dòng)清除的話會(huì)非常煩躁。那我們就讓webpack來(lái)幫我們做。

image

首先,安裝clean-webpack-plugin。

npm i clean-webpack-plugin -D

安裝完之后,我們?cè)倥渲靡幌?strong>plugins選項(xiàng)。

image

注意??:CleanWebpackPlugin是具名導(dǎo)出。

最后,我們?cè)?strong>npm run build一下,看看dist目錄下還有沒有多余的js文件。

image

soga,干凈。

那么,現(xiàn)在問題又來(lái),我們每次改動(dòng)一點(diǎn)js代碼都要重新手動(dòng)去打包一次,這樣真炒雞麻煩啊,那有沒有自動(dòng)打包的方法呢?那么答案來(lái)咯,當(dāng)然是有的。下面我們解決這個(gè)問題。

1.7 熱更新

現(xiàn)在我們來(lái)解決自動(dòng)打包的問題。首先,我們安裝一下webpack-dec-server,配合webpack內(nèi)置的HotModuleReplacementPlugin插件來(lái)完成熱更新。

npm i webpack-dev-server -D

HotModuleReplacementPlugin不用手動(dòng)下載,webpack內(nèi)置。

安裝完之后,我們先來(lái)配置一下webpack.config.js文件

image

配置完webpack.config.js,我們的package.json添加一條打包命令

image

配置完后,我們現(xiàn)在用npm run dev命令打包,然后我們回去更新我們index.js文件的任何東西,保存之后都會(huì)自動(dòng)更新。

好了,現(xiàn)在基礎(chǔ)功能,我們搭建得差不多了。接下我們配置module,繼續(xù)完善最后的基礎(chǔ)功能,比如打包js,css,image。

1.8 打包js文件

打包js文件,主要是要將es6,7...的語(yǔ)法轉(zhuǎn)義成es5的語(yǔ)法,讓不兼容es6,7...語(yǔ)法的瀏覽器也能使用。

首先我們先安裝一套包babel-loader,@babel/core,@babel/preset-env,@babel/polyfill

npm i babel-loader @babel/core @babel/preset-env @babel/polyfill -D

安裝完之后,我們來(lái)配置webpack.config.jsmodule

image

這里簡(jiǎn)單介紹一下,每個(gè)loader的作用,babel-laoder主要是打包js文件,而babel-loader又依賴@babel/core,@babel/preset-env可以轉(zhuǎn)義es6的map,every,fifler,some...語(yǔ)法,但是不轉(zhuǎn)義Promise,async,await...語(yǔ)法,所以要用@babel/polyfill來(lái)轉(zhuǎn)義。

1.9 打包c(diǎn)ss文件

首先安裝幾個(gè)loader,style-loader,css-loader

npm i style-laoder css-loader -D

安裝完,我們接著配置module

image

這里只是簡(jiǎn)單配置css文件,可能有些項(xiàng)目還會(huì)用到less,scss等,這里就不過(guò)多闡述,如果你項(xiàng)目用到可以到官方找一下對(duì)應(yīng)的loader配置一下,都是大同小異。

2.0 打包image,font,media文件

首先還是先安裝幾個(gè)loader,url-laoder,file-loader。

npm i url-loader file-loader -D

安裝完,我們繼續(xù)配置module

image

介紹一下這兩個(gè)loader的配置,配置里我們指定了limit,如果文件沒有大于這個(gè)就用url-loader,base64打包,如果大過(guò)這個(gè)值,就會(huì)用file-loader打包,同時(shí)會(huì)按options下配置的name輸出到對(duì)應(yīng)的位置。

好,基礎(chǔ)配置就說(shuō)那么多了,如果你的項(xiàng)目還有其他的需求可以到官網(wǎng)擼一番。

最終配置如下:


const path = require('path')

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

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

const webpack = require('webpack')

const config = {

    entry: ['@babel/polyfill', path.resolve(__dirname, './src/index.js')],

    output: {

        filename: '[name].[hash:8].js',

        path: path.resolve(__dirname, './dist')

    },

    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_module/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ['@babel/preset-env']
                        }
                    }
                ]
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.(jpg|png|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'img/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(mp4|webm|ogg|mp3|wav)(\?.*)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'media/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'fonts/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            }
        ]
    },

    devServer: {
        port: '3000',
        host: '127.0.0.1',
        hot: true,
        open: true
    },

    plugins: [
        new HtmlWebpackPlugin({
            filename: "index.html",
            template: path.resolve(__dirname, './src/public/index.html')
        }),
        new CleanWebpackPlugin(),
        new webpack.HotModuleReplacementPlugin()
    ]

}

module.exports = config

從0到1跑起Vue項(xiàng)目

當(dāng)你以上的基礎(chǔ)部分都熟悉了,我們從0到1搭建一個(gè)vue環(huán)境是soeasy的事。明人不說(shuō)暗話,其實(shí)上面的基礎(chǔ)部分一直在為跑起vue項(xiàng)目做鋪墊。

現(xiàn)在我們要跑起vue項(xiàng)目,在以上基礎(chǔ)的配置上,在搞幾個(gè)loader就完事了,簡(jiǎn)單得我都不好意思往下說(shuō)了。

好了,現(xiàn)在我們先來(lái)安裝幾個(gè)東西,vue,vue-loader,vue-template-compiler,vuw-style-loader

npm i vue vue-loader vue-template-compiler vue-style-loader -D

安裝完之后,我們來(lái)配置一下weback.config.js

這是module配置,記得在頂層導(dǎo)入VueLoaderPlugin

image

這是plugins配置

image

配置完webpack.config.js之后,我們來(lái)在src下創(chuàng)建一個(gè)vue文件夾,然后在里面創(chuàng)建一個(gè)app.vue文件

cd src
mkdir vue
cd vue
touch App.vue

目錄

image

接著在app.vue中,學(xué)上你熟悉的vue語(yǔ)法。

image

寫好之后,我們回到入口文件也是src下的index,js,我們來(lái)重新寫一下里面的邏輯,之前只是console.log()一下,什么也沒做。

最后我們的index.js文件是這樣的。

image

這段代碼對(duì)于用vue的同學(xué)再也熟悉不過(guò)來(lái)。

接著,我們回到src下的public下的index.html加一個(gè)根元素加上一個(gè)idroot

image

最后,我們npm run dev一下,我們的vue項(xiàng)目就跑起來(lái)了。

從0到1跑起React項(xiàng)目

我們搭建React項(xiàng)目的配置,還是重基礎(chǔ)配置上面配置。跑起react項(xiàng)目的配置比vue還要簡(jiǎn)單。

首先,我們先安裝一些東西。react,react-dom,@babel/preset-react

npm i react react-dom @babel/preset-react -D

安裝完后,我們配置一下webpack.config.js

它只需要配置一下module下的js項(xiàng)就ok了。

image

接著,我們?nèi)懸幌氯肟谖募?index.js)的邏輯。

image

這段代碼對(duì)用react的同學(xué)也是熟悉不過(guò)的了。

index.html文件也根vue寫的一樣。

image

最后,我們npm run dev一下,我們的react項(xiàng)目的跑起來(lái)了。

到這里,我們自己搭建Vue項(xiàng)目環(huán)境配置和React項(xiàng)目環(huán)境配置都說(shuō)完了。

那么我們?cè)趺纯梢园?strong>Vue環(huán)境跟React環(huán)境統(tǒng)一在放下同一個(gè)環(huán)境下,在我們學(xué)Vue的時(shí)候用Vue環(huán)境,在我們寫React的時(shí)候用React環(huán)境,這是我們接下要做的。

一套配置,兩套環(huán)境,既可跑Vue,也可跑React

我們回頭觀察一下Vue的配置跟React的配置有哪里不一致,哪里是一致的。

同:

entry,output

image

image,font,media打包

image

devServer和部分Plugin

image

不同的有:

  • js的loader配置,vue沒有@babel/preset-ract
  • css的loader的配置,vue用的是vue-style-loader,react用的是style-loader
  • vue的loader,react配置沒有
  • plugin,vue多了一個(gè)VueLoaderPlugin,react沒有

好了,現(xiàn)在我們找出它們的異同之處了,那就好辦了。

我們需要用一個(gè)叫webapck-merge的插件,我們安裝一下。

npm i webpack-merge -D

安裝完之后,我們?cè)?strong>webapck.config.js同級(jí)目錄下新建兩個(gè)文件webpack.vue.js,webpack.react.js,來(lái)放它們兩者不同的配置。

touch webpack.vue.js
touch webpack.react.js

接著我們來(lái)寫一下它們的不同配置。

webpack.vue.js

const merge = require('webpack-merge')

const webpackConfig = require('./webpack.config')

const VueLoaderPlugin  = require('vue-loader/lib/plugin')

const config = {
    module:{
        rules:[
            {
                test:/\.vue$/,
                use:['vue-loader']
            },
            {
                test: /\.js$/,
                exclude: /node_module/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ['@babel/preset-env']
                        }
                    }
                ]
            },
            {
                test: /\.css$/,
                use: ['vue-style-loader', 'css-loader']
            },
        ]
    },
    plugins:[
        new VueLoaderPlugin()
    ]
}

module.exports = merge(webpackConfig,config)

webpack.react.js

const merge = require('webpack-merge')

const webpackConfig = require('./webpack.config')

const config = {
    module:{
        rules:[
            {
                test: /\.js$/,
                exclude: /node_module/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ['@babel/preset-react','@babel/preset-env']
                        }
                    }
                ]
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
        ]
    }
}

module.exports = merge(webpackConfig,config)

最后webpack.config.js就成這樣了。


const path = require('path')

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

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

const webpack = require('webpack')

const config = {

    entry: ['@babel/polyfill', path.resolve(__dirname, './src/index.js')],

    output: {

        filename: '[name].[hash:8].js',

        path: path.resolve(__dirname, './dist')

    },

    module: {
        rules: [      
            {
                test: /\.(jpg|png|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'img/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(mp4|webm|ogg|mp3|wav)(\?.*)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'media/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'fonts/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            }
        ]
    },

    devServer: {
        port: '3000',
        host: '127.0.0.1',
        hot: true,
        open: true
    },

    plugins: [
        new HtmlWebpackPlugin({
            filename: "index.html",
            template: path.resolve(__dirname, './src/public/index.html')
        }),
        new CleanWebpackPlugin(),
        new webpack.HotModuleReplacementPlugin(),
    ]

}

module.exports = config

好了,現(xiàn)在我們把它們的配置拆分完畢了,還剩最后一步我們就大功告成了,那就是在package.json添加兩條命令。

image

在我們寫vue項(xiàng)目的時(shí)候,我們就用npm run vue,在寫react項(xiàng)目的時(shí)候,我們就用npm run react

結(jié)尾

好了,到現(xiàn)在為止,我們把從基礎(chǔ)配置到vue到react再到合并兩套環(huán)境都做完了。原創(chuàng)不易,希望這篇文章能幫助到你。如果有不到位,歡迎在下面留言;如果把你看得睡著過(guò)去的,歡迎來(lái)找我,我給你暖被窩,哈哈哈;如果有把你說(shuō)蒙的了或者還不明白的,歡迎來(lái)找我,我把我的剩余的兩個(gè)口罩送一半給你。

最后,我還想問各位大佬一下這句話什么意思,一個(gè)朋友發(fā)給我的:"油城壯志憶往昔,秋風(fēng)吹葉道難行。莫問前路多迷茫,龍門躍起終識(shí)君"。我百度了也沒有這首詩(shī)啊??。

image
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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