前段時間我寫了個打包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 來判斷當前的環境(即生產環境、開發環境)
用法以下:
在package.json中設置啓動命令
將 NODE_ENV 設置爲不一樣的值
根據該值來判斷當前的環境
一般來講該文件就是webpack 的核心配置文件
但爲下降不一樣環境的耦合度,使代碼邏輯更加清晰
我使用這個文件做爲一個「路由」 根據以前的 NODE_ENV 去請求不一樣的webpack配置文件
代碼以下:
爲了兼容VUE等框架因此個人ESlint 設爲不以分號結尾
我全部的webpack配置文件夾都存放在該文件夾下
上方要獲取的配置文件都在這裏
個人想法是在base.js 中存放兩種環境的公共代碼
dev.js、prod.js 存放對應環境的特殊配置代碼
最後輸出的文件只能有一個webpack的配置文件
因此使用
webpack-merge
來合併兩個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了
基礎配置中還有一件事
那就是performance
webpack默認入口點文件不能超過300k
超事後webpack會報warning
沒有強迫症的小夥伴能夠跳過了
有兩個解決辦法:
1.關掉webpack的警告(一看就不能選)
2.設置performance
設置以下:
好了基本配置就完成了
接下來要針對,不一樣環境進行獨立的配置
我先講開發環境的配置,生產環境的坑有點多放到最後講
對於開發環境來講,代碼會常常修改並且,咱們須要頻繁地查看樣式,因此咱們並不須要對文件進行壓縮等處理
而且要讓它可以熱更新便可,這裏咱們使用webpack-server
配置代碼以下:
這裏沒啥要注意的,直接按着配,run就行運行出來像下面這樣
頁面以下:
具體的我就不演示了
接下來開始重頭戲生產環境的配置
爲啥是重頭戲呢?生產環境那就是線上環境啊,效率、大小就是錢啊
另外呢,主要是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,原創不易,望支持
順便再給個人JS高編讀書筆記系列文章打個廣告
有什麼問題,歡迎留言,也歡迎大佬指正,共同進步。