webpack搭建項目流程(純干貨)


對於前端同學來說webpack應該一點不陌生,現在應該大部分的工程都在使用webpack來進行構建。 現在很多的框架都已經提供相應的腳手架命令行工具,直接執行之后就會生成對應的模板項目。 不需要我們在腳手架上面浪費過多的時間,從而更加關注業務的編寫, 但是我們仍然需要知道webpack的工作原理到底是什么?知道之后也方便我們對現有的項目進行改造或者擴展。

下面的教程是從0開始到完成webpack腳手架的搭建全過程,在編寫過程中除了說明如何寫之外也會穿插很多為什么。 本篇文章大概分為以下幾個部分:



  • webpack是什么?

  • 搭建第一步--npm init

  • 生成項目的目錄結構

  • 安裝基礎的依賴

  • 安裝loader、配置loader

  • 配置入口html -- html-webpack-plugin插件

  • 項目優化

webpack是什么

本質上,webpack 是一個用於現代 JavaScript 應用程序的靜態模塊打包工具。
上面是webpack官網上面的解釋,理解一下就是:webpack只是根據依賴關系把不同的模塊根據不同規則打包成一個或多個文件,這就是webpack的作用。


搭建第一步 -- npm init

首先創建一個文件夾,進入到文件夾目錄下,執行npm init命令,然后會生成一個package.json文件,此時項目初始化完成,這一步就是為了生成一個package.json文件
思考:

  1. 為什么需要package.json?
    package.json 文件里面存儲的是你整個項目的信息,包括依賴包、版本等等,方便其他人快速了解你的項目以及能夠快速修改你的項目。
  2. 不用npm init命令可以嗎?
    可以,可以從其他項目拷一份或自己新建一個package.json文件

生成項目的目錄結構

我這邊js框架使用的是react,css框架使用的是less;其他的vue/sass/stylus等道理都是一樣的。

目錄結構如下:

 

 

安裝基礎的依賴

我們使用webpack作為我們項目的構建工具時 需要安裝以下幾個包:  webpack/webpack-cli/webpack-dev-server 

//執行以下命令進行安裝
 npm install webpack webpack-cli webpack-dev-server --save-dev 

安裝webpack-cli是為了可以使用webpack命令,也就是可以使用webpack命令,否則無法使用

下面我們修改一下package.json: 在scripts中增加兩條命令:

 

 

webpack和webpack-dev-server的區別:

webpack 是一個模塊打包器,他會根據我們代碼的依賴,然后編譯文件,最后把編譯好的文件輸出到ouput選項中path路徑里面

webpack-dev-server 是webpack+httpserver 他會把編譯好的文件放到內存中同時起一個http服務,這樣我們可以本地調試代碼

除此之外我們使用react需要安裝 react/react-dom兩個包:

 //執行以下命令進行安裝
  npm install react react-dom --save

OK,至此我們已經完成了基礎的依賴的安裝,下面我們就可以直接執行npm run dev來把我們的工程跑起來了。

安裝loader、配置loader

安裝loader

不出意外的話,你執行上一步的命令后會遇到下面的報錯:

安裝loader、配置loader

安裝loader

不出意外的話,你執行上一步的命令后會遇到下面的報錯:

安裝loader、配置loader

安裝loader

不出意外的話,你執行上一步的命令后會遇到下面的報錯:

 

 ??? 是否有很多問號? 別怕 下面我們先分析一下:

  ERROR in ./src/index.js 6:4
  Module parse failed: Unexpected token (6:4)
  You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
  | 
  | ReactDOM.render(
  >     <div>Hello World!</div>,
  |     document.getElementById('root')
  | )
這個指的是webpack在編譯我們的文件的時候遇見了一個模塊轉換錯誤,提示我們需要一個loader來解析這個文件,實際上我們並沒有有個loader。
首先解釋一下loader是什么?loader就是可以對我們源代碼進行預處理,說白了就是他可以把我們的源代碼處理成webpack認識的代碼。
我們看上面報錯的地方應該是jsx的語法,也就是說我們需要用loader把jsx轉換成webpack認識的代碼, 這里我們使用的loader是: babel-loader
下面我們講一下babel-loader配置方法:
  // 安裝babel-loader需要使用的依賴包
  npm install babel-loader @babel/core @babel/preset-env @babel/preset-react  @babel/plugin-transform-runtime @babel/runtime @babel/runtime-corejs3 --save-dev 

