知識點概覽:css
Loader,HMR ,Create React App, Caching,
Plugin, SourceMap,Vue Cli 3.0 ,Shimming,
WebpackDevServer,TreeShaking, CodeSplitting,
Babel, React , Library, Eslint ,PWA,
Vue, Mode,性能優化,多頁應用,原理,
PreLoading, PreFetching ,環境變量,TypeScript
收穫:html
完全學會Webpack的配置
理解 Webpack的做用及原理
上手項目的打包過程配置
擁有工程化的前端思惟
步入高級前端工程師行列
官網圖鎮樓:前端
webpack 是一個現代 JavaScript 應用程序的靜態模塊打包工具:它作的事情是,分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),並生成一個或多個 bundle,將其打包爲合適的格式以供瀏覽器使用。vue
webpack構建:node
構建就是把源代碼轉換成發佈到線上的可執行 JavaScrip、CSS、HTML 代碼,包括以下內容。
1.代碼轉換:TypeScript 編譯成 JavaScript、SCSS或Less 編譯成 CSS 等。react
2.文件優化:壓縮 JavaScript、CSS、HTML 代碼,壓縮合並圖片等。jquery
3.代碼分割:提取多個頁面的公共代碼、提取首屏不須要執行部分的代碼讓其異步加載。webpack
4.模塊合併:在採用模塊化的項目裏會有不少個模塊和文件,須要構建功能把模塊分類合併成一個文件。ios
5.自動刷新:監聽本地源代碼的變化,自動從新構建、刷新瀏覽器,nodemon。git
6.代碼校驗:在代碼被提交到倉庫前須要校驗代碼是否符合規範,以及單元測試是否經過。
7.自動發佈:更新完代碼後,自動構建出線上發佈代碼並傳輸給發佈系統。
構建實際上是工程化、自動化思想在前端開發中的體現,把一系列流程用代碼去實現,讓代碼自動化地執行這一系列複雜的流程。 構建給前端開發注入了更大的活力,解放了咱們的生產力,更加方便了咱們的開發。
import
語句require()
語句define
和 require
語句@import
語句。url(...)
)或 HTML 文件(<img src=...>
)中的圖片連接 去官網下載node
// 查看node版本號 node -v // 查看npm版本號 npm -v
mkdir webpack-productname cd webpack-productname //初始化webpack配置清單package.json npm init -y
//全局安裝(不推薦),由於若是有兩個項目用了webpack不一樣版本,就會出現版本不統一運行不起來的狀況。只有卸了當前版本安裝對應版本很是麻煩。 npm install webpack webpack-cli -g //查看版本 webpack -v //全局卸載 npm uninstall webpack webpack-cli -g
//在項目裏安裝webpack(推薦使用)。能夠在不一樣項目中使用不一樣的webpack版本。 cd webpack-productname npm install webpack webpack-cli -D //查看版本 npx webpack -v //查看對用包的詳細信息 npm info webpack //安裝指定版本包 npm install webpack@4.16.1 webpack-cli -D
注意:
因爲npm安裝走的是國外的網絡,比較慢容易出現安裝失敗的現象。
能夠用yarn安裝,首先得全局安裝yarn,
npm install yarn -g
。或使用nrm快速切換npm源,首先得全局安裝nrm,
npm install -g nrm
。nrm 使用:
nrm ls 查看可選源。
nrm test npm 測試速度。看哪一個快就use哪一個。
nrm use cnpm 使用cnpm 。
webpack-cli:使咱們們能夠在命令行里正確的使用webpack
webpack 開箱即用,能夠無需使用任何配置文件。然而,webpack 會假定項目的入口起點爲 src/index
,而後會在 dist/main.js
輸出結果,而且在生產環境開啓壓縮和優化。一般,你的項目還須要繼續擴展此能力,爲此你能夠在項目根目錄下建立一個 webpack.config.js
文件,webpack 會自動使用它。
在項目根目錄下建立 webpack.config.js
文件,這是webpack默認配置文件
const path = require('path') module.exports = { //默認是production,打包的文件默認被壓縮。開發時能夠設置爲development,不被壓縮 mode:'production', //打包項目的入口文件 entry: './index.js', //打包項目的輸出文件 output: { //自定義打包輸出文件名 filename:'bundle.js', //輸出文件的絕對路徑 path: path.resolve(__dirname,'bundle') } }
也能夠本身指定配置文件來完成webpack的打包:
npx webpack --config + 自定義配置文件
執行 `npm run build` 後,在控制檯輸出 Hash:1b245e275a547956bf52 //本次打包對應惟一一個hash值 Version:webpack 4.29.6 //本次打包對應webpack版本 Time:162ms Built at:2019-4-11 23:13:43 //本次打包耗時,及打包的時間 Asset Size Chunks Chunk Names //打包後的文件名,大小,id,入口文件名 bundle.js 1.36 KiB 0 [emitted] main Entrypoint main=bundle.js [0]./src/index.js 159 bytes {0}[built] [1]./src/header.js 187 bytes {e}[built] [2]./src/sidebar.js 193 bytes {e}[built] [3]./src/content.js 193 bytes {e} [built]
webpack可使用 loader 來預處理文件,就是經過使用不一樣的Loader,webpack能夠把不一樣的靜態文件都編譯成js文件,好比css,sass,less,ES6/7,vue,JSX等。
須要安裝 file-loader
:解決CSS等文件中的引入圖片路徑問題
npm install file-loader -D
在 webpack.config.js
裏添加 loader 配置
module.exports = { //配置模塊,主要用來配置不一樣文件的加載器 module: { //配置模塊規則 rules: [ { test: /\.(png|jpg|gif)$/, //正則匹配要使用相應loader的文件 use: [ { loader: 'file-loader', //要用到的loader options: { //palceholder佔位符 name:'[name].[ext]', //打包後的圖片名字,後綴和打包的以前的圖片同樣 outputPath: 'images/' //圖片打包後的地址 }, }, ], }, ], }, };
詳細請看官方文檔:file-loader
須要安裝 url-loader
:當圖片小於limit的時候會把圖片BASE64編碼,大於limit參數的時候仍是使用file-loader 進行拷貝
npm install url-loader -D
在 webpack.config.js
裏添加 loader 配置
module.exports = { module: { rules: [ { test: /\.(png|jpg|gif|bmp/)$/i, use: [ { loader: 'url-loader', options: { name:'[name].[ext]', outputPath: 'images/', limit: 8192 //小於8192k,就能夠轉化成base64格式。大於就會打包成文件格式 } } ] } ] } }
詳細請看官方文檔:url-loader
須要安裝 css-loader style-loader
:
npm install css-loader style-loader -D
在 webpack.config.js
裏添加 loader 配置
module.exports = { module: { rules: [ { test: /\.css$/, //匹配以css爲後綴的文件 use: ['style-loader', 'css-loader'],//loader的執行順序是從右向左,從下到上。css-loader:分析幾個css文件之間的關係,最終合併爲一個css。style-loader:在獲得css生成的內容時,把其掛載到html的head裏,成爲內聯樣式。 }, ], }, };
須要安裝 sass-loader node-sass
:
npm install sass-loader node-sass -D
在 webpack.config.js
裏添加 loader 配置
module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [ "style-loader", // 將 JS 字符串生成爲 style 節點 "css-loader", // 將 CSS 轉化成 CommonJS 模塊 "sass-loader" // 將 Sass 編譯成 CSS,默認使用 Node Sass ] }] } };
爲了瀏覽器的兼容性,有時候咱們必須加入-webkit,-ms,-o,-moz這些前綴
npm i postcss-loader autoprefixer -D
在項目跟目錄下建立 postcss.config.js
module.exports = { plugins: [ require('autoprefixer') ] }
webpack.config.js
module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [ "style-loader", // 將 JS 字符串生成爲 style 節點 "css-loader", // 將 CSS 轉化成 CommonJS 模塊 'postcss-loader', "sass-loader", // 將 Sass 編譯成 CSS,默認使用 Node Sass ] }] } };
給loader加一些配置項:
webpack.config.js
module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [ "style-loader", { loader: "css-loader", options:{ importLoaders:2 ,//若是sass文件裏還引入了另一個sass文件,另外一個文件還會從postcss-loader向上解析。若是不加,就直接從css-loader開始解析。 modules: true //開啓css的模塊打包。css樣式不會和其餘模塊發生耦合和衝突 } }, 'postcss-loader', "sass-loader", ] }] } };
在 阿里巴巴矢量圖標庫中,把須要的字體圖標下載到本地,解壓。將iconfont.eot iconfont.svg iconfont.ttf iconfont.woff
文件放入到項目中,在src中新建一個放字體圖標的文件夾font。將iconfont.css文件拷貝到項目中,本身改一下引入字體圖標的路徑。
須要安裝 file-loader
:
npm i file-loader -D
webpack.config.js
module.exports = { ... module: { rules: [{ test: /\.(eot|ttf|svg|woff)$/, use:{ loader:'file-loader' } }, ] }] } };
詳細請看官方文檔:asset-management
使用plugins讓打包更便捷
安裝:npm i html-webpack-plugin -D
基本用法:在 webpack.config.js 中:
const HtmlWebpackPlugin = require('html-webpack-plugin'); const path = require('path'); module.exports = { entry: 'index.js', output: { path: path.resolve(__dirname, './dist'), filename: 'index_bundle.js' }, plugins: [new HtmlWebpackPlugin({ template: 'src/index.html' //以index.html爲模板,把打包生成的js自動引入到這個html文件中 })] };
安裝:npm i clean-webpack-plugin -D
基本用法:在 webpack.config.js 中:
const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const path = require('path'); module.exports = { entry: 'index.js', output: { path: path.resolve(__dirname, './dist'), filename: 'index_bundle.js' }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' //在打包以後,以.html爲模板,把打包生成的js自動引入到這個html文件中 }), new CleanWebpackPlugin(['dist']), // 在打包以前,能夠刪除dist文件夾下的全部內容 ] };
在打包多入口文件時的配置
基本用法:在 webpack.config.js 中:
const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const path = require('path'); module.exports = { entry: { main: './src/index.js', sub: './src/index.js' }, output: { publicPath: 'http://cdn.com.cn', //將注入到html中的js文件前面加上地址 path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' //在打包以後,以.html爲模板,把打包生成的js自動引入到這個html文件中 }), new CleanWebpackPlugin(['dist']), // 在打包以前,能夠刪除dist文件夾下的全部內容 ] };
詳細請看官網:Output output-management
sourcemap:打包編譯後的文件和源文件的映射關係,用於開發者調試用。
development環境推薦使用: devtool: 'cheap-module-eval-source-map',
production環境推薦使用: devtool: 'cheap-module-source-map',
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { mode: 'development', devtool: 'cheap-module-eval-source-map', //devtool:'none',//在開發者模式下,默認開啓sourcemap,將其關閉 //devtool:'source-map'//開啓映射打包會變慢 //devtool:'inline-source-map'//不單獨生成.map文件,會將生成的映射文件以base64的形式插入到打包後的js文件的底部 //devtool:'cheap-inline-source-map'//代碼出錯提示不用精確顯示第幾行的第幾個字符出錯,只顯示第幾行出錯,會提升一些性能 //devtool:'cheap-module-inline-source-map'//不只管本身的業務代碼出錯,也管第三方模塊和loader的一些報錯 //devtool:'eval'//執行效率最快,性能最好,可是針對比較複雜的代碼的狀況下,提示內容不全面 //devtool: 'cheap-module-eval-source-map',//在開發環境推薦使用,提示比較全,打包速度比較快 //devtool: 'cheap-module-source-map',//在生產環境中推薦使用,提示效果會好一些 entry: { main: './src/index.js' }, module: { rules: [{ test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }, { test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }] }, plugins: [new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'])], output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') } }
詳細請看官網:devtool
解決每次在src裏編寫完代碼都須要手動從新運行 npm run dev
1.在 package.json 中配置
{ "name": "haiyang", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "bundle": "webpack", "watch": "webpack --watch",// 加--watch自動監聽代碼的變化 }, }
2.在 webpack.config.js 中,加 devServer
安裝 npm i webpack-dev-server –D
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { mode: 'development', devtool: 'cheap-module-eval-source-map', entry: { main: './src/index.js' }, + devServer: { contentBase: './dist', open: true, port: 8080, proxy: {//配置跨域,訪問的域名會被代理到本地的3000端口 '/api': 'http://localhost:3000' } }, module: { rules: [] }, plugins: [], output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') } }
在 package.json 中:
{ "name": "haiyang", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "bundle": "webpack", "watch": "webpack --watch",// 加--watch自動監聽代碼的變化 "start": "webpack-dev-server",//配置熱更新 }, }
詳細請看官網 :dev-server
擴充知識:本身寫一個相似webpackdevserver的工具
瞭解便可,功能不全,自行擴展。
在 package.json 中:
{ "name": "haiyang", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "bundle": "webpack", "watch": "webpack --watch",// 加--watch自動監聽代碼的變化 "start": "webpack-dev-server",//配置熱更新 + "server" : "node server.js" //本身寫一個相似webpackdevserver的工具 }, }
安裝 :npm i express webpack-dev-middleware -D
在 項目根目錄下建立 server.js 文件
在 server.js 中
const express = require('express'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const config = require('./webpack.config.js'); const complier = webpack(config); const app = express(); app.use(webpackDevMiddleware(complier, {})); app.listen(3000, () => { console.log('server is running'); });
在 package.json 中:
{ "name": "haiyang", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack-dev-server" //將文件打包到內存中,有助於開發 }, }
在 webpack.config.js 中
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { mode: 'development', devtool: 'cheap-module-eval-source-map', entry: { main: './src/index.js' }, devServer: { contentBase: './dist', open: true, port: 8080, + hot: true,//開啓熱更新 + hotOnly: true//儘管html功能沒有實現,也不讓瀏覽器刷新 }, module: { rules: [{ test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }, { test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist']), + new webpack.HotModuleReplacementPlugin() //使用模塊熱更新插件 ], output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') } }
index.js
//若是模塊啓用了HMR,就能夠用 module.hot.accept(),監聽模塊的更新。 if (module.hot) { module.hot.accept('./library.js', function() { // 使用更新過的 library 模塊執行某些操做... }) }
注意點:
引入css,用框架Vue,React 時,不須要寫 module.hot.accept(),由於在使用css-loader,vue-loader,babel-preset時,就已經配置好了HMR,不須要本身寫
詳細請看官方文檔:hot-module-replacement api/hot-module-replacement concepts/hot-module-replacement
BABEL官網:https://babeljs.io/setup
安裝依賴包:
npm i babel-loader @babel/core @babel/preset-env -D //生產依賴,兼容低版本瀏覽器 npm install --save @babel/polyfill
在 webpack.config.js 中
module: { rules: [ { test: /\.js$/, exclude: /node_modules/,//不須要對第三方模塊進行轉換,耗費性能 loader: "babel-loader" , options:{ "presets": [["@babel/preset-env",{ targets: {//這個項目運行在大於什麼版本的瀏覽器上,已經支持es6的語法的高版本瀏覽器就不須要轉義成es5了 edge: "17", firefox: "60", chrome: "67", safari: "11.1", }, useBuiltIns:'usage' //按需添加polyfill,把用到的代碼都轉成低版本瀏覽器兼容的 }]] } } ] }
在 index.js 中:
//在業務代碼運行以前最頂部導入 import "@babel/polyfill";
安裝:
npm install --save-dev @babel/plugin-transform-runtime npm install --save @babel/runtime npm install --save @babel/runtime-corejs2
在 webpack.config.js 中
module: { rules: [ { test: /\.js$/, exclude: /node_modules/,//不須要對第三方模塊進行轉換,耗費性能 loader: "babel-loader" , options:{ "plugins": [["@babel/plugin-transform-runtime",{ "corejs": 2, "helpers": true, "regenerator": true, "useESModules": false }]] } } ] }
因爲babel須要配置的內容很是多,咱們須要在項目根目錄下建立一個 .babelrc
文件。
就不須要在 webpack.config.js 中寫 babel 的配置了。
在 .babelrc
中:
{ "plugins": [["@babel/plugin-transform-runtime",{ "corejs": 2, "helpers": true, "regenerator": true, "useESModules": false }]] }
業務代碼:
在 .babelrc
中:
{ "presets": [ ["@babel/preset-env",{ targets: { edge: "17", firefox: "60", chrome: "67", safari: "11.1", }, useBuiltIns:'usage' } ], "@babel/preset-react" ] } //執行順序:從下往上,從右向左的順序
安裝:
npm i react react-dom --save npm install --save-dev @babel/preset-react
詳細內容請看官網:babel-loader
import
和 export
的靜態結構特性的引入。當引入一個模塊時,不引入全部的代碼,只引入須要的代碼在 webpack.config.js 中:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { mode: 'development', devtool: 'cheap-module-eval-source-map', entry: { main: './src/index.js' }, devServer: { contentBase: './dist', open: true, port: 8080, hot: true, hotOnly: true }, module: { rules: [] }, plugins: [], + optimization: { //在開發環境中加,生產環境不加 usedExports: true }, output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') } }
在 package.json 中:
{ + "sideEffects": ["*.css"], //對 全部的css文件 不使用Tree shaking。若是填 false,就是都須要用到Tree shaking }
詳細內容請看官網:tree-shaking
在項目根目錄下建立兩個文件,webpack.dev.js,webpack.prod.js
webpack.dev.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { mode: 'development', devtool: 'cheap-module-eval-source-map', entry: { main: './src/index.js' }, devServer: { contentBase: './dist', open: true, port: 8080, hot: true, hotOnly: true }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }, { test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist']), new webpack.HotModuleReplacementPlugin() ], optimization: { usedExports: true }, output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') } }
webpack.prod.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { mode: 'production', devtool: 'cheap-module-source-map', entry: { main: './src/index.js' }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }, { test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist']), ], output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') } }
在 package.json 中:
{ "scripts": { "dev": "webpack-dev-server --config webpack.dev.js", "build": "webpack --config webpack.prod.js" }, }
解決 webpack.dev.js,webpack.prod.js 存在大量重複代碼,在項目根目錄下建立一個 webpack.common.js 文件,把公共代碼提取出來
安裝 :
npm i webpack-merge -D
webpack.common.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { entry: { main: './src/index.js' }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }, { test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'],{ root:path.resolve(__dirname,'../') }), ], output: { filename: '[name].js', path: path.resolve(__dirname, '../dist') } }
webpack.dev.js
const webpack = require('webpack'); const merge = require('webpack-merge') const commenConfig = require('./webpack.commin.js') const devConfig = { mode: 'development', devtool: 'cheap-module-eval-source-map', devServer: { contentBase: './dist', open: true, port: 8080, hot: true, hotOnly: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ], optimization: { usedExports: true }, } //將開發配置和公共配置作結合 module.exports = merge(commenConfig, devConfig)
webpack.prod.js
const merge = require('webpack-merge') const commenConfig = require('./webpack.commin.js') const prodConfig = { mode: 'production', devtool: 'cheap-module-source-map', } //將線上配置和公共配置作結合 module.exports = merge(commenConfig, prodConfig)
最後在根目錄下建立一個build文件夾,將 webpack.common.js , webpack.dev.js ,webpack.prod.js 放在build文件夾下,統一管理。
在 package.json 中:
{ "scripts": { "dev": "webpack-dev-server --config ./build/webpack.dev.js", "build": "webpack --config ./build/webpack.prod.js" }, }
詳細請看官網文檔:guides/production
安裝: npm i lodash --save
npm i babel-plugin-dynamic-import-webpack -D
代碼分割,和webpack無關,爲了提高性能
webpack中實現代碼分割,兩種方式:
第一種方法:同步代碼: 只須要在webpack.common.js中作optimization的配置便可
第二種方法:異步代碼(import): 異步代碼,無需作任何配置,會自動進行代碼分割,放置到新的文件中
第一種方法:在 webpack.common.js 中
module.exports = { entry: { main: './src/index.js' }, module: { rules: [] }, plugins: [], + optimization:{ + splitChunks:{ //啓動代碼分割,有默認配置項 + chunks:'all' + } + }, output: {} }
第二種方法在 .babelrc
中:
{ presets: [ [ "@babel/preset-env", { targets: { chrome: "67", }, useBuiltIns: 'usage' } ], "@babel/preset-react" ], + plugins: ["dynamic-import-webpack"] }
詳細內容請看官網:code-splitting
安裝:npm install --save-dev @babeL/plugin-syntax-dynamic-import
在業務 index.js 中:
function getComponent() { return import(/* webpackChunkName:"lodash" */ 'lodash').then(({ default: _ }) => { var element = document.createElement('div'); element.innerHTML = _.join(['1', '2'], '-'); return element; }) } getComponent().then(element => { document.body.appendChild(element); });
在 .babelrc
中:
{ presets: [ [ "@babel/preset-env", { targets: { chrome: "67", }, useBuiltIns: 'usage' } ], "@babel/preset-react" ], + plugins: ["@babeL/plugin-syntax-dynamic-import"] }
在 webpack.common.js 中:
module.exports = { entry: { main: './src/index.js' }, module: { rules: [] }, plugins: [], + optimization:{ + splitChunks:{ //啓動代碼分割,不寫有默認配置項 + chunks: 'all',//參數all/initial/async,只對全部/同步/異步進行代碼分割 minSize: 30000, //大於30kb纔會對代碼分割 maxSize: 0, minChunks: 1,//打包生成的文件,當一個模塊至少用多少次時纔會進行代碼分割 maxAsyncRequests: 5,//同時加載的模塊數最可能是5個 maxInitialRequests: 3,//入口文件最多3個模塊會作代碼分割,不然不會 automaticNameDelimiter: '~',//文件自動生成的鏈接符 name: true, cacheGroups:{//對同步代碼走緩存組 vendors: { test: /[\\/]node_modules[\\/]/, priority: -10,//誰優先級大就把打包後的文件放到哪一個組 filename:'vendors.js' }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true,//模塊已經被打包過了,就不用再打包了,複用以前的就能夠 filename:'common.js' //打包以後的文件名 } } + } + }, output: {} }
詳細請看官方文檔:split-chunks-plugin
用戶當前須要用什麼功能就只加載這個功能對應的代碼,也就是所謂的按需加載 在給單頁應用作按需加載優化時,通常採用如下原則:
- 對網站功能進行劃分,每一類一個chunk
- 對於首次打開頁面須要的功能直接加載,儘快展現給用戶
- 某些依賴大量代碼的功能點能夠按需加載
- 被分割出去的代碼須要一個按需加載的時機
每個文件就是一個 chunk
詳細請看官方文檔:lazy-loading
打開網址:webpack分析工具:https://github.com/webpack/analyse
在 package.json 中
{ "scripts": { "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js", //把打包過程的描述放在stats.json文件中 "dev": "webpack-dev-server --config ./build/webpack.dev.js", "build": "webpack --config ./build/webpack.prod.js" }, }
在控制檯運行 npm run dev-build
,在根目錄下生成 stats.json 文件。打開網址 http://webpack.github.io/analyse/
,把stats.json文件傳上去,會出現分析結果。
詳細請看官方文檔:bundle-analysis 打包分析工具
經過使用webpack-bundle-analyzer能夠看到項目各模塊的大小,能夠按需優化。
官網圖鎮樓:
安裝:
# NPM npm install --save-dev webpack-bundle-analyzer # Yarn yarn add -D webpack-bundle-analyzer
配置:在 webpack.config.js 中:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = { plugins: [ new BundleAnalyzerPlugin( { analyzerMode: 'server', analyzerHost: '127.0.0.1', analyzerPort: 8889, reportFilename: 'report.html', defaultSizes: 'parsed', openAnalyzer: true, generateStatsFile: false, statsFilename: 'stats.json', statsOptions: null, logLevel: 'info' } ) ] }
輸出:在 package.json 中:
"analyz": "NODE_ENV=production npm_config_report=true npm run build"
在線分析:在 控制檯輸入:
webpack --profile --json > stats.json
- profile:記錄下構建過程當中的耗時信息;
- json:以 JSON 的格式輸出構建結果,最後只輸出一個 .json 文件,這個文件中包括全部構建相關的信息。
- Webpack 官方提供了一個可視化分析工具 Webpack Analyse
- Modules:展現全部的模塊,每一個模塊對應一個文件。而且還包含全部模塊之間的依賴關係圖、模塊路徑、模塊ID、模塊所屬 Chunk、模塊大小;
- Chunks:展現全部的代碼塊,一個代碼塊中包含多個模塊。而且還包含代碼塊的ID、名稱、大小、每一個代碼塊包含的模塊數量,以及代碼塊之間的依賴關係圖;
- Assets:展現全部輸出的文件資源,包括 .js、.css、圖片等。而且還包括文件名稱、大小、該文件來自哪一個代碼塊;
- Warnings:展現構建過程當中出現的全部警告信息;
- Errors:展現構建過程當中出現的全部錯誤信息;
- Hints:展現處理每一個模塊的過程當中的耗時。
打開谷歌控制檯查看代碼使用率,按 ctrl+shift+p
,輸入 coverage 查看。
假若有一個HomePage組件,其內部有一個LoginButton.js登錄組件,再點擊後按需加載 LoginModel 組件。
LoginButton.js:
import(/* webpackPrefetch: true */ 'LoginModal');
這會生成 <link rel="prefetch" href="login-modal-chunk.js">
並追加到頁面頭部,指示着瀏覽器在閒置時間預取 login-modal-chunk.js
文件。就是說,只要首頁加載完成,就會在空閒時間把登陸模塊也加載了。
總結:
/ webpackPrefetch: true /:把主加載流程加載完畢,在空閒時在加載其餘,等再點擊其餘時,只須要從緩存中讀取便可,性能更好。推薦使用,提升代碼利用率。把一些交互後才能用到的代碼寫到異步組件裏,經過懶加載的形式,去把這塊的代碼邏輯加載進來,性能提高,頁面訪問速度更快。/ webpackPreload: true /: 和主加載流程一塊兒並行加載。
詳細請看官方文檔:prefetchingpreloading-modules
在 webpack.config.js 中
module.exports = { entry: { main: './src/index.js' }, module: { }, plugins: [], optimization: { splitChunks: { chunks: 'all' } }, output: { filename: '[name].js',//入口文件打包後生成的文件名 + chunkFilename: '[name].chunk.js',//main.js異步加載的間接的js文件。用來打包import('module')方法中引入的模塊 path: path.resolve(__dirname, '../dist') } }
由於CSS的下載和JS能夠並行,當一個HTML文件很大的時候,咱們能夠把CSS單獨提取出來加載
import('module')
方法中引入的模塊安裝 :
//抽離css文件 npm install --save-dev mini-css-extract-plugin //壓縮css文件 npm i optimize-css-assets-webpack-plugin -D
在 webpack.prod.js 中:
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); const prodConfig = { mode: 'production', devtool: 'cheap-module-source-map', module: { rules:[{ test: /\.scss$/, use: [ + MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ + MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ] }] }, + optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] }, plugins: [ + new MiniCssExtractPlugin({ filename: '[name].css',//直接引用的css文件 chunkFilename: '[name].chunk.css'//間接引用的css文件 }) ] } module.exports = merge(commonConfig, prodConfig);
在 webpack.dev.js
const webpack = require('webpack'); const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); const devConfig = { mode: 'development', devtool: 'cheap-module-eval-source-map', devServer: { contentBase: './dist', open: true, port: 8080, hot: true }, module: { rules: [{ test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }] }, plugins: [ new webpack.HotModuleReplacementPlugin() ], } module.exports = merge(commonConfig, devConfig);
在 webpack.common.js 中:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { main: './src/index.js', }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '../') }) ], optimization: { usedExports: true,//TreeShaking splitChunks: { chunks: 'all' } }, output: { filename: '[name].js', chunkFilename: '[name].chunk.js', path: path.resolve(__dirname, '../dist') } }
在 package.json 中:
{ "sideEffects": ["*.css"] //除了css文件,其他的都TreeShaking }
詳細請看官方文檔:mini-css-extract-plugin
在 webpack.common.js 中:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { main: './src/index.js', }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '../') }) ], optimization: { + runtimeChunk: {//兼容老版本webpack4,把manifest打包到runtime裏,不影響業務代碼和第三方模塊 name: 'runtime' }, usedExports: true, splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, name: 'vendors', } } } }, performance: false,//禁止提示性能上的一些問題 + output: { path: path.resolve(__dirname, '../dist') } }
在 webpack.dev.js 中:
const webpack = require('webpack'); const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); const devConfig = { mode: 'development', devtool: 'cheap-module-eval-source-map', devServer: { contentBase: './dist', open: true, port: 8080, hot: true }, module: { rules: [{ test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }] }, plugins: [ new webpack.HotModuleReplacementPlugin() ], + output: { filename: '[name].js', chunkFilename: '[name].js', } } module.exports = merge(commonConfig, devConfig);
在 webpack.prod.js 中:
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); const prodConfig = { mode: 'production', devtool: 'cheap-module-source-map', module: { rules:[{ test: /\.scss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ] }] }, optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[name].chunk.css' }) ], + output: { filename: '[name].[contenthash].js', //源代碼不變,hash值就不會變,解決瀏覽器緩存問題。打包上線時,用戶只須要更新有變化的代碼,沒有變化的從瀏覽器緩存讀取 chunkFilename: '[name].[contenthash].js' } } module.exports = merge(commonConfig, prodConfig);
詳細請看官網文檔:manifest
在 webpack.common.js 中:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { entry: { main: './src/index.js', }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: [{ loader: 'babel-loader' }, { loader: 'imports-loader?this=>window' }] }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '../') }), + new webpack.ProvidePlugin({ $: 'jquery',//發現模塊中有$字符串,就自動引入iquery,就能夠用jquery _join: ['lodash', 'join']//_join表明lodash裏的join方法 }), ], optimization: { runtimeChunk: { name: 'runtime' }, usedExports: true, splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, name: 'vendors', } } } }, performance: false, output: { path: path.resolve(__dirname, '../dist') } }
若是想讓每一個js模塊的this都指向window:
安裝: npm install imports-loader -D
在 webpack.common.js 中:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); module.exports = { entry: { main: './src/index.js', }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: [{ loader: 'babel-loader' }, {//每一個js模塊的this都指向window + loader: 'imports-loader?this=>window' }] }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '../') }), + new webpack.ProvidePlugin({ $: 'jquery',//發現模塊中有$字符串,就自動引入iquery,就能夠用jquery _join: ['lodash', 'join']//_join表明lodash裏的join方法 }), ], optimization: { runtimeChunk: { name: 'runtime' }, usedExports: true, splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, name: 'vendors', } } } }, performance: false, output: { path: path.resolve(__dirname, '../dist') } }
詳細請看官方文檔:imports-loader shimming
只須要一個common.js文件經過在package.json中傳遞不一樣的參數,區分是開發環境仍是生產環境。
在 package.json 中:
{ "name": "haiyang", "sideEffects": [ "*.css" ], "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev-build": "webpack --config ./build/webpack.common.js", "dev": "webpack-dev-server --config ./build/webpack.common.js", "build": "webpack --env.production --config ./build/webpack.common.js" //經過--env.production,把環境變量傳進去 }, "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.2.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-runtime": "^7.2.0", "@babel/preset-env": "^7.2.0", "@babel/preset-react": "^7.0.0", "autoprefixer": "^9.3.1", "babel-loader": "^8.0.4", "clean-webpack-plugin": "^1.0.0", "css-loader": "^1.0.1", "express": "^4.16.4", "file-loader": "^2.0.0", "html-webpack-plugin": "^3.2.0", "imports-loader": "^0.8.0", "mini-css-extract-plugin": "^0.5.0", "node-sass": "^4.10.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-cli": "^3.1.2", "webpack-dev-middleware": "^3.4.0", "webpack-dev-server": "^3.1.10", "webpack-merge": "^4.1.5" }, "dependencies": { "@babel/polyfill": "^7.0.0", "@babel/runtime": "^7.2.0", "@babel/runtime-corejs2": "^7.2.0", "jquery": "^3.3.1", "lodash": "^4.17.11", "react": "^16.6.3", "react-dom": "^16.6.3", "webpack": "^4.25.1" } }
在 webpack.common.js 中:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); const merge = require('webpack-merge'); const devConfig = require('./webpack.dev.js'); const prodConfig = require('./webpack.prod.js'); const commonConfig = { entry: { main: './src/index.js', }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: [{ loader: 'babel-loader' }, { loader: 'imports-loader?this=>window' }] }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '../') }), new webpack.ProvidePlugin({ $: 'jquery', _join: ['lodash', 'join'] }), ], optimization: { runtimeChunk: { name: 'runtime' }, usedExports: true, splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, name: 'vendors', } } } }, performance: false, output: { path: path.resolve(__dirname, '../dist') } } module.exports = (env) => { if(env && env.production) {//線上環境 return merge(commonConfig, prodConfig); }else {//開發環境 return merge(commonConfig, devConfig); } }
在 webpack.dev.js 中:
const webpack = require('webpack'); const devConfig = { mode: 'development', devtool: 'cheap-module-eval-source-map', devServer: { contentBase: './dist', open: true, port: 8080, hot: true }, module: { rules: [{ test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }] }, plugins: [ new webpack.HotModuleReplacementPlugin() ], output: { filename: '[name].js', chunkFilename: '[name].js', } } module.exports = devConfig;
在webpack.prod.js 中:
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const prodConfig = { mode: 'production', devtool: 'cheap-module-source-map', module: { rules:[{ test: /\.scss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'sass-loader', ] }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ] }] }, optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[name].chunk.css' }) ], output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js' } } module.exports = prodConfig;
倉庫源碼 【41】
在 webpack.config.js 中:
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', //支持經過<scritp src=ilibrary. js'></script> 標籤引入,在全局變量增長一個root變量 libraryTarget: 'umd' //別人用的時候,經過任何形式引入庫均可以,好比AMD,CMD,ES MODULE,Commonjs // library: 'root',//打包生成全局變量root // libraryTarget: 'this' //把全局變量root掛載到this上,能夠填umd,this,window,global // externals: { // lodash:{ // root:'_', //是用script標籤引入進來的,必須在全局注入一個 _ 變量,下面的library才能正常執行 // commonjs:'lodash',//在用commonjs規範引入是,名字必須是lodash // } // } } }
在 package.json 中:
"main": "./dist/library.js", //最終要給別人使用的
在 npm 官網註冊一個帳號,在命令行輸入 :
//添加用戶名和密碼 npm adduser //把項目發佈到npm官網上 npm publish //但別人用你發佈的庫時 npm i + 庫名
詳細請看官方文檔:externals author-libraries
漸進式網絡應用程序,PWA 能夠用來作不少事。其中最重要的是,在離線(offline)時應用程序可以繼續運行功能。這是經過使用名爲 Service Workers 的 web 技術來實現的。線上環境時纔用到pwa,開發時不須要
倉庫源碼 【42】
安裝:
//模擬服務器 npm i http-server -D //添加 workbox-webpack-plugin 插件,而後調整 webpack.config.js 文件 npm install workbox-webpack-plugin --save-dev
在 package.json 中:
"scripts": { + "start": "http-server dist",//在dist目錄下運行http-server服務 "dev": "webpack-dev-server --config ./build/webpack.dev.js", "build": "webpack --config ./build/webpack.prod.js" },
線上環境時纔用到pwa,開發時不須要,只須要改 webpack.prod.js ,
在 webpack.prod.js 中:
const WorkboxPlugin = require('workbox-webpack-plugin'); plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[name].chunk.css' }), + new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true }) ],
在業務代碼 index.js 中使用pwa
console.log('hello, haiyang'); if ('serviceWorker' in navigator) { //若是瀏覽器支持serviceWorker,就執行如下代碼 window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js') .then(registration => {//註冊成功 console.log('service-worker registed'); }).catch(error => {//沒註冊成功 console.log('service-worker register error'); }) }) }
詳細請看官方文檔:progressive-web-application
TypeScript 是 JavaScript 的超集,爲其增長了類型系統,能夠編譯爲普通 JavaScript 代碼。這篇指南里咱們將會學習是如何將 webpack 和 TypeScript 進行集成。
倉庫源碼 【43】
安裝:
npm install --save-dev typescript ts-loader
在 webpack.config.js 中:
const path = require('path'); module.exports = { mode: 'production', entry: './src/index.tsx', module: { rules: [{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }] }, output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } }
在項目根目錄下建立 tsconfig.json 文件:
{ "compilerOpitons": { "outDir": "./dist", "module": "es6",//模塊引入的方式 "target": "es5",//轉換爲es5,在大部分瀏覽器都能運行 "allowJs": true, //在typescript中容許引入js文件 } }
在從 npm 安裝 third party library(第三方庫) 時,必定要記得同時安裝此 library 的類型聲明文件(typing definition)。你能夠從 TypeSearch 中找到並安裝這些第三方庫的類型聲明文件。在使用時,哪有錯能夠有警告提示,方便改錯。
安裝:
//在typescript裏用loadah npm install --save-dev @types/lodash
詳細請看官方文檔:typescript
倉庫源碼 【44】
安裝:
//向服務器發送axios請求 npm i axios -D
在 index.js 中:
componentDidMount() { axios.get('/react/api/header.json') .then((res) => { console.log(res); }) }
在 webpack.config.js 中:
devServer: { contentBase: './dist', open: true, port: 8080, hot: true, hotOnly: true, + proxy: {//開發時方便接口轉發,線上不用 '/react/api': {//訪問 /react/api 時,代理到 target 上 target: 'https://www.dell-lee.com', secure: false,//對https協議的網址的請求的轉發 // 攔截,請求的是html,不走代理直接返回 /index.html文件 //bypass: function(req, res, proxyOptions) { // if (req.headers.accept.indexOf('html') !== -1) { // console.log('Skipping proxy for browser request.'); // return '/index.html'; // } // }, pathRewrite: { 'header.json': 'demo.json' //最後拿的是demo.json的數據 }, changeOrigin: true,//解決網站對接口的限制 headers: {//變動請求頭 host: 'www.dell-lee.com', } } } },
詳細請看官方文檔:devserverproxy
倉庫源碼 【45】
安裝:
npm i react-router-dom --save
在 webpack.config.js 中:
devServer: {//配置只在開發時有效,上線時後端也需配置 contentBase: './dist', open: true, port: 8080, hot: true, hotOnly: true, + historyApiFallback: true, //historyApiFallback: { // rewrites: [//訪問任何路徑都展現index.html頁面 // { from: /\.*/, to: '/index.html' }, //] //}, proxy: { '/react/api': { target: 'https://www.dell-lee.com', secure: false, pathRewrite: { 'header.json': 'demo.json' }, changeOrigin: true, headers: { host: 'www.dell-lee.com', } } } },
詳細請看官方文檔:devserverhistoryapifallback
倉庫源碼 【46】
安裝:
//安裝eslint工具,規範項目中的代碼 npm i eslint -D npm i babel-eslint -D npm i eslint-loader -D
//快速生成eslint配置 npx eslint --init
在 .eslintrc.js 中:
module.exports = { "extends": "airbnb", "parser": "babel-eslint", "rules": { "react/prefer-stateless-function": 0, "react/jsx-filename-extension": 0 }, globals: { document: false } };
詳細請看官方文檔:eslint
在 vscode 編輯器裏安裝 eslint 插件,自動檢測語法錯誤。(推薦使用)
在 webpack.config.js 中:
devServer: { + overlay: true,//在瀏覽器彈出提示有錯誤 }, rules: [{ test: /\.js$/, exclude: /node_modules/, + use: ['babel-loader', 'eslint-loader'] //先檢查代碼寫的是否規範,在轉換成es5 }, ...],
在真實項目中,也能夠不在webpack 中配置eslint,在提交git倉庫時,git 鉤子 eslint src 。可是沒有圖形交互式的錯誤提示。
詳細請看官方文檔:eslint-loader
倉庫源碼 【47】
倉庫源碼 【48】
引入資源文件寫後綴,像 圖片文件(.jpg, .png, .svg),邏輯代碼配置在extensions中:extensions: ['.js', '.jsx']
詳細請看官方文檔:dll-plugin
倉庫源碼 【49】
實現第三方模塊只打包一次
安裝:
npm i add-asset-html-webpack-plugin --save
在 build 文件夾裏建立 webpack.dll.js 文件:把第三方模塊單獨進行打包,生成一個vendors.dll.js 文件,全部的第三方模塊都在這個文件裏。
const path = require('path'); const webpack = require('webpack'); module.exports = { mode: 'production', entry: { vendors: ['lodash'], react: ['react', 'react-dom'], jquery: ['jquery'] }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, '../dll'), library: '[name]'//打包生成的庫名,經過全局變量的形式暴露到全局 }, plugins: [ new webpack.DllPlugin({//對暴露到全局的代碼進行分析,生成vendors.manifest.json 的映射文件, name: '[name]', path: path.resolve(__dirname, '../dll/[name].manifest.json'), }) ] }
在 webpack.common.js 中:
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); const files = fs.readdirSync(path.resolve(__dirname, '../dll')); files.forEach(file => { if(/.*\.dll.js/.test(file)) { plugins.push(new AddAssetHtmlWebpackPlugin({//將打包好的dll文件掛載到html中 filepath: path.resolve(__dirname, '../dll', file) })) } if(/.*\.manifest.json/.test(file)) { plugins.push(new webpack.DllReferencePlugin({//分析第三方模塊是否已經在dll文件裏,若是裏面有就不用再node_modules在分析打包了 manifest: path.resolve(__dirname, '../dll', file) })) } })
總結:
若是不使用使用 DLLPlugin 插件,當引入第三方模塊時,每一次打包都要進行分析,是消耗打包的性能的。使用 DLLPlugin 提升打包速度,在第一次打包時,把第三方模塊單獨打包生成一個文件 vendors.dll.js ,以後在打包時就能夠直接從 vendors.dll.js 中引入以前打包好的第三方模塊,速度就會變快。要想實現,就得作一些配置:
先配置 webpack.dll.js 文件,在配置 webpack.common.js 文件
==============================================================
.dll 爲後綴的文件稱爲動態連接庫,在一個動態連接庫中能夠包含給其餘模塊調用的函數和數據
在 webpack.dll.js 中:
module.exports = { entry: { react: ['react'] //react模塊打包到一個動態鏈接庫 }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].dll.js', //輸出動態鏈接庫的文件名稱 library: '_dll_[name]' //全局變量名稱 }, plugins: [ new webpack.DllPlugin({ name: '_dll_[name]', //和output.library中一致,值就是輸出的manifest.json中的 name值 path: path.join(__dirname, 'dist', '[name].manifest.json') }) ] }
webpack --config webpack.dll.config.js --mode production
在 webpack.common.js 中:
plugins: [ + new webpack.DllReferencePlugin({ + manifest: require(path.join(__dirname, 'dist', 'react.manifest.json')), + }) ],
webpack --config webpack.config.js --mode development
==============================================================
配置 Tree shaking,把用不到的代碼去除掉。配置 SplitChunksPlugin。
HappyPack就能讓Webpack把任務分解給多個子進程去併發的執行,子進程處理完後再把結果發送給主進程。 happypack
安裝:npm i happypack@next -D
配置:
module: { rules: [{ test: /\.js$/, //把對.js文件的處理轉交給id爲babel的HappyPack實例 + use: 'happypack/loader?id=babel', include: path.resolve(__dirname, 'src'), exclude: /node_modules/ }, { //把對.css文件的處理轉交給id爲css的HappyPack實例 test: /\.css$/, + use: 'happypack/loader?id=css', include: path.resolve(__dirname, 'src') }], noParse: [/react\.min\.js/] },
plugins: [ //用惟一的標識符id來表明當前的HappyPack是用來處理一類特定文件 new HappyPack({ id: 'babel', //如何處理.js文件,和rules裏的配置相同 loaders: [{ loader: 'babel-loader', query: { presets: [ "env", "react" ] } }] }), new HappyPack({ id: 'css', loaders: ['style-loader', 'css-loader'], threads: 4, //表明開啓幾個子進程去處理這一類型的文件 verbose: true //是否容許輸出日子 }) ],
ParallelUglifyPlugin
能夠把對JS文件的串行壓縮變爲開啓多個子進程並行執行
安裝:npm i -D webpack-parallel-uglify-plugin
配置:
new ParallelUglifyPlugin({ workerCount: 3, //開啓幾個子進程去併發的執行壓縮。默認是當前運行電腦的 CPU 核數減去1 uglifyJS: { output: { beautify: false, //不須要格式化 comments: false, //不保留註釋 }, compress: { warnings: false, // 在UglifyJs刪除沒有用到的代碼時不輸出警告 drop_console: true, // 刪除全部的 `console` 語句,能夠兼容ie瀏覽器 collapse_vars: true, // 內嵌定義了可是隻用到一次的變量 reduce_vars: true, // 提取出出現屢次可是沒有定義成變量去引用的靜態值 } }, })
配置多個 entry 裏的 html 頁面,用HtmlWebpackPlugin 插件,將打包好的j多個js分別插入到對應的html頁面中。
倉庫源碼 【410】
在 webpack.common.js 中:
const path = require('path'); const fs = require('fs'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); const webpack = require('webpack'); const makePlugins = (configs) => { const plugins = [ new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '../') }) ]; Object.keys(configs.entry).forEach(item => { plugins.push( + new HtmlWebpackPlugin({ template: 'src/index.html', filename: `${item}.html`, chunks: ['runtime', 'vendors', item] }) ) }); const files = fs.readdirSync(path.resolve(__dirname, '../dll')); files.forEach(file => { if(/.*\.dll.js/.test(file)) { plugins.push(new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, '../dll', file) })) } if(/.*\.manifest.json/.test(file)) { plugins.push(new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, '../dll', file) })) } }); return plugins; } const configs = { + entry: { index: './src/index.js', list: './src/list.js', detail: './src/detail.js', }, resolve: { extensions: ['.js', '.jsx'], }, module: { rules: [{ test: /\.jsx?$/, include: path.resolve(__dirname, '../src'), use: [{ loader: 'babel-loader' }] }, { test: /\.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'file-loader' } }] }, optimization: { runtimeChunk: { name: 'runtime' }, usedExports: true, splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, name: 'vendors', } } } }, performance: false, output: { path: path.resolve(__dirname, '../dist') } } configs.plugins = makePlugins(configs); module.exports = configs
倉庫源碼 【51】
mkdir make-loader cd make-loader npm init -y npm i webpack webpack-cli -D npm i loader-utils -D
在根目錄下建立文件夾loaders,裏面建立本身定義的loader.js文件
在 webpack.config.js 中:
const path = require('path'); module.exports = { mode: 'development', entry: { main: './src/index.js' }, resolveLoader: { //先在 node_modules 中找用到的loader,若是沒找到,再在loaders裏查找 modules: ['node_modules', './loaders'] }, module: { rules: [{ test: /\.js/, use: [//使用本身寫的replaceLoader { loader: 'replaceLoader', }, { loader: 'replaceLoaderAsync', options: { name: 'lee' } }, ] }] }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' } }
詳細請看官方文檔:loaders#thisquery hiscallback thisasync
倉庫源碼 【52】
詳細請看官方文檔:compiler-hooks
應對 webpack 原理面試必備:
倉庫源碼 【53】
安裝 :
//高亮顯示代碼的工具 npm i cli-highlight -g //分析源代碼 npm install @babel/parser --save npm install @babel/core --save npm install @babel/traverse --save npm install @babel/preset-env --save
倉庫源碼 【54】
倉庫源碼 【55】
倉庫源碼 【56】
詳細請看官方文檔:CreateReactApp
快速開始:
npx create-react-app my-app cd my-app npm start
把隱藏的配置項展示出來:不可逆操做
npm run eject
就會多出來兩個文件夾,config,scripts文件夾
倉庫源碼 【57】