webpack4打包nodejs項目進階版——多頁應用模板

前段時間我寫了個打包nodejs項目的文章,點擊前往css

可是,問題不少。由於以前的項目是個歷史遺留項目,重構起來可能會爆炸,當時又比較急因此就寫個的適用範圍很小的webpack的打包方法。html

最近稍微得空,便動了重構的心思,重構第一步固然要把架子搭起來前端

而搭架子的過程也是十分地艱辛啊,終於大概搞定了前端的部分,這一次就分享一下使用最新的webpack4怎麼打包nodejs的多頁應用node

歡迎大佬留言交流,想要源碼的點此前往githubjquery

 

工程目錄

走個流程先上個項目結構圖webpack

 

這裏先說明一下,爲何除了webpack.config.js這個配置文件以外還有一個config文件夾存放相關配置文件git

由於webpack分爲了開發環境和生產環境,二者在配置和表現形式上有所區別,放在一個文件中不利於維護github

這也算是一種解耦吧。web

至於其餘的一些文件我這裏就大概提一下:express

1.babelrc 配置babel-loader 用於將ES6+的JS代碼轉爲ES5的通用JS

2.eslint 主要用於代碼的在線糾錯,以及一些語法錯誤的查找

3.用於 git 的配置配置哪些文件須要上傳到git

4. package.json就是用於設置項目信息,以及項目的依賴

5.postcss 用於配置postcss 主要用於修復瀏覽器兼容的問題

6, yarn 就是一個進階版的npm 能夠並行下載 緩存等(由facebook 研發)

 

以上就是整個架子的大概模板

接下來進入主題——webpack的相關配置

 

cross-env跨平臺設置環境變量

經過cross-env 來判斷當前的環境(即生產環境、開發環境)

用法以下:

 

 在package.json中設置啓動命令

將 NODE_ENV 設置爲不一樣的值

根據該值來判斷當前的環境

 

Webpack.config.js

一般來講該文件就是webpack 的核心配置文件

但爲下降不一樣環境的耦合度,使代碼邏輯更加清晰

我使用這個文件做爲一個「路由」 根據以前的 NODE_ENV 去請求不一樣的webpack配置文件

代碼以下:

爲了兼容VUE等框架因此個人ESlint 設爲不以分號結尾

 

config文件夾

我全部的webpack配置文件夾都存放在該文件夾下

上方要獲取的配置文件都在這裏

個人想法是在base.js 中存放兩種環境的公共代碼

dev.js、prod.js 存放對應環境的特殊配置代碼

最後輸出的文件只能有一個webpack的配置文件

因此使用

webpack-merge

來合併兩個webpack配置文件

 

webpack基礎配置

下面咱們來一 一分析每一個配置文件

首先就是base.js

代碼以下:

/**
 * webpack 基礎配置
 */
const webpack = require('webpack')

const path = require('path')

const fs = require('fs')

const Entries = {} // 保存文件入口
const pages = []// 存放html-webpack-plugin實例
const env = process.env.NODE_ENV !== 'prod' // 判斷運行環境
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入mini-css-extract-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');

//  獲取html-webpack-plugin實例集合
(function () {
  let pagePath = path.join(__dirname, '../src/page')// 定義存放html頁面的文件夾路徑
  let paths = fs.readdirSync(pagePath) // 獲取pagePath路徑下的全部文件
  paths.forEach(page => {
    page = page.split('.')[0]// 獲取文件名(不帶後綴)
    pages.push(new HtmlWebpackPlugin({
      filename: `views/${page}.html`, // 生成的html文件的路徑(基於出口配置裏的path)
      template: path.resolve(__dirname, `../src/page/${page}.html`), // 參考的html模板文件
      chunks: [page, '[name]', 'commons', 'vendors', 'manifest'], // 配置生成的html引入的公共代碼塊 引入順序從右至左
      favicon: path.resolve(__dirname, '../src/img/favicon.ico'), // 配置每一個html頁面的favicon
      minify: {// 配置生成的html文件的壓縮配置
        collapseWhitespace: true,
        collapseInlineTagWhitespace: true,
        conservativeCollapse: true,
        minifyCSS: true,
        minifyJS: true,
        removeComments: true,
        trimCustomFragments: true
      }
    }))
    Entries[page] = path.resolve(__dirname, `../src/js/${page}.js`)// 入口js文件
  })
})()

