文章目录
- 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
注意:test 命令后面的逗号别忘了。{ "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" } }
- webpack5 用 webpack serve 替换 webpack-dev-server
- 用
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? > n #上面安装了就选 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" ], + //去掉打包时出现的 Warning: React version not specified in eslint-plugin-react settings. + "settings": { + "react": { + "version": "detect" + } + }, "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'); + const webpack = require('webpack'); //...省略 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.config.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.config.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.config.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.config.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.config.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.config.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.config.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.config.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.config.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: { + publicPath: '../', //防止css中图片路径错误 + 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.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, } }, ], }, + { + 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 className="bg"> <div className="hello">Hello, React!</div> + <div className={css.world}>I am World!</div> <img src={Icon}/> <audio src={Mp3} ref={(input)=>{audio = input}} /> <input type="button" value="播放" onClick={()=>{ audio && audio.play(); //点击按钮播放音频 }}/> </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]));
});
});
}