最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 从 0 搭建一个前端项目,我们要做什么?

    正文概述 掘金(前端小猿_zym)   2021-04-22   565

    一、项目启动

    1. 了解需求背景

    2. 了解业务流程

    二、项目搭建初始化

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

    1. 设置淘宝镜像仓库

    2. 工程目录 init

      .
      |-- 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
      
    3. yarn build 试试

      .
      |-- 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
      
    4. 连接 git 远程仓库

    5. 添加 .gitignore

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

      • 项目根目录新建 commitlint.config.js

        // 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: 修复 bug
          • docs: 仅仅修改了文档
          • style: 仅仅修改了空格、格式缩进、都好等等,不改变代码逻辑
          • refactor: 代码重构,没有加新功能或者修复 bug
          • perf: 增加代码进行性能测试
          • test: 增加测试用例
          • chore: 改变构建流程、或者增加依赖库、工具等
          • revert: 当前 commit 用于撤销以前的 commit
        • subject 是 commit 目的的简短描述,不超过 50 个字符,且结尾不加句号(.)

      • package.json 新加入如下配置:

        {
            ...,
            "husky": {
                "hooks": {
                    "pre-commit": "lint-staged"
                }
            },
            "lint-staged": {
                "src/**/*.{jsx,txs,ts,js,json,css,md}": [
                    "eslint"
                ]
            },
        }
        
      • 可执行 npx eslint [filePath] --fix 进行格式修复,无法修复的需手动解决

    三、项目配置一(功能配置)

    1. 安装项目常用依赖库

    2. webpack 配置拓展很有必要

      • 根目录新建 config-overrides.js,详细使用可访问:简书:React 之 config-overrides文件配置
      • 安装
      • 修改 package.json 中启动项
        // package.json
        "scripts": {
            "start": "react-app-rewired start",
            "build": "react-app-rewired build",
        }
        
      • 使用
        // config-overrides.js
        const {
            override, // 主函数
            fixBabelImports, // 配置按需加载
            addWebpackExternals, // 不做打包处理配置
            addWebpackAlias, // 配置别名
            addLessLoader // lessLoader 配置,可更改主题色等
        } = require('customize-cra')
        module.exports = override(/* ... */, config => config)
        
    3. 配置按需加载

      // config-overrides.js
      ...
      module.exports = override(
          fixBabelImports('import', {
              libraryName: 'antd',
              libraryDirectory: 'es', // library 目录
              style: true, // 自动打包相关的样式
          }),
      )
      
    4. 更改主题色

      // config-overrides.js
      ...
      module.exports = override(
          addLessLoader({
              lessOptions: {
                  javascriptEnabled: true,
                  modifyVars: {
                      '@primary-color': '#1890ff',
                  },
              }
          }),
      )
      
    5. 别名配置(typescript 项目这里有坑)

      // config-overrides.js
      const path = require('path')
      ...
      module.exports = override(
          addWebpackAlias({
              '@': path.resolve(__dirname, 'src'),
          }),
      )
      
    6. 去除注释、多进程打包压缩

      // 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
      })
      
    7. 解决埋下的两个坑

      • 修改打包出的文件夹名为 dist
        // 修改打包路径除了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 中添加一项配置
          {
              ...
              "extends": "./paths.json"
          }
          
        • 新建文件 paths.json
          {
              "compilerOptions": {
                  "baseUrl": "src",
                  "paths": {
                      "@/*": ["*"]
                  }
              }
          }
          
    8. 配置装饰器写法

      {
          "compilerOptions": {
              "experimentalDecorators": true,
              ...
          }
      }
      
    9. 配置开发代理

      • src 目录新建 setupProxy.js
        // 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": ""
                    // }
                })
            )
        }
        
    10. 加入 polyfillantd 组件国际化处理

      // 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(
          <Provider store={store}>
              <ConfigProvider locale={zhCN} renderEmpty={Empty}>
              <App />
              </ConfigProvider>
          </Provider>,
          document.getElementById('root')
      )
      

    四、项目配置二(优化配置)

    1. 实现组件懒加载 react-loadable
      import Loadable from 'react-loadable'
      
      const Loading = (props: any) => {
          if (props.error) {
              console.error(props.error)
              return <div>Error! <Button type="link" onClick={props.retry}>Retry</Button></div>
          } else if (props.timedOut) {
              return <div>Timeout! <Button onClick={props.retry}>Retry</Button></div>
          } else if (props.pastDelay) {
              return <div>Loading...</div>
          } else {
              return null
          }
      }
      
      const loadable = (path: any) => {
          return Loadable({
              loader: () => import(`@/pages${path}`),
              loading: Loading,
              delay: 200,
              timeout: 10000,
          })
      }
      
      const Home = loadable('/homePage/Home')
      
      
    2. 处理 axios 拦截响应
      const service = axios.create({
          baseURL: '/',
          timeout: 15000,
      })
      
      service.interceptors.request.use(function (config) {
          return config
      })
      
      service.interceptors.response.use(function (config) {
          return config
      })
      
    3. 处理 React router 的嵌套配置
      // 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
      
      // 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 (
                  <Route exact={item.exact || false} path={item.path} key={index} render={props => {
                      const NewComp = item.component
                      Object.assign(props, {
                          redirect: item.redirect || null,
                          permission: item.permission || [],
                          ...store
                      })
                      if (item.routes) {
                          return <NewComp {...props}>{ mapRoutes(item.routes, store) }</NewComp>
                      } else {
                          return <NewComp {...props} />
                      }
                  }} />
              )
          })
      }
      
      const Routes = (props: any) => {
          return (
              <Router>
                  <Switch>
                      { mapRoutes(routes, props.store) }
                      <Route component={() => (<div>404 Page not Found!</div>)} />
                  </Switch>
              </Router>
          )
      }
      
      export default Routes
      
      import { Redirect, Route, Switch } from 'react-router-dom'
      
      <Switch>
          {props.children}
          <Route component={() => (<div>404 Page not Found!</div>)} />
          {props.redirect && <Redirect to={props.redirect} />}
      </Switch>
      
    4. 处理 React store
      // 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
      
      // App.tsx
      import { dispatchActions } from '@/store/reducer'
      
      export default connect((state: any) => ({ store: state }), dispatchActions)(App)
      

    五、总结

    六、题外话


    起源地下载网 » 从 0 搭建一个前端项目,我们要做什么?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元