本应用程序为webpack5.0
+ React17
+ antd4.x
搭建环境
初始化项目
初始化包
我们使用npm
先创建一个package.json
, 安装webpack
webpack-cli
react
react-dom
react-router-dom
1 2 3 4 5
| npm init -y
yarn add webpack webpack-cli --dev
yarn add react react-dom react-router-dom
|
根目录创建webpack.config.js
(暂时只考虑开发环境)
根目录下创建app
文件夹作为我们的应用程序主入口,入口文件为app.js
项目结构如下
1 2 3 4 5 6
| ├─ app |-app.js ├─ node_modules |- ...... ├─ webpack.config.js └─ package.json
|
配置基本webpack
这里我们配置一些基本的webpack
打包必须的配置项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const path = require("path")
module.exports = { entry: { index: { import: '/app/app.js', }, }, mode: "development", output: { filename: '[name].[contenthash].bundle.js', chunkFilename: '[name].[id].js', publicPath: '/', path: path.resolve(__dirname, 'dist'), }, devtool: 'inline-source-map', }
|
管理资源
我们需要解析模块, 生成bundle
, 这里我们使用babel-loader
来转换js, less-loader
css-loader
style-loader
来加载样式
1 2 3
| yarn add less less-loader --dev yarn add babel-loader --dev yarn add css-loader style-loader --dev
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| const path = require("path")
module.exports = { entry: { index: { import: '/app/app.js', }, }, mode: "development", output: { filename: '[name].[contenthash].bundle.js', chunkFilename: '[name].[id].js', publicPath: '/', path: path.resolve(__dirname, 'dist'), }, devtool: 'inline-source-map', module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, include: path.resolve(__dirname, 'app'), use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.less$/i, use: ['style-loader', 'css-loader', 'less-loader'], }, { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, ], } }
|
由于用到了babel
转换js
,我们需要装一些babel
的核心模块
1
| yarn add @babel/core @babel/cli @babel/preset-env --dev
|
添加调试基础插件
接下来装一些便于我们调试的基础插件html-webpack-plugin
clean-webpack-plugin
webpack-manifest-plugin
1
| yarn add html-webpack-plugin clean-webpack-plugin webpack-manifest-plugin --dev
|
创建模板index.html
, 并添加插件
1 2 3 4 5 6 7 8 9
| <html> <head> <meta charset="utf-8" /> <title>起步</title> </head> <body> <div id="app"></div> </body> </html>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
module.exports = { entry: { index: { import: '/app/app.js', }, }, mode: "development", output: { filename: '[name].[contenthash].bundle.js', chunkFilename: '[name].[id].js', publicPath: '/', path: path.resolve(__dirname, 'dist'), }, devtool: 'eval-source-map', devServer: { contentBase: '/dist', }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, include: path.resolve(__dirname, 'app'), use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.less$/i, use: ['style-loader', 'css-loader', 'less-loader'], }, { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ inject: true, template: '/app/index.html', }), new WebpackManifestPlugin() ], }
|
创建服务
新建本地服务, 添加模块热更新, 这里我们使用Express
启动服务
我们在根目录创建一个server
的文件夹, 创建一个index.js
文件, 编写我们的启动脚本
这里我们需要安装webpack-dev-server
webpack-dev-middleware
1 2 3
| yarn add express yarn add webpack-dev-server --dev yarn add webpack-dev-middleware --dev
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const express = require('express'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware');
const app = express(); const config = require('./webpack.config.js'); const compiler = webpack(config);
app.use( webpackDevMiddleware(compiler, { publicPath: config.output.publicPath, }) );
app.listen(3000, function () { console.log('Example app listening on port 3000!\n'); });
|
webpack.config.js
文件添加contentBase
,表明从哪里去寻找文件
1 2 3
| devServer: { contentBase: './dist', },
|
编写启动脚本
我们在package.json
中编写一些脚本,便于我们启动服务和打包
1 2 3 4 5
| "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.config.js", "start": "node server/index.js" },
|
这时候基本的需要已经搭建完成, 我们在app.js
中随便写点东西
解析jsx
到此为止我们已经可以正确打包js文件, 正确启动脚本, 安装了应用程序一些必要的依赖, 但是我们还无法解析jsx
语法
如果我们在app.js文件中写如下代码, 启动或编译
1 2 3 4 5 6
|
import React from 'react' import ReactDOM from 'react-dom'
ReactDOM.render(<div>111</div>, document.getElementById("app"))
|
我们会看到如下报错
因此我们需要安装babel
转换jsx相关的插件
1
| yarn add @babel/preset-react --dev
|
按照babel
官网的教程,在根目录下创建babel.config.json
此时项目结构如下
1 2 3 4 5 6 7 8 9 10
| ├─ app |-app.js ├─ node_modules |- ...... ├─ server ├─ index.js ├─ webpack.config.js ├─ babel.config.json └─ package.json └─ yarn.lock
|
在这里我们配一些基础的 js
转换的预设, 这里包括一个解析 jsx
的babel
预设,一个js
转换的预设, 这里 module
设置为 false
是根据当前的调用者判断其已经支持ES6模块语法,则默认的auto将自动选择false
,否则将选择 commonjs
, 这里手动设置为 false
将不支持将 ES6
模块语法转换为其他模块类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { "presets": [ [ "@babel/env", { "useBuiltIns": "usage", "module": false } ], ["@babel/preset-react", { "development": true } ] ], }
|
这里注意,babel
中preset
顺序是从后往前执行,plugin
的顺序是从后往前执行
再次打包后成功
我们启动下试试看
同样, 使用class
的写法也能正确解析, 这里有个注意点, 针对类, 我们需要使用@babel/plugin-proposal-class-properties
,否则如下代码无法编译
1 2 3 4 5 6 7 8
| class A { say = () => { console.log('我说话了') } }
let a = new A() a.say()
|
1
| yarn add @babel/plugin-proposal-class-properties --dev
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| { "presets": [ [ "@babel/env", { "useBuiltIns": "usage", "module": false } ], ["@babel/preset-react", { "development": true } ] ], "plugins": [ ["@babel/plugin-proposal-class-properties", { "loose": true }] ] }
|
安装antd
我们修改app.js
,导入antd
的Button
看看效果如何
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import React from 'react' import ReactDOM from 'react-dom' import { Button } from 'antd'
class MyDemo extends React.Component{ constructor(props){ super(props) this.state = { buttonText: "我是按钮" } }
render(){ return <Button>{this.state.buttonText}</Button> } }
ReactDOM.render(<MyDemo />, document.getElementById("app"))
|
结果是确实引入了antd
的组件,但是并未引入antd
的样式
我们选择使用babel
动态导入的方法引入样式
1
| yarn add babel-plugin-import --dev
|
然后修改babel.config.json
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| { "presets": [ [ "@babel/env", { "useBuiltIns": "usage", "modules": false } ], ["@babel/preset-react", { "development": true } ] ], "plugins": [ ["@babel/plugin-proposal-class-properties", { "loose": true }], ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }] ] }
|
结果如下
浏览器兼容
为了确保项目需要, 我们保持ie10
及以上的兼容, babel
会根据配置文件的target
, package.json
, 根目录browserslist
依次去寻找浏览器版本.这里我们在 package.json
中配置 browerslist
1 2 3 4 5
| "browserslist": [ "last 1 version", "> 1%", "IE 10" ]
|
运行 npx browerslist
得到如下图