写给前端新人:从 0到1 搭建一个前端项目,都需要做什么? 您所在的位置:网站首页 web前端是做什么的需要用什么软件 写给前端新人:从 0到1 搭建一个前端项目,都需要做什么?

写给前端新人:从 0到1 搭建一个前端项目,都需要做什么?

2024-07-12 13:08| 来源: 网络整理| 查看: 265

不使用脚手架搭建 React 项目:https://github.com/zhuyuanmin/react-0-1-build。读者可根据提交的分支顺序一步步搭建,所以库都使用了最新版本,让我们在踩坑中成长!【master 分支:完整版,不包含 typescript ;typescript-react 分支: 包含 typescript 的完整版本】

一、项目启动了解需求背景了解业务流程二、项目搭建初始化

本案例使用脚手架 create-react-app 初始化了项目。此脚手架有利有弊吧,项目目录结构简洁,不需要太关心 webpack 令人头疼的配置;弊端在于,脚手架确实有些庞大,构建时间在 4mins 左右。各位看官择优选择吧,也可以完全自己搭建一个项目。

设置淘宝镜像仓库

$ yarn config set registry registry.npm.taobao.org/ -g $ yarn config set sass_binary_site cdn.npm.taobao.org/dist/node-sass -g

工程目录 init

$ create-react-app qpj-web-pc --typescript $ tree -I "node_modules"

代码语言:javascript复制. |-- README.md |-- package.json |-- public | |-- favicon.ico | |-- index.html | |-- logo192.png | |-- logo512.png | |-- manifest.json | `-- robots.txt |-- src | |-- App.css | |-- App.test.tsx | |-- App.tsx | |-- index.css | |-- index.tsx | |-- logo.svg | |-- react-app-env.d.ts | |-- reportWebVitals.ts | `-- setupTests.ts `-- tsconfig.json yarn build 试试

$ yarn build & tree -I "node_modules"

代码语言:javascript复制. |-- README.md |-- build/ # 改造点(由于 `Jenkins` 构建打包脚本有可能已经写死了 `dist` 包名) |-- package.json |-- public | |-- favicon.ico | |-- index.html | |-- logo192.png | |-- logo512.png | |-- manifest.json | `-- robots.txt |-- src | |-- App.css | |-- App.test.tsx | |-- App.tsx | |-- index.css | |-- index.tsx | |-- logo.svg | |-- react-app-env.d.ts | |-- reportWebVitals.ts | `-- setupTests.ts `-- tsconfig.json 连接 git 远程仓库

$ git remote add origin yuanmin.zhu%40wetax.com.cn:[email protected]/front/qpj-web-pc.git

添加 .gitignore

$ echo -e " yarn.lock \n package-lock.json \n /dist \n .idea" >> .gitignore

添加 eslint 代码及提交评论校验

$ yarn add husky lint-staged @commitlint/cli @commitlint/config-conventional -D $ npx husky install $ npx husky add .husky/pre-commit "npx lint-staged" $ npx husky add .husky/prepare-commit-msg "npx commitlint -e"

项目根目录新建 commitlint.config.js代码语言:javascript复制// commitlint.config.js module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [ 2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'revert'], ], 'subject-full-stop': [0, 'never'], 'subject-case': [0, 'never'], }, } vscode 扩展中搜索 ESLint 并安装,项目根目录新建 .eslintrc.js,内容可参考文章配置:zhuanlan.zhihu.com/p/84329603 看第五点Commit message 格式说明

:

type 值枚举如下:feat: 添加新特性fix: 修复 bugdocs: 仅仅修改了文档style: 仅仅修改了空格、格式缩进、都好等等,不改变代码逻辑refactor: 代码重构,没有加新功能或者修复 bugperf: 增加代码进行性能测试test: 增加测试用例chore: 改变构建流程、或者增加依赖库、工具等revert: 当前 commit 用于撤销以前的 commitsubject 是 commit 目的的简短描述,不超过 50 个字符,且结尾不加句号(.)package.json 新加入如下配置:代码语言:javascript复制{ ..., "lint-staged": { "src/**/*.{jsx,txs,ts,js,json,css,md}": [ "eslint --quiet" ] }, } 可执行 npx eslint [filePath] \--fix 进行格式修复,无法修复的需手动解决三、项目配置一(功能配置)安装项目常用依赖库