module.exports = {
  // 配置入口文件
  entry: Entries,
  // 啓用 sourceMap
  devtool: 'cheap-module-source-map',
  // mode爲none表示這是默認配置
  mode: 'none',
  // 配置文件出口
  output: {
    // 將打包好的js輸出到public(靜態資源目錄)下的js文件夾中
    filename: 'public/js/[name].bundle.[hash].js',
    path: path.resolve(__dirname, '../dist'), // 輸出目錄,全部文件的輸出路徑都基於此路徑之上(須要絕對路徑)
    publicPath: '../'
  },
  // 省略文件後綴
  resolve: {
    extensions: ['.js'] // 配置事後,書寫該類文件路徑的時候能夠省略文件後綴
  },
  // loader
  module: {
    rules: [
      // 使用expose處理JQuery(JQ使用npm安裝)配置了這一條後就不要使用external(主要用於cdn引入)
      {
        test: require.resolve('jquery'), // 此loader配置項的目標是NPM中的jquery
        loader: 'expose-loader?$!expose-loader?jQuery' // 先把jQuery對象聲明成爲全局變量`jQuery`,再經過管道進一步又聲明成爲全局變量`$`
      },
      // 處理html中的圖片,考慮到node使用模板的狀況因此不能使用html-loader
      {
        test: /\.html$/,
        use: [{
          loader: 'html-withimg-loader' // 處理img標籤中的圖片
        }]
      },
      // 處理樣式表
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          env ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.(less)$/,
        use: [
          env ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      // 使用babel處理js文件
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: 'babel-loader'
      },
      // 處理圖片
      {
        test: /\.(png|jpg|gif|svg)$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 10000, // 設置圖像大小超過多少轉存爲單獨圖片
            name: 'public/img/[name].[hash].[ext]' // 轉存的圖片目錄
          }
        }]
      },
      // 處理字體
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['url-loader']
      }
    ]
  },
  // 配置插件
  plugins: [
    // 分離tml-webpack-plugin實例數組、引入jq
    ...pages, new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.$': 'jquery',
      'window.jQuery': 'jquery'
    })
  ],
  // 配置webpack執行相關
  performance: {
    maxEntrypointSize: 1000000, // 最大入口文件大小1M
    maxAssetSize: 1000000 // 最大資源文件大小1M
  }
}

關於上述代碼

首先咱們要配置的是入口:

我這裏使用一個函數來遍歷page文件夾中的全部html文件

這裏咱們約定對應html的js與html同名以便咱們自動化生成入口對象

以下圖所示:

這樣咱們就能使用html的名字來設置入口了,獲取的入口對象以下:

該函數的另外一個功能就是,根據html文件使用

html-webpack-plugin

來自動生成咱們的html頁面

 

看到這裏或許有的小夥伴會有疑問,爲啥不用html-loader來解析html文件而後打包進去?

正好我也解答一下一些,node項目中使用ejs等模板的小夥伴的疑問,不是html怎麼辦?

 

緣由以下:

1.我這個架子主要考慮的是node項目,一般來講node不論是作中間層,仍是作全棧都有可能會使用模板引擎

而html-loader沒法解析ejs等模板語法

2.以ejs來舉例,若是我使用ejs-loader來解析呢?若是使用ejs-loader那麼只能適用於用ejs作組件化開發的

狀況,而不能適用於使用ejs作數據渲染(中間層)的狀況

3.那麼在已是ejs等模板的狀況下的node項目怎麼使用個人架子呢?

答案很簡單,在app.js中加入如下代碼(express),若仍是不懂參考我上一篇初級版的webpack

 

 

4 從另外一個方面來講將ejs等文件改成html文件有利於搜索引擎優化(小聲嗶嗶)

 

關於入口和html的問題就解答到這

下一步咱們就應該配置出口了

話很少說先上代碼:

若是是搞node的小夥伴應該知道public(靜態資源目錄)

因此我將webpack打包後的文件輸出到該目錄下

關於publicpath 我這裏用的相對路徑,就是讓webpack-server 的項目根路徑和個人靜態資源文件一致 否則 run dev 的時候會404

 

在這裏提一下JQuery的問題,目前來講jq有三種引入方式

1.cdn 引入

2.import 本地文件

3.expose-loader 暴露出 npm 安裝的jquery

 

這裏我採用的是第三種方法

有幾個好處

1.在頁面中不用顯式地引入jq了 (懶是人類進步的第一輩子產力)

2.使jq也歸入了npm模塊化管理的範疇

3. 前面兩點足夠了,emm

 

代碼以下

 

說完了jq的問題而後就是配置不一樣文件的loader了

 

 詳細代碼在個人github上,幫到大家的小夥伴,跪求星星

 

基礎配置中還有一件事

那就是performance

webpack默認入口點文件不能超過300k

超事後webpack會報warning

沒有強迫症的小夥伴能夠跳過了

有兩個解決辦法:

1.關掉webpack的警告(一看就不能選)

2.設置performance

設置以下:

 

好了基本配置就完成了

接下來要針對,不一樣環境進行獨立的配置

 

開發環境配置

我先講開發環境的配置,生產環境的坑有點多放到最後講

對於開發環境來講,代碼會常常修改並且,咱們須要頻繁地查看樣式,因此咱們並不須要對文件進行壓縮等處理

而且要讓它可以熱更新便可,這裏咱們使用webpack-server

配置代碼以下:

