webpack官方文件學習
安裝依賴的簡寫說明:以vue為例
cnpm i vue -S // 生產依賴 等價於 --save
cnpm i vue-loader css-loader vue-template-compiler -D // 開發依賴 等價於--save-dev
文件很長,我在前面把坑都踩了,希望大家看原文的時候結合我的文件來學習,要有耐心看哦。畢竟祖國的未來還是要靠你們去建設的^_^。
Step1.先學習概念,大概30分鐘
Step2.學習指南,這個內容比較多,照著上面的示例慢慢練習
1.指南(太簡單了,略)
2.安裝(略)
3.起步(略)
4.管理資源(略)
5.管理輸出, HtmlWebpackPlugin 這裡可能會遇到問題,那是因為沒有在專案中安裝webpack
clean-webpack-plugin 預設只能清除dist下的檔案
引用圖片:import src from "./g1.jpg" 不能直接src = "./g1.jpg";否則圖片載入不了。
6.開發
6.1 配置 devtool:"inline-source-map" ,會跳到錯誤的原始檔中,助力開發除錯
6.2 "watch” :"webpack --watch" //不用安裝,改程式碼--重新整理頁面--成功
6.3 1.webpack-dev-server 的使用,要安裝,配置
2.webpack.json配置:"dev":"webpack-dev-server --open" // cnpm run dev 就啟動了
3. webpack.config.js配置: devServer:{ contentBase:"./dist" }
// 以上配置告知 webpack-dev-server
,在 localhost:8080
下建立服務,將 dist
目錄下的檔案,作為可訪問檔案。
//選擇了webpack-dev-server 就不要webpack-dev-middleware 和watch 了。
webpack-dev-server --hot // 也能起到模組熱更新的作用
7.模組熱替換,如下配置就可以自動更新了
以下幾種loader也是熱模組更新:
rules: [
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader']
+ }
+ ]
還有:Vue Loader 、angular HMR也會熱替換
8.tree shaking,配置webpack.json檔案,去除被打包檔案中沒有用到的程式碼。
// 8.1通常用於描述移除 JavaScript 上下文中的未引用程式碼(dead-code)
8.2 生產環境會自動壓縮打包的程式碼,
到這步的時候,webpack.json是這樣的
{
"name": "webpack0827",
"sideEffects": false,
"version": "1.0.0",
"private": true,
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"watch": "webpack --watch",
"dev": "webpack-dev-server --open"
},
"author": "tangcc",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.10"
},
"devDependencies": {
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^1.0.0",
"file-loader": "^2.0.0",
"html-webpack-plugin": "^3.2.0",
"style-loader": "^0.23.0",
"webpack": "^4.17.1",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.6"
}
}
webpack.config.js 是這樣的
const webpack = require('webpack');
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// entry:"./src/index.js",
entry:{
app:"./src/index.js",
// print:"./src/print-edit1.js" // 做更新先幹掉
},
// output:{
// filename :"bundle.js",
// path:path.resolve(__dirname,"dist")
// },
output:{
path:path.resolve(__dirname,"dist")
,filename:'[name].bundle.js'
}
,
module:{
rules:[
{
test: /.css$/,
use: [
'style-loader',
"css-loader"
]
},
{
test:/\.(png|svg|jpg|gif)$/,
use:["file-loader"]
},
// {
// include: path.resolve("node_modules", "lodash"),
// sideEffects: false
// }
]
}
,
plugins :[
new HtmlWebpackPlugin({
title: 'Output Management tnagcc'
}),
new CleanWebpackPlugin(["dist"])
,new webpack.NamedModulesPlugin() // 模組熱替換
,new webpack.HotModuleReplacementPlugin() // 模組熱替換
],
devtool : "inline-source-map",
devServer : { //告知 webpack-dev-server,在 localhost:8080 下建立服務,將 dist 目錄下的檔案,作為可訪問檔案。
contentBase:"./dist"
,hot:true // 模組熱替換開啟,要配合plugins ,還得手動改一下index檔案
},
mode: "production"// 生產環境會自動壓縮程式碼,注意,--optimize-minimize 標記也會在 webpack 內部呼叫 UglifyJsPlugin。
}
9.生產環境構建,要拆分配置檔案了,所以目錄結構要改
npm install --save-dev webpack-merge
接下來配置檔案也要分開寫了,dev +base or pro + base
webpack.common.js
const webpack = require('webpack');
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry:{
app:"./src/index.js",
},
output:{
path:path.resolve(__dirname,"dist"),
filename:"[name].bundle.js"
},
module:{
rules:[
{
test:/.css$/,
use:["style-loader","css-loader"]
},
{
test:/\.(png|jpg|gif|svg)$/,
use:["file-loader"]
}
]
},
plugins:[
new CleanWebpackPlugin(["dist"]) // 先清除dist後再去構建
,
new HtmlWebpackPlugin({ // 會自動在dist目錄構建一個包含所有依賴的html,預設名字叫index.html
title:"product environment",
})
]
}
webpack.dev.js ,這裡文件裡面是有個大坑的,不信你啟動webpack.dev.js的時候,
報錯了,應該如下:
const merge = require("webpack-merge"); // 用於合併配置,有很多其它功能
const common = require("./webpack.common.js")
module.exports = merge(common,{
devtool:"inline-source-map", // 在執行時生成一個原檔案便於除錯的
devServer:{
contentBase:"./dist", // 告訴webpack-dev-server, 開戶的除錯用的本地服務localhost:8080 可用的本地目錄是dist
hot:true // 是否進行熱更新,配合 plugins:[ new webpack.NamedModulesPlugin() // 給模組命名 ,new webpack.HotModuleReplacementPlugin() // 熱模組更新]
},
plugins:[
new webpack.NamedModulesPlugin() // 模組熱替換
,new webpack.HotModuleReplacementPlugin() // 模組熱替換
]
})
快看圖吧!心血呀,一步步的給你們踩坑 !^_^
webpack.prod.js
const merge = require("webpack-merge");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin")
const common = require("./webpack.common.js")
module.exports = merge(common,{
plugins:[
new UglifyJSPlugin()
]
})
重新配置一下NPM Scripts
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.prod.js",
"watch": "webpack --watch",
"dev": "webpack-dev-server --open --config webpack.dev.js"
},
優化:給生產環境也配置source map
// 我們鼓勵你在生產環境中啟用 source map,因為它們對除錯原始碼(debug)和執行基準測試(benchmark tests)很有幫助。雖然有如此強大的功能,然而還是應該針對生成環境用途,選擇一個構建快速的推薦配置(具體細節請檢視 devtool
)。對於本指南,我們將在生產環境中使用 source-map
選項,而不是我們在開發環境中用到的 inline-source-map
:
修改一下webpack.prod.js
const merge = require("webpack-merge");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin")
const common = require("./webpack.common.js")
module.exports = merge(common,{
devtool:"source-map", // 生產環境也要配置一下便於除錯原碼
plugins:[ new UglifyJSPlugin({ sourceMap:true // }) ]
})
指定環境
許多 library 將通過與 process.env.NODE_ENV
環境變數關聯,以決定 library 中應該引用哪些內容。例如,當不處於生產環境中時,某些 library 為了使除錯變得容易,可能會新增額外的日誌記錄(log)和測試(test)。其實,當使用 process.env.NODE_ENV === 'production'
時,一些 library 可能針對具體使用者的環境進行程式碼優化,從而刪除或新增一些重要程式碼。我們可以使用 webpack 內建的 DefinePlugin
為所有的依賴定義這個變數:
webpack.prod.js 更改
const webpack = require("webpack")
const merge = require("webpack-merge");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin")
const common = require("./webpack.common.js")
module.exports = merge(common,{
devtool:"source-map",
plugins:[
new UglifyJSPlugin({
sourceMap:true
})
,new webpack.DefinePlugin({ // 改動
"process.env.NODE_ENV":JSON.stringify('production')
})
]
})
CLI 替代選項(建議讀一讀哦)
以上描述也可以通過命令列實現。例如,--optimize-minimize
標記將在後臺引用 UglifyJSPlugin
。和以上描述的 DefinePlugin
例項相同,--define process.env.NODE_ENV="'production'"
也會做同樣的事情。並且,webpack -p
將自動地呼叫上述這些標記,從而呼叫需要引入的外掛。
這些簡便方式雖然都很不錯,但是我們通常建議只使用配置方式,因為在這兩種場景中下,配置方式能夠更好地幫助你瞭解自己正在做的事情。配置方式還可以讓你更方便地控制這兩個外掛中的其他選項。
10.程式碼分離,去除各個模組程式碼重複的部分(用webpack自帶的方法)
我們多個入口,預設打包出來的會包含重複的模組。如,兩個檔案都引用了jquery,則,打包出來兩個檔案裡都包含了jquery.
src:裡面兩個用來測試的檔案都引入一個相同的包如 import _ from "lodash"
輸入輸出不變:還是一般的多入口
entry:{
app:"./src/index.js"
// ,
// another:'./src/another_module.js' // 多入口,也意味著多輸出
},
output:{
path:path.resolve(__dirname,"dist"),
filename:"[name].bundle.js"
//,
// 決定非入口chunk的名稱,動態匯入的包就是一個非入口chunk,
// 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
//chunkFilename:"[name].bundle.js"
}
webpack4+,不支援在plugins裡配置
new webpack.optimize.CommonsChunkPlugin({
+ name: 'common' // 指定公共 bundle 的名稱。
+ })
解決辦法是在與plugins平級的地方:
optimization:{
splitChunks: {
cacheGroups: {
commons: {
test: /node_modules/,
name: "vendors",
chunks: "all"
}
}
},
runtimeChunk:true
}
執行結果如下:
方法二:動態匯入(dynamic imports),我只講重點,其它的自己看官方文件
原理:import()
呼叫會在內部用到 promises。如果在舊有版本瀏覽器中使用 import()
,記得使用 一個 polyfill 庫(例如 es6-promise 或 promise-polyfill),來 shim Promise
。
請看index.js 新寫法:
import "./style.css"
function component() { return import(/* webpackChunkName: "lodash" */"lodash" ).then(_=>{
var element = document.createElement('div');
element.innerHTML = _.join(["我是動態引入的" ],"lodash component 哦!");
return element;
}).catch(error => "'An error occurred while loading the lodash component,haha tangcc'")
} component().then(element =>{ document.body.appendChild(element); })
非入口chunk的名稱,:webpack.config.js 中的output配置一下
entry:{
app:"./src/index.js"
// ,
// another:'./src/another_module.js' // 多入口,也意味著多輸出
},
output:{
path:path.resolve(__dirname,"dist"),
filename:"[name].bundle.js"
,
// 決定非入口chunk的名稱,動態匯入的包就是一個非入口chunk,
// 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
chunkFilename:"[name].bundle.js"
},
結果:ok^_^(已測過)
這一小節還差分析檢查模組就完結了,bundle 分析(bundle analysis) -- 我沒有研究,先跳過去先。。。^_^
如果我們以分離程式碼作為開始,那麼就以檢查模組作為結束,分析輸出結果是很有用處的。官方分析工具 是一個好的初始選擇。下面是一些社群支援(community-supported)的可選工具:
- webpack-chart: webpack 資料互動餅圖。
- webpack-visualizer: 視覺化並分析你的 bundle,檢查哪些模組佔用空間,哪些可能是重複使用的。
- webpack-bundle-analyzer: 一款分析 bundle 內容的外掛及 CLI 工具,以便捷的、互動式、可縮放的樹狀圖形式展現給使用者。
11.懶載入(按需載入) ,當用戶點選時才去載入對應的模組,難點在於如何去分模組(未研究)
新建print.js檔案的時候,一定要“注意”當呼叫 ES6 模組的 import()
方法(引入模組)時,必須指向模組的 .default
值,因為它才是 promise 被處理後返回的實際的 module
物件。
print.js
export default function printMe(){
console.log("是誰點我的?print me")
}
index.js
import _ from "lodash" // 這是靜態匯入
import "./style.css"
function component(){
var element = document.createElement('div');
var button = document.createElement("button") ;
button.innerHTML = "click me print ...";
button.onclick = e => import(/* webpackChunkName:"print" */"./print-edit1.js").then(pExport => pExport.default()).catch(err =>{
console.log("import component encounter some err")
});
element.appendChild(button);
// return import(/* webpackChunkName: "lodash" */"lodash" ).then(_=>{
// var element = document.createElement('div');
// element.innerHTML = _.join(["我是動態引入的" ],"lodash component 哦!");
// return element;
// }).catch(error => "'An error occurred while loading the lodash component,haha tangcc'")
document.body.appendChild(element);
}
component();
配置不用改,有一點點問題就是 chunkFilename:"[name].bundle.js" 命名沒有成功, 打包出來的名字竟然是用了id.bundle.js。
entry:{
app:"./src/index.js"
// ,
// another:'./src/another_module.js' // 多入口,也意味著多輸出
},
output:{
path:path.resolve(__dirname,"dist"),
filename:"[name].bundle.js"
,
// 決定非入口chunk的名稱,動態匯入的包就是一個非入口chunk,
// 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
chunkFilename:"[name].bundle.js"
},
又完成了一小節。
12.快取(概念自己去看官網了^_^)
此指南的重點在於"通過必要的配置",以確保 webpack 編譯生成的檔案能夠被客戶端快取,而在檔案內容變化後,能夠請求到新的檔案。
輸出檔案的檔名(Output Filenames),用chunkhash命名
其實非常簡單啦: filename:chunkhash
output:{
path:path.resolve(__dirname,"dist")
,
filename: "[name].[chunkhash].js"
},
提取模板(Extracting Boilerplate樣板檔案)
小於webpack4.0用以下兩個配置可以實現公共程式碼的提取。
entry:{
main:"./src/index.js",
vendor:["lodash"]
},
plugins:[
new CleanWebpackPlugin(["dist"]) // 先清除dist後再去構建
,
new HtmlWebpackPlugin({ // 會自動在dist目錄構建一個包含所有依賴的html,預設名字叫index.html
title:"product environment",
})
,
// 程式碼分離時,去除重複模組,不用裝 ,第10點 程式碼分離
new webpack.optimize.CommonsChunkPlugin({ // webpack4 廢棄了
name:'common' // 把兩個入口重複的chunk提出來,以common.bundle.js的名字打包
})
]
如果是webpack4.0會報以下錯誤了,
Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
解決辦法:
plugins:[ // 略
new CleanWebpackPlugin(["dist"]) // 先清除dist後再去構建
,
new HtmlWebpackPlugin({ // 會自動在dist目錄構建一個包含所有依賴的html,預設名字叫index.html
title:"product environment",
})
]
,
// 關鍵程式碼
optimization:{ // 程式碼分離時,去除重複模組,不用裝 ,第10點 程式碼分離// 把兩個入口重複的chunk提出來,以vendors.bundle.js的名字打包
splitChunks: {
cacheGroups: {
commons: {
test: /node_modules/,
name: "vendors----",
chunks: "all"
}
}
},
runtimeChunk:true
}
模組識別符號(Module Identifiers)
主要是控制chunkhash的變化,理想狀態下,只有相關檔案改動,對應的chunkhash才能變
而 vendor沒有改變但它
的 hash 發生變化,所以我們要處理。可以使用兩個外掛來解決這個問題。
第一個外掛是 NamedModulesPlugin
,將使用模組的路徑,而不是數字識別符號。雖然此外掛有助於在開發過程中輸出結果的可讀性,然而執行時間會長一些。
第二個選擇是使用 webpack.HashedModuleIdsPlugin()
,推薦用於生產環境構建.
實際上,我用下面的配置,打包出的vendors檔案的chunkhash也不會亂變。
optimization:{ // 程式碼分離時,去除重複模組,不用裝 ,第10點 程式碼分離// 把兩個入口重複的chunk提出來,以vendors.bundle.js的名字打包
splitChunks: {
cacheGroups: {
commons: {
test: /node_modules/,
name: "vendors----", //會提取出各模組的公共程式碼,自動命名按 output.chunkFilename 的規則 ,這個檔案一般比較大>=70K。
chunks: "all"
}
}
},
runtimeChunk:false
}
這一節過了^_^!
13.建立library(除了打包應用程式程式碼,webpack 還可以用於打包 JavaScript library--就是自己寫一個js庫發到npm上去)
可能你看了官方的文件,還是不會操作呀,下面請按步驟。
1.按照文件把要釋出的程式碼寫好
2.給要釋出的程式碼寫一個README.md 檔案,釋出成功後這個檔案會出現在你的包的描述說明中。
.md 檔案的寫法參考學習文件
https://www.cnblogs.com/liugang-vip/p/6337580.html
.md 檔案的線上測試工具
http://tool.oschina.net/markdown/
3.進入專案目錄,然後 npm publish (一定是npm 不要用淘寶的cnpm)
4.更新包(其實就是重新發布,在package.json )
a.改:"version":"1.0.1" 或者 npm version 1.0.1
b.npm publish
如果有報錯請參考:
首先要設定一個版本號
npm社群版本號規則採用的是semver(語義化版本),主要規則版本格式:主版本號.次版本號.修訂號,版本號遞增規則如下:
- 主版本號:當你做了不相容的 API 修改,
- 次版本號:當你做了向下相容的功能性新增,
- 修訂號:當你做了向下相容的問題修正。
先行版本號及版本編譯資訊可以加到“主版本號.次版本號.修訂號”的後面,作為延伸。
$ npm publish
這裡有時候會遇到幾個問題
問題1:
npm ERR! no_perms Private mode enable, only admin can publish this module:
這裡注意的是因為國內網路問題,許多小夥伴把npm的映象代理到淘寶或者別的地方了,這裡要設定回原來的映象。
$ npm config set registry=http://registry.npmjs.org
問題2:
npm ERR! you do not have permission to publish "your module name". Are you logged in as the correct user?
提示沒有許可權,其實就是你的module名
在npm
上已經被佔用啦,這時候你就去需要去npm
搜尋你的模組名稱,如果搜尋不到,就可以用,並且把package.json
裡的name
修改過來,重新npm publish
,看到如下資訊就表示安裝完成了,songpackage
就是我的模組名。
作者:郝小淞
連結:https://www.jianshu.com/p/d9fb02a891d9
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。
ok,過了!