webpack4.x開始官方文檔是說要單獨安裝webpack-cli因此若是是用4.+就須要卸載clijavascript
//刪除全局webpack-cli 卸載uninstall能夠簡寫成un,全局-g的完整寫法是--global npm uninstall -g webpack-cli //刪除本地(局部)webpack-cli npm uninstall webpack-cli //刪除全局webpack npm uninstall -g webpack //刪除本地webpack npm un webpack
在空文件夾pratice下執行:css
//全局安裝,默認最新版本,可使用@指定版本 npm install webpack -g //可使用cnpm淘寶鏡像,這樣速度更快 npm install -g cnpm --registry=https://registry.npm.taobao.org //用cnpm來安裝。速度較快 cnpm install webpack -g //在3版本及以前安裝webpack的時候就一同把webpack-cli也下載了,可是webpack4開始須要手動安裝webpack-cli //安裝webpack-cli cnpm install webpack-cli -g //查看版本號 webpack -v
至此webpack安裝成功。html
而後執行webpack命令,結果報錯:vue
緣由是webpack命令會默認打包該路徑下src文件夾下的index.js文件,可是咱們這個pratice是個空文件夾。咱們能夠新建一下src/index.js,而後再執行webpack就不會報錯了。執行webpack後會自動生成一個dist文件夾,裏面有將index.js打包好的main.js文件,默認是production壓縮形式,能夠執行webpack --mode development
打包成未壓縮的格式,方便閱讀。java
由於瀏覽器不支持require、import等那幾個模塊化變量,因此若是在src的js代碼中使用了模塊化,直接引用這樣的js文件,瀏覽器是會報錯的,因此須要經過webpack解析打包生成新的js文件,如上述的dist/main.js,而後引用這個js文件才ok。
node
新建sum.js:jquery
export default function(a,b){ return (a+b); }
新建minus.js:webpack
module.exports = function(a,b){ return a -b; }
項目pratice中新建demo.js:css3
import sum from './sum.js'/* 使用ES Module規範 */ var minus = require('./minus.js')/* 使用commonjs規範 */ var a = sum(1,5); console.log(a); var b = minus(100,9); console.log(b);
引入了ES Module和commonJS的模塊化代碼,瀏覽器是不支持的,因此要使用webpack來打包生成目標js文件,可是此次我不想被默認地打包到dist/main.js中,故可使用命令webpack demo.js -o ./hh/bundle.js
將模塊化代碼打包到指定的路徑下。git
在此以前,我已經安裝了全局的webpack,所以除了上述的項目路徑下可使用webpack指令,其餘的路徑也是可使用的。可是最好再當前的項目路徑下再安裝一個本地局部的webpack。
先在項目根路徑下執行npm init
指令進行項目初始化,該過程當中能夠一路回車。會生成一個package.json文件,裏面會記錄你的各類加載器loader、插件、webpack或者jQuery等等相關信息。這是很是方便的一個事情,舉個例子:好比我拿到了別人給個人一個項目,package.json裏面就記錄了該項目所依賴的各類東西,好比webpack是3.0版本的,而個人全局webpack是4.0版本的,那麼不用慌,我能夠查看package.json裏記錄的信息,手動安裝一次本地的webpack3.0。更方便的是我能夠直接執行npm install
指令自動下載package.json裏面的全部依賴。
項目初始化完成後,執行npm install webpack --save-dev
安裝本地開發版本的webpack。以後會生成一個node modules的文件夾,裏面都是安裝下來的包。webpack-cli也是同樣本地安裝一次。
webpack.config.js是webpack的默認配置文件,它的書寫必須遵循CommonJs規範。它不是惟一的,一個項目能夠有多個配置文件。
var path = require('path');/* path是node裏面的變量 */ module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'),/* __dirname表示當前路徑文件夾 */ filename: 'my-first-webpack.bundle.js'/* 打包文件的名字 */ }, mode: 'development',/* 設置打包出來的文件是未壓縮的格式 */ };
可使用對象的方式
var path = require('path');/* path是node裏面的變量 */ module.exports = { entry: { index: './src/index.js', app: './src/app.js' }, output: { path: path.resolve(__dirname, 'dist'),/* __dirname表示當前路徑文件夾 */ filename: '[name].bundle.js' /* 注意這裏用了[name] */ }, mode: 'development',/* 設置打包出來的文件是未壓縮的格式 */ };
執行webpack
指令後會在dist文件夾下生成對應的app.bundle.js和index.bundle.js。注意filename: '[name].bundle.js'
loader用於處理非js文件。在webpack中寫相應的配置規則,而且安裝相應的插件。下面以less-loader爲例:
//安裝less-loader以前先安裝less cnpm i style-loader less-loader css-loader less --save-dev
修改webpack.config.js:
var path = require('path');/* path是node裏面的變量 */ module.exports = { entry: { index: './src/index.js', app: './src/app.js' }, output: { path: path.resolve(__dirname, 'dist'),/* __dirname表示當前路徑文件夾 */ filename: '[name].bundle.js' /* 注意這裏用了[name] */ }, module: { rules: [ { test: /\.less$/, use: ['style-loader','css-loader','less-loader'] } ] }, mode: 'development',/* 設置打包出來的文件是未壓縮的格式 */ };
如下是常見的幾個loader:
module: { // 配置全部第三方loader 模塊的 rules: [ // 第三方模塊的匹配規則 { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }, { test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=7631&name=[hash:8]-[name].[ext]' }, // 處理圖片路徑的loader // limit給定的值,是圖片的大小,單位是byte, 若是咱們引用的圖片,大於或等於給定的limit值,則不會被轉爲base64格式的字符串, 若是圖片小於給定的limit值,則會被轉爲base64的字符串 { test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' }, //處理字體文件的 loader { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, //配置Babel來轉換ES6語法 { test: /\.vue$/, use: 'vue-loader' } // 處理 .vue 文件的 loader ] },
loader被用於轉換某些非js類型的模塊,而插件則能夠執行範圍更廣的任務,包括:從打包優化和壓縮,一直到從新定義環境中的變量等等。
想要使用一個插件,先安裝下載,而後須要require()它,而後把它添加到plugins數組中。
cnpm i html-webpack-plugin -D
執行該指令安裝插件。
var path = require('path');/* path是node裏面的變量 */ const HtmlWebpackPlugin = require('html-webpack-plugin'); //經過 npm 安裝 module.exports = { entry: { index: './src/index.js', app: './src/app.js' }, output: { path: path.resolve(__dirname, 'dist'),/* __dirname表示當前路徑文件夾 */ filename: '[name].bundle.js' /* 注意這裏用了[name] */ }, module: { rules: [ { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.ts$/, use: 'ts-loader' } ] }, plugins: [ new HtmlWebpackPlugin()/* 這裏可傳參也可不傳參 */ ], mode: 'development',/* 設置打包出來的文件是未壓縮的格式 */ };
而後會在dist文件中生成index.html文件,裏面自動引入了咱們配置的js文件。
項目文件 treeShaking,先項目初始化npm init -y
,而後在生成的package.json裏面添加"dev": "webpack --mode development"
,以後就不用每次都執行那麼長的指令了,直接npm run dev
便可。同理也能夠配置如下生產環境的指令"prod": "webpack --mode production"
js tree shaking一般用於描述移除 JavaScript 上下文中的未引用代碼(dead-code),就是把未使用到的js代碼去掉,精簡js代碼。它依賴於 ES2015 模塊語法的 靜態結構 特性,例如 import
和 export
。
舉個例子:
index.js:
import {abc} from '../sum.js'; abc();
sum.js
var abc = function(){ console.log('這是abc'); }; var heihei = function(){ console.log('這是heihei'); }; export { abc, heihei }
執行npm run dev後生成main.js,事實上咱們只引用到了sum.js中的abc,並無使用heihei,可是在webpack生成的main.js中搜索heihei關鍵字,發現依舊生成了關於heihei的代碼,這形成了沒必要要的冗餘。
若是咱們執行npm run prod生成main.js,咱們發現沒有生成關於heihei的代碼,由於production模式下會自動去掉未使用的js代碼,實現了tree shaking。
Loadsh
是一個一致性、模塊化、高性能的 JavaScript 實用工具庫。在數據操做時,咱們常常會用的 loadsh 封裝好的一些工具方法,可是並不想把整個包打包進項目裏面。要用到 tree-shaking,必然要保證引用的模塊都是 ES6 規範的。lodash-es 是着具有 ES6 模塊化的版本,只須要直接引入就能夠。
先安裝lodash-es: cnpm i lodash-es -D
sum.js中引入lodash-es庫:
import lodash from 'lodash-es' var abc = function(){ console.log('這是abc'); }; var isArray = function(arg){ console.log(lodash.isArray(arg)); /* 判斷傳入的參數是否爲數組 */ }; export { abc, isArray }
可是index.js中仍然只使用abc:
import {abc} from '../sum.js'; abc();
執行npm run dev,發現打包後的文件大小多達1.5Mb,相比於npm run prod生成的只有86Kb大小,由於它把lodash-es也打包進去了儘管咱們index.js中並無真正使用到lodash-es。可是儘管npm run prod生成的main.js確實比development的小,事實上它也打包了lodash-es裏的代碼了,只是比前者少了一點。由於npm run prod只能進行詞法語法層面上的分析(sum.js中確實寫了些關於lodash-es的代碼),沒法進行scope做用域上的分析,到底真正用沒用它是不知道的。所以,npm run prod並非最佳的tree shaking。
這裏 就須要用到一個插件: webpack-deep-scope-plugin。 先安裝npm i webpack-deep-scope-plugin -D
在webpack.config.js中進行配置:
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin').default; module.exports = { plugins: [ new WebpackDeepScopeAnalysisPlugin() ] }
而後,執行npm run prod
,咱們發現打包出來的main.js大小不到1Kb。js tree shaking獲得了較好的結果。
首先關於css的,就必定要先安裝好style-loader和css-loader,以及寫好配置規則。
而後須要安裝一個插件mini-css-extract-plugin,用於單獨抽離出css文件而非被寫進main.js中。
寫好配置規則:
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin').default; const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { module: { rules: [ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }, ] }, plugins: [ new WebpackDeepScopeAnalysisPlugin(), new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), ] }
注意的是,這裏用MiniCssExtractPlugin.loader代替了以前的style-loader,style-loader是生成行間樣式的,這個mini-css-extract-plugin插件能夠生成一個main.css文件,而後咱們就可使用<link rel="stylesheet" href="../dist//main.css">
來引入css。在生成的dist/main.js中是沒有關於demo.css的代碼的,作到了單獨抽離出css文件。
渡一視頻這裏用到了一個新的插件purifycss-webpack。可是這個插件已經被棄用了,用起來也有些bug,有些有被使用到的css沒有被打包。我這裏就不寫了。
npm init -y npm i webpack -D npm i webpack-cli -D npm i css-loader -D npm i style-loader -D npm i less -D npm i less-loader -D npm i mini-css-extract-plugin -D
//demo.css *{ margin:0; padding:0; } .wrapper { position: relative; width: 500px; height: 500px; border: 1px solid #000; margin: 100px auto; transform: rotate(45deg); .box { position: absolute; width: 300px; height: 300px; border: 1px solid #000; top: 40%; left: 40%; text-align: center; a{ color: pink; font-size: 30px; line-height: 300px; } } }
//demo.less *{ margin:0; padding:0; } .wrapper { position: relative; width: 500px; height: 500px; border: 1px solid #000; margin: 100px auto; transform: rotate(45deg); .box { position: absolute; width: 300px; height: 300px; border: 1px solid #000; top: 40%; left: 40%; text-align: center; a{ color: pink; font-size: 30px; line-height: 300px; } } }
//index.js import './demo.css';
//index.html <body> <div class="wrapper"> <div class="box"> <a href="#">hello world</a> </div> </div> <script src="./dist/index.bundle.js"></script> </body>
var path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //單獨抽離出css文件 module.exports = { entry:{ index: './index.js' }, output:{ path: path.resolve(__dirname, 'dist'), filename: '[name].bundle.js' }, module:{ rules:[ { test: /\.css$/, use:[ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader', } ] }, { test: /\.less$/, use:[ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader', }, { loader: 'less-loader', } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), ] }
有個問題就是關於css3的兼容代碼,像demo.less裏面的transform: rotate(45deg);
,須要寫:
transform: rotate(45deg); -ms-transform: rotate(45deg); /* IE 9 */ -webkit-transform: rotate(45deg); /* Safari and Chrome */ -o-transform: rotate(45deg); /* Opera */ -moz-transform: rotate(45deg); /* Firefox */
基於postcss能夠自動幫咱們加上這些兼容的前綴。
另外一個問題就是基於postcss能夠解析cssnext的代碼:
//帶有cssnext代碼的demo.less *{ margin:0; padding:0; } .wrapper { position: relative; width: 500px; height: 500px; border: 1px solid #000; margin: 100px auto; transform: rotate(45deg); .box { position: absolute; width: 300px; height: 300px; border: 1px solid #000; top: 40%; left: 40%; text-align: center; a{ color: pink; font-size: 30px; line-height: 300px; } } } :root{ --color: red; } a{ color: var(--color); }
cnpm i postcss postcss-loader autoprefixer cssnano postcss-cssnext -D
webpack.config.js
var path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry:{ index: './index.js' }, output:{ path: path.resolve(__dirname, 'dist'), filename: '[name].bundle.js' }, module:{ rules:[ { test: /\.css$/, use:[ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader', } ] }, { test: /\.less$/, use:[ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader', }, { /* 這部分要寫在css-loader以後、less-loader以前 */ loader: 'postcss-loader', options:{ ident: 'postcss', plugins:[ require('postcss-cssnext')(), /* 用於解析cssnext代碼,並實現autoprefixer的功能 */ // require('autoprefixer')(),/* 用於自動添加兼容前綴 , 注意在數組裏面不能夠有分號*/ require('cssnano')() /* 用於壓縮代碼 */ ] } }, { loader: 'less-loader', } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), ] }
npm install --save-dev html-webpack-plugin 安裝之,而後再webpack.config.js中配置:
var HtmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), new HtmlWebpackPlugin({ filename: 'index.html', template: './index.html', minify:{ removeComments: true, /* 清理掉index.html裏面的註釋 */ collapseWhitespace: true, /* 清理掉空格 */ } }), ]
以後會生成dist/index.html,並且裏面會自動引入所須要的css和 js,所以沒必要再src/index.html中手動加入了。
事實上html-webpack-plugin還有不少能夠配置的東西,網上都有。
另外咱們每次webpack要從新生成dist文件夾的時候,以前生成的一直都在着須要手動刪除,比較麻煩,因此可使用一個插件clean-webpack-plugin
。 命令npm i clean-webpack-plugin -D
執安裝之。
而後再webpack.config.js中配置:
const CleanWebpackPlugin = require('clean-webpack-plugin'); plugins: [ new CleanWebpackPlugin() ]
便可。
存在一個狀況,假若有a,b兩個js文件,它倆都引入了一個相同的第三方庫或者是相同的自定義的模塊。那麼當咱們加載a文件的時候會把它打包出來,包括引用了的相同的庫和模塊,而加載b文件的時候也作了相同的事情,那麼明明是相同的一個庫或模塊,卻被打包了兩次,這樣就冗餘了,下降了效率。咱們將同一個代碼/模塊/庫打包了屢次,所以咱們須要作到提取公共JS代碼,避免上述狀況發生,實現減小代碼冗餘,提升項目的加載速度。
注意的是,提取公共代碼針對的是多入口文件,若是隻有一個js文件的話是不成立的。提取公共代碼至少是兩個文件。
//這是一個公共模塊 module.js export default 'module'
//subPageA.js import './module.js'; export default 'subPageA';
//subPageB.js import './module.js'; export default 'subPageB';
//pageA.js import './subPageA.js'
//pageB.js import './subPageB.js'
entry:{ // index: './index.js' pageA: './src/pageA.js', pageB: './src/pageB.js', }, output:{ path: path.resolve(__dirname, 'dist'), filename: '[name][hash:5].bundle.js', chunkFilename: '[name][hash:5].js' }, optimization:{ splitChunks:{ cacheGroups:{ common:{ name:"common", /* 表示打包出來的公共模塊的名字,不寫的話就默認是chunkFilename */ chunks:'all', /* 表示做用的範圍 */ minSize: 1, /* minSize表示生成公共代碼塊所須要最小的體積,默認是30kb */ minChunks: 2/* 表示公共模塊被引用的最小次數,這裏表示當一個模塊被引用了2次以上就視爲公共代碼塊 */ } } } },
打包後的:
先安裝一下lodash: cnpm i lodash -D
//pageA.js import './subPageA.js'; import * as _ from 'lodash'; //引用lodash
//pageB.js import './subPageB.js'; import * as _ from 'lodash'; //引用lodash
注意的是,咱們以前打包出來的公共js文件是咱們自定義的公共模塊,這裏咱們引用了第三方庫lodash,最好是不要和咱們自定義的模塊一同被打包到一個公共js文件中,所以須要再配置一下。
entry:{ // index: './index.js' pageA: './src/pageA.js', pageB: './src/pageB.js', }, output:{ path: path.resolve(__dirname, 'dist'), filename: '[name][hash:5].bundle.js', chunkFilename: '[name][hash:5].js' }, optimization:{ splitChunks:{ cacheGroups:{ common:{ name:"common", /* 表示打包出來的公共模塊的名字,不寫的話就默認是chunkFilename */ chunks:'all', /* 表示做用的範圍 */ minSize: 1, /* minSize表示生成公共代碼塊所須要最小的體積,默認是30kb */ minChunks: 2,/* 表示公共模塊被引用的最小次數,這裏表示當一個模塊被引用了2次以上就視爲公共代碼塊 */ priority: 1 /* 設置優先級,低於vendor的 */ }, vendor:{ name:'vendor', test:/[\\/]node_modules[\\/]/,/* 表示匹配node_modules裏的第三方庫lodash */ priority: 10,/* 設置優先級,先打包node_modules的*/ chunks: 'all' } } } }
打包以後:
項目根目錄img
npm init -y npm i webpack -D npm i webpack-cli -D npm i css-loader -D npm i style-loader -D npm i mini-css-extract-plugin -D npm i clean-webpack-plugin -D npm i html-webpack-plugin -D //處理圖片要安裝如下loader和插件 cnpm i url-loader img-loader -D cnpm i file-loader -D npm install imagemin-pngquant -D npm i imagemin -D npm i html-loader -D
關於img-loader和imagemin-pngquant
var path = require("path"); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //單獨抽離出css文件 var HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { index: './index.js' }, output: { path: path.resolve(__dirname, "dist"), filename: '[name][hash:5].bundle.js' }, module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, // options:{ } }, { loader: 'css-loader' } ] }, { // limit 給定的值,是圖片的大小,單位是 byte, 若是咱們引用的 圖片,大於或等於給定的 limit值,則不會被轉爲base64格式的字符串, 若是 圖片小於給定的 limit 值,則會被轉爲 base64的字符串 test: /\.(jpg|png|gif|bmp|jpeg)$/, use: [ { loader: 'url-loader', options: { name: '[name][hash:5].[ext]', /* 限制圖片大小 */ limit: 77967, outputPath: 'img' /* 會建立dist/img文件夾 */ } }, // use: 'url-loader?limit=7631&name=[hash:8]-[name].[ext]' { loader:'img-loader', options:{ plugins:[ require('imagemin-pngquant')({ quality:[0.3,0.6] }), ] } } ] }, { test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // chunkFilename: '[id].css', }), new HtmlWebpackPlugin({ filename: 'index.html', template: './index.html', // minify:{ // removeComments: true, /* 清理掉index.html裏面的註釋 */ // collapseWhitespace: true, /* 清理掉空格 */ // } }), new CleanWebpackPlugin() ] }
以上到這一步爲止,只能處理css中的url,尚沒法解析html中引用的圖片路徑,好比html中的<img src="./logo.png" alt="">
是會報錯的。所以須要安裝一個html-loader,而後在webpack.config.js中的module->rules添加一個test:
{ test:/\.html$/, use: [ { loader: 'html-loader', options:{ attrs:['img:src'] } } ] }
而後就 ok 了
先安裝 : npm install webpack-dev-server -g 要全局安裝
安裝成功後執行命令webpack-dev-server --open
就能夠了,默認在localhost:8080端口,項目就啓動了。可是要注意的是,該命令並無把項目進行打包,因此最後發佈的時候仍是要用webpack指令進行打包。
它會實時監聽代碼的變化。
module.exports = { ... devServer:{ port:'9091', // contentBase:'dist' /* 不填的話默認是項目根路徑 */ } }
mock一個假數據
//data.json { "name": "新褲子樂隊", "birthday": "1996" }
//index.js var data = require('./data.json'); console.log(data);
而後就能夠了。
再來講說使用ajax的:
//index.js import $ from 'jquery'; var data = require('./data.json'); console.log(data); $.ajax({ url: 'http://localhost:9091/data.json', success: function(data){ console.log('ajax獲取數據成功:'); console.log(data); }, error:function(){ console.log('獲取數據失敗!'); } })
雖說webpack-dev-server能夠自動監聽代碼變化並自動刷新,可是它每次刷新都要整個頁面刷新,整個頁面的全部數據都要從新請求一遍,實際上修改的只是一部分,其餘的是沒必要要從新請求的。這裏引出了熱更新這個概念。熱更新,英文爲 hot module replace,直譯爲「熱模塊替換」,這裏的熱模塊就是被修改的那部分模塊。
webpack中內置了熱更新的插件,因此沒必要再下載,直接再webpack.config.js中配置:
... var Webpack = require('webpack');//第一步 module.exports = { ... plugins:[ ..., //第二步 new Webpack.HotModuleReplacementPlugin(), /* webpack內置的熱更新插件 */ ], devServer:{ port: "9091", // contentBase: 'dist' /* 默認是項目根路徑 */ hot: true, /* 開啓熱更新 */ } }
這樣熱更新基本實現了。
【須要注意的是】:
對於CSS,若是使用了MiniCssExtractPlugin單獨抽離出來css文件,那麼這個熱更新是無效的。之對於使用style-loader的有效。
對於JS,目前這一步是沒法實現對於js的熱更新的 ,須要再js文件中添加以下代碼:
if(module.hot){ module.hot.accept(); }
這樣就實現了對於js代碼的熱更新。