1. 程式人生 > >webpack官方文件學習

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,過了!