zoukankan      html  css  js  c++  java
  • 博客管理系统开发 -- 构建React开发环境

    一、开发环境

    1、开发环境

    • windows 10操作系统;
    • Node.js v10.16.0;
    • webstorm 2019.3.4 x64;

    2、前端技术栈

    • react v16.13.1 hooks + redux v7.2  + react-router v5.1.2;
    • antd v4.1.0;
    • marked hightlight.js;
    • webpach打包优化;
    • axios封装;

    3、后端技术栈

    • spring boot;
    • mybatis-plus;
    • swagger;
    • spring security;

    二、前端知识回顾

    1、基础学习

    如果没有接触过react的话,推荐先学习一下react基础知识:

    1、react的入门教学视频入口:React 入门教程(开发文档)

    2、官方教程入口:入门教程: 认识 React

    3、redux教程:从零实现一个 redux

    2、命名规范

    html标签:小写字符开始;

    自定义React组件:大写字符开始;

    其它变量、方法、函数:驼峰命名法;

    文件夹、文件命名:全部小写,中间使用_分割,如data_assets;

    三、npm使用

    1、npm介绍

    npm是随同Node.js一起安装的包管理工具,能解决Node.js代码部署上的很多问题,常见的使用场景有以下几种:

    • 允许用户从npm服务器下载别人编写的第三方包到本地使用;
    • 允许用户从npm服务器下载并安装别人编写的命令行程序到本地使用;
    • 允许用户将自己编写的包或命令行程序上传到npm服务器供别人使用;

    由于新版的Node.js已经集成了npm,所以npm也一并安装好了。同样可以通过输入如下命令 来测试是否成功安装。命令如下,出现版本提示表示安装成功:

    npm -v

    2、npm使用

    如果你安装的是旧版本的 npm,可以很容易得通过 npm 命令来升级,命令如下:

    npm install npm -g

    由于npm安装较慢,我们可以使用淘宝镜像的命令:

    npm install -g cnpm --registry=https://registry.npm.taobao.org

    这样就可以使用 cnpm 命令来安装模块了:

    npm 安装 Node.js 模块语法格式如下:

    npm install <Module Name>

    以下实例,我们使用 npm 命令安装常用的 Node.js web框架模块 express:

    npm install express

    安装好之后,express 包就放在了工程目录下的 node_modules 目录中,因此在代码中只需要通过 require('express') 的方式就好,无需指定第三方包路径。

    var express = require('express');

    3、全局安装与本地安装

    npm 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有-g而已,比如:

    npm install express          # 本地安装
    npm install express -g      # 全局安装

    本地安装

    • 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录;
    • 可以通过 require() 来引入本地安装的包;

    全局安装

    • npm 全局模块的存放路径以及cache的路径的配置,默认是在 C 盘 "C:Users用户" 下;
    • 可以直接在命令行里使用;

    我们可以指定全局模块以及cache路径:

    (1).在 nodejs 安装目录下,创建 ”node_global” 和 ”node_cache” 两个文件夹;

    (2). 进入 cmd 命令行,输入如下命令:

    npm config set prefix "D:
    odejs
    ode_global"
    npm config set cache "D:
    odejs
    ode_cache"

    设置全局模块的安装路径到 "node_global" 文件夹, 设置缓存到 "node_cache" 文件夹;

    (3).由于 node 全局模块大多数都是可以通过命令行访问的,还要把 “D: odejs ode_global” 加入到系统环境变量 PATH 中,方便直接使用命令行运行;

    查看安装信息

    你可以使用以下命令来查看所有全局安装的模块:

    npm list -g

    4、使用 package.json

    package.json 位于模块的目录下,用于定义包的属性。接下来让我们来看下 express 包的 package.json 文件,位于 node_modules/express/package.json 内容:

    {
      "name": "express",
      "description": "Fast, unopinionated, minimalist web framework",
      "version": "4.13.3",
      "author": {
        "name": "TJ Holowaychuk",
        "email": "tj@vision-media.ca"
      },
      "contributors": [
        {
          "name": "Aaron Heckmann",
          "email": "aaron.heckmann+github@gmail.com"
        },
        {
          "name": "Ciaran Jessup",
          "email": "ciaranj@gmail.com"
        },
        {
          "name": "Douglas Christopher Wilson",
          "email": "doug@somethingdoug.com"
        },
        {
          "name": "Guillermo Rauch",
          "email": "rauchg@gmail.com"
        },
        {
          "name": "Jonathan Ong",
          "email": "me@jongleberry.com"
        },
        {
          "name": "Roman Shtylman",
          "email": "shtylman+expressjs@gmail.com"
        },
        {
          "name": "Young Jae Sim",
          "email": "hanul@hanul.me"
        }
      ],
      "license": "MIT",
      "repository": {
        "type": "git",
        "url": "git+https://github.com/strongloop/express.git"
      },
      "homepage": "http://expressjs.com/",
      "keywords": [
        "express",
        "framework",
        "sinatra",
        "web",
        "rest",
        "restful",
        "router",
        "app",
        "api"
      ],
      "dependencies": {
        "accepts": "~1.2.12",
        "array-flatten": "1.1.1",
        "content-disposition": "0.5.0",
        "content-type": "~1.0.1",
        "cookie": "0.1.3",
        "cookie-signature": "1.0.6",
        "debug": "~2.2.0",
        "depd": "~1.0.1",
        "escape-html": "1.0.2",
        "etag": "~1.7.0",
        "finalhandler": "0.4.0",
        "fresh": "0.3.0",
        "merge-descriptors": "1.0.0",
        "methods": "~1.1.1",
        "on-finished": "~2.3.0",
        "parseurl": "~1.3.0",
        "path-to-regexp": "0.1.7",
        "proxy-addr": "~1.0.8",
        "qs": "4.0.0",
        "range-parser": "~1.0.2",
        "send": "0.13.0",
        "serve-static": "~1.10.0",
        "type-is": "~1.6.6",
        "utils-merge": "1.0.0",
        "vary": "~1.0.1"
      },
      "devDependencies": {
        "after": "0.8.1",
        "ejs": "2.3.3",
        "istanbul": "0.3.17",
        "marked": "0.3.5",
        "mocha": "2.2.5",
        "should": "7.0.2",
        "supertest": "1.0.1",
        "body-parser": "~1.13.3",
        "connect-redis": "~2.4.1",
        "cookie-parser": "~1.3.5",
        "cookie-session": "~1.2.0",
        "express-session": "~1.11.3",
        "jade": "~1.11.0",
        "method-override": "~2.3.5",
        "morgan": "~1.6.1",
        "multiparty": "~4.1.2",
        "vhost": "~3.0.1"
      },
      "engines": {
        "node": ">= 0.10.0"
      },
      "files": [
        "LICENSE",
        "History.md",
        "Readme.md",
        "index.js",
        "lib/"
      ],
      "scripts": {
        "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
        "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
        "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
        "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
      },
      "gitHead": "ef7ad681b245fba023843ce94f6bcb8e275bbb8e",
      "bugs": {
        "url": "https://github.com/strongloop/express/issues"
      },
      "_id": "express@4.13.3",
      "_shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3",
      "_from": "express@*",
      "_npmVersion": "1.4.28",
      "_npmUser": {
        "name": "dougwilson",
        "email": "doug@somethingdoug.com"
      },
      "maintainers": [
        {
          "name": "tjholowaychuk",
          "email": "tj@vision-media.ca"
        },
        {
          "name": "jongleberry",
          "email": "jonathanrichardong@gmail.com"
        },
        {
          "name": "dougwilson",
          "email": "doug@somethingdoug.com"
        },
        {
          "name": "rfeng",
          "email": "enjoyjava@gmail.com"
        },
        {
          "name": "aredridel",
          "email": "aredridel@dinhe.net"
        },
        {
          "name": "strongloop",
          "email": "callback@strongloop.com"
        },
        {
          "name": "defunctzombie",
          "email": "shtylman@gmail.com"
        }
      ],
      "dist": {
        "shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3",
        "tarball": "http://registry.npmjs.org/express/-/express-4.13.3.tgz"
      },
      "directories": {},
      "_resolved": "https://registry.npmjs.org/express/-/express-4.13.3.tgz",
      "readme": "ERROR: No README data found!"
    }
    View Code

    Package.json 属性说明

    • name - 包名;

    • version - 包的版本号;

    • description - 包的描述;

    • homepage - 包的官网 url ;

    • author - 包的作者姓名;

    • contributors - 包的其他贡献者姓名;

    • dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下;

    • repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上;

    • main - main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js;

    • keywords - 关键字;

    5、卸载模块

    我们可以使用以下命令来卸载 Node.js 模块:

    npm uninstall express

    6、更新模块

    我们可以使用以下命令更新模块:

    npm update express

    7、搜索模块

    使用以下来搜索模块,会打开浏览器查看存在的模块版本:

    npm search express

    四、快速构建React开发环境

    1、全局安装create-react-app脚手架工具

    cnpm install -g create-react-app

    create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。 create-react-app 自动创建的项目是基于 Webpack + ES6 。

    2、创建新的react项目

    create-react-app react-blog-zy

    此时会在当前工作路径创建一个文件夹react-blog-zy:

    • node_modules:用于存放项目的依赖包,也就是构建这个React项目可能会用到的工具;
    • public:文件夹是 index.html文件的存放目录;
    • src:用于存放js文件,也就是项目开发中的主要区域;
    • package.json:用于记录项目信息,以及外部依赖包的导入信息等;

    跳转到新建项目目录下:

    cd react-blog-zy

     运行项目:

     npm start

    可以看到项目启动成功,打开网页 http://localhost:3000

    3、运行npm run eject

    使用npm run eject 可以看到webpack.config配置信息,此时会多出两个文件夹:

    config文件夹:

    • env.js :处理.env环境变量配置文件;
    • path.js:提供各种路径;
    • webpack.config.js: webpack配置文件;
    • webpackDevServer.config.js:测试服务器配置文件;

    env.js用来处理.env文件中配置的环境变量:

    const NODE_ENV = process.env.NODE_ENV;
    if (!NODE_ENV) {
      throw new Error(
        'The NODE_ENV environment variable is required but was not specified.'
      );
    }
    
    // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
    const dotenvFiles = [
      `${paths.dotenv}.${NODE_ENV}.local`,
      `${paths.dotenv}.${NODE_ENV}`,
      // Don't include `.env.local` for `test` environment
      // since normally you expect tests to produce the same
      // results for everyone
      NODE_ENV !== 'test' && `${paths.dotenv}.local`,
      paths.dotenv,
    ].filter(Boolean);
    
    // Load environment variables from .env* files. Suppress warnings using silent
    // if this file is missing. dotenv will never modify any environment variables
    // that have already been set.  Variable expansion is supported in .env files.
    // https://github.com/motdotla/dotenv
    // https://github.com/motdotla/dotenv-expand
    dotenvFiles.forEach(dotenvFile => {
      if (fs.existsSync(dotenvFile)) {
        require('dotenv-expand')(
          require('dotenv').config({
            path: dotenvFile,
          })
        );
      }
    });
    NODE_ENV获取node运行环境,当我们运行npm start时,运行的是scripts/start.js脚本,在该脚本中指定了运行环境为development:
    // Do this as the first thing so that any code reading it knows the right env.
    process.env.BABEL_ENV = 'development';
    process.env.NODE_ENV = 'development';

    dotenvFiles 是一个数组,保存要扫描的.env*文件,然后遍历每个文件,从这些文件中加载环境变量;

    scripts文件夹:

    • build.js:打包脚本;
    • start.js: 启动脚本;
    • test.js: 测试脚本;

    我们可以通过修改脚本,指定项目运行在其它端口:

    // Tools like Cloud9 rely on this.
    const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
    const HOST = process.env.HOST || '0.0.0.0';

    4、项目结构调整

    删除src、public目录下所有文件:

    1、在public下新建index.html文件:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="theme-color" content="#000000" />
        <meta
          name="description"
          content="Web site created using create-react-app"
        />
        <!--
          manifest.json provides metadata used when your web app is installed on a
          user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
        -->
        <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
        <!--
          Notice the use of %PUBLIC_URL% in the tags above.
          It will be replaced with the URL of the `public` folder during the build.
          Only files inside the `public` folder can be referenced from the HTML.
    
          Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
          work correctly both with client-side routing and a non-root public URL.
          Learn how to configure a non-root public URL by running `npm run build`.
        -->
        <title>zy的博客</title>
    
        <!-- SEO -->
        <meta name="keywords" content="zy的博客">
        <meta name="keywords" content="zy">
        <meta name="description" content="前端开发爱好者">
        <meta property="og:type" content="website">
        <meta property="og:title" content="zy的博客">
        <meta property="og:url" content="https://www.cnblogs.com/zyly/">
        <meta property="og:site_name" content="zy的博客">
        <meta property="og:description" content="前端开发爱好者">
        <meta name="twitter:card" content="summary">
        <meta name="twitter:title" content="zy的博客">
        <meta name="twitter:description" content="前端开发爱好者">
    
        <!-- 禁用 移动端缩放 -->
        <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
    
        <!-- md 代码高亮 -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/highlight.min.js"></script>
    
      </head>
      <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
        <!--
          This HTML file is a template.
          If you open it directly in the browser, you will see an empty page.
    
          You can add webfonts, meta tags, or analytics to this file.
          The build step will place the bundled scripts into the <body> tag.
    
          To begin the development, run `npm start` or `yarn start`.
          To create a production bundle, use `npm run build` or `yarn build`.
        -->
      </body>
    </html>

    2、在public下新建favicon.ico文件:

    该文件就是一个icn图标;

    3、在public下新建manifest.json文件:

    {
      "short_name": "React App",
      "name": "Create React App Sample",
      "icons": [
        {
          "src": "favicon.ico",
          "sizes": "64x64 32x32 24x24 16x16",
          "type": "image/x-icon"
        }
      ],
      "start_url": ".",
      "display": "standalone",
      "theme_color": "#000000",
      "background_color": "#ffffff"
    }

    4、在src下新建App.js文件:

    import React from 'react';
    
    export default function App(props){
        return (
            <div>
                <h1>博客</h1>
            </div>
        )
    }

    5、在src下新建index.js文件:

    import React from 'react'
    import ReactDOM from 'react-dom'
    import App from './App'
    
    ReactDOM.render(
        <App/>,
        document.getElementById('root')
    )

    该文件也是该项目的默认入口文件,我们可以在webpack.config.js文件中看到:

    // These are the "entry points" to our application.
        // This means they will be the "root" imports that are included in JS bundle.
        entry: [
          // Include an alternative client for WebpackDevServer. A client's job is to
          // connect to WebpackDevServer by a socket and get notified about changes.
          // When you save a file, the client will either apply hot updates (in case
          // of CSS changes), or refresh the page (in case of JS changes). When you
          // make a syntax error, this client will display a syntax error overlay.
          // Note: instead of the default WebpackDevServer client, we use a custom one
          // to bring better experience for Create React App users. You can replace
          // the line below with these two lines if you prefer the stock client:
          // require.resolve('webpack-dev-server/client') + '?/',
          // require.resolve('webpack/hot/dev-server'),
          isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
          // Finally, this is your app's code:
          paths.appIndexJs
          // We include the app code last so that if there is a runtime error during
          // initialization, it doesn't blow up the WebpackDevServer client, and
          // changing JS code would still trigger a refresh.
        ].filter(Boolean),

     paths.appIndexJs指向的正是src/index.html文件;

    此时我们运行程序就可以看到:

    由于create-creat-app创建的项目,在webpack.config.js中配置了webpack-dev-server热加载:

    可以看到如果当前运行在开发环境下,我们项目将会使用自定义的webpackDevServer客户端,当我们文件发生变化时,该客户端将会通知WebpackDevServer服务器,然后进行页面刷新。

    我们可以实时改变App组件输出的内容,然后查看浏览器的变化。此外,如果我们想使用默认的WebpackDevServer客户端,可以取消下面代码注释:

    require.resolve('webpack-dev-server/client') + '?/',
    require.resolve('webpack/hot/dev-server'),

    这种页面的刷新有个缺点,每次刷新都会清空我们组件的状态值。因此,我们需要使用局部热刷新技术,这个后面我们会介绍 react-hot-loader 工具。

    至此,我们的准备工作就完成了,下一节将会介绍前端框架的搭建。

    五、样式配置

    1、CSS Modules

    在react-scripts@2.0.0 及更高版本,项目使用.module.css 文件命名约定支持 CSS Modules  。 CSS Modules 允许通过自动创建格式唯一的classname 来确定 CSS 的作用域,从而避免污染全局样式。CSS Modules 允许你在不同的文件中使用相同的 CSS classname,而无需担心命名冲突

    由create-react-app创建的项目,在webpack.config.js中指定了匹配各种样式文件的正则表达式:

    // style files regexes
    const cssRegex = /.css$/;           
    const cssModuleRegex = /.module.css$/;
    const sassRegex = /.(scss|sass)$/;
    const sassModuleRegex = /.module.(scss|sass)$/;

    (1).针对.css文件,样式处理器配置如下:

      // "postcss" loader applies autoprefixer to our CSS.
    // "css" loader resolves paths in CSS and adds assets as dependencies.
    // "style" loader turns CSS into JS modules that inject <style> tags.
    // In production, we use MiniCSSExtractPlugin to extract that CSS
    // to a file, but in development "style" loader enables hot editing
    // of CSS.
    // By default we support CSS Modules with the extension .module.css
    {
        test: cssRegex,
        exclude: cssModuleRegex,
        use: getStyleLoaders({
            importLoaders: 1,
            sourceMap: isEnvProduction && shouldUseSourceMap,
        }),
        // Don't consider CSS imports dead code even if the
        // containing package claims to have no side effects.
        // Remove this when webpack adds a warning or an error for this.
        // See https://github.com/webpack/webpack/issues/6571
        sideEffects: true,
    },

    这段代码表明,凡是匹配cssRegex,但是不匹配cssModuleRegex的文件,样式处理器由getStyleLoaders()指定:

    // common function to get style loaders
        const getStyleLoaders = (cssOptions, preProcessor) => {
            const loaders = [
                isEnvDevelopment && require.resolve('style-loader'),
                isEnvProduction && {
                    loader: MiniCssExtractPlugin.loader,
                    // css is located in `static/css`, use '../../' to locate index.html folder
                    // in production `paths.publicUrlOrPath` can be a relative path
                    options: paths.publicUrlOrPath.startsWith('.')
                        ? { publicPath: '../../' }
                        : {},
                },
                {
                    loader: require.resolve('css-loader'),
                    options: cssOptions,
                },
                {
                    // Options for PostCSS as we reference these options twice
                    // Adds vendor prefixing based on your specified browser support in
                    // package.json
                    loader: require.resolve('postcss-loader'),
                    options: {
                        // Necessary for external CSS imports to work
                        // https://github.com/facebook/create-react-app/issues/2677
                        ident: 'postcss',
                        plugins: () => [
                            require('postcss-flexbugs-fixes'),
                            require('postcss-preset-env')({
                                autoprefixer: {
                                    flexbox: 'no-2009',
                                },
                                stage: 3,
                            }),
                            // Adds PostCSS Normalize as the reset css with default options,
                            // so that it honors browserslist config in package.json
                            // which in turn let's users customize the target behavior as per their needs.
                            postcssNormalize(),
                        ],
                        sourceMap: isEnvProduction && shouldUseSourceMap,
                    },
                },
            ].filter(Boolean);
            if (preProcessor) {
                loaders.push(
                    {
                        loader: require.resolve('resolve-url-loader'),
                        options: {
                            sourceMap: isEnvProduction && shouldUseSourceMap,
                        },
                    },
                    {
                        loader: require.resolve(preProcessor),
                        options: {
                            sourceMap: true,
                        },
                    }
                );
            }
            return loaders;
        };

    最终其配置大致等价于:

    {
        test: /.(css)$/,
        use: [
            {
                loader: 'style-loader'
            },
            {
                loader: 'css-loader'
            },
            {
                loader: 'postcss-loader',            
            }
        ]
    }
    • style-loader将css插入到页面的style标签中;
    • css-loader 是处理css文件中的url()等;
    • postcss-loader自动给能够添加厂商前缀的样式添加厂商前缀;(这个不是必须的可以不配置)
    • less-loader是将less文件编译成css;
    • sass-loader是将sass文件编译成css;

    loader的加载顺序是从右往左,从下往上,如果你配置了上面的loader就一定按照这个顺序。因此针对.css文件的处理:先经过postcss-loader给样式加入厂商前缀,然后使用css-loader处理,最后使用style-loader将css引入页面;

    注意:上面忽略了每个处理器的options配置,以及非样式处理器。

    针对每一个加载器,我们可以指定不同的附加选项(options):

    • importLoaders: 用于配置「css-loader 作用于 @import 的资源之前」有多少个 loader,比如importLoaders: 1 是在css-loader 之后指定1个数量的loader(即 postcss-loader)来处理import进来的资源。
    • sourceMap:默认为false,不开启sourceMap; true开启sourceMap,开发环境下开启比较实用;
    • modules:如果指定了modules:,表示开启CSS Modules,所以,所有经过处理的css文件,类名都将被改变。modules具有如下参数:
    1. getLocalIdent:默认为getCSSModuleLocalIdent,用function自定义生成的类名
    2. localIdentName: 默认未定义 ,可以自定义了类名的模板,如[path][name]-[local];

    (2).同理,.module.css文件的配置如下:

    // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
    // using the extension .module.css
    {
        test: cssModuleRegex,
        use: getStyleLoaders({
            importLoaders: 1,
            sourceMap: isEnvProduction && shouldUseSourceMap,
            modules: {
                getLocalIdent: getCSSModuleLocalIdent,
            },
        }),
    }

    与.css不同的是,这里指定了:

    modules: {
        getLocalIdent: getCSSModuleLocalIdent,
    },

    我们前面已经说过如果指定了modules,表示开启CSS Modules,所以,所有经过处理的css文件,类名都将被改变。getCSSModuleLocalIdent()就是用来生成这个唯一的classname。我们来看个例子:

    Button.module.css:

    .error {
      background-color: red;
    }

    another-stylesheet.css:

    .error {
      color: red;
    }

    Button.js:

    import React, { Component } from 'react';
    import styles from './Button.module.css'; // 将 css modules 文件导入为 styles
    import './another-stylesheet.css'; // 导入常规 CSS 文件
    
    class Button extends Component {
      render() {
        // 作为 js 对象引用
        return <button className={styles.error}>Error Button</button>;
      }
    }

    结果不会和其他 .error 类名冲突:

    <!-- This button has red background but not red text -->
    <button class="Button_error_ax7yz"></div>

    使用了 CSS Modules 后,就相当于给每个 class 名外加了一个 :local,以此来实现样式的局部化,如果你想切换到全局模式,使用对应的 :global:

    .normal {
      color: green;
    }
    /* 以上与下面等价 */
    :local(.normal) {
      color: green;
    }
    /* 定义全局样式 */
    :global(.btn) {
      color: red;
    }
    /* 定义多个全局样式 */
    :global {
      .link {
        color: green;
      }
      .box {
        color: yellow;
      }
    }

    2、less样式支持

    由于之前配置的正则表达式,并不支持.less文件,因此我们添加配置项

    const lessRegex = /.less$/;
    const lessModuleRegex = /.module.less$/;

    然后我们添加样式处理器为:

    {
        test: lessRegex,
        exclude: lessModuleRegex,
        use: getStyleLoaders({
            importLoaders: 2,
            sourceMap: isEnvProduction && shouldUseSourceMap,
        }),
    },

    然后配置less-loader:

    注意官方要求less-loader一定要添加在postcss-loader之后,顺序不能错误。

    最后,我们需要安装依赖包:

    cnpm install  less less-loader --save

    最后效果类似:

    {
        test: /.less$/,
        use: [{
            loader: "style-loader" // creates style nodes from JS strings
        }, {
             loader: "css-loader" // translates CSS into CommonJS
        }, {
             loader: "postcss-loader" // Adds vendor prefixing 
        },{
            loader: "less-loader",// compiles Less to CSS
        }]
    },

    这样我们就可以在项目中使用.less文件了。

    3、sass样式支持

    要使用sass,我们需要首先安装node-sass:

    cnpm install node-sass --save

    注意使用sass,不能保证css类的唯一性,可能污染全局样式。因此我们建议针对.sass的样式处理器,也指定一个modules,比如:

    modules: {
        localIdentName: '[path][name]-[local]'
    }

    此外,如果我们之前由于配置.less的支持,添加了less-loader,那么需要修改sass样式处理器的importLoaders参数为4。实际上针对.sass样式支持是不需要配置less-loader的。

    4、CSS Modules使用驼峰命名

    如果我们在loader的options中配置了modules,那么在引用样式的时候我们需要采用以下格式:

    import styles from './style.module.css';
    <div className={styles.pageHeader}></div>
    <div className={styles['page-header']}></div>

    每次写className时,都要用a.b方式,能不能更简单一些?当然可以:具体参考CSS Modules初识

    不过如果我们将class设置为全局性,那么我们就可以采用如下格式:

    <div className="page-header"></div>

    六、antd按需加载

    我们可以配置 babel-plugin-import 来实现antd组件及样式的按需加载,修改package.json文件如下:

      "babel": {
        "presets": [
          "react-app"
        ],
        "plugins": [
          [
            "import",
            {
              "libraryName": "antd",
              "style": "css"
            }
          ]
        ]
      }

    这里style配置为css,表示引入antd的css样式文件,如果配置为true,则引入antd的less样式文件。

     安装babel-plugin-import 依赖:

    cnpm install babel-plugin-import --save

    参考文章

    [1]import和require的区别

    [2]添加 CSS Modules 样式表

    [3]CSS Modules初识

    [4]css模块化及CSS Modules使用详解

    [5]React引入AntD按需加载报错

  • 相关阅读:
    发现了一个开源的sip软电话项目(C#)
    有没有.Net下的开源工作流框架推荐
    C#生成32位MD5加密
    web安全问题与Safe3 Web应用防火墙
    Safe3网页防篡改系统 v4.0
    C和C++混合编程问题
    Safe3 WEB安全网关linux 3.1版
    Dns信息收集工具集合
    lizamoon.com挂马解决办法
    Safe3网站安全网关 3.1发布
  • 原文地址:https://www.cnblogs.com/zyly/p/12631745.html
Copyright © 2011-2022 走看看