webpack學(xué)習(xí)筆記(2)

安裝開發(fā)服務(wù)器webpack-dev-server
避免開發(fā)過程中重復(fù)的打包操作,自動編譯打包、自動刷新瀏覽器,開發(fā)者只需寫好代碼就可以。
特點:將編譯好的文件保存在內(nèi)存中。

npm i webpack-dev-server -D
webpack.config.js中的配置:

devServer:{
    //為哪個文件夾提供此服務(wù)
    contentBase:'rsolve(__dirname,'dist')',
    //頁面實時刷新
    inline:true,
    //啟動gzip壓縮
    compress:true
    //自動打開瀏覽器
    //open:true
}

啟動:本地下載的話可以使用npx webpack-dev-serve命令啟動這個服務(wù)或者在package.json文件中進行配置,然后直接使用npm run dev命令啟動服務(wù)。成功啟動后,直接點擊http://localhost:8080進行調(diào)試。

"scripts":{
  "dev":"webpack-dev-server"
}

npm run dev——>執(zhí)行這個命令,優(yōu)先從本地中查找有沒有安裝webpack-dev-server包。
有了這個命令后,在開發(fā)中就不用頻繁的去執(zhí)行npm run build命令打包了。npm run dev會自動更新改動的地方,等開發(fā)結(jié)束后,執(zhí)行npm run build最終打包即可,打包結(jié)束后,把dist文件夾放到服務(wù)器進行部署。


提取css文件為單獨文件(生產(chǎn)環(huán)境下配置)
使用css-loader能將css文件變成commonJS模塊內(nèi)嵌到JS文件中,這樣操作會使得js文件體積變大,加載js文件的速度降低,為了優(yōu)化速度,可以將內(nèi)嵌在js中的css提取出來,提取出來的css文件就可以和js文件并行加載。提取需要安裝mini-css-extract-plugin插件。提取出來的css文件會以<link>標簽的形式添加到document中。
使用這個插件就不需要使用style-loader包了。

use:[
    MiniCssExtractPlugin.loader,
    'css-loader'
]
plugins:[
    new MiniCssExtractPlugin({
        //對提起的css文件重命名
        filename:'css/index.css'
})
]

css文件的兼容性處理(生產(chǎn)環(huán)境下配置)
需要用到postcss庫---->此庫要在webpack中使用,需要安裝postcss-loader以及插件postcss-preset-env。
壓縮css文件(生產(chǎn)環(huán)境下配置)
使用optimize-css-access-webpack-plugin插件
壓縮js文件(生產(chǎn)環(huán)境下配置)
只要把webpack.config.js中的mode改為production即可,在生產(chǎn)環(huán)境下,會自動啟動很多插件,其中UglyfyJsPlugin插件會直接對js文件壓縮,不用下載壓縮包。
壓縮html文件(生產(chǎn)環(huán)境下配置)
也不需要下載什么壓縮包,在plugins配置即可。

        new HtmlWebpackPlugin({
            template:'./src/index.html',
            //壓縮html,不需要下載插件
            minify:{
                //清除空格
                collapseWhitespace:true,
                //清除注釋
                removeComments:true
            }
      })

es6轉(zhuǎn)為es5的處理(生產(chǎn)環(huán)境下配置)
部分低版本瀏覽器不認識es6語法,比如let/const關(guān)鍵字,箭頭函數(shù)等,需要轉(zhuǎn)為瀏覽器能識別的es5。
需要下載babel-loader @babel/core @babel-preset-env

//第一種配置方法
//這種配置只能做基本的轉(zhuǎn)換,es6中的promise對象還是不能轉(zhuǎn)換
 {
                test:/\.js$/,
                exclude:/node_modules/,//不對node_modules里的js文件匹配處理
                use:{
                    loader:'babel-loader',
                    options:{
                        //preset:指示babel做什么樣的轉(zhuǎn)換處理
                        preset:['@babel/preset-env']
                    }
                }
            }
//第二種配置方法
//安裝@babel/polyfill,能把全部的es6語法轉(zhuǎn)為es5語法
//安裝好后,在index.js文件中引入'import @babel/polyfill'

