参考资源: 从零搭建完整的React项目模板(Webpack + React hooks + Mobx + Antd)
name: 项目名称;
version: 项目版本;
main: 项目入口文件,通常默认为 index.js;
author: 项目作者
. . .
webpack 相关教程:
!https://www.codercto.com/a/23518.html
webpack是一个前端构建工具。那么什么是构建 工具 呢?
前端构建工具就是把开发环境的代码转化成运行环境代码。一般来说,开发环境的代码是为了更好的阅读,而运行环境的代码则是为了能够更快地执行。因此开发环境和运行环境的代码形式也不相同。比如,开发环境的代码,要通过混淆压缩后才能放在线上运行,这样代码体积更小,而且对代码执行也不会有任何影响。一般需要构建工具处理的几种情况:
代码压缩:
将JS、CSS代码混淆压缩,让代码体积更小,加载更快
编译语法:
编写CSS时使用Less、Sass,编写JS时使用ES6、TypeScript等,这些标准目前都无法被浏览器兼容,因此需要构建工具编译,例如使用Babel编译ES6语法。
处理模块化:
CSS和JS的模块化语法,目前都无法被浏览器兼容。因此开发环境可以使用既定的模块化语法,但是需要构建工具将模块化语法编译为浏览器可识别形式。例如使用webpack、Rollup等处理JS模块化。
webpack.common.js:公共配置文件 – 抽离出公共的部分,通过 merge 在dev prod 文件内进行合并
webpack.dev.js:研发环境配置文件
webpack.prod.js:生产环境配置文件
该插件的两个主要作用:
为html文件中引入的外部资源如script、link,动态添加每次compile后的hash,防止引用缓存的外部文件问题
可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口
简而言之,插件的基本作用就是生成html文件
// ./build/webpack.common.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const srcDir = path.join(__dirname, "../src");
module.exports = {
// 配置 html 模板
plugins: [
new HtmlWebpackPlugin({
template: `${srcDir}/index.html`
})
]
}
主要功能:webpack-dev-server是webpack官方提供的一个小型Express服务器。使用它可以为webpack打包生成的资源文件提供web服务。
webpack-dev-server 主要提供两个功能:
为静态文件提供服务
自动刷新和热替换(HMR): 不刷新网页的情况下, 改变代码后, 会自动编译并更新页面内容
// ./build/webpack.dev.js
const { merge } = require("webpack-merge");
const { NamedModulesPlugin, HotModuleReplacementPlugin} = require("webpack");
const commonConfig = require("./webpack.common");
module.exports = merge(commonConfig, {
// 开发模式
mode: "development",
// 配置本地服务及热更新
devServer: { // 本地服务配置
port: 3000, // 当前服务在本地运行在3000端口
hot: true, // 是否开启热更新:即本地代码修改后是否自动更新浏览器页面
// open: 该属性用于DevServer启动且第一次构建完成时,自动使用系统默认浏览器去打开网页。
open: false,
// historyApiFallback 用于应对返回404页面时定向跳转到特定页面
// 一般应用在 HTML5中History API 的单页应用,比如在访问路由的
// 时候,若访问不到该路由会跳转到index.html页面。
historyApiFallback: true,
// compress: 该属性是一个布尔型的值,默认为false,当他为true的
// 时候,它会对所有服务器资源采用gzip进行压缩。
compress: true,
// proxy 实现跨域: 我们使用webpack在本地启动服务器的时候,访问的
// 域名通常是 http://localhost:3000 这样的,但是服务端的接口是其他的,
// 那么就存在域名或端口号跨域的情况下, 幸运的是 devServer有一个叫proxy
// 的配置项,可以通过该配置来解决跨域的问题,因为 dev-server 使用了
// http-proxy-middleware 包
proxy: { // 代理
"/api": {
// target: 访问的目标域名或 ip:port
target: "https://www.easy-mock.com/mock/5dff0acd5b188e66c6e07329/react-template",
changeOrigin: true, // 是否跨域
// secure: false, // https 的时候 使用该参数
pathRewrite: { "^/api": "" } // 重写路径
}
}
},
plugins: [
// 在热加载时直接返回更新文件名,而不是文件的id
new NamedModulesPlugin(),
// 不刷新网页的情况下, 改变代码后, 会自动编译并更新页面内容
new HotModuleReplacementPlugin()
]
});
yarn add -D babel-loader @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react babel-plugin-import @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators
为什么使用 Babel ?
webpack中,默认只能处理一部分ES6(2015)新语法,一部分更高级的ES6语法或者ES7语法,weback是处理不了的,这时候就要借助第三方的loader,来帮助webpack处理这些高级的语法,当第三方loader将这些语法处理完毕后,就会交给webpack进行打包到bundle.js文件里面。
怎样使用 Babel ?
使用 Babel 需要进行一下配置:
yarn add -D babel-loader: babel-loader 与其他的 loader 一样,用来实现对特定文件的处理;
yarn add -D babel-core: babel-core 提供 babel-loader 处理的api, 也就是说, 在 babel-loader 对文件进行处理的时候,实际上是调用 babel-core 提供的 api 进行 语法树与代码 之间的转换;
yarn add -D babel-preset-env: babel-preset-env 用来告诉 babel 使用哪种规则对文件进行处理
以上步骤仅仅安装了 babel 对文件处理的依赖包, 还需要配置 babel 规则使 babel 起作用
配置 babel 规则有两种方式,一种是在 package.json 文件中设置 babel: {},另一种是在项目根目录下创建 .babelrc 文件写入配置.
a) 在 package.json 中配置:
// package.json
"babel": {
"presets": [],
"plugins": []
}
b) 新建 .babelrc 文件写入配置:
// .babelrc
{
"presets": ["env"]
}
“presets” 属性字段设定转码规则,事实上,babel有几种规则都可以实现对ES6语法的转码,如babel-preset-es2015、babel-preset-latest、babel-preset-env,不过官方现已建议采用babel-preset-env.
babel/preset-env 选项说明, useBuiltIns: “usage” | “entry” | false
a). false: 默认值。不对polyfill处理,全部引入。
b). usage: 按需加载。
c). entry 在入口文件手工引入polyfill。根据配置的target浏览器,引入浏览器不兼容的polyfill,这里需要指定 core-js的版本
“plugins” 属性设置使用到的插件。在本项目中只需将 “babel” 属性 的 “presets” 设置为[ “env” ]即可
上面 presets 与 plugins 的设置告诉npm本项目将使用babel,并且使用 bable-preset-env 规则进行转码,即实现对ES2015+语法进行转码。
经过 Step1 和 Step2 的配置后, webpack仍然不知道何时使用该规则,这便需要使用webpack.config.js文件。这个文件的作用是对webpack打包的参数进行配置。
// ./build/webpack.config.js
module.exports = {
module: {
rules: {
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"]
}
}
}
这就告诉webpack打包时,一旦匹配到 .js 文件就使用 babel-loader 进行处理,但是将 node_modules 目录文件排除. 如前文所述,babel-loader调用babel-core的api使用bable-preset-env的规则进行转码。
注: 本项目使用 React 框架进行搭建, 因此安装支持 React 的babel 包:
@babel/preset-react: 转换React jsx语法;
@babel/plugin-proposal-class-properties: 转换 Class语法;
@babel/plugin-proposal-decorators: 转换 Mobx 等更高级的语法;
babel-plugin-import: 配合实现React组件的按需加载;
// ./build/webpack.common.js
const path = require("path");
const srcDir = path.join(__dirname, "../src");
module.exports = {
. . .
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: [srcDir],
use: ["babel-loader?cacheDirectory=true"] // // 开启缓存
}
]
}
. . .
}
// .babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [
"@babel/transform-runtime",
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
["import", {
"libraryName": "antd",
"libraryDirectory": "es",
// 配置 style: true 则在项目编译阶段,可以对引入的 antd 样式文件进行编译,
// 从而可以压缩打包尺寸;而配置style: "css", 则直接引入经过打包后的 antd 样式文件
"style": "css" // `style: true` 会加载 less 文件
}]
]
}
plugins 配置项说明:
@babel/plugin-proposal-decorators:
legacy: Boolean 类型, 默认false;true为用stage1的语法(用废弃的语法)
@babel/plugin-proposal-class-properties:
loose: Boolean 默认 false; 定义类属性的方式
import:
"plugins": [
["import", { "libraryName": "antd", "libraryDirectory": "lib" }, "ant"],
["import", { "libraryName": "antd-mobile", "libraryDirectory": "lib"}, "antd-mobile"]
]
/**
* "libraryDirectory": "" 表示从库的package.json的main入口;否则可设置为lib文件夹
* /
yarn add -D less less-loader style-loader css-loader url-loader mini-css-extract-plugin postcss-loader autoprefixer
less-loader、style-loader、css-loader处理加载less、css文件;
postcss-loader、autoprefixer处理css样式浏览器前缀兼容;
url-loader处理图片、字体文件等资源;
mini-css-extract-plugin 分离css成单独的文件;
// ./build/webpack.common.js
. . .
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
// 从环境变量判断是否为生产环境
const devMode = process.env.NODE_ENV !== 'production';
module.exports = {
module: {
// rules: [
// {
// test: /\.(js|jsx)$/,
// include: [srcDir],
// use: ["babel-loader?cacheDirectory=true"] // 开启缓存
// }
// ]
rules: [
// css loader
{
test: /\.css$/,
use: [
devMode ? "style-loader" : MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "less-loader"
]
},
// less loader
{
test: /\.less$/,
use: [
devMode ? "style-loader" : MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"
]
},
// 处理图片文件
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: ["url-loader"],
include: [srcDir]
},
// 处理音频文件
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: ["url-loader"],
include: [srcDir]
},
// 处理字体文件
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: ["url-loader"],
include: [srcDir]
}
]
},
plugins: [
// 配置 html 模板
new HtmlWebpackPlugin({
template: `${srcDir}/index.html`
}),
// 配置 MiniCssExtractPlugin 插件
new MiniCssExtractPlugin({
filename: "[name].[contenthash:8].css",
chunkFilename: "chunk/[id].[contenthash:8].css"
})
],
}
注:
在node中,有全局变量 process 表示的是当前的node进程。
process.env 包含着关于系统环境的信息,但是 process.env 中并不存在 NODE_ENV 这个属性。
NODE_ENV是一个用户自定义的变量,在webpack中它的用途是判断生产环境或开发环境。
postcss 是一款使用插件去转换CSS的工具, 配置 postcss 表示指定使用的 css 预编译器,里面默认配置了 autoprefixer ,自动补全浏览器前缀;
配置方法:
在项目根目录下创建 .postcssrc.js 文件, 并在 .postcssrc.js 和 package.json 中分别写入以下配置项:
// .postcssrc.js
module.exports = {
plugins: {
autoprefixer: {} // 默认配置了 autoprefixer ,自动补全浏览器前缀
}
};
// package.json 中配置兼容浏览器
// 如下目标环境为:
// 超过市场份额1%的浏览器,且支持每个浏览器最后
// 两个版本和ie不小于等于10版本所需的polyfill代码转换
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
]
Autoprefixer 可以自动在样式中添加浏览器厂商前缀,避免手动处理样式兼容问题(为其添加浏览器厂商前缀的PostCSS插件)。
如下, 一个不加浏览器兼容设置的 css 样式设置为:
.example {
display: flex;
transition: all .5s;
user-select: none;
background: linear-gradient(to bottom, white, black);
}
Autoprefixer 解析后:
.example {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-transition: all .5s;
transition: all .5s;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background: -webkit-linear-gradient(top, white, black);
background: linear-gradient(to bottom, white, black);
}
webpack 在 nodejs 中是以单线程模型运行的, 需要一个一个处理 js, css, 图片以及文字等多种文件, 当文件数量较多时, webpack 构建速度就会很慢. 因此HappyPack出现了,它能让webpack同时处理多个任务,它将任务分解给多个子进程去并发执行,子进程处理完成后再将结果发送给主进程中。
a) 安装 Happypack 依赖: yarn add -D happypack
b) 将配置写入 webpack.common.js 文件
为了深入理解如何配置 happypack 的使用,我们先看一下不使用 happypack 时webpack 对js 或 jsx 文件的打包配置:
// webpack.common.js
const path = require("path");
module.exports = {
. . .
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: ["babel-loader?cacheDirectory=true"],
exclude: path.resolve(__dirname, "node_modules")
}
]
}
}
使用 happypack 后,变成如下配置:
// webpack.common.js
const path = require("path");
const os = require("os");
const HappyPack = require("happypack");
const happyPackThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
. . .
module: {
rules: [
{
test: /\.(js|jsx)$/,
// 将对.js 或 .jsx 文件的处理转交给id为happybabel的HappyPack的实例
use: ["happypack/loader?id=happybabel"],
exclude: path.resolve(__dirname, "node_modules")
}
]
}
// 同时配置 plugins
plugins: {
// 开启 happypack 线程池
new HappyPack({
// 用唯一的标识符id来代表当前的HappyPack 处理一类特定的文件
id: "happybabel",
// 如何处理.js文件,用法和Loader配置是一样的
loaders: ["babel-loader?cacheDirectory=true"],
threadPool: happyPackThreadPool,
cache: true,
//允许 HappyPack 输出日志
verbose: true
})
}
}
根据实际项目情况拆分模块,配合异步加载,防止单个文件过大。
webpack4 之前, 用 CommonsChunkPlugin 插件进行模块拆分,是一个可选的用于建立一个独立文件(又称作 chunk)的功能,这个文件包括多个入口 chunk 的公共模块。
从webpack4 开始官方移除了 CommonsChunkPlugin 插件, 取而代之的是两个新的配置项 optimization.splitChunks 和 optimization.runtimeChunk, 这也应该是从V3升级到V4的代码修改过程中最为复杂的一部分.
// ./build/webpack.prod.js
const merge = require("webpack-merge"); // 用于合并common config 与 当前的 prod config
const commonConfig = require("./webpack.common");
let prodConfig = merge(commonConfig, {
mode: 'production', // 设置当前配置为生产环境的配置
// 模块拆分配置
optimization: {
runtimeChunk: {
name: 'manifest',
},
splitChunks: {
// chunks: 共有3个值"initial","async"和"all"。配置后,代码分割优化仅选择初始块,按需块或所有块
// 默认只作用于异步模块,为`all`时对所有模块生效,`initial`对同步模块有效
chunks: 'all',
// minSize: 30000, // 单位: 字节, 引入的文件大于30kb才进行分割
// maxSize: 50000, //50kb,尝试将大于50kb的文件拆分成n个50kb的文件
// minChunks: 2, // 模块至少使用次数. 当值为2时,代表只引用了一次的模块不做分割打包处理
cacheGroups: { // 缓存组,定义分割的代码放到哪个文件中
dll: {
test: /[\\/]node_modules[\\/](react|react-dom|react-dom-router|babel-polyfill|mobx|mobx-react|mobx-react-dom|antd|@ant-design)/,
minChunks: 1, //引入的库大于30kb,才做代码分割
priority: 2, // 若一个模块符合多个组的 test 条件,就由priority决定放哪个组,值越大优先级越高
name: 'dll', // 打包后的文件名
},
codeMirror: {
test: /[\\/]node_modules[\\/](react-codemirror|codemirror)/,
minChunks: 1,
priority: 2,
name: 'codemirror',
},
vendors: {
test: /[\\/]node_modules[\\/]/, //引入的库是从node_modules引入,就分割库代码到当前的 vendors 组
minChunks: 1,
priority: 1,
name: 'vendors',
},
},
} // splitChunks
} // optimization
});
module.exports = prodConfig;
上述配置中 chunks 的配置提及异步代码与同步代码问题, 接下来看一下两种代码的例子:
// async code: set chunks to "async"
function getComponent() {
return import(/* webpackChunkName: "lodash" */lodash).then(({default: _}) => {
let element = document.createElement('div');
element.innerHTML = _.join(['Dell', ' ', 'Lee', '-']);
return element;
})
}
getComponent().then(el => {
document.body.appendChild(el);
})
// sync code: set chunks to "initial"
import _ from 'lodash'; //第三方库
import test from './test.js'; //业务代码
import jquery from 'jquery'; //第三方库
console.log(test.name);
var element = document.createElement('div');
element.innerHTML = _.join(['Dell', ' ', 'Lee', '-']);
document.body.appendChild(element);
console.log(jquery('div'));
在团队编程中,同样的功能可能会有多种写法,为了方便大家对于代码的维护,我们会使用ESLint来约束自己的代码,实际上,它是一种代码规范。
如果你要在项目中使用这种“规范”,我们首先需要进行安装:
yarn add -D eslint
光安装,还不能约束我们的代码规范,我们需要用一个配置文件来进行配置,这样的配置,我们可以简单的通过命令来生成,也可以通过引入外部的配置文件来配置,这里我们通过指令来进行配置。
npx eslint --init
提示的配置项,我们采用默认的即可.
执行完命令后,会在我们的根目录下生成一个’.eslintrc.js’的文件,不同版本和不同的配置方式生成的文件内容可能不一样,这里我生成的内容是
// .eslintrc.js
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
}
}
关于简单的Eslint用法,我们只需要执行指令即可,比如我要检测src下的代码
npx eslint src
接下来配置 eslint 解析器:
先安装babel-eslintr: yarn add -D babel-eslint
然后在 .eslintrc.js 中指定 eslint 解析器
// .eslintrc.js
. . .
parser: "babel-eslint"
. . .
babel-eslint 解析器是一种使用频率很高的解析器,因为现在很多公司的很多项目目前都使用了es6,为了兼容性考虑基本都使用babel插件对代码进行编译。而用babel编译后的代码使用 babel-eslint 这款解析器可以避免不必要的麻烦。
补充一些 eslint 的配置项(在 .eslintrc.js 中):
root: 限定配置文件的使用范围. 在开发中有时根据需要,我们可能在同一个项目不同的目录使用不同的 .eslintrc.js 文件,这时我们就需要使用配置项 root: true 。
parser: 指定eslint的解析器
parserOptions: 设置解析器选项
extends: 指定eslint规范, 我们可以使用eslint官方推荐的,也可以使用一些大公司提供的的,如:aribnb, google, standard。使用第三方eslint 扩展需要安装依赖, 如:
yarn add -D eslint-config-airbnb // aribnb
yarn add -D eslint-config-standard // standard
plugins: 引用第三方的插件: 我们的项目中可能会有一些其他的文件也需要进行格式规范,如:html, vue, react等,对于这些文件的处理,我们需要引入第三方插件, 如:
yarn add -D eslint-plugin-html
这个插件将会提醒模块脚本之间模拟浏览器共享全局变量的行为,因为这不适用于模块脚本。
这个插件也可以扩展文件,如:.vue,.jsx
在配置文件里配置插件时, 可以使用 plugins 关键字来存放插件名字的列表。插件名称可以省略 eslint-plugin- 前缀。
env: 指定代码运行的宿主环境, 如 browser, node等
rules: 启用额外的规则或覆盖默认的规则
globals: 声明在代码中的自定义全局变量
因为使用的是react项目,需要将 eslint 集成到webpack中。
我们需要安装 eslint-loader:
yarn add -D eslint-loader
并在 webpack 中写入如下配置:
// webpack.common.js
. . .
module.exports = {
. . .
module: {
rules: [
{
enforce: "pre",
test: /\.(js|jsx)$/,
include: [srcDir],
use: ["eslint-loader"]
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["happypack?id=happy-babel-loader"]
},
. . .
]
},
. . .
}
注意, use 中的 loader 的执行顺序是从右往左, 因此
{
test: /\.(js|jsx)$/,
include: [srcDir],
use: ["babel-loader", "eslint-loader"]
}
配置中压迫将 eslint-loader 配置写到 babel-loader 后面, 在编译前先执行eslint 检查对代码做规范
以上配置针对js文件的代码规范检查给出了运行npx eslint src检查一样的效果,但是这始终是在命令行里,为了方便使用 ,我们在devServer中加入配置项overlay: true这时候,运行项目,对于有错误的页面,会直接在页面上弹一个层,里面的内容就是我们ESlint的内容,这样即便是我们的编辑器中没有ESlint插件,也可以很快的定位错误。
如果不确定 loader 执行的顺序,建议将 webpack 的配置写成如下形式:
// webpack.common.js
. . .
module.exports = {
. . .
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: [srcDir],
loader: 'eslint-loader',
enforce: 'pre',
options: {
fix: true,
},
},
// {
// enforce: "pre",
// test: /\.(js|jsx)$/,
// include: [srcDir],
// use: ["eslint-loader"]
// },
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["happypack?id=happy-babel-loader"]
},
. . .
]
},
. . .
}
下面我们针对几个代表性的配置项,做一下简单的介绍. 们可以进入到eslint-loader的git仓库https://github.com/webpack-contrib/eslint-loader, 可以看到一些配置项:
fix: 当配置为true时,对于代码中一些浅显的问题,webpack会帮我们自动改掉
cache: 因为eslint会有一个对代码分析的过程,势必会造成一些性能损耗,开启cache将对这种性能损耗,做出一些优化。
在项目根目录创建 .eslintignore 文件,写入如下配置:
// .eslintignore
.DS_Store
package.json
dist/
yarn.lock
yarn-error.log
*.sh
.gitignore
.prettierignore
.editorconfig
.eslintignore
在 package.json 中配置npm 脚本命令:
// package.json
. . .
"scripts": {
"start": "webpack-dev-server --color --progress --config build/webpack.dev.js",
"build": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js",
"build:report": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js",
"build:watch": "NODE_ENV=production webpack --progress --config ./build/webpack.prod.js"
},
. . .
命令解释:
// 运行开发环境;
yarn start
// 生产环境打包压缩;
yarn build
// 图形化分析打包文件大小;
yarn build:report
// 方便排查生产环境打包后文件的错误信息(文件source map);
yarn build:watch
yarn add react react-dom react-router-dom mobx mobx-react mobx-react-router axios antd moment
// src/App.js
function App() {
return (
{/* */}
Edit src/App.js
and save to reload.
Learn React
);
}
export default App;
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// import reportWebVitals from './reportWebVitals';
ReactDOM.render(
,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
// reportWebVitals();
// src/index.html
管理平台
运行 yarn start:
报错1:
Module build failed (from ./node_modules/eslint-loader/dist/cjs.js):
TypeError: Cannot read properties of undefined (reading 'getFormatter')
解决办法:
package.json 中修改 eslint 和eslint-loader 的版本:
“eslint”: “^7.32.0”,
“eslint-loader”: “^4.0.2”
然后重新安装依赖: yarn install
报错2:
[webpack-cli] TypeError: webpack.NamedModulesPlugin is not a constructor
原因:
webpack5.0 的时候,NamedModulesPlugin已经移除了
解决办法:
webpack.dev.js: 中修改new webpack.NamedModulesPlugin() →
webpack.prod.js:
optimization: {
moduleIds: 'named'
}
“eslint”: “^7.32.0”,
“eslint-loader”: “^4.0.2”
报错3:
yarn start 启动服务器后, 浏览器无法显示内容, 控制台报错:
Uncaught ReferenceError: React is not defined
原因:
babel 需要建 ReactJs 语法转换为 CommonJs, 举个例子:
转换前:
import React, { Component } from 'react';
class Process extends Component {
render() {
return (哈哈哈)
}
}
转换后:
import React, { Component } from 'react';
class Process extends Component {
render() {
// 用到了React.createElement方法
return React.createElement(
'div',
null,
'\u54C8\u54C8\u54C8'
);
}
}
为什么代码没有使用react相关方法,也要在文件顶部import react?
原因: 通过babel会将React jsx / React js 编译成普通js代码(如下)会用到React.createElement,所以需要React. 即:
import React from "react";
const App = () => (
Hello World!!!
);
// 等同于:
export default App;
var App = function App() {
return React.createElement(
"div",
{className:'test'}
"Hello World!!!"
);
};
解决办法:
App.js 中导入 React:
import React from 'react';
若不想每个模块都引入 React, 可在webpack中全局配置引入:
修改 webpack.common.js:
const webpack = require('webpack');
. . .
module.exports = {
. . .
plugins: [
. . .
new webpack.ProvidePlugin({
React: 'react'
})
. . .
]
}