這裏沒啥要注意的,直接按着配,run就行運行出來像下面這樣

頁面以下:

具體的我就不演示了

仍是那句話github見

 

接下來開始重頭戲生產環境的配置

 

 

生產環境

爲啥是重頭戲呢?生產環境那就是線上環境啊,效率、大小就是錢啊

另外呢,主要是webpack4 和 min-css的配合有點問題,我這搭架子的時候搞的我頭皮發麻

我不太清楚這是bug仍是個人操做有啥問題

 

好了,進入正題

關於生產環境,主要的配置是:

1.要可以刪除以前的過時文件,手動刪多low啊

2.要壓縮代碼,用webpack的目的是啥,除了構建自動化的前端工做流以外,最主要的目的無非是壓縮代碼嘛

 

壓縮代碼的好處我這裏就不說了,網上一搜一堆

好了開搞

首先清理過時代碼:

 

這一步就完成了

下一步抽離css樣式

這裏要說一下,webpack4中抽離css要使用

mini-css-extract-plugin

原來的那個在webpack4不能使用

 

這裏我要吐槽一下官網給的示例,坑了我一下

這裏的兩個屬性是相似域output中的同名屬性的,通常來講只用配置一個就行

 

另外可能就是這個插件有點bug

我先說一下我但願達到的效果

我但願將每一個html的全部css做爲一個單獨文件

最好再將css的重複代碼提取一下

若是不將css提取成一個單獨的文件就無法CDN加速了啊

 

可是問題來了無法提取公共css代碼,網上有的說用Extractcss那個插件的@next能夠搞,我試了一下只能不重複打包,不能提取公共代碼

我以爲人家既然專門爲webpack4新出了一個,應該是有過人之處的,因此我就沒有用這個方法

 

我就本身開始折騰,我試着用那個提取js重複代碼的

splitChunks

我試了一下居然能夠處理css,可是有個問題,生成的公共CSS無法自動引入html頁面

由於splitChunks是處理js的無法自動引入css

若是實在有提取公共css需求的小夥伴,頁面又很少的狀況(指你願意手動引入)

不妨試試這種方法

 

主要步驟以下

在spplitChunks中建立緩存組過濾掉全部的js文件

 

而後再建一個優先級很低的緩存組,將剩下的文件中後綴爲css的文件都強制提取到該組

用enforce:true 就能夠提取出來,因爲不是本文主題,也不知道是否是個bug,感興趣的小夥伴能夠留言我私聊,這裏就不過多去講了

 

 

繼續來講,我這提不提取公共css影響不大

因此個人代碼以下:

/**
 * 生產環境配置
 */

const webpackBase = require('./webpack.config.base') // 引入基礎配置
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') //  提取css
const webpackMerge = require('webpack-merge') // 引入 webpack-merge 插件
const CleanWebpackPlugin = require('clean-webpack-plugin') // 清理dist文件夾

// 合併配置文件
module.exports = webpackMerge(webpackBase, {
  plugins: [
    new MiniCssExtractPlugin({// 提取出的Css的相關配置
      filename: 'public/css/[name].[hash].css' // 文件存放路徑
    }),
    new CleanWebpackPlugin(['dist'], {// 自動清理 dist 文件夾
      root: path.resolve(__dirname, '../'), // 根目錄
      verbose: true, // 開啓在控制檯輸出信息
      dry: false // 啓用刪除文件
    })
  ],
  optimization: {
    minimize: true,
    splitChunks: {// 配置提取公共代碼
      chunks: 'all',
      minSize: 30000, // 配置提取塊的最小大小(即不一樣頁面之間公用代碼的大小)
      minChunks: 3, // 最小共享塊數,即公共代碼最少的重複次數通常設爲3
      automaticNameDelimiter: '.', // 生成的名稱指定要使用的分隔符
      cacheGroups: {// 設置緩存組
        vendors: {
          name: 'vendors',
          test (module) {
            let path = module.resource
            return /[\\/]node_modules[\\/]/.test(path) || /[\\/]lib[\\/]/.test(path)
          },
          priority: 30
        },
        commons: {
          name: 'commons',
          test: /\.js$/,
          enforce: true,
          priority: 20
        }
      }
    },
    runtimeChunk: {
      name: 'manifest' // 打包運行文件
    }
  }
})

 

這裏我爲js設置了兩個緩存組,並提取出了運行時的manifest

一個是依賴的插件等js(知足3個頁面引用)生成 vender.js

不知足3個或本身寫的js提取到commons.js中

 

結語

以上就是webpack4 打包 nodejs 項目的架子,若是幫到你的小夥伴能夠,關注我、收藏走一波

須要代碼的小夥伴請移步github,原創不易,望支持

github連接

順便再給個人JS高編讀書筆記系列文章打個廣告

有什麼問題,歡迎留言,也歡迎大佬指正,共同進步。

相關文章
相關標籤/搜索