Bundling

当运行npm run build的时候,webpack将源代码转化为单个的文件
image.png
image.png
新建一个项目目录解构如下
image.png
package.json

  1. {
  2. "name": "webpack-part7",
  3. "version": "0.0.1",
  4. "description": "practising webpack",
  5. "scripts": {},
  6. "license": "MIT"
  7. }

安装webpack

  1. npm install --save-dev webpack webpack-cli

webpack.config.js添加配置
entry属性是绑定应用的入口点

  1. const path = require('path')
  2. const config = {
  3. entry: './src/index.js',
  4. output: {
  5. path: path.resolve(__dirname, 'build'),
  6. filename: 'main.js'
  7. }
  8. }
  9. module.exports = config

package.json添加script

  1. "scripts": {
  2. "build": "webpack --mode=development"
  3. },

在src/index.js添加一些代码,新建App.js并添加一些代码,执行命令**npm run build**,在build目录会生成main.js文件, 里面包含了index.js和App.js中转译后的代码
image.png

Bundling React

安装react

  1. npm install react react-dom

写一些react代码后,build,报错,提示需要一个合适的loader
image.png

Loaders

webpack默认只能转译纯javascript脚本,要转移react,需要添加一个loader
在webpack.config.js中添加module rules

  1. const config = {
  2. entry: './src/index.js',
  3. output: {
  4. path: path.resolve(__dirname, 'build'),
  5. filename: 'main.js',
  6. },
  7. module: {
  8. rules: [
  9. {
  10. test: /\.js$/,
  11. loader: 'babel-loader',
  12. options: {
  13. presets: ['@babel/preset-react'],
  14. },
  15. },
  16. ],
  17. },
  18. }
  • test属性指定loader用于.js结尾的文件
  • loader属性指定处理这个文件的loader是babel-loader
  • options属性用于传递参数

安装loader为开发依赖

  1. npm install @babel/core babel-loader @babel/preset-react --save-dev

安装完成后再build就不会报错了,转译后的文件可以直接用浏览器打开查看效果
如果代码中使用了async/await, 需要安装另一个依赖: @babel/polyfill

  1. npm install @babel/polyfill

Transpilers

将代码从一种 JavaScript 形式转换为另一种 JavaScript 形式的过程称为transpiling
ES6以上的JS代码需要转译成ES5
大多数人使用的是@babel/pressing-env插件
它包含使用所有最新特性编写代码并将其转化为兼容 ES5标准的代码所需的所有内容
在配置文件的presets中添加’@babel/preset-env’

  1. {
  2. test: /\.js$/,
  3. loader: 'babel-loader',
  4. options: {
  5. presets: ['@babel/preset-env', '@babel/preset-react']
  6. }
  7. }

安装@babel/preset-env

  1. npm install @babel/preset-env --save-dev

转移后的代码就像这样

  1. var App = function App() {
  2. return _react2.default.createElement('div', null, 'hello webpack')
  3. };

CSS

加入CSS后build报错

  1. .container {
  2. margin: 10;
  3. background-color: #dee8e4;
  4. }

image.png
转译css需要用到css-loader 和 style-loader
css-loader用于加载css文件
style-loader生成并注入一个style元素,css定义最后包含在main.js文件中

  1. {
  2. rules: [
  3. {
  4. test: /\.js$/,
  5. loader: 'babel-loader',
  6. options: {
  7. presets: ['@babel/preset-react', '@babel/preset-env'],
  8. },
  9. },
  10. {
  11. test: /\.css$/,
  12. use: ['style-loader', 'css-loader'],
  13. },
  14. ];
  15. }

安装

  1. npm install style-loader css-loader --save-dev

Webpack-dev-server

当前的工作流非常糟糕,每次修改代码,都要重新build,手动刷新浏览器查看效果
webpack-dev-server 解决了这个问题, 当代码修改时,自动build并刷新页面
安装成开发者依赖

  1. npm install --save-dev webpack-dev-server

