想要疾速构建理论利用,离不开一个好的利用模版,React作为大厂出品工具,有着稳定性和可维护性的保障,同时能够应用相干的全套全家桶(React + React-router + Axios + Mobx + Antd)进行连贯麻利开发,本文将从如何在云开发平台创立我的项目利用模版,基于利用模版创立《后盾治理》我的项目,以及上传并且通过云平台将我的项目上线部署利用,为我的项目开发提供更加便捷的操作环境。

一 、通过云开发平台疾速创立初始化利用

1.创立相干利用模版请参考链接:https://developer.aliyun.com/...

2.实现创立后就能够在github中查看到新增的react仓库

二 、本地编写《后盾治理》我的项目

1.将利用模版克隆到本地

• 首先假设你曾经装置了Git、node,没有装置请移步node官网进行装置。克隆我的项目:

git clone + 我的项目地址

• 进入我的项目文件

cd create-react-app

• 切换到feature/1.0.0 分支上

git checkout feature/1.0.0

• 应用一下命令全局装置 React :

npm install -g create-react-app

• 装置依赖包

npm install

• 启动服务

npm start

这里关上浏览器3000端口,并呈现默认页面。

2.架构与成果预览

• 《后盾治理》我的项目架构

• 成果预览

3.初始化我的项目

• 初始化package.json

npm init

• 装置webpack

npm add -D webpack webpack-cli webpack-merge

我的项目中应用的Webpack版本是^5.10.0,Webpack4.0 打包构建做了很多默认的优化配置,不少配置项无需配置或更改。
比方:针对开发模式的放慢打包速度,合并chunk; 针对生产模式的代码压缩,缩小打包体积等。

// 一部分默认配置  optimization: {        removeAvailableModules: true, // 删除已解决的chunk (默认 true)        removeEmptyChunks: true, // 删除空的chunks (默认 true)        mergeDuplicateChunks: true // 合并反复的chunk (默认 true)    } // 针对生产环境默认配置    optimization: {        sideEffects:true, //配合tree shaking        splitChunks: {...}, //拆包        namedModules: false, // namedChunks:false 不启用chunk命名,默认自增id        minimize: true, // 代码压缩    }

依据开发环境/生产环境 辨别webpack配置十分有必要,能够放慢开发环境的打包速度,有时候遇到开发环境打包过慢,能够排查下是否配置有误(比方开发环境开启了代码压缩等)。
我的项目中配合webpack-merge依据开发环境/生产环境进行拆分配置:

Webpack4.0公布曾经很长时间了,置信基本上我的项目都已迁徙至4.0,在这里就不多赘述了。
• 配置Html模版
装置:

npm add -D html-webpack-plugin

配置:

const srcDir = path.join(__dirname, "../src");plugins: [    new HtmlWebpackPlugin({        template: `${srcDir}/index.html` })]

• 配置本地服务及热更新
装置:

npm add -D webpack-dev-server clean-webpack-plugin

开发环境利用webpack-dev-server搭建本地 web server,并启用模块热更新(HMR)。
为不便开发调试,转发代理申请(本例中配合axios封装 转发接口到easy-mock在线平台)
配置:

mode: "development", // 开发模式devServer: { // 本地服务配置    port: 9000,    hot: true,    open: false,    historyApiFallback: true,    compress: true,    proxy: { // 代理        "/testapi": {            target:            "https://www.easy-mock.com/mock/5dff0acd5b188e66c6e07329/react-template",             changeOrigin: true,             secure: false,             pathRewrite: { "^/testapi": "" }        }    }},plugins: [    new webpack.NamedModulesPlugin(),    new webpack.HotModuleReplacementPlugin()],

• 配置Babel
装置:

npm 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

Webpack中Babel配置,是比拟重要的一环。关系着ES6语法、React jsx、Mobx等语法通过打包后是否失常运行。
其中:
@babel/preset-react转换React jsx语法;
@babel/plugin-proposal-class-properties 转换 Class语法;
@babel/plugin-proposal-decorators 转换 Mobx 等更高级的语法;
babel-plugin-import 配合实现React组件的按需加载;
这里须要留神Babel7.0 相较于Babel6.0的区别。
配置:

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": "css" // `style: true` 会加载 less 文件            }        ]    ]}

• 解决Less款式和图片等资源
装置:

