CRA+react-app-rewired搭建reactH5架子

10,267 阅读8分钟

这是我参与更文挑战的第1天

前言

  • 这篇文章主要通过 create-react-app 脚手架配合 react-app-rewired 添加一些额外的webpack配置搭建一个基于vw的H5架子

开始

1、安装

    npx create-react-app juejin-react-h5(你的项目名称)

2、接下来我们安装下相关依赖

    // 安装基本UI库andt-mobile
    yarn add antd-mobile
    // 安装 react-app-rewired customize-cra 用来添加webpack相关配置,
    // 这俩个包应为不用打包在项目中,所以只需要安装在开发环境就好了
    yarn add react-app-rewired customize-cra -D
    // babel-plugin-import这个是分包工具
    yarn add babel-plugin-import -D

3、修改package.json启动项

     {
        "start": "react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test --env=jsdom",
        "eject": "react-scripts eject"
      }

4、在项目根目录创建一个 config-overrides.js 用于修改默认配置

const { override, fixBabelImports }  = require("customize-cra")

module.exports = override(
    fixBabelImports("import", {
      libraryName: 'antd-mobile',
      style: 'css',
    })
)

5、修改app.js

import { Button } from 'antd-mobile'

function App() {
  return (
    <div className="App">
      <Button type="primary">按钮</Button>
    </div>
  );
}

export default App;

6、启动项目,并访问localhost:3000

 yarn start

7、打开页面调成手机开发模式就可以看到效果图了

tu1.png

8、接下来我们安装css预编译工具(这里选择sass)

yarn add sass

9、接着我们在src下新建一个app.scss文件,并写入一些测试css

.p{
    color: red;
    width: 750px;
}

10、最后在app.js引入app.scss,并重新启动项目

当我们看到这个效果图的时候,sass也就引入成功了

sass.png

11、配置sass全局变量

    //先安装一个loader 和 path
    yarn add sass-resources-loader path -D
    const resolve = _path => path.resolve(__dirname, _path)
    module.exports = override(
        fixBabelImports('import', {
            libraryName: 'antd-mobile',
            style: 'css',
        }),
        adjustStyleLoaders(rule => {
            if (rule.test.toString().includes('scss')) {
              rule.use.push({
                loader: require.resolve('sass-resources-loader'),
                options: {
                  resources: [resolve("./src/styles/theme.scss")]
                }
              });
            }})
           )
  • 接下来我们创建theme.scss文件,写入一个颜色变量
    $red: red;
  • 我们在app.scss中使用就行了
    .p{
        color: $red;
        width: 750px;
    }
  • 效果依旧没问题 sass.png

12、下面我们就把单位px转换成vw

    //安装 postcss-px-to-viewport
    yarn add postcss-px-to-viewport -D

13、下面我们就修改该config-overrides.js中的代码

const { override, fixBabelImports, addPostcssPlugins }  = require("customize-cra")

module.exports = override(
    fixBabelImports('import', {
        libraryName: 'antd-mobile',
        style: 'css',
    }),
    adjustStyleLoaders(rule => {
            if (rule.test.toString().includes('scss')) {
              rule.use.push({
                loader: require.resolve('sass-resources-loader'),
                options: {
                  resources: [resolve("./src/styles/theme.scss")]
                }
              });
            }}),
    addPostcssPlugins([
        require("postcss-px-to-viewport")({
            unitToConvert: 'px',  // 需要转换的单位,默认‘px’
            viewportWidth: 750,   // 设计稿的视口宽度
            unitPrecision: 6,     // px转vw之后保留的精度(保留几位小数)
            propList: ['*'],      // 那些属性可以被转换成vw * 代表全部
            viewportUnit: 'vw',   // 希望使用的视口单位 // vw
            fontViewportUnit: 'vw', //字体使用的视口单位
            selectorBlackList: [],  // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位
            minPixelValue: 1,       // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
            mediaQuery: false,      // 媒体查询里的单位是否需要转换单位
            replace: true,          // 是否直接更换属性值,而不添加备用属性
            exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
            landscape: false,       // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
            landscapeUnit: 'vw'     // 横屏时使用的单位
        })
    ])
)