//缺點:轉(zhuǎn)換后的bundle.js文件變得很大,因為里面包含了所有的方法,有些方法根本沒用到,也一起打包進去。
//第三種配置方法(最好)
//需要轉(zhuǎn)換的就去轉(zhuǎn)換,不需要的就不做轉(zhuǎn)換,按需轉(zhuǎn)換。
//安裝core-js   npm i core-js -D

         use:{
                    loader:'babel-loader',
                    options:{
                        //preset:指示babel做什么樣的轉(zhuǎn)換處理
                        preset:[
                            [
                                '@babel/preset-env',
                                {
                                    useBuiltIns:'usage',//按需加載
                                    corejs:{ //指定corejs版本
                                        version:3
                                    },
                                    targets:{ //指定應(yīng)用到哪些版本的瀏覽器
                                        chrome:'60',
                                        firefox:'60',
                                        ie:'9',
                                        safari:'10',
                                        edge:'17'
                                    }
                                }
                            ]
                        ]
                    }
               }

webpack開發(fā)環(huán)境下的性能優(yōu)化(使用HMR、source map)

HMR(hot module replacement,熱模塊替換)
在開發(fā)階段,當(dāng)對某個模塊作出修改后,在控制臺會看到其它模塊的結(jié)果也會跟著一起刷新,雖然其它模塊并沒做任何改動。這顯然不是我們想要的結(jié)果,我們希望的是哪個模塊修改了,只刷新這個模塊的值,其它模塊不做處理。所以就有了HMR。
HMR是加快開發(fā)階段開發(fā)速度,節(jié)省開發(fā)時間的一種方式。
HMR能實現(xiàn)只對單個模塊的刷新,而不用重新刷新頁面中的所有模塊。(有點類似ajax技術(shù),只刷新局部頁面)
配置:

devServer:{
//webpack.config.js中的配置修改后,需要重啟webpack-dev-server
    hot:true
}

配置好HMR后,分別對css文件、js文件、HTML文件進行測試。在webpack-dev-server下,發(fā)現(xiàn)當(dāng)單獨對css文件修改時,控制臺提示Nothing hot updated.。無論修改什么樣式,這些樣式都不會生效。目前還找不到原因。
單獨對js文件修改時,發(fā)現(xiàn)HMR功能無效;單獨修改HTML文件時,也發(fā)現(xiàn)HMR功能無效,并且HTML文件無法更新。

//js文件默認不能使用HMR功能,要使用必須添加支持HMR的代碼
//且HMR只能處理非index.js的文件,對于入口文件index.js無法處理
//在index.js文件中寫上下面的配置代碼:
if(module.hot){ //
    module.hot.accept('./print.js',function(){
        //監(jiān)聽print.js文件的變化,如若變化,打包此文件,并執(zhí)行回調(diào)函數(shù)
        print()
    })
}

//對于HTML文件,需要修改entry
entry:['./src/index.js','./src/index.html'],
//修改entry后測試,HTML頁面能得到更新,但還是無法使用HMR功能。不過由于HTML文件只有一個,所以,沒有HMR功能也無妨。

source map
當(dāng)使用webpack打包源代碼時,可能很難發(fā)現(xiàn)出錯誤的地方在源代碼中的位置。為了能更好地追蹤錯誤,提供了source map功能,將構(gòu)建后出錯的代碼映射到源代碼的位置。
配置很簡單:一行代碼。但是source map有很多種不同的選項供選擇。

devtool:'source-map' //外部,直接使用source-map,在dist/js文件夾下會生成一個.map文件
      - 生成的source-map能映射到源代碼出錯的具體位置
devtool:'inline-source-map' //生成的sourcemap文件嵌套在bundle.js文件中,所以也稱為內(nèi)聯(lián)sourcemap。
    - bundle.js文件中只有一個sourcemap
    - 映射功能和上面的source-map一致
devtool:'hidden-source-map'//外部,在dist/js文件夾內(nèi)會生成一個外部的.map文件
    - 能提示錯誤的原因,但是不會映射到源代碼的位置,而是映射到構(gòu)建后代碼的位置。
devtool:'eval-source-map'//內(nèi)聯(lián),沒有生成外部文件,生成的sourcemap文件嵌套在bundle.js文件中。
    - 每一個文件(css、js、less...)都會生成對應(yīng)的source-map在bundle.js文件中
    - 和source-map、inline-source-map一樣,能映射到源代碼的具體位置
devtool:'nosource-source-map'//外部
    - 能提示錯誤原因,以及源代碼哪行代碼出了問題,但是不會顯示源代碼信息