@babel/core babel的核心庫
@babel/preset-env 把es6,es7語法轉換成es5,babel7以上的版本只用這一個預設包就可以實現語法的轉換了,已經廢棄了preset-stage-0,perset-stage-1,perset-stage-2等這些包了,但是這個包還不能轉換es6,es7的一些新特性例如Array.includes(),這就需要我們使用@babel/plugin-reansform-runtime了
默認情況下 preset-env只會轉換語法,如果要轉換內置對象或者實例方法的話,需要使用polyfill來轉換
使用useBuiltlns屬性來控制@babel/preset-env使用何種的方式幫我們導入polyfill的核心,有三個值可選(entry,usage,false)

  1. entry : 是一種入口導入方法,只要我們在打包配置入口,或者 文件入口寫入 import “core-js”這樣的一串代碼,babel就會跟據我們配置的目標瀏覽器配置來引入所需要的polyfill(也就是說不管你用不用得到,只要瀏覽器不支持的都要引入)
     import "core-js"
     function test(){
       new Promise()
     }
     test()
     const arr = [1,3,4,5].map(item=>item*item)

     

  2. usage:會參考目標瀏覽器(browserslit)和代碼中使用到的特性來按需加入polyfill 使用該參數的話還需要傳一個corejs的版本號 core-js支持兩個版本 2/3 ,很多新特性已經不會加到2里面了,比如:flat等等最新的方法,2版本里面沒有,推薦使用3

  3. false: 不引入polyfill

    @babel/preset-react 把react語法轉換成es5

    @babel/plugin-transform-runtime 支持一些es6,es7的新語法 把每個文件中的使用preset-env轉換成的helper函數 --> 轉換成使用babel-runtime中的helper函數,減少包體積

    @babel/runtime 這個包是 在進行es6轉換成es5的時候會生成很多helper函數,這些函數每一個文件轉換都會重復生成,這個包里面會包含這些helper函數,這樣每個文件中引入helper函數的時候就直接引用這個包里面的文件了,減少了每個文件的包體積

    @babel-runtime 和 @babel/plugin-tranform-runtime 兩個是什么關系?
    @babel-runtime是一個核心,一種helper function的實現方式,而@babel/plugin-tranform-runtime 像是一個管家,負責更好的重復使用@babel-runtime

配置loader


根目錄下新建一個build文件夾,新建webpack.config.js文件,用於更改webpack配置
為什么? 因為我們現在使用的是webpack的默認命令,所以要更改的話需要指定webpack命令執行時的配置文件

 

// build/webpack.config.js   
   const path = require("path")
   module.exports = {
       // 指定構建環境
       mode: "development",
       //入口 這里路徑是相對於項目的根目錄的 所以這里是 ./src/index.js
       entry:{
           app:'./src/index'
       },
       output:{
           path:path.resolve("../dist"),
           filename:'js/[name].js',
           publicPath:"/" //打包后的資源的訪問路徑前綴
       },
       module:{
           rules:[
               {
                   test:/\.jsx?$/,
                   exclude:/node_modules/, // 這個node_modules文件夾里面的js/jsx文件不需要使用babel-loader
                   loader:'babel-loader'
                   // babel-loader的參數配置也可以這樣寫,我們這里是新建一個.babelrc文件的方式來配置
                   // use: {  
                   //     loader: 'babel-loader',
                   //     options: {
                   //     presets: ['@babel/preset-env']
                   //     }
                   // }
               }
           ]
       }
   }
   // .babelrc文件
   {
     "presets": [
         ["@babel/preset-env",{
             "modules":false,
             "targets":{
                 "browsers":["> 1%","last 2 versions","not ie<=8"]
             }
         }],
         "@babel/preset-react"
     ],
     "plugins": [
         ["@babel/plugin-transform-runtime",{
             "corejs":3,
             "useBUildIns":"usage"
         }]
     ]
   }
   // package.json 修改scripts選項中的dev命令 
   
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
     "dev": "webpack-dev-server --config build/webpack.config.js",
     "build": "webpack --config build/webpack.config.js" // --config 指的是使用指定文件的配置 這里我們使用的是剛剛創建的webpack.config.js
   },

   //  里面重要的配置已經加了注釋,.babelrc中的如何寫請查看官方文檔,里面每個插件的作用是什么前面已經講過了

 