14、当我们再次启动项目就可以看到p标签中的750px就已经被改成了100vw(因为我们的视口宽度设置的就是750)

image.png

15、添加Lodash工具库,实现按需加载

    yarn add lodash
    // config-overrides.js 中添加 lodash 按需加载
    fixBabelImports("lodash", {
        libraryDirectory: "",
        camel2DashComponentName: false
    })

16、添加@别名

  • 在vue中我们经常会写@/xx/xx,在CRA中这样配置即可
    
    const path = require("path")
    const { override, fixBabelImports, addPostcssPlugins, addWebpackAlias }  = require("customize-cra")
    const resolve = dir => path.resolve(__dirname, dir)
    module.exports = override(
    fixBabelImports('import', {
        libraryName: 'antd-mobile',
        style: 'css',
    }),
        addWebpackAlias({
            ["@"]: resolve("./src")
        }),
        addPostcssPlugins([
            require("postcss-px-to-viewport")({
                unitToConvert: 'px',  // 需要转换的单位,默认‘px’
                viewportWidth: 750,   // 设计稿的视口宽度
                unitPrecision: 6,     // px转vw之后保留的精度(保留几位小数)
                propList: ['*'],      // 那些属性可以被转换成vw * 代表全部
                viewportUnit: 'vw',   // 希望使用的视口单位 // vw
                fontViewportUnit: 'vw', //字体使用的视口单位
                selectorBlackList: [],  // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位
                minPixelValue: 1,       // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
                mediaQuery: false,      // 媒体查询里的单位是否需要转换单位
                replace: true,          // 是否直接更换属性值,而不添加备用属性
                exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
                landscape: false,       // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
                landscapeUnit: 'vw'     // 横屏时使用的单位
            })
        ])
)

17、处理跨域

  • 在我们和后端小伙伴联调接口的过程中,会存在跨域问题,CRA的配置如下
const path = require("path");

const { override,
    fixBabelImports,
    addWebpackAlias,
    addPostcssPlugins,
    overrideDevServer } = require('customize-cra');

const resolve = _path => path.resolve(__dirname, _path)

module.exports = {
    webpack: override(
        fixBabelImports('import', {
            libraryName: 'antd-mobile',
            style: 'css',
        }),
        fixBabelImports("lodash", {
            libraryDirectory: "",
            camel2DashComponentName: false
        }),
        addWebpackAlias({
            ['@']: resolve("./src")
        }),
        addPostcssPlugins([
            require("postcss-px-to-viewport")({
                unitToConvert: 'px',  // 需要转换的单位,默认‘px’
                viewportWidth: 750,   // 设计稿的视口宽度
                unitPrecision: 6,     // px转vw之后保留的精度(保留几位小数)
                propList: ['*'],      // 那些属性可以被转换成vw * 代表全部
                viewportUnit: 'vw',   // 希望使用的视口单位 // vw
                fontViewportUnit: 'vw', //字体使用的视口单位
                selectorBlackList: [],  // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位
                minPixelValue: 1,       // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
                mediaQuery: false,      // 媒体查询里的单位是否需要转换单位
                replace: true,          // 是否直接更换属性值,而不添加备用属性
                exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
                landscape: false,       // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
                landscapeUnit: 'vw'     // 横屏时使用的单位
            })
        ])
    ),

    devServer: overrideDevServer([
        config => ({
            ...config,
            proxy: {
                "/api": {
                    target: "http://localhost:4444",
                    pathRewrite: { "^/api": "" }
                }
            }
        })
    ]),
}
  • 这个时候当我们访问/api开头的接口时,请求地址就会被代理到 http://localhost:4444
  • 🌰 /api/test 会被 代理成 http://localhost:4444/test

18、真机问题

  • 首先修改我们的app.jsapp.scss
  • 我们添加了50个p标签到页面中,用来测试
    import { Button } from 'antd-mobile'
    import '@/app.scss'

    function App() {
      return (
        <div className="App">
          {
            Array(50).fill(1).map((item, idx) => (<p className="p" key={idx}>一段自定义的css样式文字,第{idx}条</p>))
          }
          <Button type="primary">按钮</Button>
        </div>
      );
    }

    export default App;
  • 接着我们清除p标签边距
