react+umi+dva+typescript创建及配置项目方案总结
背景描述:
- Umi是蚂蚁金服开源的 react 应用框架,是可扩展的企业级应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。具有以下优势:
- 可扩展,Umi 实现了完整的生命周期,并使其插件化,Umi 内部功能也全由插件完成。此外还支持插件和插件集,以满足功能和垂直域的分层需求。
- 开箱即用,Umi 内置了路由、构建、部署、测试等,仅需一个依赖即可上手开发。并且还提供针对 React 的集成插件集,内涵丰富的功能,节省部分框架搭建及配置工作,可满足日常 80% 的开发需求。
- 企业级,经蚂蚁、阿里、优酷、网易、飞猪、口碑等公司项目的验证,适合于大型企业级项目。
- 大量自研,包含微前端、组件打包、文档工具、请求库、hooks 库、数据流等,满足日常项目的周边需求。
- 完备路由,同时支持配置式路由和约定式路由,同时保持功能的完备性,比如动态路由、嵌套路由、权限路由等等。
- 面向未来,在满足需求的同时,我们也不会停止对新技术的探索。比如 dll 提速、modern mode、webpack@5、自动化 external、bundler less 等等。 不适用于:IE8 或更低版本的浏览器、React 16.8.0 以下版本、Node 10 以下的环境。
- 关于umi相对于其他应用级框架create-react-app、next.js的对比,这里引用官方:
- create-react-app 是基于 webpack 的打包层方案,包含 build、dev、lint 等,他在打包层把体验做到了极致,但是不包含路由,不是框架,也不支持配置。所以,如果大家想基于他修改部分配置,或者希望在打包层之外也做技术收敛时,就会遇到困难。
- next.js 是个很好的选择,Umi 很多功能是参考 next.js 做的。要说有哪些地方不如 Umi,我觉得可能是不够贴近业务,不够接地气。比如 antd、dva 的深度整合,比如国际化、权限、数据流、配置式路由、补丁方案、自动化 external 方面等等一线开发者才会遇到的问题。
- dva是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的、面向数据流处理的应用框架。具有以下优势:
- 易学易用,仅有 6 个 api,对 redux 用户尤其友好,配合 umi 使用后更是降低为 0 API。
- elm 概念,通过 reducers, effects 和 subscriptions 组织 model。
- 插件机制,比如 dva-loading 可以自动处理 loading 状态,不用一遍遍地写 showLoading 和 hideLoading。
- 支持 HMR,基于 babel-plugin-dva-hmr 实现 components、routes 和 models 的 HMR。
创建项目:
-
// 1.新建空白文件夹 mkdir myapp && cd myapp // 2.创建umi项目(也可以:npm create umi // 或 yarn create umi) npx @umijs/create-umi-app // 或 yarn create @umijs/umi-app // 3.安装并运行 npm install npm start
配置项目:
-
当按照第一步创建完项目后,对于typescript而言已经在项目里有了默认的配置,此时查看umi版本,确认是否是v3及以上版本,因为v3在细节上做了优化,参考升级到v3版本(注意:Node版本在 10.13 或以上)。
-
1.umi本身默认了less,其实完全够用,但也可以配置sass预编译(**注意:**由于 node-sass 插件本生与node之间存在一定的兼容问题,可以不安装此插件,默认使用dart-sass)。
-
npm install sass sass-loader --save -dev
-
-
2.安装antd。
-
npm install antd --save
-
-
3.配置axios请求:
-
// 1.安装 npm install axios --save // 2.请求封装 import axios from 'axios' const service = axios.create({ baseURL: import.meta.env.VITE_API_URL as any, timeout: 50000, headers: { 'Content-Type': 'application/json' } }) service.interceptors.request.use( (config) => { config.headers.common['Authorization'] = 'Authorization' return config }, (error) => { return Promise.reject(error) } ) service.interceptors.response.use( (response) => { const res = response.data if (res.code !== 200) { return Promise.reject(service.interceptors.response) } else { return response.data } }, (error) => { return Promise.reject(error) } ) export default service
-
-
4.配置 ESlint 代码审查(**注意:**如果配置完eslint后,格式化没有效果,此时需要注意编辑器是否开启eslint):
-
// 1.安装 npm i eslint eslint-config-prettier eslint-plugin-typescript eslint-plugin-vue typescript-eslint-parser vue-eslint-parser @typescript-eslint/eslint-plugin @typescript-eslint/parser -S -D // 2.配置 .eslintignore *.sh node_modules lib *.md *.scss *.woff *.ttf .vscode .idea dist mock public bin build config index.html src/assets // 3.配置 .eslintrc.js module.exports = { root: true, env: { browser: true, es2021: true, node: true, }, parser: 'vue-eslint-parser', parserOptions: { ecmaVersion: 12, parser: '@typescript-eslint/parser', sourceType: 'module', }, extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'], plugins: ['vue', 'typescript', '@typescript-eslint'], rules: { "vue/max-attributes-per-line": [2, { "singleline": 10, "multiline": { "max": 1, "allowFirstLine": false } }], "vue/singleline-html-element-content-newline": "off", "vue/multiline-html-element-content-newline":"off", "vue/name-property-casing": ["error", "PascalCase"], "vue/no-v-html": "off", 'accessor-pairs': 2, 'arrow-spacing': [2, { 'before': true, 'after': true }], 'block-spacing': [2, 'always'], 'brace-style': [2, '1tbs', { 'allowSingleLine': true }], 'camelcase': [0, { 'properties': 'always' }], 'comma-dangle': [2, 'never'], 'comma-spacing': [2, { 'before': false, 'after': true }], 'comma-style': [2, 'last'], 'constructor-super': 2, 'curly': [2, 'multi-line'], 'dot-location': [2, 'property'], 'eol-last': 2, 'eqeqeq': ["error", "always", { "null": "ignore"}], 'generator-star-spacing': [2, { 'before': true, 'after': true }], 'handle-callback-err': [2, '^(err|error)$'], 'indent': [2, 2, { 'SwitchCase': 1 }], 'jsx-quotes': [2, 'prefer-single'], 'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }], 'keyword-spacing': [2, { 'before': true, 'after': true }], 'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }], 'new-parens': 2, 'no-array-constructor': 2, 'no-caller': 2, 'no-console': 'off', 'no-class-assign': 2, 'no-cond-assign': 2, 'no-const-assign': 2, 'no-control-regex': 0, 'no-delete-var': 2, 'no-dupe-args': 2, 'no-dupe-class-members': 2, 'no-dupe-keys': 2, 'no-duplicate-case': 2, 'no-empty-character-class': 2, 'no-empty-pattern': 2, 'no-eval': 2, 'no-ex-assign': 2, 'no-extend-native': 2, 'no-extra-bind': 2, 'no-extra-boolean-cast': 2, 'no-extra-parens': [2, 'functions'], 'no-fallthrough': 2, 'no-floating-decimal': 2, 'no-func-assign': 2, 'no-implied-eval': 2, 'no-inner-declarations': [2, 'functions'], 'no-invalid-regexp': 2, 'no-irregular-whitespace': 2, 'no-iterator': 2, 'no-label-var': 2, 'no-labels': [2, { 'allowLoop': false, 'allowSwitch': false }], 'no-lone-blocks': 2, 'no-mixed-spaces-and-tabs': 2, 'no-multi-spaces': 2, 'no-multi-str': 2, 'no-multiple-empty-lines': [2, { 'max': 1 }], 'no-native-reassign': 2, 'no-negated-in-lhs': 2, 'no-new-object': 2, 'no-new-require': 2, 'no-new-symbol': 2, 'no-new-wrappers': 2, 'no-obj-calls': 2, 'no-octal': 2, 'no-octal-escape': 2, 'no-path-concat': 2, 'no-proto': 2, 'no-redeclare': 2, 'no-regex-spaces': 2, 'no-return-assign': [2, 'except-parens'], 'no-self-assign': 2, 'no-self-compare': 2, 'no-sequences': 2, 'no-shadow-restricted-names': 2, 'no-spaced-func': 2, 'no-sparse-arrays': 2, 'no-this-before-super': 2, 'no-throw-literal': 2, 'no-trailing-spaces': 2, 'no-undef': 2, 'no-undef-init': 2, 'no-unexpected-multiline': 2, 'no-unmodified-loop-condition': 2, 'no-unneeded-ternary': [2, { 'defaultAssignment': false }], 'no-unreachable': 2, 'no-unsafe-finally': 2, 'no-unused-vars': [2, { 'vars': 'all', 'args': 'none' }], 'no-useless-call': 2, 'no-useless-computed-key': 2, 'no-useless-constructor': 2, 'no-useless-escape': 0, 'no-whitespace-before-property': 2, 'no-with': 2, 'one-var': [2, { 'initialized': 'never' }], 'operator-linebreak': [2, 'after', { 'overrides': { '?': 'before', ':': 'before' } }], 'padded-blocks': [2, 'never'], 'quotes': [2, 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], 'semi': [2, 'never'], 'semi-spacing': [2, { 'before': false, 'after': true }], 'space-before-blocks': [2, 'always'], 'space-before-function-paren': [2, 'never'], 'space-in-parens': [2, 'never'], 'space-infix-ops': 2, 'space-unary-ops': [2, { 'words': true, 'nonwords': false }], 'spaced-comment': [2, 'always', { 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] }], 'template-curly-spacing': [2, 'never'], 'use-isnan': 2, 'valid-typeof': 2, 'wrap-iife': [2, 'any'], 'yield-star-spacing': [2, 'both'], 'yoda': [2, 'never'], 'prefer-const': 2, 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 'object-curly-spacing': [2, 'always', { objectsInObjects: false }], 'array-bracket-spacing': [2, 'never'] } };
-