文章目錄
- 1. webpack 介紹
- 2. 安裝 Webpack
- 3. 使用 webpack 打包 js 項目
- 4. 清除打包的舊文件
- 5. 使用 Babel 對 js 轉譯
- 6. 在 webpack 配置文件中判斷構建環境
- 7. 使用 webpack 打包 web 項目
- 8. 打包完成後自動打開瀏覽器
- 9. 支持 React
- 10. 讓 React 支持熱更新
- 11. 代碼校驗 ESLint
- 12. 讓 ESLint 支持 React Hook 規則
- 13. 在代碼中判斷編譯環境
- 14. 加載 CSS
- 15. 加載圖片
- 16. 加載字體
- 17. 加載數據
- 18. 加載音頻
- 19. CSS 分離
- 20. CSS Module
- 21. PostCSS 處理 CSS 壓縮/去重/自動前綴轉換
- 22. 公共資源拆分
- 23. 動態(按需)加載
1. webpack 介紹
返回目錄
webpack 是用於打包 JavaScript 項目的。可以在 cli (命令行) 和 API 中調用其接口。
2. 安裝 Webpack
- 要安裝的包
包名 說明 環境 webpack webpack 主程序 dev webpack-cli webpack 命令行工具 dev - 安裝命令
mkdir webpackdemo #新建項目目錄 cd webpackdemo #進入目錄 npm init -y #初始化 node,生成 package.json npm install --save-dev webpack webpack-cli #安裝 webpack
- webpack 的默認配置
默認入口是 / src / index.js
默認出口是 / dist / main.js
3. 使用 webpack 打包 js 項目
-
創建 index.js 和 webpack 配置文件
webpack-demo + |- /src #源碼目錄 + |- index.js #示例代碼文件 |- package.json + |- webpack.config.js #webpack配置文件
-
index.js
function main() { console.log("hello, webpack!"); } main();
-
webpack.config.js
const path = require("path"); module.exports = { entry: "./src/index.js", //入門文件 output: { filename: "js/[name].[hash:8].bundle.js", //出口文件名 path: path.resolve(__dirname, "dist") //輸出路徑 } };
-
package.json
{ "name": "webpack-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "start": "webpack --progress", }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.42.0", "webpack-cli": "^3.3.11" } }
-
執行命令
npm start
進行打包
如果 webpack.config.js 存在,則 webpack 默認情況下該命令將其選中。參數 --config 的使用只是爲了表明可以傳遞任何名稱的配置。這對於需要拆分爲多個文件的更復雜的配置很有用。
4. 清除打包的舊文件
- 需要安裝插件
包名 說明 環境 clean-webpack-plugin 清空構建文件夾 dev - 安裝命令
npm install --save-dev clean-webpack-plugin
- webpack.config.js
const path = require('path'); + //引入插件 + const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { entry: './src/index.js', //入口文件 + //配置插件 + plugins: [ + new CleanWebpackPlugin(), + ], output: { filename: 'js/[name].[hash:8].bundle.js', path: path.resolve(__dirname, 'dist'), }, };
- 執行命令
npm start
進行打包
5. 使用 Babel 對 js 轉譯
返回目錄
babel 很複雜的,只是寫了常用配置,更詳細的去官網查看
-
需要安裝的包
包名 說明 環境 babel-loader Babel 加載器 dev @babel/core Babel 核心包 dev @babel/cli Bable 命令行 dev @babel/preset-env 根據配置的目標瀏覽器或運行環境,自動的將代碼轉爲 es5 dev @babel/plugin-transform-runtime generator、Array.from 等新功能的支持 dev @babel/runtime-corejs3 @babel/plugin-transform-runtime 的依賴運行時 prod -
安裝命令
npm i -D babel-loader @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-runtime npm i -S @babel/runtime-corejs3
-
新建 .babelrc.js 配置文件
webpack-demo |- /src |- index.js + |- .babelrc.js |- package.json |- webpack.config.js #配置文件
-
.babelrc.js
const presets = [ "@babel/preset-env", ]; const plugins = [ [ "@babel/plugin-transform-runtime", //避免全局污染,支持新功能 { corejs: 3, version: "7.8.7" //版本支持越高,支持的新功能越多 } ] ]; module.exports = { presets, plugins };
-
webpack.config.js
const path = require('path'); //引入插件 const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { entry: './src/index.js', //入口文件 + //配置加載器 + module: { + rules: [ + { + test: /\.js|jsx$/, //匹配 js 文件 + exclude: /node_modules/, //排除文件夾 + use: [ + { loader: 'babel-loader' }, //使用babel + ] + }, + ] + }, //配置插件 plugins: [ new CleanWebpackPlugin(), ], output: { filename: 'js/[name].[hash:8].bundle.js', path: path.resolve(__dirname, 'dist'), }, };
-
執行
npx webpack
或npm start
打包項目。
6. 在 webpack 配置文件中判斷構建環境
- 要安裝的包
包名 說明 環境 cross-env 配置環境變量 dev - 安裝命令
npm install --save-dev cross-env
- package.json
{ "name": "webpack-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "webpack --progress", + "start": "cross-env NODE_ENV=development webpack --progress", + "build": "cross-env NODE_ENV=production webpack --progress" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { //...省略 } }
- webpack.config.js
const path = require('path'); //引入插件 const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const isProd = process.env.NODE_ENV === 'production' module.exports = { + mode: process.env.NODE_ENV, //編譯環境 開發development 生產production entry: './src/index.js', //入口文件 //...省略 output: { - filename: "js/[name].bundle.js", //出口文件名 + filename: `js/[name]${isProd?'.[hash:8]':''}.bundle.js`, //出口文件名 path: path.resolve(__dirname, 'dist'), //輸出路徑 }, }
7. 使用 webpack 打包 web 項目
- 新建 index.html 模版文件
webpack-demo + |- /public + |- index.html # html 模版 |- /src |- index.js |- .babelrc.js |- package.json |- webpack.config.js #配置文件
- index.html
<!doctype html> <html> <head> <!-- 頁面標題 html-webpack-plugin 插件替換 --> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> </body> </html>
- index.js
- function main() { - console.log('hello, webpack!'); - } - main(); + function component() { + const element = document.createElement('div'); + element.innerHTML ='Hello webpack'; + return element; + } + document.body.appendChild(component());
- 需要安裝的包
包名 說明 環境 html-webpack-plugin 自動修改的 html dev - 安裝命令
npm install --save-dev html-webpack-plugin
- webpack.config.js
const path = require('path'); //引入插件 const { CleanWebpackPlugin } = require('clean-webpack-plugin'); + const HtmlWebpackPlugin = require('html-webpack-plugin'); const isProd = process.env.NODE_ENV === 'production' module.exports = { //...省略 //配置插件 plugins: [ new CleanWebpackPlugin(), + new HtmlWebpackPlugin({ + title: 'HTML頁面標題', //替換index.html的title標籤內容 + template: './public/index.html', //html模版的位置 + }), ], output: { filename: 'js/[name].[hash:8].bundle.js', path: path.resolve(__dirname, 'dist'), }, };
- 執行命令
npm start
進行打包,然後用瀏覽器打開 /dsit/index.html 文件,就能看到 Hello webpack。
8. 打包完成後自動打開瀏覽器
- 需要安裝的包
包名 說明 環境 webpack-dev-server web服務,方便開發 dev - 安裝命令
npm install --save-dev webpack-dev-server
- webpack.config.js
const path = require('path'); //...省略 module.exports = { //...省略 output: { filename: `js/[name]${isProd?'.[hash:8]':''}.bundle.js`, //出口文件名 path: path.resolve(__dirname, 'dist'), }, + devServer: { + contentBase: './dist', //內容目錄 + open: 'Google Chrome', //設置啓動的瀏覽器 + port: 3000, //啓動端口 + } };
webpack-dev-server 編譯後不寫入任何輸出文件。相反,它將捆綁文件保留在內存中,並像在服務器根路徑上掛載的真實文件一樣提供它們。如果您的頁面希望在其他路徑上找到捆綁文件,則可以使用 publicPath 開發服務器的配置中的選項進行更改。
- package.json
{ "name": "webpack-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "cross-env NODE_ENV=development webpack --progress", + "start": "cross-env NODE_ENV=development webpack-dev-server", "build": "cross-env NODE_ENV=production webpack --progress" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { //...省略 }, "dependencies": { "@babel/runtime-corejs3": "^7.8.7" } }
- 用
npm start
打包項目,等打包完成就會打開 chrome 瀏覽器,看到頁面了
9. 支持 React
- 需要安裝的包
包名 說明 環境 react react 主程序 prod react-dom 讓 react 支持 dom 操作 prod react-router-dom react 路由,支持 dom 操作 prod @babel/preset-react 讓Babel 支持 React dev - 安裝命令
npm i -S react react-dom react-router-dom npm i -D @babel/preset-react
- index.js
- function component() { - const element = document.createElement('div'); - element.innerHTML ='Hello webpack'; - return element; - } - document.body.appendChild(component()); + 'use strict'; + import React from 'react'; + import ReactDOM from 'react-dom'; + function Home() { + return (<div>Hello, React!</div>) + } + ReactDOM.render(<Home />, document.getElementById('root'));
- index.html
<!doctype html> <html> <head> <!-- 頁面標題 html-webpack-plugin 插件替換 --> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> + <div id="root"></div> </body> </html>
- .babelrc.js
const presets = [ "@babel/preset-env", + "@babel/preset-react" ]; const plugins = [ [ "@babel/plugin-transform-runtime", { "corejs": 3, "version": "7.8.7" } ] ]; module.exports = { presets, plugins };
- 運行打包命令
npm start
,在瀏覽器中看到 Hello, React! 就成功了。
10. 讓 React 支持熱更新
- 要安裝的包
包名 說明 環境 react-hot-loader 讓 react-dev-server 支持 React 熱更新 dev @hot-loader/react-dom 消除 react-hot-loader 對 React 最新版不支持的提示 dev - 安裝
npm i -D react-hot-loader @hot-loader/react-dom
- index.js
'use strict'; import React from 'react'; import ReactDOM from 'react-dom'; + import { hot } from "react-hot-loader/root"; function Home() { return (<div>Hello, React!</div>) } + let App = hot(Home) - ReactDOM.render(<Home />, document.getElementById('root')); + ReactDOM.render(<App />, document.getElementById('root'));
- .babelrc.js
const presets = [ "@babel/env", "@babel/preset-react" ]; const plugins = [ [ "@babel/plugin-transform-runtime", { "corejs": 3, "version": "7.8.7" } ], + "react-hot-loader/babel" ]; module.exports = { presets, plugins };
- webpack.config.js
const path = require('path'); //...省略 module.exports = { //...省略 output: { filename: `js/[name]${isProd?'.[hash:8]':''}.bundle.js`, //出口文件名 path: path.resolve(__dirname, 'dist'), }, + resolve: { + alias: { 'react-dom': '@hot-loader/react-dom' } //消除提示文字 + } devServer: { contentBase: './dist', //內容目錄 open: 'Google Chrome', //設置啓動的瀏覽器 port: 3000, //啓動端口 + hot: true //支持熱更新 } };
- 執行打包命令
npm start
,改變 index.js 中的顯示內容,顯示新內容就不用刷新瀏覽器了
11. 代碼校驗 ESLint
- 要安裝的包
包名 說明 環境 eslint 主程序 dev eslint-loader webpack加載器 dev eslint-plugin-html 用於檢查寫在 script 標籤中的代碼 dev eslint-friendly-formatter 報錯時輸出的信息格式 dev eslint-plugin-react 用於React的ESLint規則,初始化 ESLint 時會提示安裝 dev - 安裝命令
npm i -D eslint eslint-loader eslint-friendly-formatter eslint-plugin-html eslint-plugin-react
- 在命令行中執行 eslint 命令,生成 .eslintrc.js
npx eslint --init #或 node_modules\.bin\eslint --init #您想如何使用ESLint? ? How would you like to use ESLint? (Use arrow keys) > To check syntax, find problems #您的項目使用什麼類型的模塊? ? What type of modules does your project use? (Use arrow keys) > JavaScript modules (import/export) #您的項目使用哪個框架? ? Which framework does your project use? (Use arrow keys) > React #您的項目使用 typescript 嗎? ? Does your project use TypeScript? (y/N) > n #你的代碼在哪裏運行? ? Where does your code run? >(*) Browser #配置文件用什麼格式 ? What format do you want your config file to be in? > JavaScript #是否安裝 eslint-plugin-react@latest 包 The config that you've selected requires the following dependencies: eslint-plugin-react@latest ? Would you like to install them now with npm? > Yes #上面安裝了就選 no
- 修改 .eslintrc.js
module.exports = { "env": { "browser": true, "es6": true + 'amd': true, //表示使用 amd 模塊規範,支持 require + 'node': true, //支持node }, "extends": [ "eslint:recommended", "plugin:react/recommended" ], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parserOptions": { "ecmaFeatures": { "jsx": true }, "ecmaVersion": 2018, "sourceType": "module" }, "plugins": [ "react" ], "rules": { } };
- webpack.config.js 參考
const path = require('path'); //...省略 module.exports = { //...省略 //配置加載器 module: { rules: [ { test: /\.js|jsx$/, //匹配 js 文件 exclude: /node_modules/, //排除文件夾 use: [ { loader: 'babel-loader' }, //使用babel + { loader: 'eslint-loader', // eslint 加載器 + options: { // eslint 選項 + enforce: 'pre', //在加載前執行 + fix: true, //自動修復 + include: [path.resolve(__dirname, 'src')], //指定檢查的目錄 + formatter: require('eslint-friendly-formatter') // 指定錯誤報告的格式規範 + } + }, ] }, ] }, //...省略 };
12. 讓 ESLint 支持 React Hook 規則
- 要安裝的包
包名 說明 環境 eslint-plugin-react-hooks 讓 ESLint 支持 React Hook 規則 dev - 安裝命令
npm install --save-dev eslint-plugin-react-hooks
- 修改 .eslintrc.js
module.exports = { //...省略其它配置 "plugins": [ "react", + "react-hooks" ], "rules": { + "react-hooks/rules-of-hooks": "error", // 檢查 Hook 的規則 + "react-hooks/exhaustive-deps": "warn" // 檢查 effect 的依賴 } };
13. 在代碼中判斷編譯環境
- 使用 webpack 內置的包
包名 說明 環境 DefinePlugin 構建時設置環境變量 內置 - 規則
每個傳進 DefinePlugin 的鍵值都是一個標誌符或者多個用 . 連接起來的標誌符。- 如果這個值是一個字符串,它會被當作一個代碼片段來使用。
- 如果這個值不是字符串,它會被轉化爲字符串(包括函數)。
- 如果這個值是一個對象,它所有的 key 會被同樣的方式定義。
- 如果在一個 key 前面加了 typeof,它會被定義爲 typeof 調用。
- webpack.config.js
const path = require('path'); //...省略 module.exports = { //...省略 //配置插件 plugins: [ + new webpack.DefinePlugin({ + PRODUCTION: JSON.stringify(true), + }) ], //...省略 };
- index.js
'use strict'; import React from 'react'; import ReactDOM from 'react-dom'; import { hot } from "react-hot-loader/root"; function Home() { + /*global PRODUCTION*/ //避免eslint報錯 + return (<div>{PRODUCTION ? "Hello, React!" : "is Development" }</div>) } let App = hot(Home) ReactDOM.render(<App />, document.getElementById('root'));
14. 加載 CSS
-
要安裝的包
包名 說明 環境 style-loader 將處理完的 css 存在 js 中,運行時嵌入 <style>, 並掛載到 html 頁面上 dev css-loader 使 webpack 可以識別 css 文件 dev -
安裝命令
npm install --save-dev style-loader css-loader
-
webpack.common.js
const path = require('path'); //...省略 module.exports = { //...省略 module: { rules: [ //...省略 js 配置 + { + test: /\.css$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + importLoaders: 1, + } + }, + ], + }, ], }, //... 省略 plugin 設置 //... 省略 output 設置 };
說明:
- webpack 使用正則表達式來確定應查找哪些文件並將其提供給特定的加載程序。所以任何以 .css 結尾的文件都將提供給 style-loader 和 css-loader。
- 用 import ‘./style.css’ 引入該樣式的文件。當該模塊運行時, 帶有字符串化 CSS 的 <style> 標籤將插入到 html 文件中。
- loader 的執行順序是從右到左,此例是先用 css-loader 處理 css,然後在用 style-loader 把 css 插入的 html 文件中
-
項目中添加 style.css 文件
webpack-demo |- /node_modules |- /src |- index.js + |- style.css |- package.json |- webpack.common.js #其它文件省略...
-
style.css
.hello { color: red; }
-
index.js
+ import './style.css'; function component() { const element = document.createElement('div'); element.innerHTML ='Hello webpack'; + element.classList.add('hello'); return element; } document.body.appendChild(component());
react 的 index,js
'use strict'; import React from 'react'; import ReactDOM from 'react-dom'; + import './style.css'; function Home() { - return (<div>Hello, React!</div>) + return (<div className="hello">Hello, React!</div>) } ReactDOM.render(<Home />, document.getElementById('root'));
-
運行打包命令
npm start
在瀏覽器中會發現 Hello webpack 變成了紅色。
15. 加載圖片
-
要安裝的包
包名 說明 環境 file-loader 解析 url 方法引入的文件 dev url-loader Loads files as base64 encoded URL dev -
安裝命令
npm install --save-dev file-loader url-loader
-
webpack.common.js
const path = require('path'); //...省略 module.exports = { //...省略 module: { rules: [ //...省略 js 配置 { test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 1, } }, ], }, + { + test: /\.(png|jpg|jpeg|gif|svg)$/, + use: ['file-loader'], //或者 + use: [ + { + loader: 'url-loader', + options: { + outputPath: 'imgs/', //輸出路徑 + name: '[name]-[hash:5].[ext]', //文件名 + limit: 8192, //超過限制會使用file-loader + esModule: false, //支持 require("imgUrl") 方式 + } + } + ] + }, ], }, //...省略 plugin 設置 //...省略 output 設置 };
說明:
- 當用 import MyImage from ‘./my-image.png’ 時,該圖像將被處理並添加到 output 目錄中,並且 MyImage 變量將包含處理後該圖像的最終 url。
- 當使用 css-loader 時,CSS 中的 url(’./my-image.png’) 也會發生類似的過程。加載程序將識別出這是本地文件,然後將該 ‘./my-image.png’ 路徑替換爲 output 目錄中圖像的最終路徑。
說明:url-loader 會把尺寸小於8192 byte 的圖片轉爲base64,大於的圖片轉給 file-loader 處理
-
項目中添加圖片
webpack-demo |- /node_modules |- /src + |- bg.png + |- icon.png |- index.js |- style.css |- package.json |- webpack.common.js #其它文件省略...
-
style.css
.hello { color: red; } + .bg { + background: url('./bg.png'); + }
-
index.js
import _ from 'lodash'; import './style.css'; + import Icon from './icon.png'; //引入圖片 function component() { const element = document.createElement('div'); element.innerHTML ='Hello webpack'; element.classList.add('hello'); + element.classList.add('bg'); //添加帶圖片的css + const myIcon = new Image(); //添加圖片到div + myIcon.src = Icon; + element.appendChild(myIcon); return element; } document.body.appendChild(component());
react 的 index,js
'use strict'; import React from 'react'; import ReactDOM from 'react-dom'; import './style.css'; + import Icon from './icon.png'; function Home() { - return (<div className="hello">Hello, React!</div>) + return (<div className="bg"> + <div className="hello">Hello, React!</div> + <img src={Icon}/> + </div>) } ReactDOM.render(<Home />, document.getElementById('root'));
-
運行打包命令
npm start
在瀏覽器中會看添加的圖片
16. 加載字體
- webpack.common.js
const path = require('path'); //...省略 module.exports = { //...省略 module: { rules: [ //...省略 js 配置 //...省略 css 配置 { test: /\.(png|svg|jpg|gif)$/, use: ['file-loader'], }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/, + use: ['file-loader'], + }, ], }, //... 省略 plugin 設置 //... 省略 output 設置 };
- 在項目中添加字體文件
webpack-demo |- /node_modules |- /src |- bg.png |- icon.png |- index.js + |- my-font.woff + |- my-font.woff2 |- style.css |- package.json |- webpack.common.js #其它文件省略...
- style.css
+ @font-face { + font-family: 'MyFont'; + src: url('./my-font.woff2') format('woff2'), + url('./my-font.woff') format('woff'); + font-weight: 600; + font-style: normal; + } .hello { color: red; + font-family: 'MyFont'; } .bg { background: url('./bg.png'); }
- 運行打包命令
npm start
,在瀏覽器中會看到 Hello webpack 字體變了。
17. 加載數據
數據格式:JSON、CSV、TSV 和 XML 等等
js 原生支持 JSON,可直接 import Data from ‘./data.json’; 引用。
- 要安裝的包
包名 說明 環境 csv-loader 解析 CSV、TSV 文件 dev xml-loader 解析 XML 文件 dev - 安裝命令
npm install --save-dev csv-loader xml-loader
- webpack.common.js
const path = require('path'); //...省略 module.exports = { //...省略 module: { rules: [ //...省略 js 配置 //...省略 css 配置 //...省略 圖片 配置 { test: /\.(woff|woff2|eot|ttf|otf)$/, use: ['file-loader'], }, + { + test: /\.(csv|tsv)$/, + use: ['csv-loader'], + }, + { + test: /\.xml$/, + use: ['xml-loader'], + }, ], }, //... 省略 plugin 設置 //... 省略 output 設置
- 在項目中添加 xml 文件
webpack-demo |- /node_modules |- /src |- bg.png + |- data.xml |- icon.png |- index.js |- my-font.woff |- my-font.woff2 |- style.css |- package.json |- webpack.common.js #其它文件省略...
- data.xml
用 import 可以引入JSON、CSV、TSV 和 XML 這四種數據類型<?xml version="1.0" encoding="UTF-8"?> <note> <to>Mary</to> <from>John</from> <heading>Reminder</heading> <body>Call Cindy on Tuesday</body> </note>
- index.js
react 同上import './style.css'; import Icon from './icon.png'; + import Data from './data.xml'; //引入數據 function component() { const element = document.createElement('div'); element.innerHTML = 'Hello webpack'; element.classList.add('hello'); element.classList.add('bg'); const myIcon = new Image(); myIcon.src = Icon; element.appendChild(myIcon); + console.log(Data); //輸出數據 return element; } document.body.appendChild(component());
- 運行打包命令
npm start
,在瀏覽器中的控制檯(cosole)中能看到加載的數據。
18. 加載音頻
- 要安裝的包
包名 說明 環境 url-loader Loads files as base64 encoded URL dev - 安裝命令
npm install --save-dev url-loader
- webpack.config.js
const path = require('path'); //...省略 module.exports = { //...省略 module: { rules: [ //...省略 js 配置 //...省略 css 配置 //...省略 圖片 配置 //...省略 字體 配置 //...省略 csv|tsv 配置 { test: /\.xml$/, use: ['xml-loader'], }, + { + test: /\.(mp3)(\?.*)?$/, + loader: 'url-loader', + options: { + name:'audios/[name].[ext]', + limit:10 + } + } ], }, //... 省略 plugin 設置 //... 省略 output 設置
- 在項目中添加音頻文件
webpack-demo |- /node_modules |- /src + |- alarm.mp3 |- bg.png |- data.xml |- icon.png |- index.js |- my-font.woff |- my-font.woff2 |- style.css |- package.json |- webpack.common.js #其它文件省略...
- index.js
react 的 index.jsimport './style.css'; import Icon from './icon.png'; import Data from './data.xml'; + import Mp3 from './alarm.mp3'; function component() { const element = document.createElement('div'); element.innerHTML = 'Hello, webpack'; element.classList.add('hello'); const myIcon = new Image(); myIcon.src = Icon; element.appendChild(myIcon); + const myMp3 = new Audio(); + myMp3.src = Mp3; + myMp3.loop = true; + element.appendChild(myMp3); + var inputElement = document.createElement('input'); + inputElement.type = "button"; + inputElement.value = "播放"; + inputElement.onclick = () =>{ + myMp3.play(); + } + element.appendChild(inputElement); console.log(Data); return element; } document.body.appendChild(component());
'use strict'; - import React from 'react'; + import React, { useState } from 'react'; import ReactDOM from 'react-dom'; import './style.css'; import Icon from './icon.png'; + import Mp3 from './alarm.mp3'; function Home() { + let [ audio ] = useState(0); return (<div className="bg"> <div className="hello">Hello, React!</div> <img src={Icon}/> + <audio src={Mp3} ref={(input)=>{audio = input}} /> + <input type="button" value="播放" onClick={()=>{ + audio && audio.play(); //點擊按鈕播放音頻 + }}/> </div>) } ReactDOM.render(<Home />, document.getElementById('root'));
- 運行打包命令
npm start
在瀏覽器中點擊播放按鈕播放聲音。
19. CSS 分離
- 要安裝的包
包名 說明 環境 extract-text-webpack-plugin 分離css,支持webpack1/2/3 dev mini-css-extract-plugin 分離css,官方推薦,幾個月不更新了 dev extract-css-chunks-webpack-plugin 分離css,一直在更新 dev
19.1. mini-css-extract-plugin
- 安裝
npm i -D mini-css-extract-plugin
- webpack.config.js
const path = require('path'); //...省略 + const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { //...省略 module: { rules: [ //...省略 js 配置 { test: /\.css$/, use: [ - 'style-loader', + { + loader: MiniCssExtractPlugin.loader, + options: { + hmr: process.env.NODE_ENV === 'development', + reloadAll: true + }, + }, { loader: 'css-loader', options: { importLoaders: 1, } }, ], }, //...省略 ], }, plugins: [ + new MiniCssExtractPlugin({ + filename: `css/[name]${isProd ? '.[contenthash:8]':''}.css`, + chunkFilename: `css/[id]${isProd ? '.[contenthash:8]':''}.css`, + ignoreOrder: false, + }), ], //... 省略 output 設置
19.2. extract-css-chunks-webpack-plugin
- 安裝
npm i -D extract-css-chunks-webpack-plugin
- 修改 webpack.config.js
const path = require('path'); //...省略 + const ExtractCssChunks = require("extract-css-chunks-webpack-plugin"); module.exports = { //...省略 module: { rules: [ //...省略 js 配置 { test: /\.css$/, use: [ - 'style-loader', + { + loader: ExtractCssChunks.loader, + options: { + hmr: process.env.NODE_ENV === 'development', + reloadAll: true + }, + }, { loader: 'css-loader', options: { importLoaders: 1, } }, ], }, //...省略 ], }, plugins: [ + new ExtractCssChunks({ + filename: `css/[name]${isProd ? '.[contenthash:8]':''}.css`, + chunkFilename: `css/[id]${isProd ? '.[contenthash:8]':''}.css`, + orderWarning: true, + }), ], //... 省略 output 設置 }
20. CSS Module
- 修改 webpack.common.js
//...省略 module.exports = { //省略.... module: { rules: [ //..,省略 js 配置 { test: /\.css$/, + exclude: [/node_modules/, /\.module\.css$/], //排除css模塊 use: [ { loader: MiniCssExtractPlugin.loader, options: { hmr: process.env.NODE_ENV === 'development', reloadAll: true }, }, { loader: 'css-loader', options: { importLoaders: 1, } }, ], }, + { + test: /\.module\.css$/, + exclude: /node_modules/, + use: [ + { + loader: MiniCssExtractPlugin.loader, + options: { + hmr: process.env.NODE_ENV === 'development', + reloadAll: true, + } + }, + { + loader: 'css-loader', + options: { + importLoaders: 1, + modules: { + localIdentName: '[path][name]__[local]--[hash:base64:5]' + }, + } + }, + ] + } ] }, //...省略 plugin 配置 //...省略 output 配置 }
- style.module.css
.world { color: blue; }
- index.js
import React from 'react'; import './style.css'; + import css from './style.module.css'; function Home() { return (<div> <ul> <!-- 全局 css --> <li className="hello">Hello, World!</li> <!-- css module --> + <li className={css.world}>I am Test!</li> </ul> </div>) } export default Home;
21. PostCSS 處理 CSS 壓縮/去重/自動前綴轉換
- 要安裝的包
包名 說明 環境 postcss-loader postcss 加載器 dev precss 支持現代 CSS 語法(SASS,SCSS)
包含 postcss-preset-env 轉換爲瀏覽器識別的 CSS
包含 Autoprefixer 自動添加(瀏覽器)前綴dev cssnano css 優化處理,壓縮去重 dev postcss-scss 解析 SCSS 語法 dev postcss-calc 在編譯階段進行計算 dev postcss-import 轉換 @import 規則 用於內聯內容 dev postcss-normalize 提取 normalize.css 中支持瀏覽器的部分(去重) dev normalize.css 標準 css,讓各個瀏覽器的默認標籤顯示一致 dev postcss-plugin-px2rem px 轉 rem dev - 安裝命令
npm i -D postcss-loader precss cssnano postcss-import postcss-scss postcss-calc postcss-plugin-px2rem postcss-normalize normalize.css
- 新建 postcss.config.js
|- /node_modules |- /src |- index.js |- style.css |- .babelrc.js |- .eslintrc.js |- package.json + |- postcss.config.js |- webpack.config.js
- postcss.config.js
module.exports = ({ file, options, env }) => ({ parser: require('postcss-scss'), plugins: [ require('postcss-import'), require('postcss-normalize')({ forceImport:true, //強制插入 browsers: 'last 2 versions' //瀏覽器近2個版本 }), //支持“現代css”(Sass,Scss)語法,並轉成 css require('precss'), //編譯前計算 require('postcss-calc'), //px 轉 rem require('postcss-plugin-px2rem')({ rootValue: 100, minPixelValue: 2 }), //壓縮css,去除所有註釋 require('cssnano')({ preset: ['default', { discardComments: { removeAll: true } }] }) ] });
- webpack.config.js
//...省略 module.exports = { //省略.... module: { rules: [ //..,省略 js 配置 { test: /\.css$/, + exclude: [/node_modules/, /\.module\.css$/], //排除css模塊 use: [ { loader: MiniCssExtractPlugin.loader, options: { hmr: process.env.NODE_ENV === 'development', reloadAll: true }, }, { loader: 'css-loader', options: { importLoaders: 1, } }, + 'postcss-loader' ], }, { test: /\.module\.css$/, exclude: /node_modules/, use: [ { loader: MiniCssExtractPlugin.loader, options: { hmr: process.env.NODE_ENV === 'development', reloadAll: true, } }, { loader: 'css-loader', options: { importLoaders: 1, modules: { localIdentName: '[path][name]__[local]--[hash:base64:5]' }, } }, + 'postcss-loader' ] } ] }, //...省略 plugin 配置 //...省略 output 配置 }
22. 公共資源拆分
返回目錄
SplitChunksPlugin 是 webpack 內置模塊
- webpack.config.js
點擊查看更多關於 SplitChunksPluginconst path = require('path'); //...省略 module.exports = { //...省略 output: { filename: `js/[name]${isProd?'.[hash:8]':''}.bundle.js`, //出口文件名 path: path.resolve(__dirname, 'dist'), }, + optimization: { + splitChunks: { + chunks: 'all', + }, + }, };
- 運行打包命令
npm start
有了 optimization.splitChunks 配置選項後,現在看到從index.bundle.js 和 another.bundle.js 中刪除了重複的依賴代碼,並將 lodash 分離爲一個單獨的文件。
23. 動態(按需)加載
返回目錄
當涉及動態代碼拆分時,webpack支持兩種類似的技術。
第一是用符合 ECMAScript 建議的 import() 語法;
第二是用 Webpack 的傳統方法 require.ensure。推薦使用。
23.1 ECMAScript 建議的 import 方法
- index.js
使用動態導入來分離 lodash 模塊
也可以與 async 一起使用,前提是安裝 @babel/plugin-syntax-dynamic-import包。function getComponent() { const element = document.createElement('div'); return import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => { const element = document.createElement('div'); element.innerHTML = _.join(['Hello', 'webpack'], ' '); return element; }).catch(error => 'An error occurred while loading the component'); } getComponent().then(component => { document.body.appendChild(component); })
- webpackPrefetch 預取:將來可能需要一些資源
import(/* webpackPrefetch: true */ 'LoginModal');
- webpackPreload 預加載:當前期間可能需要資源
import(/* webpackPreload: true */ 'ChartingLibrary');
- 預加載的塊開始並行於父塊加載。父塊完成加載後,將開始預提取塊。
- 預加載的塊具有中等優先級,可以立即下載。瀏覽器空閒時,將下載預提取的塊。
- 父塊應立即請求預加載的塊。預取的塊可以在將來的任何時候使用。
- 瀏覽器支持不同。
23.2 webpack 的 require.ensure 方法
返回目錄
webpack 的遺留功能
- 代碼
// 空參數 require.ensure([], function(require){ var = require('module-b'); }); // 依賴模塊 "module-a", "module-b",會和'module-c'打包成一個chunk來加載 // 不同的分割點可以使用同一個chunkname,這樣可以保證不同分割點的代碼模塊打包爲一個chunk require.ensure(["module-a", "module-b"], function(require) { var a = require("module-a"); var b = require("module-b"); var c = require('module-c'); },"custom-chunk-name");
23.3 bundle-loader
返回目錄
用於分離代碼和延遲加載生成的 bundle
// 在require bundle時,瀏覽器會立即加載
var waitForChunk = require("bundle!./file.js");
// 使用lazy模式,瀏覽器並不立即加載,只在調用wrapper函數才加載
var waitForChunk = require("bundle?lazy!./file.js");
// 等待加載,在回調中使用
waitForChunk(function(file) {
var file = require("./file.js");
});
默認普通模式wrapper:
var cbs = [],data;
module.exports = function(cb) {
if(cbs) cbs.push(cb);
else cb(data);
},
require.ensure([], function(require) {
data = require('./file.js');
var callbacks = cbs;
cbs = null;
for(var i = 0, l = callbacks.length; i < l; i++) {
callbacks[i](data);
}
});
lazy模式wrapper:
module.exports = function (cb) {
require.ensure([], function(require) {
var app = require('./file.js');
cb(app);
});
};
使用 bundle-loader 在代碼中 require 文件的時候只是引入了 wrapper 函數,而且因爲每個文件都會產生一個分離點,導致產生了多個打包文件,而打包文件的載入只有在條件命中的情況下才產生,也就可以按需加載。
- 支持自定義Chunk名稱:
require("bundle-loader?lazy&name=my-chunk!./file.js");
23.4 promise-loader
返回目錄
類似於 bundle-loader ,但是使用了 promise API
// 使用Bluebird promise庫
var load = require("promise?bluebird!./file.js");
// 使用全局Promise對象
var load = require("promise?global!./file.js");
load().then(function(file) {
});
wrapper函數:
var Promise = require('bluebird');
module.exports = function (namespace) {
return new Promise(function (resolve) {
require.ensure([], function (require) {
resolve(require('./file.js')[namespace]));
});
});
}