devtool:'cheap-source-map'//外部
    - 能提示錯誤原因,也能映射到源代碼出錯位置。不同的是,它只有行映射,沒有列映射,即你知道這行代碼出了問題,但是不清楚這行代碼的哪個位置出了問題。
devtool:'cheap-module-source-map'//外部
    - 同cheap-source-map
內(nèi)聯(lián)和外部的區(qū)別:
1.外部生成的文件,內(nèi)聯(lián)沒有
2.內(nèi)聯(lián)構(gòu)建速度比外部快

開發(fā)環(huán)境的選擇:選擇速度快,調(diào)試友好的
速度快(eval>inline>cheap>...)
- eval-source-map
- eval-cheap-source-map
調(diào)試更友好
- source-map(能精確到行、列)
- cheap-module-source-map
- cheap-source-map(只能精確到行)

綜合選擇:eval-source-map

生產(chǎn)環(huán)境的選擇:源代碼要不要隱藏?調(diào)試要不要更友好?
生產(chǎn)環(huán)境不考慮內(nèi)聯(lián)的source-map,因為會讓bundle.js文件體積變大。所以,inline-source-map、eval-source-map這兩種方式排除。
代碼若要隱藏:
hidden-source-map、nosource-source-map
調(diào)試更友好:
source-map(能精確到行、列)、cheap-module-source-map

webpack生產(chǎn)環(huán)境下的性能優(yōu)化

1、oneOf
在module中配置了很多的loader,某文件在處理過程中會去尋找指定的loader處理,但處理完后,并不會停止,還會去匹配其它的loader,直到全部匹配完,很顯然,后續(xù)的操作都是無意義的,都是在浪費性能。
所以,oneOf的作用就是:根據(jù)文件類型匹配對應(yīng)的loader轉(zhuǎn)換即可。
配置:
把所有的loader配置放在oneOf中即可。注意:在oneOf中不能有2個loader處理同一個文件類型,必須分離開。

module:{
        rules:[
            {
                test:/\.js$/,
                exclude:/node_modules/,
                // //優(yōu)先執(zhí)行
                enforce:'pre',
                loader:'eslint-loader',
                options:{
                    fix:true
                }
            },
            {
                //下面的loader只會匹配一個,處理性能更好
                //注意:不能有兩個配置處理同一種類型文件
                oneOf:[
                    {
                        test:/\.css$/,
                        use:[...commenCssLoader]
                    },
                    {
                        test:/\.less$/,
                        use:[...commenCssLoader,'less-loader']
                    },
                ]
            }
        ]
    },

2、緩存
對資源做緩存處理,等用戶下次請求的時候就可以直接從緩存中拿取,速度會快很多。瀏覽器緩存可以降低網(wǎng)絡(luò)流量,加快網(wǎng)站的訪問速度。
但是,緩存也有弊端。當(dāng)把文件全部部署到服務(wù)器上,之后如果沒有更改文件名,瀏覽器會認為此文件沒更新,就會使用此資源的緩存版本。由于緩存的存在,可能獲取不到最新的文件。
babel緩存:
babel能對js代碼進行處理,編譯成瀏覽器能夠識別的語法。babel也能對眾多的js文件進行處理,將它們緩存起來,如果修改了哪份js文件,打包后,只會請求這一份js文件,其它文件依然是從緩存中獲取。
配置:https://www.bilibili.com/video/BV1e7411j7T5?p=23
文件資源緩存:

/*在強制緩存期間,服務(wù)器是不會向瀏覽器返回請求資源的,而是讓瀏覽器直接從緩存中拿取資源。這樣有個問題,假如在強制緩存期間,某些文件代碼發(fā)生了變化,那就無法獲取到最新的頁面數(shù)據(jù)。
可以給請求的資源,比如css文件、js文件的名稱加個版本號,如果文件更新了,會有個更新的版本號。
這個版本號怎么加上去?用hash表示。
output:{
    filename:'js/[name].[hash:8].js',
    path:resolve(__dirname,'dist')
}
new MiniCssExtractPlugin({
    //對提取的css文件重命名
    filename:'css/index.[hash:8].css'
}),
在輸出的css文件、js文件名稱上就能看到hash結(jié)尾,在控制臺也能看到這兩種文件就不再是memory cache了
css文件、js文件每次修改打包后,都會生成新的hash值
但是又帶來個問題,因為css和js文件都使用了hash,即使我只修改了其中一個文件,打包后,另一個文件的hash值也會跟著變化,因為它們共用一個hash,所以導(dǎo)致另一個文件也重新發(fā)送了請求,可事實是另一個文件根本沒有做任何修改。
換成chunkhash:如果打包的兩個文件來自同一個chunk,那么它們的hash就一樣,否則不一樣。
試試。
實際結(jié)果不如人意,css文件和js文件的hash值仍一樣,因為webpack打包過程中將css以commonJS規(guī)范編譯引入到了js文件中,所以它倆來自同一個chunk。
換成contenthash:根據(jù)文件內(nèi)容生成的hash,文件內(nèi)容不一樣,hash肯定不一樣
試試。
兩種文件的hash不一樣了,當(dāng)修改css文件的內(nèi)容,再打包后,css文件重新發(fā)送請求,js文件的hash不變,不會重新發(fā)送請求,而是使用上次的緩存了*/
new MiniCssExtractPlugin({
    //對提取的css文件重命名
    filename:'css/index.[contenthash:8].css'
}),