如果只使用了jsx語法的話 就已經可以編譯和打包了

css-loader style-loader less-loader

如果你使用了我下面的語法的話 恭喜你,你在代碼編譯的時候還會繼續報錯

 

 

 

 報錯還是提示我們缺少loader,下面我們來講一下關於css的loader

css-loader是為了處理import require @import url 這樣的引入

style-loader 是動態創建一個style 標簽,然后把樣式塞進去 添加到頁面中的

less-loader 是編譯less語法

執行順序:如果loader選項是一個數組 那執行順序是從右向左的 loader:['style-loader','css-loader'] 配置多個loader的話 是從下到上的執行順序的

因此我們需要給我們的less文件同樣配置loader


  // 同樣的需要先安裝

  npm install style-loader css-loader less-loader --save-dev

  // build/webpack.config.js
  module:{
        rules:[
            {
                test:/\.jsx?$/,
                exclude:/node_modules/,
                loader:'babel-loader'
            },
            {
                test:/\.css$/,
                use:[
                    {
                        loader:"style-loader"
                    },
                    {
                        loader:"css-loader"
                    }
                ]
            },
            {
                test:/\.less$/,
                use:[
                    {
                        loader:"style-loader"
                    },
                    {
                        loader:"css-loader"
                    },
                    {
                        loader:"less-loader"
                    }
                ]
            }
        ]
    }

正常情況下執行npm run dev 項目就會跑起來,

如果出現以下報錯:

 

 

 這個報錯說我們缺少less模塊, less-loader我們安裝的版本是7.0.0的版本,也就是說這個版本里面不在包含less包需要我們單獨安裝less包了

//執行以下命令安裝以下就好了
npm install less --save-dev 

到此我們的項目就已經成功跑起來了 並且沒有報錯了

 

 

 

配置入口html -- html-webpack-plugin插件

當我們打開 http://localhost:8080 的時候竟然是這樣的?

 

 

 是不是一臉蒙蔽?別怕,是因為我們盡管已經把工程跑起來了,但是我們並沒有給他指定一個入口html,他這里是webpack-dev-server中默認的頁面,所以我們就需要使用html-webpack-plugin來配置我們的入口html

// html-webpack-plugin 插件用法參考官網 
// 我們需要在webpack.config.js中增加plugins選項 來配置我們的入口html

// 同樣首先需要安裝一下html-webpack-plugin插件
npm install html-webpack-plugin --save-dev

// build/webpack.config.js 增加plugins選項
// 安裝html-webpack-plugin
const HtmlWebpackPlugin = require("html-webpack-plugin")

plugins:[
    new HtmlWebpackPlugin({
        filename:path.resolve(__dirname,"../dist/index.html"),
        template:path.resolve(__dirname,"../public/index.html"),
        inject:true,// 注入選項 有四個值 true,body(script標簽位於body底部),head,false(不插入js文件)
        hash:true,//回給script標簽中的js文件增加一個隨機數 防止緩存 bundle.js?22b9692e22e7be37b57e
    })
]

重新執行npm run dev 打開http://localhost:8080 就會看到我們寫的Hello world!

 

 到現在為止我們已經完成了工程的基礎架構,如果是一些簡單的頁面上面的流程已經完全夠用了,但是對於一些大中型的項目仍然需要做一些優化,下面會講一下如何優化我們的工程

 

項目優化

這一部分我們會使用一些常用的插件來優化我們的構建流程,提升我們的構建時間和減少我們構建包體積大小

區分不同環境

我們日常工作中都是存在好幾套環境的,測試環境、預生產環境、生產環境等等。因此在不同環境使用不同的配置項是有必要的。比如我們在測試環境沒有必要壓縮我們的代碼,這樣調試起來比較方便等等。下面我們來看一下區分不同環境需要怎么搞?


第一種方法:使用cross-env設置不同的環境變量,然后判斷環境變量來使用不同的配置

