React 框架搭建
Creact React App(CRA)是创建React应用的一个构建脚本,并不处理后端逻辑及数据库,它与其他构建脚本不同的一点就是,它使用了 Babel 和 Webpack 这样的构建工具,使开发者不用单独去配置这些工具,从而降低了开发人员的学习难度。
但对于一些高阶的开发人员,想要对Webpack做一些修改,包括对less、跨域访问的支持以及antd的配置等,下面就是我的具体做法。
注意当前webpack的版本"webpack": "4.19.1"
,时基于此给出的解决方案,webpack至少是4以上版本。
按照React 官网创建你的React项目,这里就不在讲解。
使用CRA创建好项目之后,打开package.json文件:
{
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
}
执行yarn eject
将封装到CRA中的全部配置反编译到当前项目,由此webpack的配置全部暴露给了开发者。
// eject 后项目根目录下会出现 config 文件夹,里面就包含了 webpack 配置
config
├── env.js
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── paths.js
├── webpack.config.dev.js // 开发环境配置
├── webpack.config.prod.js // 生产环境配置
└── webpackDevServer.config.js
// eject 后项目根目录下会出现 scripts 文件夹,里面就包含了 项目启动文件
├── build.js //生产环境
├── start.js // 开发环境
├── test.js // 测试环境
CRA和其他构建脚本不同的是,它可以通过升级react-scripts
来升级CRA特性,但是如果使用eject
命令后,就无法使用了,因为react-scripts
已经是以文件的形式存在于你的项目,而不是以包的形式,所以无法对其升级。
Webpack配置
这时,一个简单的项目已经可以运行成功了并且webpack配置暴露了出来,下面安装antd,配置antd
1. antd
yarn add antd
安装后,你可以使用antd的组件,官网提供了两种配置方案,我们这里只介绍eject配置。
- 配置less,antd 的样式使用了 Less 作为开发语言,安装
yarn add less
及yarn add less-loader
- 按需加载,
yarn babel-plugin-import
是一个用于按需加载组件代码和样式的 babel 插件 - 自定义主题
打开webpack.config.dev.js,作出相应的修改:
...
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
//--------------------注意:这里需要增加less的相关配置--------------------
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
...
// --------------------注意,在此处修改了判断语句的代码,增加对less的支持--------------------
if (preProcessor) {
if (preProcessor === 'less-loader') {
loaders.push({
loader:require.resolve(preProcessor),
options: {
modules: false,
modifyVars: {
"@primary-color": "#1890ff" // 这里配置antd的自定义主题色
},
javascriptEnabled: true
}
})
} else {
loaders.push(require.resolve(preProcessor));
}
}
return loaders;
};
// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
...
module: {
strictExportPresence: true,
rules: [
...
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
...
// Process application JS with Babel.
// The preset includes JSX, Flow, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '@svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
// --------------------注意此处增加:实现按需加载--------------------:
['import', { libraryName: 'antd', style: true }], // import less
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
// Don't waste time on Gzipping the cache
cacheCompression: false,
},
},
...
// --------------------注意此处在cssRegex配置之上增加--------------------
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, but
// in development "style" loader enables hot editing of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders({
importLoaders: 2,
}, 'less-loader'),
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: lessModuleRegex,
use: getStyleLoaders({
importLoaders: 2,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}, 'less-loader'),
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, but
// in development "style" loader enables hot editing of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
}),
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
...
],
},
...
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
...
};
同样,对webpack.config.prod.js做相同的调整。
至此,antd配置结束。
2. 跨域-- webpack-dev-server
webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。
在eject之后暴露出来的scripts/start.js, 找到
...
const paths = require('../config/paths');
...
// We require that you explictly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
// 获取端口号
return choosePort(HOST, DEFAULT_PORT);
})
.then(port => {
if (port == null) {
// We have not found a port.
return;
}
// 获取协议类型
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
// 获取名称
const appName = require(paths.appPackageJson).name;
// 获取url
const urls = prepareUrls(protocol, HOST, port);
// 根据惯用的信息创建一个webpack编译器
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
// **加载代理**-----------
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
// 创建WebpackDevServer服务器
const serverConfig = createDevServerConfig(
proxyConfig,
urls.lanUrlForConfig
);
const devServer = new WebpackDevServer(compiler, serverConfig);
// 启动WebpackDevServer服务器
devServer.listen(port, HOST, err => {
if (err) {
return console.log(err);
}
if (isInteractive) {
clearConsole();
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
['SIGINT', 'SIGTERM'].forEach(function(sig) {
process.on(sig, function() {
devServer.close();
process.exit();
});
});
})
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
注意 加载代理配置项proxySetting,它是由paths.appPackageJson加载来的,我们向上找到const paths = require('../config/paths');
...
// config after eject: we're in ./config/
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
// -------加载代理配置栏------
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
publicUrl: getPublicUrl(resolveApp('package.json')),
servedPath: getServedPath(resolveApp('package.json')),
};
...
查找当前src
文件夹
src
├── App.css
├── App.js
├── App.test.js
├── index.css
├──index.js
├── logo.svg
└── serviceWorker.js
并没有setupProxy.js
文件,我们创建src/setupProxy.js
,并安装yarn add http-proxy-middleware
,设置相应的代理
// src/setupProxy.js 配置代理
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(proxy('/api/',
{
target: 'https://api.dev.com',// 后端服务器地址
changeOrigin: true,
secure: false
}
));
}
更多devServer的配置,详见开发中 Server(devServer),这里不在详细介绍。
至此,若后续有修改,将继续更新。