tree shaking:樹搖
你可以將應(yīng)用程序想象成一棵樹。綠色表示實際用到的源碼和 library,是樹上活的樹葉。灰色表示無用的代碼,是秋天樹上枯萎的樹葉。為了除去死去的樹葉,你必須搖動這棵樹,使它們落下。
具體實踐:

//創(chuàng)建了一個print.js,文件中包含兩個方法
export function sum(x,y){
    console.log(x+y)
}

export function mul(x,y){
    console.log(x*y)
}
//在index.js文件中引入其中的sum()方法,不引入mul()方法
//既然mul()沒被導(dǎo)入,它就是“未引用代碼(dead code)”,這種代碼應(yīng)該被刪除的
import {sum} from './print.js'
sum(3,3)
//在package.json中配置:
"sideEffects":false //標記所有的文件都是無副作用的,可以刪除那些未被使用的代碼
//如果有些代碼有副作用,就以數(shù)組的形式把代碼加進去。
"sideEffects":[
      ./src/some-side-effectful-file.js
]
//配置了sideEffects后,發(fā)現(xiàn)css文件在tree shaking后無意被刪除了,因為css文件都是直接引入,沒有執(zhí)行什么操作。
//所以,為了避免被刪除,需要把css文件添加到sideEffects列表中
"sideEffects":[
      ./src/some-side-effectful-file.js,
      *.css,
      *.less
]

tree shaking要能生效,必須是在mode:'production'環(huán)境下。
4、代碼分割(只針對js文件)
未使用代碼分割的情況下,所有的js文件都是打包到了同一個文件中,導(dǎo)致代碼體積很大。代碼分割將一個打包后的大文件,分成幾個獨立的小文件??梢蕴岣呶募虞d速度、實現(xiàn)文件按需加載功能。
分兩種情況:
第一種:將單入口拆分成多入口,一個入口形成一個chunk代碼塊,然后輸出對應(yīng)的文件

//未拆分前是單入口
 entry:['./src/index.js'],
//拆分成多入口---->打包后,就會輸出兩個對應(yīng)的bundle.js
//index.js中導(dǎo)入test.js測試沒問題
 entry:{
    index:'./src/index.js',
    test:'./src/test.js'
},
 output:{
        filename:'js/[name].[contenthash:8].js',
        path:resolve(__dirname,'dist')
    }

執(zhí)行npm run build命令后,查看dist/js下的js文件,出現(xiàn)了兩個js文件,說明兩個文件是分開打包的。
test.js文件

但是修改單入口為多入口這種方式并不方便,所以有了第二種途徑。

//在webpack.config.js中加上如下配置(單頁面情況)。
 entry:'./src/index.js',
 output:{
     filename:'js/[name].[contenthash:8].js',
     path:resolve(__dirname,'dist')
 },
optimization:{
      splitChunks:{
          chunks:'all'
    }
}

optimization的作用:
如果是單頁面應(yīng)用,只有一個entry,即都是從index.js作為入口,如果index.js中引入了第三方庫,optimization可以把這個第三方包單獨打包成一個chunk輸出,就不會打包到bundle.js中去。
如果是多頁面應(yīng)用,即entry中有多個入口(index.js、test.js),它會分析這些文件有沒有使用相同的依賴,比如有沒有都用到j(luò)Query,如果都用到了jQuery,就會把jQuery提取成單獨的一個chunk,避免jQuery被打包兩次。
注意:optimization只是對第三方包起作用。如果不同的js文件還是打包到一個bundle.js文件中,想要把不同的js文件分開打包,此方式不可行
如果我想把index.js和test.js文件分開打包,該怎么實現(xiàn)呢?
import()
在index.js文件中使用import()動態(tài)導(dǎo)入語法,可以對指定的js文件單獨打包。