cross-env的作用就不在多說了,簡單總結就是他可以跨不同環境設置變量的值

 

   // 安裝 cross-env
   npm install cross-env --save-dev 

   // package.json 增加測試命令 
   "build:test": "cross-env APP_ENV=test  webpack --config build/webpack.config.js",

   // webpack.config.js 增加測試代碼
   // 我們根據APP_ENV不同的值來把構建的js文件放到不同的文件夾下面
   output:{
       path:process.env.APP_ENV === 'test' ? path.resolve(__dirname,"../test"): path.resolve(__dirname,"../dist"),
       filename:'js/[name].js',
       publicPath:"/" //打包后的資源的訪問路徑前綴
   },

 

修改完之后,執行 npm run build:test 命令,會打包生成一個test文件夾,如圖所示:

 

 

 除了可以配置不同的輸出目錄之前也可以根據環境變量的值判斷是否需要一些其他的插件等等從而實現不同環境,不同配置的目標

 

第二種方法:生成不同的配置文件,執行相應的命令來執行不同的配置文件

首先我們復制一份webpack.config.js,然后分別命名為webpack.dev.config.js和webpack.prod.config.js 分別對應我們在development和production環境的配置,但是我們發現他們之前有很多的共同配置,因此我們還有把共同的配置抽離出來webpack.base.config.js文件。然后通過webpack-merge插件把他們合並起來。 具體實現如下:


 
   // 安裝依賴 webpack-merge 
   npm install webpack-merge --save-dev 

   // webpack.base.config.js
   
   const path = require("path")

   module.exports = {
       //入口
       entry:{
           app:'./src/index'
       },
       output:{
           path:path.resolve(__dirname,"../dist"),
           filename:'js/[name].js',
           publicPath:"/" //打包后的資源的訪問路徑前綴
       },
       module:{
           rules:[
               {
                   test:/\.jsx?$/,
                   exclude:/node_modules/, // 這個node_modules文件夾里面的js/jsx文件不需要使用babel-loader
                   loader:'babel-loader'
                   // babel-loader的參數配置也可以這樣寫,我們這里是新建一個.babelrc文件的方式來配置
                   // use: {  
                   //     loader: 'babel-loader',
                   //     options: {
                   //     presets: ['@babel/preset-env']
                   //     }
                   // }
               },
               {
                   test:/\.css$/,
                   use:[
                       {
                           loader:"style-loader"
                       },
                       {
                           loader:"css-loader"
                       }
                   ]
               },
               {
                   test:/\.less$/,
                   use:[
                       {
                           loader:"style-loader"
                       },
                       {
                           loader:"css-loader"
                       },
                       {
                           loader:"less-loader"
                       }
                   ]
               }
           ]
       },
   }

   // webpack.dev.config.js
       
   const path = require("path")
   // 安裝html-webpack-plugin
   const HtmlWebpackPlugin = require("html-webpack-plugin")
   const webpackBaseConfig = require("./webpack.base.config.js")
   // 根據不同規則合並兩個配置項,
   const { merge } = require("webpack-merge")

   module.exports = merge(webpackBaseConfig,{
       // 指定構建環境
       mode: "development",
       plugins:[
           new HtmlWebpackPlugin({
               filename:path.resolve(__dirname,"../dist/index.html"),
               template:path.resolve(__dirname,"../public/index.html"),
               inject:true,// 注入選項 有四個值 true,body(script標簽位於body底部),head,false(不插入js文件)
           })
       ]
   })

   // webpack.prod.config.js
   
   const path = require("path")
   // 安裝html-webpack-plugin
   const HtmlWebpackPlugin = require("html-webpack-plugin")

   const webpackBaseConfig = require("./webpack.base.config.js")
   const { merge } = require("webpack-merge")


   module.exports = merge(webpackBaseConfig,{
       // 指定構建環境
       mode: "production",
       plugins:[
           new HtmlWebpackPlugin({
               filename:path.resolve(__dirname,"../dist/index.html"),
               template:path.resolve(__dirname,"../public/index.html"),
               inject:true,// 注入選項 有四個值 true,body(script標簽位於body底部),head,false(不插入js文件)
               hash:true,//回給script標簽中的js文件增加一個隨機數 防止緩存 bundle.js?22b9692e22e7be37b57e
               //壓縮選項 會有很多選項 可以去npm官網上查看對應的配置
               minify:{
                   removeComments:true,//去注釋
                   collapseWhitespace:true,//去空格
                   removeAttributeQuotes:true, // 去屬性的引號
               }
           })
       ]
   })
   

   // 最后修改package.json中的打包命令配置文件
   "scripts": {
       "test": "echo \"Error: no test specified\" && exit 1",
       "dev": "webpack-dev-server --config build/webpack.dev.config.js",
       "build": "webpack --config build/webpack.prod.config.js"
   },