npm 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成独自的文件;
配置:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");...module: {    rules: [        {            test: /\.less$/,            use: [                devMode ? "style-loader" : MiniCssExtractPlugin.loader,                "css-loader",                "postcss-loader",                "less-loader"            ]        },        {            test: /\.css$/,            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: [    new MiniCssExtractPlugin({        filename: "[name].[contenthash:8].css",        chunkFilename: "chunk/[id].[contenthash:8].css"    }),    ],

配置postcss .postcssrc.js 文件

// .postcssrc.jsmodule.exports = {    plugins: {        autoprefixer: {}    }};// package.json中配置兼容浏览器"browserslist": [    "> 1%",    "last 2 versions",    "not ie <= 10"]

• 利用happypack多线程打包
装置:

npm add -D happypack

配置:

const os = require("os");const HappyPack = require("happypack");const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });module: {    rules: [        {            test: /\.(js|jsx)$/,            include: [srcDir],            exclude: /(node_modules|bower_components)/,            use: ["happypack/loader?id=happybabel"]        },    ]},plugins: [    //开启 happypack 的线程池    new HappyPack({        id: "happybabel",        loaders: ["babel-loader?cacheDirectory=true"],        threadPool: happyThreadPool,        cache: true,        verbose: true    }),]

• 生产环境 拆分模块
依据理论我的项目状况拆分模块,配合异步加载,避免单个文件过大。

optimization: {        runtimeChunk: {            name: "manifest"        },        splitChunks: {            chunks: "all", //默认只作用于异步模块,为`all`时对所有模块失效,`initial`对同步模块无效            cacheGroups: {                dll: {                    test: /[\\/]node_modules[\\/](react|react-dom|react-dom-router|babel-polyfill|mobx|mobx-react|mobx-react-dom|antd|@ant-design)/,                    minChunks: 1,                    priority: 2,                    name: "dll"                },                codeMirror: {                    test: /[\\/]node_modules[\\/](react-codemirror|codemirror)/,                    minChunks: 1,                    priority: 2,                    name: "codemirror"                },                vendors: {                    test: /[\\/]node_modules[\\/]/,                    minChunks: 1,                    priority: 1,                    name: "vendors"                }            }        }    }

• 其余配置
引入 ESLint 与 Prettier 配合,规范化团队我的项目代码开发,对立代码格调。

npm add -D prettier babel-eslint eslint eslint-loader eslint-config-airbnb eslint-config-prettier eslint-plugin-babel eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react

具体配置详见 /build目录 下 https://github.com/now1then/r...
• npm scripts
package.json 文件

{    ...    "scripts": {        "start": "webpack-dev-server --color --inline --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"    },    ...}

命令行运行:
// 命令行执行
// 运行开发环境;

npm start

// 生产环境打包压缩;

npm build

// 图形化剖析打包文件大小;

npm build:report

// 不便排查生产环境打包后文件的错误信息(文件source map);

npm build:watch

其中 build:report 、build:watch 可能实现性能,是在build/webpack.prod.js 中有如下代码:
// 不便排查生产环境打包后文件的错误信息(文件source map)

if (process.env.npm_lifecycle_event == "build:watch") {    config = merge(config, {        devtool: "cheap-source-map"    });}// 图形化剖析打包文件大小if (process.env.npm_lifecycle_event === "build:report") {    const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")        .BundleAnalyzerPlugin;    config.plugins.push(new BundleAnalyzerPlugin());}

• 我的项目代码架构

npm add react react-dom react-router-dom mobx mobx-react mobx-react-router axios antd moment

4.函数化Hooks

以后React版本已更新到16.12,Hooks 齐全应该成为 React 应用的支流。本我的项目中将齐全拥抱Hook,个别不再用 class 来实现组件。
以下为局部实现代码(可暂疏忽mobx的应用):

import React, { useState, useEffect, useContext } from 'react';import { observer } from 'mobx-react';import { Button } from 'antd';import Store from './store';import './style.less';const HomePage = () => {    // useContext 订阅mobx数据    const pageStore = useContext(Store);    // useState state状态    const [num, setNum] = useState(0);    // useEffect副作用    useEffect(() => {        pageStore.qryTableDate();    }, []);    return (        <div className="page-home page-content">            <h2>{pageStore.pageTitle}</h2>            <div>                <span>num值:{num}</span>                <Button type="primary" size="small" style={{ marginLeft: 10 }}                    onClick={() => setNum(num + 1)}                >+1</Button>            </div>        </div>    );};export default observer(HomePage);

5.Router路由配置

我的项目是单页利用,路由配置个别分为约定式动静路由和集中配置式路由。
在 React 的世界里,间接采纳成熟的react-router工具治理页面路由。咱们当初说到react-router,基本上都是在说 react-router 的第4版之后的版本,以后的最新版本曾经更新到5.1.x了。
以后react-router反对动静路由,齐全用React组件来实现路由,在渲染过程中动静设置路由规定,匹配命中规定加载对应页面组件。
本我的项目采纳集中配置式路由(不便路由鉴权、从服务端接口获取菜单路由配置等),同时兼顾不便地设置侧边菜单栏。 当然为简略起见,我的项目中读取本地动态菜单配置,也暂未引入路由鉴权。

6.动态路由配置 src/routes/config.js :

import React, { lazy } from "react";import BasicLayout from "@/layouts/BasicLayout";import BlankLayout from "@/layouts/BlankLayout";const config = [    {        path: "/",        component: BlankLayout, // 空白页布局        childRoutes: [ // 子菜单路由            {                 path: "/login", // 路由门路                name: "登录页", // 菜单名称 (不设置,则不展现在菜单栏中)                icon: "setting", // 菜单图标                component: lazy(() => import("@/pages/Login")) // 懒加载 路由组件            },            // login等没有菜单导航栏等根本布局的页面, 要放在根本布局BasicLayout之前。            {                path: "/",                component: BasicLayout, // 根本布局框架                childRoutes: [                    {                        path: "/welcome",                        name: "欢送页",                        icon: "smile",                        component: lazy(() => import("@/pages/Welcome"))                    },                    {... /* 其余 */},                     { path: "/", exact: true, redirect: "/welcome" },                    { path: "*", exact: true, redirect: "/exception/404" }                ]            }        ]    }];export default config;

下面是动态路由的一部分配置,
留神:中会用包裹,会匹配命中的第一个。"/login"等没有菜单导航栏等根本布局的页面, 要放在根本布局BasicLayout之前。
利用和React.lazy()实现页面组件懒加载。

7.路由组建渲染 src/routes/AppRouter.js :

import React, { lazy, Suspense } from "react";import LoadingPage from "@/components/LoadingPage";import {    HashRouter as Router,    Route,    Switch,    Redirect} from "react-router-dom";import config from "./config";const renderRoutes = routes => {    if (!Array.isArray(routes)) {        return null;    }    return (        <Switch>            {routes.map((route, index) => {                if (route.redirect) {                    return (                        <Redirect                            key={route.path || index}                            exact={route.exact}                            strict={route.strict}                            from={route.path}                            to={route.redirect}                        />                    );                }                return (                    <Route                        key={route.path || index}                        path={route.path}                        exact={route.exact}                        strict={route.strict}                        render={() => {                            const renderChildRoutes = renderRoutes(route.childRoutes);                            if (route.component) {                                return (                                    <Suspense fallback={<LoadingPage />}>                                        <route.component route={route}>                                            {renderChildRoutes}                                        </route.component>                                    </Suspense>                                );                            }                            return renderChildRoutes;                        }}                    />                );            })}        </Switch>    );};const AppRouter = () => {    return <Router>{renderRoutes(config)}</Router>;};export default AppRouter;

8.路由 hooks 语法

react-router-dom 也曾经反对 hooks语法,获取路由信息或路由跳转,能够应用新的hooks 函数:
• useHistory:获取历史路由,回退、跳转等操作;
• useLocation:查看以后路由信息;
• useParams:读取路由附带的params参数信息;
• useRouteMatch:匹配以后路由;
只有包裹在中的子组件都能够通过这几个钩子函数获取路由信息。
代码演示:

import { useHistory } from "react-router-dom";function HomeButton() {    const history = useHistory();    function onClick() {        history.push("/home");    }    return (        <button type="button" onClick={onClick}>            跳转Home页        </button>    );}

9.联合mobx治理数据状态

我的项目中是否应用状态管理工具或应用何种管理工具,根据理论我的项目状况而定。
本我的项目应用本人比拟相熟的Mobx,Mobx是一个功能强大,上手非常容易的状态管理工具。
为了应用简洁及治理不便,在组织上,分为全局公共数据状态和页面数据状态。
专用数据状态寄存在/src/stores目录下;页面几数据寄存于对应页面目录下。
在实现上,利用mobx + useContext Hook个性 实现函数式组件的状态治理。
具体在于利用React的createdContext构建蕴含Mobx 的context上下文;函数式组件中应用useContext Hook 订阅Mobx数据变动。
• 页面级store.js 代码:

import { createContext } from "react";import { observable, action, computed } from "mobx";import request from "@/services/newRequest";class HomeStore {    @observable tableData = [];    @observable pageTitle = "Home主页";    @observable loading = false;    @action.bound setData(data = {}) {        Object.entries(data).forEach(item => {            this[item[0]] = item[1];        });    }    // 列表数据    @action.bound    async qryTableDate(page = 1, size = 10) {        this.loading = true;        const res = await request({            url: "/list",            method: "post",            data: { page, size }        });        if (res.success) {            const resData = res.data || {};            console.log(resData);        }        this.loading = false;    }}export default createContext(new HomeStore());

• 页面组件代码 :

import React, { useContext } from "react";import { observer } from "mobx-react";import Store from "./store";import "./style.less";const HomePage = () => {    const pageStore = useContext(Store);    return (        <div className="page-home page-content">            home页面            <h2>{pageStore.pageTitle}</h2>        </div>    );};

export default observer(HomePage);
以上为局部演示代码,具体业务实现能够查看我的项目代码。

10.Axios Http 申请封装

Axios申请封装,具体代码见 /src/services/newRequest.js

11.UI组件及页面布局

UI组件应用优良的Ant Design 组件库,留神应用 babel-plugin-import 配置实现组件的按需加载。
本我的项目的外部页面布局采纳 Antd 上经典的布局形式:

页面布局须要正当拆分模块,左侧菜单导航栏依据动态菜单渲染。理论残缺代码详见我的项目,以下为BasicLayout组件:

import React from "react";import { Layout } from "antd";import SiderMenu from "../SiderMenu";import MainHeader from "../MainHeader";import MainFooter from "../MainFooter";import "./style.less";const BasicLayout = ({ route, children }) => {    return (        <Layout className="main-layout">            {/* 左侧菜单导航 */}            <SiderMenu routes={route.childRoutes} />             <Layout className="main-layout-right">                {/* 顶部展现布局 */}                <MainHeader></MainHeader>                <Layout.Content className="main-layout-content">                    {/* 理论页面布局 */}                    {children}                    {/* <MainFooter></MainFooter> */}                </Layout.Content>            </Layout>        </Layout>    );};export default BasicLayout;

对于登录页等页面无需套在下面的根本布局之类,须要独自解决(菜单配置在BasicLayout配置之前)。

三 、云端一键部署上线利用

1.上传代码

git add . git commit -m '增加你的正文'git push

2.在日常环境部署

一键进行利用部署。在利用详情页面点击日常环境的「部署」按钮进行一键部署,部署状态变成绿色已部署当前能够点击拜访部署网站查看成果。

3.配置自定义域名在线上环境上线

• 配置线上环境自定义域名。在性能开发验证实现后要在线上环境进行部署,在线上环境的「部署配置」-「自定义域名」中填写本人的域名。例如咱们增加一个二级域名 company.workbench.fun 来绑定咱们部署的前端利用。而后复制自定义域名下方的API网关地址对增加的二级域名进行CNAME配置。

• 配置CNAME地址。复制好 API网关域名地址后,来到你本人的域名治理平台(此示例中的域名治理是阿里云的域名治理控制台,请去本人的域名控制台操作)。增加记录的「记录类型」抉择「CNAME」,在「主机记录」中输出你要创立的二级域名,这里咱们输出「company」,在「记录值」中粘贴咱们之前复制的 API网关域名地址,「TTL」保留默认值或者设置一个你认为适合的值即可。

• 在线上环境部署上线。回到云开发平台的利用详情页面,依照部署的操作,点击线上环境的「部署按钮」,部署实现当前就在你自定义的域名进行了上线。CNAME 失效之后,咱们输出 company.workbench.fun(示例网址) 能够关上部署的页面。至此,如何部署一个利用到线上环境,如何绑定本人的域名来拜访一个线上的利用就实现了,连忙部署本人的利用到线上环境,用本人的域名玩起来吧 ;)

一键创立React利用模版链接 :https://workbench.aliyun.com/...

参考文献:https://juejin.cn/post/684490...