$ yarn add antd axios dayjs qs -S # UI 库 及工具库 $ yarn add react-router-dom redux react-redux redux-logger redux-thunk -S # 路由及状态管理

webpack 配置拓展很有必要根目录新建 config-overrides.js,详细使用可访问:简书:React 之 config-overrides文件配置安装$ yarn add react-app-rewired customize-cra -D修改 package.json 中启动项代码语言:javascript复制// package.json "scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", } 使用代码语言:javascript复制// config-overrides.js const { override, // 主函数 fixBabelImports, // 配置按需加载 addWebpackExternals, // 不做打包处理配置 addWebpackAlias, // 配置别名 addLessLoader // lessLoader 配置,可更改主题色等 } = require('customize-cra') module.exports = override(/* ... */, config => config) 配置按需加载代码语言:javascript复制// config-overrides.js ... module.exports = override( fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', // library 目录 style: true, // 自动打包相关的样式 }), ) 更改主题色代码语言:javascript复制// config-overrides.js ... module.exports = override( addLessLoader({ lessOptions: { javascriptEnabled: true, modifyVars: { '@primary-color': '#1890ff', }, } }), ) 别名配置(typescript 项目这里有坑)代码语言:javascript复制// config-overrides.js const path = require('path') ... module.exports = override( addWebpackAlias({ '@': path.resolve(__dirname, 'src'), }), ) 去除注释、多进程打包压缩代码语言:javascript复制// config-overrides.js const UglifyJsPlugin = require('uglifyjs-webpack-plugin') const HardSourceWebpackPlugin = require('hard-source-webpack-plugin') ... module.exports = override(/* ... */, config => { config.plugins = [...config.plugins, { new UglifyJsPlugin({ uglifyOptions: { warnings: false, compress: { drop_debugger: true, drop_console: true, }, }, }), new HardSourceWebpackPlugin() }] return config }) 解决埋下的两个坑修改打包出的文件夹名为 dist代码语言:javascript复制// 修改打包路径除了output,这里也要修改 const paths = require('react-scripts/config/paths') paths.appBuild = path.join(path.dirname(paths.appBuild), 'dist') module.exports = override(/* ... */, config => { config.output.path = path.resolve(__dirname, 'dist') return config }) 解决 typescript 别名配置查阅相关资料,需要在 tsconfig.json 中添加一项配置代码语言:javascript复制{ ... "extends": "./paths.json" } 新建文件 paths.json代码语言:javascript复制{ "compilerOptions": { "baseUrl": "src", "paths": { "@/*": ["*"] } } } 配置装饰器写法代码语言:javascript复制{ "compilerOptions": { "experimentalDecorators": true, ... } } 配置开发代理在 src 目录新建 setupProxy.js代码语言:javascript复制// src/setupProxy.js const proxy = require('http-proxy-middleware').createProxyMiddleware module.exports = function(app) { // app 为 Express 实例,此处可以写 Mock 数据 app.use( proxy('/api', { "target": "https://qpj-test.fapiaoer.cn", "changeOrigin": true, "secure": false, // "pathRewrite": { // "^/api": "" // } }) ) } 加入 polyfill 和 antd 组件国际化处理代码语言:javascript复制// src/index.tsx import React from 'react' import ReactDOM from 'react-dom' // 注入 store import { Provider } from 'react-redux' import store from '@/store/store' import { ConfigProvider, Empty } from 'antd' import App from './App' import zhCN from 'antd/es/locale/zh_CN' import 'moment/locale/zh-cn' // polyfill import 'core-js/stable' import 'regenerator-runtime/runtime' ReactDOM.render( , document.getElementById('root') ) CSS Modules

create-react-app 自带支持以 xxx.module.(c|le|sa)ss 的样式表文件,使用上 typescript 项目中要注意:

代码语言:javascript复制const styles = require('./index.module.less') retrun ( record.id} pagination={false} /> ) 代码语言:javascript复制// index.module.less .container { padding: 24px; background-color: #fff; height: 100%; overflow: auto; .border-setting { tr { td:nth-child(3) { border-left: 1px solid #F0F0F0; border-right: 1px solid #F0F0F0; } } td { text-align: left !important; } } :global { // 这个标识之后,其子代元素可以不需要使用 `styles['type-check-box']` 的方式,直接写 `className` .type-check-box { .ant-checkbox-wrapper + .ant-checkbox-wrapper{ margin-left: 0; } } } } 【新】配置 React jsx 指令式属性 r-if、r-for、r-model、r-show,提升开发效率:安装依赖