到這里我們就把不同環境的配置文件分離開來,這樣后面再增加配置的時候就會更加清晰。

壓縮js

原來我們構建的時候壓縮js使用的是uglifyjs-webpack-plugin,但是webpack4以后官網推薦使用terser-webpack-plugin terser-webpack-plugin 也是webpack 4的內置插件(也就是說我們不需要自己添加這個插件就可以壓縮js代碼,但是如果我們不想使用默認的配置的話 需要重新定義) 使用方法如下:


   // 安裝插件terser-webpack-plugin
   npm install terser-webpack-plugin --save-dev
   // 因為這個插件只會在生產環境生效也就是說這個配置我們要寫在webpack.prod.config.js中
   // webpack.prod.config.js  增加optimization選項 (和plugins/mode平級的)
   // Optimization 這個選項是可以覆蓋webpack內置的一些配置 比如 壓縮、分包機制等等
   optimization:{
       minimizer:[
           new TerserWebpackPlugin({
               parallel:true,
               sourceMap:false,
               exclude:/\/node_modules/,
               extractComments: true, // 這個選項如果為true 會生成一個app.js.LICENSE.txt文件 存儲特定格式的注釋
               terserOptions:{
                   warnings:false,
                   compress:{
                       unused:true,
                       drop_debugger:true,
                       drop_console:true
                   },
               }
           })
       ]
   }

 

這個時候執行npm run build 打包的話,打包之后的提交不會有太大的改變,因為webpack4已經內置了這個插件,對我們的代碼已經進行了壓縮處理,但是我們還是需要了解一下這個插件的一些配置項的

提取css文件/壓縮css

提取css文件

我們可以看一下我們打包生成的文件,是沒有css文件的,因為webpack默認會把css當做一個模塊打包到相應的js文件中去, 但是這樣的話也導致我們的bundle文件比較大,所以把css單獨拆分出來是有必要的。
在這里我們提取單獨的css文件使用的插件是:mini-css-extract-plugin(該插件同樣只在生產環境下生效)

具體的配置如下:


    // 安裝插件
    npm install mini-css-extract-plugin --save-dev

    // 修改webpack.base.config.js中 增加插件mini-css-extract-plugin 
    plugins:[
        new MiniCssExtractPlugin({
            filename:'css/[name].css',
            chunkFilename:'css/[id].css'
        })
    ],
    // 同時要修改css/less的loader  
    // 此處是截取的module --> rules里面的需要修改的代碼 
    // 把style-loader注釋掉是因為兩個會沖突,導致報錯
    {
                test:/\.css$/,
                use:[
                    {
                        loader:MiniCssExtractPlugin.loader,
                        options:{
                            hmr:true,
                            reloadAll:true
                        }
                    },
                    // {
                    //     loader:"style-loader"
                    // },
                    {
                        loader:"css-loader"
                    }
                ]
            },
            {
                test:/\.less$/,
                use:[
                    {
                        loader:MiniCssExtractPlugin.loader,
                        options:{
                            hmr:true,
                            reloadAll:true
                        }
                    },
                    // {
                    //     loader:"style-loader"
                    // },
                    {
                        loader:"css-loader"
                    },
                    {
                        loader:"less-loader"
                    }
                ]
            }

 

我們再次打包之后就可以看見已經生成了一個app.css文件

 

 

 

壓縮css

我們打開剛打包的app.css文件可以看到並沒有對內容進行壓縮,

 

 

 下面我們來實現把css壓縮的功能,這里我們使用的插件是:optimize-css-assets-webpack-plugin 具體實現如下:

 

// 安裝插件 optimize-css-assets-webpack-plugin
npm install optimize-css-assets-webpack-plugin --save-dev 