.p{
    color: red;
    width: 750px;
    margin: 0;
    padding: 0;
}
  • 接下来我们用手机连上电脑对应的wifi,访问电脑的ip,
    输入 ifconfig

image.png

  • 然后用手机打开 http://10.5.9.250:3000/,就可以看到我们的页面了

image.png

  • 这时我们看到手机(iphoneX)按钮被底边小黑条挡住了
  • 解决方法
    • 找到 public/index.html mate标签中添加 viewport-fit=cover属性 <meta name="viewport" content="width=device-width, initial-scale=1,viewport-fit=cover" />
    • body添加这样的css
      body {
          padding-bottom: constant(safe-area-inset-bottom);
          padding-bottom: env(safe-area-inset-bottom);
        }
    
  • 然后我们重启项目,真机上再次查看

image.png

  • 这个时候底部小黑条就不会挡住按钮了

19、完整代码

  • package.json
{
  "name": "juejin-react-h5",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "antd-mobile": "^2.3.4",
    "path": "^0.12.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "sass": "^1.34.1",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "babel-plugin-import": "^1.13.3",
    "customize-cra": "^1.0.0",
    "postcss-px-to-viewport": "^1.1.1",
    "react-app-rewired": "^2.1.8",
    "sass-resources-loader": "^2.2.1"
  }
}

  • config-overrides.js
const path = require("path");

const { override,
    fixBabelImports,
    addWebpackAlias,
    addPostcssPlugins,
    adjustStyleLoaders,
    overrideDevServer } = require('customize-cra');

const resolve = _path => path.resolve(__dirname, _path)

module.exports = {
    webpack: override(
        // antd-mobile 分包
        fixBabelImports('import', {
            libraryName: 'antd-mobile',
            style: 'css',
        }),
        // lodash分包
        fixBabelImports("lodash", {
            libraryDirectory: "",
            camel2DashComponentName: false
        }),
        //别名
        addWebpackAlias({
            ['@']: resolve("./src")
        }),
        // 添加loader 全局css
        adjustStyleLoaders(rule => {
            if (rule.test.toString().includes('scss')) {
              rule.use.push({
                loader: require.resolve('sass-resources-loader'),
                options: {
                  resources: [resolve("./src/styles/theme.scss")]
                }
              });
            }}),
            // px转 vw
        addPostcssPlugins([
            require("postcss-px-to-viewport")({
                unitToConvert: 'px',  // 需要转换的单位,默认‘px’
                viewportWidth: 750,   // 设计稿的视口宽度
                unitPrecision: 6,     // px转vw之后保留的精度(保留几位小数)
                propList: ['*'],      // 那些属性可以被转换成vw * 代表全部
                viewportUnit: 'vw',   // 希望使用的视口单位 // vw
                fontViewportUnit: 'vw', //字体使用的视口单位
                selectorBlackList: [],  // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位
                minPixelValue: 1,       // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
                mediaQuery: false,      // 媒体查询里的单位是否需要转换单位
                replace: true,          // 是否直接更换属性值,而不添加备用属性
                exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
                landscape: false,       // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
                landscapeUnit: 'vw'     // 横屏时使用的单位
            })
        ])
    ),
// 本地开发相关
    devServer: overrideDevServer([
        config => ({
            ...config,
            proxy: {
                "/api": {
                    target: "http://localhost:4444",
                    pathRewrite: { "^/api": "" }
                }
            }
        })
    ]),
}

20、项目地址 github.com/Tyf2345/jue…

参考文献

写在最后

  • 当你看到这里的时候,首先你是个很有毅力的人,这篇文章没有插图,都是干活,从头看到尾的话给自己点个赞吧
  • 这篇文章主要搭建了一个react H5的架子,从布局到样式再到接口转发,基本功能都已经搭建完毕了
  • 欢迎大家评论,指出不完善的地方