在scripts中添加start命令

  1. {
  2. // ...
  3. "scripts": {
  4. "build": "webpack --mode=development",
  5. "start": "webpack serve --mode=development"
  6. },
  7. // ...
  8. }

在webpack.config.js配置中添加devServer

  1. const config = {
  2. entry: './src/index.js',
  3. output: {
  4. path: path.resolve(__dirname, 'build'),
  5. filename: 'main.js',
  6. },
  7. devServer: {
  8. static: path.resolve(__dirname, 'build'),
  9. compress: true,
  10. port: 3000,
  11. },
  12. // ...
  13. };

webpack-dev-server的转译是在内存中的,而不是在build目录
注意,在build目录中需要添加index.html作为主页

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>React App</title>
  6. </head>
  7. <body>
  8. <div id="root"></div>
  9. <script type="text/javascript" src="./main.js"></script>
  10. </body>
  11. </html>

Source maps

image.png
当控制台发生错误,点击App.js跳转到调试页面,发现不是我们的源代码
image.png
在配置中添加devtool

  1. const config = {
  2. entry: './src/index.js',
  3. output: {
  4. // ...
  5. },
  6. devServer: {
  7. // ...
  8. },
  9. devtool: 'source-map',
  10. // ..
  11. };

重启应用后,再点击错误就能跳转到自己写的代码了
image.png

Minifying the code

在版本4以上的webpack中,只需要将build命令的mode设置为production,就能压缩代码

  1. "scripts": {
  2. "build": "webpack --mode=production",
  3. "start": "webpack serve --mode=development"
  4. },

执行命令 npm run build
所有的便笺、甚至不必要的空格和换行符都被删除了,变量名被单个字符替换。

Development and production configuration

实现当在本地使用时,应用使用端口3001中可用的 json-server 作为其后端
而打包时,指定为后端服务器地址
将webpack的配置改成函数形式

  1. const path = require('path');
  2. const config = (env, argv) => {
  3. return {
  4. entry: './src/index.js',
  5. output: {
  6. // ...
  7. },
  8. devServer: {
  9. // ...
  10. },
  11. devtool: 'source-map',
  12. module: {
  13. // ...
  14. },
  15. plugins: [
  16. // ...
  17. ],
  18. }
  19. }
  20. module.exports = config

定义全局常量backend_url

  1. const path = require('path')
  2. const webpack = require('webpack')
  3. const config = (env, argv) => {
  4. console.log('argv', argv.mode)
  5. const backend_url = argv.mode === 'production'
  6. ? 'https://blooming-atoll-75500.herokuapp.com/api/notes'
  7. : 'http://localhost:3001/notes'
  8. return {
  9. entry: './src/index.js',
  10. output: {
  11. path: path.resolve(__dirname, 'build'),
  12. filename: 'main.js'
  13. },
  14. devServer: {
  15. contentBase: path.resolve(__dirname, 'build'),
  16. compress: true,
  17. port: 3000,
  18. },
  19. devtool: 'source-map',
  20. module: {
  21. // ...
  22. },
  23. plugins: [
  24. new webpack.DefinePlugin({
  25. BACKEND_URL: JSON.stringify(backend_url)
  26. })
  27. ]
  28. }
  29. }
  30. module.exports = config

使用backend_url

  1. const App = () => {
  2. const [counter, setCounter] = useState(0)
  3. const [values, setValues] = useState([])
  4. const notes = useNotes(BACKEND_URL)
  5. // ...
  6. return (
  7. <div className="container">
  8. hello webpack {counter} clicks
  9. <button onClick={handleClick} >press</button>
  10. <div>{notes.length} notes on server {BACKEND_URL}</div>
  11. </div>
  12. )
  13. }

在build目录执行如下命令可查看生产版本的效果

  1. npx static-server

应用会在http://localhost:9080运行

Polyfill

IE不支持axios promises和数组的find方法
在应用中添加下列内容,以支持promise

  1. import PromisePolyfill from 'promise-polyfill'
  2. if (!window.Promise) {
  3. window.Promise = PromisePolyfill
  4. }