繼 24 個實例入門並掌握「Webpack4」(一) 後續:css
demo9 源碼地址html
什麼是 Tree Shaking?vue
字面意思是搖樹,項目中沒有使用的代碼會在打包的時候丟掉。JS 的 Tree Shaking 依賴的是 ES6 的模塊系統(好比:import 和 export)node
項目目錄以下:jquery
在 util.js 文件中寫入測試代碼webpack
// util.js export function a() { return 'this is function "a"' } export function b() { return 'this is function "b"' } export function c() { return 'this is function "c"' }
在 app.js 中引用 util.js 的 function a() 函數,按需引入:ios
// app.js import { a } from './vendor/util' console.log(a())
命令行運行 webpack 打包後,打開打包後生成的 /dist/app.bundle.js 文件。查找咱們 a()
函數輸出的字符串,以下圖所示:nginx
若是將查找內容換成 this is function "c"
或者 this is function "b"
, 並無相關查找結果。說明 JS Tree Shaking 成功。git
1. 如何處理第三方 JS 庫?github
對於常用的第三方庫(例如 jQuery、lodash 等等),如何實現 Tree Shaking ?
下面以 lodash.js 爲例,進行介紹。
安裝 lodash.js : npm install lodash --save
在 app.js 中引用 lodash.js 的一個函數:
// app.js import { chunk } from 'lodash' console.log(chunk([1, 2, 3], 2))
命令行打包。以下圖所示,打包後大小是 70kb。顯然,只引用了一個函數,不該該這麼大。並無進行 Tree Shaking。
開頭講過,js tree shaking 利用的是 ES 的模塊系統。而 lodash.js 使用的是 CommonJS 而不是ES6 的寫法。因此,安裝對應的模塊系統便可。
安裝 lodash.js 的 ES 寫法的版本:npm install lodash-es --save
修改一下 app.js:
// app.js import { chunk } from 'lodash-es' console.log(chunk([1, 2, 3], 2))
再次打包,打包結果只有 3.5KB(以下圖所示)。顯然,tree shaking 成功。
在一些對加載速度敏感的項目中使用第三方庫,請注意庫的寫法是否符合 ES 模板系統規範,以方便 webpack 進行 tree shaking。
CSS Tree Shaking 並不像 JS Tree Shaking 那樣方便理解,首先要模擬一個真實的項目環境,來體現 CSS 的 Tree Shaking 的配置和效果。
此章節源碼基於第八節處理 CSS 項目上作修改
咱們首先編寫 /src/css/base.css 樣式文件,在文件中,咱們編寫了 3 個樣式類。但在代碼中,咱們只會使用 .box 和 .box--big 這兩個類。代碼以下所示:
/* base.css */ html { background: red; } .box { height: 200px; width: 200px; border-radius: 3px; background: green; } .box--big { height: 300px; width: 300px; border-radius: 5px; background: red; } .box-small { height: 100px; width: 100px; border-radius: 2px; background: yellow; }
按照正常使用習慣,DOM 操做來實現樣式的添加和卸載,是一向技術手段。因此,入口文件 /src/app.js
中建立了一個 <div>
標籤,而且將它的類設爲 .box
// app.js import base from './css/base.css' // 給 app 標籤再加一個 div 而且類名爲 box var app = document.getElementById('app') var div = document.createElement('div') div.className = 'box' app.appendChild(div)
最後,爲了讓環境更接近實際環境,咱們在 index.html
的一個標籤,也引用了定義好的 box-big 樣式類。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>CSS Tree Shaking</title> </head> <body> <div id="app"> <div class="box-big"></div> </div> </body> </html>
PurifyCSS將幫助咱們進行 CSS Tree Shaking 操做。爲了能準確指明要進行 Tree Shaking 的 CSS 文件,還有 glob-all (另外一個第三方庫)。
glob-all 的做用就是幫助 PurifyCSS 進行路徑處理,定位要作 Tree Shaking 的路徑文件。
安裝依賴:
npm i glob-all purify-css purifycss-webpack --save-dev
更改配置文件:
const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 將 css 單獨打包成文件 const PurifyCSS = require('purifycss-webpack') const glob = require('glob-all') module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路徑或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的輸出目錄 filename: '[name].bundle.js', // 代碼打包後的文件名 chunkFilename: '[name].js' // 代碼拆分後的文件名 }, module: { rules: [ { test: /\.css$/, // 針對 .scss 或者 .css 後綴的文件設置 loader use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包輸出HTML title: '自動生成 HTML', minify: { // 壓縮 HTML 文件 removeComments: true, // 移除 HTML 中的註釋 collapseWhitespace: true, // 刪除空白符與換行符 minifyCSS: true // 壓縮內聯 css }, filename: 'index.html', // 生成後的文件名 template: 'index.html', // 根據此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才會被打包 }), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }), // 清除無用 css new PurifyCSS({ paths: glob.sync([ // 要作 CSS Tree Shaking 的路徑文件 path.resolve(__dirname, './*.html'), // 請注意,咱們一樣須要對 html 文件進行 tree shaking path.resolve(__dirname, './src/*.js') ]) }) ] }
打包完查看 dist/app.css 文件
在 index.html 和 src/app.js 中引用的樣式都被打包了,而沒有被使用的樣式類–box-small,沒有被打包進去
目錄結構:
webpack4 中的圖片經常使用的基礎操做:
如項目代碼目錄展現的那樣,除了常見的 app.js
做爲入口文件,咱們將用到的 3 張圖片放在 /src/assets/imgs/
目錄下,並在樣式文件 base.css
中引用這些圖片。
剩下的內容交給 webpack
打包處理便可。樣式文件和入口 app.js
文件的代碼分別以下所示:
/* base.css */ *, body { margin: 0; padding: 0; } .box { height: 400px; width: 400px; border: 5px solid #000; color: #000; } .box div { width: 100px; height: 100px; float: left; } .box .ani1 { background: url('./../assets/imgs/1.jpg') no-repeat; } .box .ani2 { background: url('./../assets/imgs/2.png') no-repeat; } .box .ani3 { background: url('./../assets/imgs/3.png') no-repeat; }
在 app.js
中
import './css/base.css'
安裝依賴:
npm install url-loader file-loader --save-dev
在 webpack.config.js
中的 module.rules 選項中進行配置,以實現讓 loader 識別圖片後綴名,而且進行指定的處理操做。
module.exports = { module: { rules: [ { test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', outputPath: 'images/', //輸出到 images 文件夾 limit: 20000 //把小於 20kb 的文件轉成 Base64 的格式 } } ] } ] } }
完整的配置文件
const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 將 css 單獨打包成文件 module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路徑或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的輸出目錄 filename: '[name].bundle.js', // 代碼打包後的文件名 chunkFilename: '[name].js' // 代碼拆分後的文件名 }, module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader' ] }, { test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', outputPath: 'images/', //輸出到 images 文件夾 limit: 20000 //把小於 20kb 的文件轉成 Base64 的格式 } } ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包輸出HTML title: '自動生成 HTML', minify: { // 壓縮 HTML 文件 removeComments: true, // 移除 HTML 中的註釋 collapseWhitespace: true, // 刪除空白符與換行符 minifyCSS: true // 壓縮內聯 css }, filename: 'index.html', // 生成後的文件名 template: 'index.html', // 根據此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才會被打包 }), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }) ] }
打包項目,查看打包結果,並在瀏覽器中打開 index.html
文件
能夠看到除了 1.jpg,另外兩張圖片已經被打包成 base64
格式,在 app.css
文件中
1.jpg 這個文件超過咱們在 url-loader 選項中設置的 limit 值,因此被單獨打包
這就是利用了 file-loader 的能力,若是在 url-loader 中設置了 limit 的值,卻沒有安裝 file-loader 依賴,會怎麼樣?來試試看,首先卸載 file-loader 依賴,npm uninstall file-loader
,再運行打包命令,npm run build
若是圖片較多,會發不少 http 請求,會下降頁面性能。url-loader 會將引入的圖片編碼,轉爲
base64
字符串。再把這串字符打包到文件中,最終只須要引入這個文件就能訪問圖片了,節省了圖片請求。
可是,若是圖片較大,編碼會消耗性能。所以 url-loader 提供了一個 limit 參數,小於 limit 字節的文件會被轉爲 base64
,大於 limit 的使用 file-loader 進行處理,單獨打包。
url-loader 依賴 file-loader,url-loader 能夠看做是加強版的 file-loader
圖片壓縮須要使用 img-loader 插件,除此以外,針對不一樣的圖片類型,還要引用不一樣的插件。好比,咱們項目中使用的是 png 圖片,所以,須要引入 imagemin-pngquant
,而且指定壓縮率。壓縮 jpg/jpeg 圖片爲 imagemin-mozjpeg
插件
這裏有個 bug,能夠先不急着操做,先把這一小節看完,再決定!!
安裝依賴
npm i img-loader imagemin imagemin-pngquant imagemin-mozjpeg --save-dev
在以前的配置上更改:
{ test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', outputPath: 'images/', // 輸出到 images 文件夾 limit: 20000 //把小於 20kb 的文件轉成 Base64 的格式 } } ] }
更改成:
{ test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 1000, // size <= 1KB outputPath: 'images/' } }, // img-loader for zip img { loader: 'img-loader', options: { plugins: [ require('imagemin-pngquant')({ quality: '80' // the quality of zip }), require('imagemin-mozjpeg')({ quality: '80' }) ] } } ] }
打包結果:
緣由在 png 圖片上,jpg 圖片能夠壓縮,可是去 imagemin-pngquant github 上也沒發現有人提出相似 issue ,百度、google 找了半天,仍是沒發現怎麼解決 😭,因而使用另外一種壓縮圖片的插件 image-webpack-loader
首先卸載了以前的依賴:
npm uni img-loader imagemin imagemin-pngquant imagemin-mozjpeg
安裝依賴:
npm i image-webpack-loader --save-dev
這個依賴安裝的時間會比較久。。。能夠先去作別的。。。
在以前的配置上更改:
{ test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', outputPath: 'images/', // 輸出到 images 文件夾 limit: 20000 //把小於 20kb 的文件轉成 Base64 的格式 } } ] }
更改成:
{ test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 1000, // size <= 1KB outputPath: 'images/' } }, // img-loader for zip img { loader: 'image-webpack-loader', options: { // 壓縮 jpg/jpeg 圖片 mozjpeg: { progressive: true, quality: 65 // 壓縮率 }, // 壓縮 png 圖片 pngquant: { quality: '65-90', speed: 4 } } } ] }
這裏故意把 url-loader 的 limit 屬性值設的很小,不讓它轉化 png 圖片爲 base64
,由於咱們要測試壓縮 png 圖片
打包結果:
圖片壓縮成功,這裏我仔細看了下image-webpack-loader 的 github,其實這個 image-webpack-loader
插件內置了好幾種圖片壓縮的插件
這裏讓我很疑惑,爲何我直接安裝 imagemin-pngquant
不行,反而使用 image-webpack-loader
卻能夠,因而我去查看 package-lock.json
文件,搜索 image-webpack-loader
我看了下我以前安裝的是最新的版本, ^7.0.0 !!!
阿西吧... 終於找到問題所在,新版本有些問題沒處理好,致使壓縮 png 圖片失敗,知道問題就好辦了,在 package.json 中,將 imagemin-pngquant
版本改成 ^6.0.0,從新 npm install
再按照以前的操做,就能夠壓縮成了,對應版本以下:
{ "devDependencies": { "imagemin": "^6.1.0", "imagemin-mozjpeg": "^8.0.0", "imagemin-pngquant": "^6.0.0", "img-loader": "^3.0.1" } }
若是使用 image-webpack-loader
,版本爲 4.6.0
,引入的依賴版本也在白框內
此次我仍是使用 image-webpack-loader
,朋友們能夠自行選擇使用哪一個插件,只是 image-webpack-loader
引入了其餘圖片格式壓縮的依賴,如 svg/webp/gif 等,只安裝 image-webpack-loader
就夠了,而另外一種則是要一個個插件裝過去,其實原理都同樣
通過此次調試,明白 並非最新的版本就是最好的,新版本也許有哪些地方沒處理好,或者是不能兼容其餘插件,致使報錯因此安裝第三方依賴的時候,仍是要謹慎一點,npm install 默認是安裝最新版,若是出了問題,回滾到以前的穩定版,不只僅適用於
webpack
插件,對於其餘軟件或者工具也是這樣
寫這一小節的時間爲:2019-3-9
,以後的版本變更出現報錯的話,能夠不用安裝最新版,回滾到以前的版本試試
安裝依賴:
npm i postcss-loader postcss-sprites --save-dev
完整配置:
const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 將 css 單獨打包成文件 /*********** sprites config ***************/ let spritesConfig = { spritePath: './dist/images' } /******************************************/ module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路徑或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的輸出目錄 filename: '[name].bundle.js', // 代碼打包後的文件名 chunkFilename: '[name].js' // 代碼拆分後的文件名 }, module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader', /*********** loader for sprites ***************/ { loader: 'postcss-loader', options: { ident: 'postcss', plugins: [require('postcss-sprites')(spritesConfig)] } } /*********************************************/ ] }, { test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 1000, // size <= 1KB outputPath: 'images/' } }, // img-loader for zip img { loader: 'image-webpack-loader', options: { // 壓縮 jpg/jpeg 圖片 mozjpeg: { progressive: true, quality: 65 // 壓縮率 }, // 壓縮 png 圖片 pngquant: { quality: '65-90', speed: 4 } } } ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包輸出HTML title: '自動生成 HTML', minify: { // 壓縮 HTML 文件 removeComments: true, // 移除 HTML 中的註釋 collapseWhitespace: true, // 刪除空白符與換行符 minifyCSS: true // 壓縮內聯 css }, filename: 'index.html', // 生成後的文件名 template: 'index.html', // 根據此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才會被打包 }), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }) ] }
打包後查看結果:
雪碧圖是爲了減小網絡請求,因此被處理雪碧圖的圖片多爲各式各樣的 logo 或者大小相等的小圖片。
而對於大圖片,不推薦使用雪碧圖。這樣會使得圖片體積很大
除此以外,雪碧圖要配合 css 代碼進行定製化使用。要經過 css 代碼在雪碧圖上精準定位須要的圖片
項目目錄爲:
package.json 中使用的依賴以下:
{ "scripts": { "dev": "webpack --mode development", "build": "webpack --mode production" }, "devDependencies": { "clean-webpack-plugin": "^2.0.0", "css-loader": "^2.1.0", "file-loader": "^3.0.1", "url-loader": "^1.1.2", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.5.0", "webpack": "^4.29.6", "webpack-cli": "^3.2.3" } }
app.js 中引入字體文件
import './assets/fonts/iconfont.css'
配置 webpack.config.js 文件來處理字體
藉助 url-loader,能夠識別而且處理 eot、woff 等結尾的字體文件。同時,根據字體文件大小,能夠靈活配置是否進行 base64 編碼。
下面的 demo 就是當文件大小小於 5000B 的時候,進行 base64 編碼。
const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 將 css 單獨打包成文件 module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路徑或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的輸出目錄 filename: '[name].bundle.js', // 代碼打包後的文件名 chunkFilename: '[name].js' // 代碼拆分後的文件名 }, module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader' ] }, { test: /\.(eot|woff2?|ttf|svg)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file publicPath: 'fonts/', outputPath: 'fonts/' } } ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包輸出HTML title: '自動生成 HTML', minify: { // 壓縮 HTML 文件 removeComments: true, // 移除 HTML 中的註釋 collapseWhitespace: true, // 刪除空白符與換行符 minifyCSS: true // 壓縮內聯 css }, filename: 'index.html', // 生成後的文件名 template: 'index.html', // 根據此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才會被打包 }), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }) ] }
在 index.html 中使用字體
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>處理字體文件</title> </head> <body> <div id="app"> <div class="box"> <i class="iconfont icon-xiazai"></i> <i class="iconfont icon-shoucang"></i> <i class="iconfont icon-erweima"></i> <i class="iconfont icon-xiangshang"></i> <i class="iconfont icon-qiehuanzuhu"></i> <i class="iconfont icon-sort"></i> <i class="iconfont icon-yonghu"></i> </div> </div> </body> </html>
打包後查看 index.html 文件,打包成功
項目目錄:
1. 如何使用和管理第三方 JS 庫?
項目作大以後,開發者會更多專一在業務邏輯上,其餘方面則盡力使用第三方 JS 庫來實現。
因爲 js 變化實在太快,因此出現了多種引入和管理第三方庫的方法,經常使用的有 3 中:
<script></script>
標籤引入便可針對第三種方法,若是沒有 webpack,則須要手動引入 import 或者 require 來加載文件;可是,webpack 提供了 alias 的配置,配合 webpack.ProvidePlugin 這款插件,能夠跳過手動入,直接使用!
2. 編寫入口文件
如項目目錄圖片所展現的,咱們下載了 jquery.min.js,放到了項目中。同時,咱們也經過 npm 安裝了 jquery。
爲了儘量模仿生產環境,app.js 中使用了 $ 來調用 jq,還使用了 jQuery 來調用 jq。
由於正式項目中,因爲須要的依賴過多,掛載到 window 對象的庫,很容易發生命名衝突問題。此時,就須要重命名庫。例如:$ 就被換成了 jQuery。
在 app.js 中進行修改
// app.js $('div').addClass('new') jQuery('div').addClass('old') // 運行webpack後 // 瀏覽器打開 index.html, 查看 div 標籤的 class
webpack.ProvidePlugin 參數是鍵值對形式,鍵就是咱們項目中使用的變量名,值就是鍵所指向的庫。
webpack.ProvidePlugin 會先從 npm 安裝的包中查找是否有符合的庫
若是 webpack 配置了 resolve.alias 選項(理解成 「別名」),那麼 webpack.ProvidePlugin 就會順着這條鏈一直找下去。
const path = require('path') const webpack = require('webpack') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路徑或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的輸出目錄 filename: '[name].bundle.js', // 代碼打包後的文件名 chunkFilename: '[name].js' // 代碼拆分後的文件名 }, resolve: { alias: { jQuery$: path.resolve(__dirname, 'src/vendor/jquery.min.js') } }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包輸出HTML title: '自動生成 HTML', minify: { // 壓縮 HTML 文件 removeComments: true, // 移除 HTML 中的註釋 collapseWhitespace: true, // 刪除空白符與換行符 minifyCSS: true // 壓縮內聯 css }, filename: 'index.html', // 生成後的文件名 template: 'index.html', // 根據此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才會被打包 }), new webpack.ProvidePlugin({ $: 'jquery', // npm jQuery: 'jQuery' // 本地Js文件 }) ] }
修改 index.html 文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>處理第三方 js 庫</title> </head> <body> <div></div> </body> </html>
打包並在 Chrome 中打開 index.html。以下圖所示,<div>
標籤已經被添加上了 old 和 new 兩個樣式類。證實在 app.js 中使用的 $ 和 jQuery 都成功指向了 jquery 庫。
1. 爲何須要開發模式?
這十幾節來咱們使用最多的就是生產環境,也就是執行 npm run build
命令,打包項目中的各類文件及壓縮
而開發模式就是指定 mode 爲 development。對應咱們在 package.json
中配置的,就是 npm run dev
,在第二小節也涉及到了這一點
在開發模式下,咱們須要對代碼進行調試。對應的配置就是:devtool 設置爲 source-map。在非開發模式下,須要關閉此選項,以減少打包體積。詳情見: devtool
在開發模式下,還須要熱重載、路由重定向、設置代理等功能,webpack4 已經提供了 devServer 選項,啓動一個本地服務器,讓開發者使用這些功能。
目錄結構:
安裝依賴
npm i webpack-dev-server --save-dev
修改 package.json
{ "scripts": { "dev": "webpack-dev-server --open", "build": "webpack --mode production" }, "devDependencies": { "clean-webpack-plugin": "^2.0.0", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "jquery": "^3.3.1", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" } }
由於咱們在 package.json 中配置了 script,因此開啓開發模式直接 npm run dev
便可
雖然控制檯輸出了打包信息(假設咱們已經配置了熱重載),可是磁盤上並無建立 /dist/ 文件夾和打包文件。控制檯的打包文件的相關內容是存儲在內存之中的。
修改 index.html 文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>webpack-dev-server</title> </head> <body> This is Index html </body> </html>
按照項目目錄,簡單封裝下 /vendor/ 下的三個 js 文件,以方便 app.js 調用:
// minus.js module.exports = function(a, b) { return a - b } // multi.js define(function(require, factory) { 'use strict' return function(a, b) { return a * b } }) // sum.js export default function(a, b) { console.log('I am sum.js') return a + b }
app.js 中使用三種引入方式引入 js 文件:
import sum from './vendor/sum' console.log('sum(1, 2) = ', sum(1, 2)) var minus = require('./vendor/minus') console.log('minus(1, 2) = ', minus(1, 2)) require(['./vendor/multi'], function(multi) { console.log('multi(1, 2) = ', multi(1, 2)) })
如今開始更改 webpack.config.js, 完整的配置文件以下:
const webpack = require('webpack') const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: { app: './src/app.js' }, output: { publicPath: '/', // js 引用的路徑或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的輸出目錄 filename: '[name].bundle.js', // 代碼打包後的文件名 chunkFilename: '[name].js' // 代碼拆分後的文件名 }, mode: 'development', // 開發模式 devtool: 'source-map', // 開啓調試 devServer: { contentBase: path.join(__dirname, 'dist'), port: 8000, // 本地服務器端口號 hot: true, // 熱重載 overlay: true, // 若是代碼出錯,會在瀏覽器頁面彈出「浮動層」。相似於 vue-cli 等腳手架 proxy: { // 跨域代理轉發 '/comments': { target: 'https://m.weibo.cn', changeOrigin: true, logLevel: 'debug', headers: { Cookie: '' } } }, historyApiFallback: { // HTML5 history模式 rewrites: [{ from: /.*/, to: '/index.html' }] } }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包輸出HTML title: '自動生成 HTML', minify: { // 壓縮 HTML 文件 removeComments: true, // 移除 HTML 中的註釋 collapseWhitespace: true, // 刪除空白符與換行符 minifyCSS: true // 壓縮內聯 css }, filename: 'index.html', // 生成後的文件名 template: 'index.html', // 根據此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才會被打包 }), new webpack.HotModuleReplacementPlugin(), // 熱部署模塊 new webpack.NamedModulesPlugin(), new webpack.ProvidePlugin({ $: 'jquery', // npm jQuery: 'jQuery' // 本地Js文件 }) ] }
對上面的配置進行單獨分析:
模塊熱更新須要 HotModuleReplacementPlugin 和 NamedModulesPlugin 這兩個插件,而且順序不能錯,而且指定 devServer.hot 爲 true,
const webpack = require('webpack') // 引入 webpack module.exports = { plugins: [ new webpack.HotModuleReplacementPlugin(), // 熱部署模塊 new webpack.NamedModulesPlugin() ] }
有了這兩個插件,在項目的 js 代碼中能夠針對偵測到變動的文件而且作出相關處理,也就不用寫完代碼從新刷新頁面,它會自動檢測變動的代碼而且在頁面上更改
注意是 js 代碼,若是你去改動 index.html 文件,保存後,頁面並不會更改,反之你去修改了 js 文件,保存後,頁面會更新
好比,咱們啓動開發模式後,修改了 vendor/sum.js
這個文件,此時,須要在瀏覽器的控制檯打印一些信息。那麼,app.js 中就能夠這麼寫:
if (module.hot) { // 檢測是否有模塊熱更新 module.hot.accept('./vendor/sum.js', function() { // 針對被更新的模塊, 進行進一步操做 console.log('/vendor/sum.js is changed') }) }
每當 sum.js 被修改後,均可以自動執行回調函數。
瀏覽器控制檯輸出信息以下:
可是咱們平常開發中使用 vue 腳手架根本沒有寫過這樣的代碼,也能熱更新,是由於 vue-loader 中內置了這種方法,css-loader 中也有,因此咱們改完 js 和 css 代碼就能直接看到更新
隨着先後端分離開發的普及,跨域請求變得愈來愈常見。爲了快速開發,能夠利用 devServer.proxy 作一個代理轉發,來繞過瀏覽器的跨域限制。
devServer 模塊的底層是使用了 http-proxy-middleware,能配置的東西很是多
按照前面的配置文件,若是想調用微博的一個接口:https://m.weibo.cn/comments/h...。只須要在代碼中對 /comments/hotflow 進行請求便可,在 app.js 中添加以下代碼:
$.get( '/comments/hotflow', { id: '4263554020904293', mid: '4263554020904293', max_id_type: '0' }, function(data) { console.log(data) } )
上面代碼是使用 jQuery 發送 get 請求,若是是在 vue 項目中,通常是使用 axios 來發送請求
修改完 app.js 後保存,打開以前的 localhost:8000 網頁,能夠看到 Network 發送的請求
當項目使用 HTML5 History API 時,任意的 404 響應均可能須要被替代爲 index.html。
在 SPA(單頁應用)中,任何響應直接被替代爲 index.html。
在 vuejs 官方的腳手架 vue-cli 中,開發模式下配置以下:
historyApiFallback: { // HTML5 history模式 rewrites: [{ from: /.*/, to: '/index.html' }] }
最終 app.js 中的代碼以下:
import sum from './vendor/sum' console.log('sum(1, 2) = ', sum(1, 2)) var minus = require('./vendor/minus') console.log('minus(1, 2) = ', minus(1, 2)) require(['./vendor/multi'], function(multi) { console.log('multi(1, 2) = ', multi(1, 2)) }) $.get( '/comments/hotflow', { id: '4263554020904293', mid: '4263554020904293', max_id_type: '0' }, function(data) { console.log(data) } ) if (module.hot) { // 檢測是否有模塊熱更新 module.hot.accept('./vendor/sum.js', function() { // 針對被更新的模塊, 進行進一步操做 console.log('/vendor/sum.js is changed') }) }
打開控制檯,能夠看到代碼都正常運行沒有出錯。除此以外,因爲開啓了 source-map,因此能夠定位代碼位置(下圖紅框內):
參考文章: webpack4 系列教程 (十五):開發模式與 webpack-dev-server
首先,新建一個文件夾:demo15,執行 npm init -y
初始化 package.json
,生成後的文件以下:
{ "name": "example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
咱們先將無用的代碼清除掉,只留下關鍵代碼:
{ "scripts": {} }
首先安裝 webpack 所需依賴
npm i webpack webpack-cli webpack-dev-server --save-dev
安裝 babel7,由於目前主要是用 ES6 來編寫代碼,因此須要轉譯
npm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime --save-dev
npm i @babel/polyfill @babel/runtime
如今 package.json 中的依賴爲:
{ "scripts": {}, "devDependencies": { "@babel/core": "^7.3.4", "@babel/plugin-transform-runtime": "^7.3.4", "@babel/preset-env": "^7.3.4", "babel-loader": "^8.0.5", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" }, "dependencies": { "@babel/polyfill": "^7.2.5", "@babel/runtime": "^7.3.4" } }
新建 .babelrc 來配置 babel 插件,代碼以下:
{ "presets": ["@babel/preset-env"], "plugins": ["@babel/plugin-transform-runtime"] }
新建 .browserslistrc 文件配置該項目所支持的瀏覽器版本
# 所支持的瀏覽器版本 > 1% # 全球使用狀況統計選擇的瀏覽器版本 last 2 version # 每一個瀏覽器的最後兩個版本 not ie <= 8 # 排除小於 ie8 及如下的瀏覽器
在開始配置 webpack.config.js 文件以前,須要注意一下,由於如今咱們是有兩種模式, production(生產) 和 development(開發) 模式。
安裝自動生成 html 依賴
npm i html-webpack-plugin html-loader clean-webpack-plugin --save-dev
安裝 css/字體圖標處理依賴
npm i css-loader style-loader mini-css-extract-plugin optimize-css-assets-webpack-plugin --save-dev
安裝 scss 處理依賴
npm i node-sass sass-loader --save-dev
爲不一樣內核的瀏覽器加上 CSS 前綴
npm install postcss-loader autoprefixer --save-dev
圖片及字體處理:
npm i url-loader file-loader image-webpack-loader --save-dev
第三方 js 庫
npm i jquery
如今 package.json 爲:
{ "scripts": {}, "devDependencies": { "@babel/core": "^7.3.4", "@babel/plugin-transform-runtime": "^7.3.4", "@babel/preset-env": "^7.3.4", "autoprefixer": "^9.4.10", "babel-loader": "^8.0.5", "clean-webpack-plugin": "^2.0.0", "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "image-webpack-loader": "^4.6.0", "mini-css-extract-plugin": "^0.5.0", "node-sass": "^4.11.0", "optimize-css-assets-webpack-plugin": "^5.0.1", "postcss-loader": "^3.0.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" }, "dependencies": { "@babel/polyfill": "^7.2.5", "@babel/runtime": "^7.3.4", "jquery": "^3.3.1" } }
以前咱們大多都是寫生產模式,也就是常常說的打包,可是咱們平常開發項目,用的是開發模式。
只有在項目作完後,要部署到 nginx 上的時候才使用生產模式,將代碼打包後放到 nginx 中
之因此要分兩種模式是由於,開發模式下,須要加快編譯的速度,能夠熱更新以及設置跨域地址,開啓源碼調試(devtool: 'source-map')
而生成模式下,則須要壓縮 js/css 代碼,拆分公共代碼段,拆分第三方 js 庫等操做
因此這裏的配置咱們分紅三個文件來寫,一個是生產配置,一個是開發配置,最後一個是基礎配置
即:webpack.base.conf.js(基礎配置)、webpack.dev.conf.js(開發配置)、webpack.prod.conf.js(生產配置)
新建 build 文件夾,建立上述三個文件,項目結構爲:
這裏須要使用到一個插件,webpack-merge 用來合併配置,好比開發環境就合併開發配置 + 基礎配置,生產就合併生產配置 + 基礎配置
npm i webpack-merge --save-dev
先簡單寫個 webpack.base.conf.js 的示例代碼
const merge = require('webpack-merge') const productionConfig = require('./webpack.prod.conf') // 引入生產環境配置文件 const developmentConfig = require('./webpack.dev.conf') // 引入開發環境配置文件 const baseConfig = {} // ... 省略 module.exports = env => { let config = env === 'production' ? productionConfig : developmentConfig return merge(baseConfig, config) // 合併 公共配置 和 環境配置 }
在代碼中區分不一樣環境:
module.exports = env => { let config = env === 'production' ? productionConfig : developmentConfig return merge(baseConfig, config) // 合併 公共配置 和 環境配置 }
這裏的 env 在 package.json 中進行配置,修改 scripts,添加 "dev" 和 "build" 命令
注意,這裏有個 --env 字段,與 webpack.base.conf.js 中的 env 是聯動的,告訴它當前是什麼環境,而後合併成什麼環境
{ "scripts": { "dev": "webpack-dev-server --env development --open --config build/webpack.base.conf.js", "build": "webpack --env production --config build/webpack.base.conf.js" } }
const webpack = require('webpack') const merge = require('webpack-merge') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 將 css 單獨打包成文件 const CleanWebpackPlugin = require('clean-webpack-plugin') const path = require('path') const productionConfig = require('./webpack.prod.conf.js') // 引入生產環境配置文件 const developmentConfig = require('./webpack.dev.conf.js') // 引入開發環境配置文件 /** * 根據不一樣的環境,生成不一樣的配置 * @param {String} env "development" or "production" */ const generateConfig = env => { // 將須要的 Loader 和 Plugin 單獨聲明 let scriptLoader = [ { loader: 'babel-loader' } ] let cssLoader = [ 'style-loader', 'css-loader', 'sass-loader', // 使用 sass-loader 將 scss 轉爲 css 'postcss-loader' // 使用 postcss 爲 css 加上瀏覽器前綴 ] let cssExtractLoader = [ { loader: MiniCssExtractPlugin.loader }, 'css-loader', 'sass-loader', // 使用 sass-loader 將 scss 轉爲 css 'postcss-loader' // 使用 postcss 爲 css 加上瀏覽器前綴 ] let fontLoader = [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file publicPath: 'fonts/', outputPath: 'fonts/' } } ] let imageLoader = [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 10000, // size <= 10KB outputPath: 'images/' } }, // 圖片壓縮 { loader: 'image-webpack-loader', options: { // 壓縮 jpg/jpeg 圖片 mozjpeg: { progressive: true, quality: 50 // 壓縮率 }, // 壓縮 png 圖片 pngquant: { quality: '65-90', speed: 4 } } } ] let styleLoader = env === 'production' ? cssExtractLoader // 生產環境下壓縮 css 代碼 : cssLoader // 開發環境:頁內樣式嵌入 return { entry: { app: './src/app.js' }, output: { publicPath: env === 'development' ? '/' : './', path: path.resolve(__dirname, '..', 'dist'), filename: '[name]-[hash:5].bundle.js', chunkFilename: '[name]-[hash:5].chunk.js' }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, use: scriptLoader }, { test: /\.(sa|sc|c)ss$/, use: styleLoader }, { test: /\.(eot|woff2?|ttf|svg)$/, use: fontLoader }, { test: /\.(png|jpg|jpeg|gif)$/, use: imageLoader } ] }, plugins: [ // 開發環境和生產環境兩者均須要的插件 new HtmlWebpackPlugin({ title: 'webpack4 實戰', filename: 'index.html', template: path.resolve(__dirname, '..', 'index.html'), // chunks: ['app'], minify: { collapseWhitespace: true } }), new webpack.ProvidePlugin({ $: 'jquery' }), new CleanWebpackPlugin() ] } } module.exports = env => { let config = env === 'production' ? productionConfig : developmentConfig return merge(generateConfig(env), config) // 合併 公共配置 和 環境配置 }
以上配置建議多看幾遍熟悉熟悉,爲何要這樣寫
const webpack = require('webpack') const path = require('path') module.exports = { mode: 'development', devtool: 'source-map', // 調試源碼 devServer: { contentBase: path.join(__dirname, '../dist/'), port: 8000, hot: true, overlay: true, proxy: { '/comments': { target: 'https://m.weibo.cn', changeOrigin: true, logLevel: 'debug', headers: { Cookie: '' } } }, historyApiFallback: true }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin() ] }
開發配置主要是設置跨域、開啓源碼調試、熱更新
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 將 css 單獨打包成文件 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 壓縮 css module.exports = { mode: 'production', optimization: { splitChunks: { chunks: 'all', cacheGroups: { jquery: { name: 'chunk-jquery', // 單獨將 jquery 拆包 priority: 15, test: /[\\/]node_modules[\\/]jquery[\\/]/ } } } }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }), // 壓縮 css new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, //一個正則表達式,指示應優化/最小化的資產的名稱。提供的正則表達式針對配置中ExtractTextPlugin實例導出的文件的文件名運行,而不是源CSS文件的文件名。默認爲/\.css$/g cssProcessor: require('cssnano'), //用於優化\最小化 CSS 的 CSS處理器,默認爲 cssnano cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, //傳遞給 cssProcessor 的選項,默認爲{} canPrint: true //一個布爾值,指示插件是否能夠將消息打印到控制檯,默認爲 true }) ] } **生產配置主要是拆分代碼,壓縮 css**
運行 npm run dev
而且自動打開瀏覽器,圖片和字體都出來了,打開控制檯也能看到跨域成功、源碼定位,由於將 devtool 設置爲 'source-map',因此就會生成 map 文件,體積較大
運行 npm run build
打開 dist/index.html 文件
生產模式下跨域失敗是很正常的,並且若是是 vue 項目打包完以後是沒法直接打開 index.html 文件查看效果的,必需要放在服務器上,通常都是將打包後的文件放入 nginx 中,在 nginx 中配置跨域地址
還有一種配置 webpack 開發和生產環境的方式,會比較經常使用:
修改 webpack.base.conf.js
const path = require('path') const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { entry: { app: './src/app.js' }, output: { path: path.resolve(__dirname, '..', 'dist') }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader' } ] }, { test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 1000, // size <= 1KB outputPath: 'images/' } }, // img-loader for zip img { loader: 'image-webpack-loader', options: { // 壓縮 jpg/jpeg 圖片 mozjpeg: { progressive: true, quality: 65 // 壓縮率 }, // 壓縮 png 圖片 pngquant: { quality: '65-90', speed: 4 } } } ] }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file publicPath: 'fonts/', outputPath: 'fonts/' } } } ] }, plugins: [ // 開發環境和生產環境兩者均須要的插件 new HtmlWebpackPlugin({ title: 'webpack4 實戰', filename: 'index.html', template: path.resolve(__dirname, '..', 'index.html'), minify: { collapseWhitespace: true } }), new webpack.ProvidePlugin({ $: 'jquery' }), new CleanWebpackPlugin() ], performance: false }
修改 webpack.dev.conf.js
const webpack = require('webpack') const merge = require('webpack-merge') const commonConfig = require('./webpack.base.conf.js') const path = require('path') const devConfig = { mode: 'development', output: { filename: '[name].js', chunkFilename: '[name].js' }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 // 在一個 css 中引入了另外一個 css,也會執行以前兩個 loader,即 postcss-loader 和 sass-loader } }, 'sass-loader', // 使用 sass-loader 將 scss 轉爲 css 'postcss-loader' // 使用 postcss 爲 css 加上瀏覽器前綴 ] } ] }, devtool: 'cheap-module-eval-soure-map', devServer: { contentBase: path.join(__dirname, '../dist/'), port: 8000, hot: true, overlay: true, proxy: { '/comments': { target: 'https://m.weibo.cn', changeOrigin: true, logLevel: 'debug', headers: { Cookie: '' } } }, historyApiFallback: true }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin() ] } module.exports = merge(commonConfig, devConfig)
修改 webpack.prod.conf.js
const merge = require('webpack-merge') const commonConfig = require('./webpack.base.conf.js') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 將 css 單獨打包成文件 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 壓縮 css const prodConfig = { mode: 'production', output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js' }, devtool: 'cheap-module-source-map', module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ { loader: MiniCssExtractPlugin.loader }, { loader: 'css-loader', options: { importLoaders: 2 // 在一個 css 中引入了另外一個 css,也會執行以前兩個 loader,即 postcss-loader 和 sass-loader } }, 'sass-loader', // 使用 sass-loader 將 scss 轉爲 css 'postcss-loader' // 使用 postcss 爲 css 加上瀏覽器前綴 ] } ] }, optimization: { splitChunks: { chunks: 'all', cacheGroups: { jquery: { name: 'jquery', // 單獨將 jquery 拆包 priority: 15, test: /[\\/]node_modules[\\/]jquery[\\/]/ }, vendors: { test: /[\\/]node_modules[\\/]/, name: 'vendors' } } } }, plugins: [ new MiniCssExtractPlugin({ filename: '[name]-[contenthash].css', chunkFilename: '[id]-[contenthash].css' }), // 壓縮 css new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, //一個正則表達式,指示應優化/最小化的資產的名稱。提供的正則表達式針對配置中ExtractTextPlugin實例導出的文件的文件名運行,而不是源CSS文件的文件名。默認爲/\.css$/g cssProcessor: require('cssnano'), //用於優化\最小化 CSS 的 CSS處理器,默認爲 cssnano cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, //傳遞給 cssProcessor 的選項,默認爲{} canPrint: true //一個布爾值,指示插件是否能夠將消息打印到控制檯,默認爲 true }) ] } module.exports = merge(commonConfig, prodConfig)
修改 package.json 的 script 命令
{ "scripts": { "dev": "webpack-dev-server --open --config ./build/webpack.dev.conf.js", "build": "webpack --config ./build/webpack.prod.conf.js" } }
在以前的基礎又修改了一下配置,從新打包便可
新建 index.js、math.js、string.js
// index.js import * as math from './math' import * as string from './string' export default { math, string } // math.js export function add(a, b) { return a + b } export function minus(a, b) { return a - b } export function multiply(a, b) { return a * b } export function division(a, b) { return a / b } // string.js export function join(a, b) { return a + ' ' + b }
代碼寫完,使用 webpack 打包,安裝 webpack,-D 表示 --save-dev 的簡寫
npm i webpack webpack-cli -D
修改 package.json
{ "name": "library", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack" }, "keywords": [], "author": "xh", "license": "MIT", "devDependencies": { "webpack": "^4.29.6", "webpack-cli": "^3.3.0" } }
"license": "MIT"
表示徹底開源的協議,name
表示你的組件庫的名稱
新建 webpack.config.js 並配置
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js' } }
運行打包命令,生成 library.js 文件
這個文件就能夠在項目中用了,可是咱們如今是要作一個開源庫,是給別人用的,別人可能會這麼用
// ES module import library from 'library' // CommonJS const library = require('library') // AMD require(['library'], function() {})
若是咱們要支持這三種形式的使用,能夠在 webpack 裏配置,加上 libraryTarget 參數
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js', libraryTarget: 'umd' } }
固然,若是你但願用戶還可使用 script 標籤的形式引入
<script src="library.js"></script>
用戶但願能夠經過 library 全局變量來使用,好比 library.math 要怎麼辦
能夠再配置一個參數,叫 library
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js', library: 'root', // root 能夠隨便更換,不是固定值 libraryTarget: 'umd' } }
umd 是支持前面三種語法,可是不支持全局變量這種用法,若是配置了 library,打包以後就會將代碼掛載到 root 這個全局變量上,經過 script 來引入 library,如今來打包一下,打包完以後來測試用 script 標籤來引入咱們寫的庫
在 dist 目錄下新建個 index.html 文件,並打開頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>自定義庫</title> <script src="./library.js"></script> </head> <body></body> </html>
在控制檯中輸入 root,回車,就能看到咱們前面封裝的函數了
libraryTarget: 'umd'
若是將 umd 改成 this
再去打包,在控制檯輸入 this.root 也能看到效果
libraryTarget
也能夠填 window,若是在 node 環境下也可使用 global
不過通常咱們都是使用 umd
還有一種狀況要注意,咱們如今寫的 string.js 我以爲寫的很差,lodash 寫的更好,我要引入這個第三方庫,來代替我寫的一些功能
npm install lodash
// string.js import _ from 'lodash' export function join(a, b) { return _.join([a, b], ' ') }
從新打包,體積爲 70kb,由於咱們也把 lodash 也打包進去了
別人要使用咱們的庫的話,須要這樣 import library from 'library'
,也許別人也會用到 lodash 庫,結果變成了這樣:
import _ from 'lodash' import library from 'library'
最終用戶的代碼中就會存在兩份 lodash 的代碼,這種狀況就要再去更改一下咱們的 webpack 配置
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', externals: ['lodash'], output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js', library: 'root', libraryTarget: 'umd' } }
externals 會在打包的過程當中,若是遇到了 lodash 這個庫,就不會打包進去,能夠寫成數組形式也能夠是字符串,更改完後再次打包
能夠發現咱們庫裏使用的 lodash 並無被打包進去,體積只有 1kb
這個時候別人再次使用咱們的 library 這個庫,若是不引入 lodash,則會失敗,別人在使用 library 以前要先引入 lodash
若是改成 externals: 'lodash'
,則使用的時候爲,import lodash from lodash
,而不能用 _ 下劃線來代替 lodash, import _ from lodash
若是要讓別人使用你的庫,其實就是使用你打包後的文件,須要先在 package.json,將 main: index.js 改成 main: ./dist/library.js,經過 npm 發佈以前,你要確保你的庫的 name 不會和別人上線的 name 衝突,改一個有特色的 name,來確保能發佈成功,如 library-xh-2019
,感興趣的能夠本身去研究一下如何經過 npm 發佈
{ "name": "library-xh-2019", "version": "1.0.0", "description": "", "main": "./dist/library.js", "scripts": { "build": "webpack" }, "keywords": [], "author": "xh", "license": "MIT", "devDependencies": { "webpack": "^4.29.6", "webpack-cli": "^3.3.0" }, "dependencies": { "lodash": "^4.17.11" } }