//同樣修改optimization選項 來重寫默認配置
optimization:{
    minimizer:[
        new TerserWebpackPlugin({
            parallel:true,
            sourceMap:false,
            exclude:/\/node_modules/,
            extractComments: true, // 這個選項如果為true 會生成一個app.js.LICENSE.txt文件 存儲特定格式的注釋
            terserOptions:{
                warnings:false,
                compress:{
                    unused:true,
                    drop_debugger:true,
                    drop_console:true
                },
            }
        }),
        // 壓縮css
        new OptimizeCssAssetsPlugin({
            cssProcessorOptions:{safe:true,discardComments:{removeAll:true}}
        })
    ]
}

 

再次打包之后,打開app.css里面的代碼已經被壓縮過了

 

 

 

清空構建目錄/構建包分析

清空構建目錄

現在我們打包的話生成的app.js文件都沒有帶隨機數來防止緩存,我們現在加上這個功能: 就是在我們的output里面加一個配置

 

 

然后我們就可以看到我們的dist文件夾里面會出現這樣的情況:

 

 

 

也就是說我們以前構建的文件都沒有被刪除
webpack提供了一個插件來幫助我們刪除原來的dist目錄:clean-webpack-plugin 不需要什么特殊的配置,直接添加到plugins選項里就好了

 

 

這樣我們在重新構建之前就會清空dist目錄了

構建包分析

在構建之后我們想要查看我們的構建包里面都包含什么東西,方便我們后期進行優化,這個時候就需要用到包分析插件了,
我們使用webpack-bundle-analyzer來實現這個功能。
同樣的添加到plugins選項中就好了:

 

 

打包結束的時候會自動打開瀏覽器,展示對應的包大小和包里面包含的內容:

 

 

 

構建包拆分

webpack3中拆包使用 common-chunk-plugin拆包 具體用法參考百度 webpack4中通過設置splitChunks參數 splitChunks默認參數:(是在optimization中的一個property)

 splitChunks: {
   // async表示只從異步加載得模塊(動態加載import())里面進行拆分(會拆分出通過懶加載等方式異步加載的模塊)
   // initial表示只從入口模塊進行拆分(入口文件會包含node_modules中的react-dom等包,但是在blog.js中異步加載的marterial等插件就沒有拆分出來 和業務代碼打包成了一個包)
   // all表示以上兩者都包括
   chunks: "async",
   minSize: 30000,   // 大於30k會被webpack進行拆包
   minChunks: 1,     // 被引用次數大於等於這個次數進行拆分
   // import()文件本身算一個
   // 只計算js,不算css
   // 如果同時有兩個模塊滿足cacheGroup的規則要進行拆分,但是maxInitialRequests的值只能允許再拆分一個模塊,那尺寸更大的模塊會被拆分出來
   maxAsyncRequests: 5,  // 最大的按需加載(異步)請求次數
   // 最大的初始化加載請求次數,為了對請求數做限制,不至於拆分出來過多模塊
   // 入口文件算一個
   // 如果這個模塊有異步加載的不算
   // 只算js,不算css
   // 通過runtimeChunk拆分出來的runtime不算在內
   // 如果同時又兩個模塊滿足cacheGroup的規則要進行拆分,但是maxInitialRequests的值只能允許再拆分一個模塊,那尺寸更大的模塊會被拆分出來
   maxInitialRequests: 3,
   automaticNameDelimiter: '~', // 打包分隔符
   name:true,
   cacheGroups: {
       // 默認的配置
       vendors: {
           test: /[\\/]node_modules[\\/]/,
           priority: -10
       },
       // 默認的配置,vendors規則不命中的話,就會命中這里
       default: {
           minChunks: 2, // 引用超過兩次的模塊 -> default
           priority: -20,
           reuseExistingChunk: true
       },
   },
 }

我們這里使用 chunks:"all" 方式拆分的結果如下:

 

 

 

 

 

靜態資源打包

一般我們的項目中的靜態資源是不需要webpack處理的只需要把他們復制到對應的構建目錄中就可以了, 我們使用的插件是:copy-webpack-plugin

 

 

多頁應用打包配置