$ yarn add babel-react-rif babel-react-rfor babel-react-rmodel babel-react-rshow -D

配置 .babelrc :代码语言:javascript复制// .babelrc { ..., "plugins": [ "babel-react-rif", "babel-react-rfor", "babel-react-rmodel", "babel-react-rshow", ] } 使用示例:r-if代码语言:javascript复制 good 180}>best other r-for代码语言:javascript复制{/* eslint-disable-next-line no-undef */} 内容 {item + '-' + index} r-model代码语言:javascript复制 r-show代码语言:javascript复制内容 # 注意:这是 `r-if` 的效果,不会渲染节点 四、项目配置二(优化配置)实现组件懒加载 react-loadable代码语言:javascript复制import Loadable from 'react-loadable' const Loading = (props: any) => { if (props.error) { console.error(props.error) return Error! Retry } else if (props.timedOut) { return Timeout! Retry } else if (props.pastDelay) { return Loading... } else { return null } } const loadable = (path: any) => { return Loadable({ loader: () => import(`@/pages${path}`), loading: Loading, delay: 200, timeout: 10000, }) } const Home = loadable('/homePage/Home') 处理 axios 拦截响应代码语言:javascript复制const service = axios.create({ baseURL: '/', timeout: 15000, }) service.interceptors.request.use(function (config) { return config }) service.interceptors.response.use(function (config) { return config }) 处理 React router 的嵌套配置

我们知道 React 中不支持类似 Vue Router 路由配置方式,React 中一切皆组件,路由也是组件,需要用到路由要临时加上路由组件,写起来就很繁琐,但我们可以自己实现路由配置表方式。

代码语言:javascript复制// router/router.config.ts const routes = [ { path: '/home', component: loadable('components/Index'), exact: true, }, { path: '/new', component: loadable('components/New'), redirect: '/new/list', // exact: true, routes: [ { path: '/new/list', component: loadable('components/NewList'), exact: true, }, { path: '/new/content', component: loadable('components/NewContent'), exact: true, }, ], }, ] export default routes 代码语言:javascript复制// router/router.ts import React from 'react' import { Switch, BrowserRouter as Router, Route } from 'react-router-dom' import routes from './index' function mapRoutes(routes: any[], store: object): any { return routes.map((item: any, index: number) => { return ( { const NewComp = item.component Object.assign(props, { redirect: item.redirect || null, permission: item.permission || [], ...store }) if (item.routes) { return { mapRoutes(item.routes, store) } } else { return } }} /> ) }) } const Routes = (props: any) => { return ( { mapRoutes(routes, props.store) } (404 Page not Found!)} /> ) } export default Routes

子路由承载页面需要加上如下代码:

代码语言:javascript复制import { Redirect, Route, Switch } from 'react-router-dom' {props.children} (404 Page not Found!)} /> {props.redirect && } 处理 React store代码语言:javascript复制// store/store.ts import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' import logger from 'redux-logger' import reducers from './reducer' const store = process.env.NODE_ENV === 'development' ? createStore(reducers, applyMiddleware(thunk, logger)) : createStore(reducers, applyMiddleware(thunk)) export default store

为了方便使用,避免每个组件都需要 connect ,这边实现了 redux store 的全局注入,但是如果项目庞大的话就会损耗些性能。

代码语言:javascript复制// App.tsx import { dispatchActions } from '@/store/reducer' export default connect((state: any) => ({ store: state }), dispatchActions)(App) 五、总结

自此项目搭建就全部完成了,剩下的就是业务代码了。相信你可以得到如下收获: ① 项目构建在宏观上有个极大的能力提升; ② 项目整体功能了解清晰; ③ 排查问题不慌乱; ④ 封装能力有加强; ⑤ 业务功能很清楚。

六、题外话

基于 create-react-app 创建的 React 项目,本人实现了一个脚手架,以上配置默认已经全部加入实现,欢迎 Github 试用并 star 。链接:https://github.com/zhuyuanmin/zym-cli



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有