/*webpackChunkName:'test'*/:給打包后的js文件加上test名稱
import(/*webpackChunkName:'test'*/'./test.js')
    .then((result)=>{ //result:是個module對象
        console.log(result)//如下圖
        result.mul(2,3)//6
    })
    .catch(()=>{
        console.log('打包失敗')
    })


5、懶加載和預(yù)加載
懶加載:文件需要使用的時候才加載。比如執(zhí)行點擊事件,再去異步加載代碼,可以提高代碼利用率。
預(yù)加載:等其它資源加載完畢了,瀏覽器空閑的時候再去偷偷加載。

使用index.js和test.js示例。

//index.js
import { mul } from './test.js'

console.log('我是index.js文件')

//文件懶加載+預(yù)加載
document.getElementById('btn').addEventListener('click',()=>{ //點擊按鈕的時候才去加載test.js文件,不要一打開頁面就加載test.js
    import(/*webpackChunkName:'test',webpackPrefetch:true*/'./test.js') 
        .then((result)=>{ //result:是個module對象
            result.mul(2,3)
        })
        .catch(()=>{
            console.log('打包失敗')
        })
})

//引入css文件
import './index.css'
//引入less文件
import './index.less'

//js文件默認不能使用HMR功能,要使用必須添加支持HMR的代碼
//且HMR只能處理非index.js的文件,對于入口文件index.js無法處理
if(module.hot){ //
    module.hot.accept('./print.js',function(){
        //監(jiān)聽print.js文件的變化,如若變化,打包此文件,并執(zhí)行回調(diào)函數(shù)
        print()
    })
}
//test.js
console.log('我是test.js文件')
export function sum(x,y){
    console.log(x+y)
}

export function mul(x,y){
    console.log(x*y)
}

6、externals
作用:拒絕某些包被打包,比如通過import $ from 'jQuery',這些包的體積很大,在js文件打包的時候如果將它們也打包進去,到時候加載速度肯定會降低。

 externals:{
      jquery:'jQuery'
  }

7.dll(配置、理解上有點難)
dll將引入的第三方庫(jQuery、React、Vue)單獨拆開來,打包成不同的文件,有利于性能優(yōu)化。
7.1 創(chuàng)建一個webpack.dll.js文件
先打包jQuery,打包后的文件名稱為jquery.js,這個文件中以[name][hash:8]的名稱向外暴露了jQuery第三方庫的內(nèi)容。
plugin的插件會生成manifest.json文件,文件中以"name":"[name]
[hash:8]"建立了和jQuery的映射關(guān)系。使用dll打包后,將來就不要再打包了,直接引入打包后的jQuery文件即可。

const {resolve} = require('path')
const webpack = require('webpack')
module.exports = {
    entry:{
//jquery:表示打包后的文件名稱是jquery
//['jQuery']:表示要打包的是jQuery這個庫
       jquery:['jQuery']
    },
    output:{
        filename:'[name].js',
        path:resolve(__dirname,'dll'),
        library:'[name]_[hash:8]'//打包后的庫向外暴露的內(nèi)容名稱
    },
plugins:[
    new webpack.DllPlugin({
        name:'[name].[hash:8]',//打包后的庫向外暴露的內(nèi)容名稱
        path:resolve(__dirname,'dll/manifest.json')//輸出文件路徑
//manifest.json的作用:提供和jQuery的映射關(guān)系,通過
  })
],
mode:'production'
}
注意:運行webpack的時候,默認查找webpack.config.js文件,為了能執(zhí)行webpack.dll.js文件,需要執(zhí)行webpack --config webpack.dll.js

7.2 dll將第三方庫打包好后,回到webpack.config.js配置文件

const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
plugins:[
    new HtmlWebpackPlugin({
      template:'./src/index.html'
}),
//告訴webpack不用打包jQuery了
    new webpack.DllReferencePlugin({
      manifest:resolve(__dirname,'dll/manifest.json')
    }),
//將jquery.js文件自動引入到html文件中去,這樣就能使用jQuery了
    new AddAssetHtmlWebpackPlugin ({
      filepath:resolve(__dirname,'dll/jquery.js')
  })
],
mode:'production'
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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