目前我們的這個應用是單頁應用,如果要是多頁應用的話需要我們增加多個入口和多個出口、以及增加對應的html模板文件 也就是說我們直接改entry,以及增加html模板就好了

  // 首先增加我們的多頁應用 目錄結構如下
  // 我們把不同的頁面的文件拆分開 這樣后期處理起來比較方便,結構也比較清晰
  -src
   - detail
    - index.js
   - index
    - css
     - index.css
    - index.js
  //修改entry 增加入口
    entry:{
        app:'./src/index/index',
        detail:"./src/detail/index"
    },
  // 增加html模板 
    plugins:[
        new HtmlWebpackPlugin({
            filename:path.resolve(__dirname,"../dist/index.html"),
            template:path.resolve(__dirname,"../public/index.html"),
            inject:true,
            chunks:['app'] // 這個和entry中的key對應 代表這個入口html需要引入哪個依賴的文件
        }),
        new HtmlWebpackPlugin({
            filename:path.resolve(__dirname,"../dist/detail.html"),
            template:path.resolve(__dirname,"../public/index.html"),
            inject:true,
            chunks:['detail']
        })
    ]
  

這樣寫的話 當頁面比較少的話可以一個一個加 但是當頁面比較多的時候不夠智能,這個時候我們可以使用glob插件 來直接拿到我們src文件夾下面的page文件夾下面的文件名,然后編寫函數來實現動態的設置入口和html模板

// 安裝glob插件
npm install glob --save-dev 

// build文件夾下面增加util.js 文件
const path = require("path")
const glob = require("glob")

let chunksList = []
let entry = {}

function  getModulesList() {
    //[ '../src/pages/detail', '../src/pages/index' ]
    let modulesList = glob.sync(path.resolve(__dirname,'../src/pages/*'))
    for(let i = 0,len = modulesList.length; i<len;i++){
        let moduleName = modulesList[i].split('/').slice(-1).join()
        chunksList.push(moduleName)
        entry[moduleName] = path.resolve(__dirname,"../src/pages/"+moduleName+'/index.js')
    }
}

getModulesList()


module.exports = {
    resolve:function (dir) {
        return path.resolve(__dirname,dir)
    },
    entry:entry,
    chunks:chunksList
}

通過上面的函數能夠拿到entry,然后同樣也可以得到對應的html模板

其他優化

打包提示信息優化

我們在打包的時候會有build信息打印出來,但是我們並不關注這些東西,所以我們可以把這些信息直接去掉。 具體實現如下:.

 

 

總結

到這里的話我們就已經實現了一個相對完整的webpack構建的項目代碼。其中除了說明怎么配置之外還加入了很多為什么的思考,希望大家看完之后不僅僅知道怎么樣去搭建一個項目,而是知道為什么

思考

  1. 為什么loader的執行順序是從右向左,從下向上(提示:compose與pipe)

  2. terser-webpack-plugin插件和uglifyjs-webpack-plugin插件的區別

 

  •  const arr = [1,3,4,5].map(item=>item*item)
    復制代碼
  • usage:會參考目標瀏覽器(browserslit)和代碼中使用到的特性來按需加入polyfill 使用該參數的話還需要傳一個corejs的版本號 core-js支持兩個版本 2/3 ,很多新特性已經不會加到2里面了,比如:flat等等最新的方法,2版本里面沒有,推薦使用3

  • false: 不引入polyfill

    @babel/preset-react 把react語法轉換成es5

    @babel/plugin-transform-runtime 支持一些es6,es7的新語法 把每個文件中的使用preset-env轉換成的helper函數 --> 轉換成使用babel-runtime中的helper函數,減少包體積

    @babel/runtime 這個包是 在進行es6轉換成es5的時候會生成很多helper函數,這些函數每一個文件轉換都會重復生成,這個包里面會包含這些helper函數,這樣每個文件中引入helper函數的時候就直接引用這個包里面的文件了,減少了每個文件的包體積

    @babel-runtime 和 @babel/plugin-tranform-runtime 兩個是什么關系?
    @babel-runtime是一個核心,一種helper function的實現方式,而@babel/plugin-tranform-runtime 像是一個管家,負責更好的重復使用@babel-runtime


作者:前端小菜雞_求指教
鏈接:https://juejin.im/post/6870312297055649800
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM