欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Next.js搭建Monorepo組件庫(kù)文檔實(shí)現(xiàn)詳解

 更新時(shí)間:2022年11月23日 08:29:52   作者:狂奔滴小馬  
這篇文章主要為大家介紹了Next.js搭建Monorepo組件庫(kù)文檔,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

  • 使用 pnpm 搭建一個(gè) Monorepo 組件庫(kù)
  • 使用 Next.js 開發(fā)一個(gè)組件庫(kù)文檔
  • changesets 來管理包的 version 和生成 changelog
  • 使用 vercel 部署在線文檔

代碼倉(cāng)庫(kù):github.com/maqi1520/ne…

組件化開發(fā)是前端的基石,正因?yàn)榻M件化,前端得以百花齊放,百家爭(zhēng)鳴。我們每天在項(xiàng)目中都寫著各種各樣的組件,如果在面試的時(shí)候,跟面試官說,你每天的工作是開發(fā)組件,那么顯然這沒有什么優(yōu)勢(shì),如果你說,你開發(fā)了一個(gè)組件庫(kù),并且有一個(gè)在線文檔可以直接預(yù)覽,這可能會(huì)是你的一個(gè)加分項(xiàng)。今天我們就來聊聊組件庫(kù)的開發(fā),主要是組件庫(kù)的搭建和文檔建設(shè),至于組件數(shù)量,那是時(shí)間問題,以及你是否有時(shí)間維護(hù)好這個(gè)組件庫(kù)的問題。

基礎(chǔ)組件和業(yè)務(wù)組件

首先組件庫(kù)分為基礎(chǔ)組件和業(yè)務(wù)組件,所謂基礎(chǔ)組件就是 UI 組件,類似 Ant design,它是單包架構(gòu),所有的組件都是在一個(gè)包中,一旦其中一個(gè)組件有改動(dòng),就需要發(fā)整包。另外一種是業(yè)務(wù)組件,組件中包含了一些業(yè)務(wù)邏輯,它在企業(yè)內(nèi)部是很有必要的。比如飛書文檔,包含在線文檔,在線 PPT、視頻會(huì)議等,這些都是獨(dú)立的產(chǎn)品,單獨(dú)迭代開發(fā),單獨(dú)發(fā)布,卻有一些共同的邏輯,比如沒有登錄的時(shí)候都需要調(diào)用一個(gè)”登錄彈窗“,或者說在項(xiàng)目協(xié)同的時(shí)候,都需要邀請(qǐng)人員加入,那么需要一個(gè)“人員選擇組件”, 這就是業(yè)務(wù)組件。業(yè)務(wù)組件不同于基礎(chǔ)組件,單獨(dú)安裝,依賴發(fā)包,而并不是全量發(fā)包。那么這些業(yè)務(wù)組件也需要一個(gè)文檔,因此我們使用 Monorepo(單倉(cāng)庫(kù)管理),這樣方便管理和維護(hù)。

為什么選用 Next.js 來搭建組件庫(kù)文檔?

組件文檔有個(gè)特別重要的功能就是“寫 markdown 文檔,可以看到代碼以及運(yùn)行效果”,這方面有很多優(yōu)秀的開源庫(kù),比如 Ant design 使用的是 bisheng, react use 使用的是 storybook, 還有一些優(yōu)秀的庫(kù),比如:dumi,Docz 等。 本地跑過 Ant design 的同學(xué)都知道, Ant design 的啟動(dòng)速度非常慢,因?yàn)榈讓邮褂玫?webpack,要啟動(dòng)開發(fā)服務(wù)器,必須將所有組件都進(jìn)行編譯,這會(huì)對(duì)開發(fā)者造成一些困擾,因?yàn)槿绻菢I(yè)務(wù)組件的話,開發(fā)者只關(guān)注單個(gè)組件,而不是全部組件。而使用 Next.jz 就有 2 個(gè)非常大的優(yōu)勢(shì):

  • 使用 swc 編譯,Next.js 中實(shí)現(xiàn)了快 3 倍的快速刷新和快 5 倍的構(gòu)建速度;
  • 按需編譯,在開發(fā)環(huán)境下,只有訪問的頁(yè)面才會(huì)進(jìn)行編譯

那么接下來的問題就是:要在 Next.js 中實(shí)現(xiàn) “寫 Markdown Example 可預(yù)覽”的功能,若要自己實(shí)現(xiàn)這個(gè)功能,確實(shí)是一件麻煩的事情。我們換一個(gè)思維,組件展示,也就是在 markdown 中運(yùn)行 react 組件,這不就是 mdx 的功能嗎? 而在 Next.js 中可以很方便地集成 MDX。

效果演示  

目前這是一個(gè)簡(jiǎn)易版,只為展示 Next.js 搭建文檔

項(xiàng)目初始化

首先我們創(chuàng)建一個(gè) next typescript 作為我們項(xiàng)目的主目錄,用于組件庫(kù)的文檔開發(fā)

npx create-next-app@latest --ts

要想啟動(dòng) pnpm 的 workspace 功能,需要工程根目錄下存在 pnpm-workspace.yaml 配置文件,并且在 pnpm-workspace.yaml 中指定工作空間的目錄。比如這里我們所有的子包都是放在 packages 目錄下

packages:
  - 'packages/*'

接下來,我們?cè)?packages 文件夾下創(chuàng)建三個(gè)子項(xiàng)目,分別是:user-select、login 和 utils, 對(duì)應(yīng)用戶選擇,登錄 和工具類。

├── packages
│   ├── user-select
│   ├── login
│   ├── utils

user-select 和 login 依賴 utils,我們可以將一些公用方法放到 utils 中。

給每個(gè) package 下面創(chuàng)建 package.json 文件,包名稱通常是”@命名空間+包名@“的方式,比如@vite/xx 或@babel/xx,在本例中,這里我們都以@mastack開頭

{
  "name": "@mastack/login",
  "version": "1.0.0",
  "description": "",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

給每個(gè) package 安裝 typescript

pnpm add typescript -r  -D

給每個(gè) package 創(chuàng)建 tsconfig.json 文件

{
  "include": ["src/**/*"],
  "compilerOptions": {
    "jsx": "react",
    "outDir": "dist",
    "target": "ES2020",
    "module": "esnext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "moduleResolution": "node",
    "declaration": true,
    "forceConsistentCasingInFileNames": true
  }
}

執(zhí)行下面代碼,往 login 組件中安裝 utils;

pnpm i @mastack/utils --filter @mastack/login

安裝完成后,設(shè)置依賴版本的時(shí)候推薦用 workspace:*,就可以保持依賴的版本是工作空間里最新版本,不需要每次手動(dòng)更新依賴版本。

pnpm 提供了 -w, --workspace-root 參數(shù),可以將依賴包安裝到工程的根目錄下,作為所有 package 的公共依賴,這么我們安裝 antd

pnpm install antd -w

組件開發(fā)

我們?cè)?login 組件下,新建一個(gè)組件 src/index.tsx

import React, { useState } from "react";
import { Button, Modal } from "antd";
interface Props {
  className: string;
}
export default function Login({ className }: Props) {
  const [open, setopen] = useState(false);
  return (
    <>
      <Button onClick={() => setopen(true)} className={className}>
        登錄
      </Button>
      <Modal
        title="登錄"
        open={open}
        onCancel={() => setopen(false)}
        onOk={() => setopen(false)}
      >
        <p>登錄彈窗</p>
      </Modal>
    </>
  );
}

先寫一個(gè)最簡(jiǎn)單版本,組件代碼并不是最重要的,后續(xù)可以再優(yōu)化。

在package.json 中添加構(gòu)建命令

"scripts": {
    "build": "tsc"
  }

然后在組件目錄下執(zhí)行 yarn build 。此時(shí)組件以及可以打包成功!

Next.js 支持 MDX

接下來要讓文檔支持 MDX,在根目錄下執(zhí)行以下命令,安裝 mdx 和 loader 相關(guān)包

pnpm add @next/mdx @mdx-js/loader @mdx-js/react -w

修改 next.config.js 為以下代碼

const withMDX = require('@next/mdx')({
  extension: /\.mdx?$/,
})
module.exports = withMDX({
  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
  reactStrictMode: true,
  swcMinify: true,
})

這樣就可以在 Next 中支持 MDX 了。

我們?cè)?src/pages 目錄下,新建一個(gè) docs/index.mdx

先寫一個(gè)簡(jiǎn)單的 markdown 文件測(cè)試下

這樣 Next.js 就支持 mdx 文檔了。

Next 動(dòng)態(tài)加載 md 文件

接下來,我們要實(shí)現(xiàn)動(dòng)態(tài)加載 packages 中的文件 md 文件。新建一個(gè) pages/docs/[...slug].tsx 文件。

export async function getStaticPaths(context: GetStaticPathsContext) {
  return {
    paths: [
      { params: { slug: ["login"] } },
      { params: { slug: ["user-selecter"] } },
    ],
    fallback: false, // SSG 模式
  };
}
export async function getStaticProps({
  params,
}: GetStaticPropsContext<{ slug: string[] }>) {
  const slug = params?.slug.join("/");
  return {
    props: {
      slug,
    }, // 傳遞給組件的props
  };
}

我們使用的是 SSG 模式。上面代碼中 getStaticPaths 我先寫了 2 條數(shù)據(jù),因?yàn)槲覀兡壳爸挥?2 個(gè)組件,它會(huì)在構(gòu)建的時(shí)候會(huì)生成靜態(tài)頁(yè)面。 getStaticProps函數(shù)可以獲取 URL 上的參數(shù),我們將 slug 參數(shù)傳遞給組件,然后在 Page 函數(shù)中,我們使用 next/dynamic 動(dòng)態(tài)加載 packages 中的 mdx 文件

import React from "react";
import {
  GetStaticPathsContext,
  InferGetServerSidePropsType,
  GetStaticPropsContext,
} from "next";
import dynamic from "next/dynamic";
type Props = InferGetServerSidePropsType<typeof getStaticProps>;
export default function Page({ slug }: Props) {
  const Content = dynamic(() => import(`../packages/${slug}/docs/index.mdx`), {
    ssr: false,
  });
  return (
    <div>
      <Content />
    </div>
  );
}

此時(shí)我們?cè)L問 http://localhost:3000/docs/login 查看效果

在頁(yè)面上會(huì)提示,無法找到@mastack/login 這個(gè)包,我們需要在項(xiàng)目的根目錄下的 tsconfig.json 中加入別名

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "@mastack/login": ["packages/login/src"],
      "@mastack/user-select": ["packages/user-select/src"]
    },
  }
}

保存后,頁(yè)面會(huì)自動(dòng)刷新,我們就可以在頁(yè)面上看到如下效果。

至此文檔與 packages 目錄下的 mdx 已經(jīng)打通。修改 packages/login/docs/index.mdx 中的文檔,頁(yè)面會(huì)自動(dòng)熱更新。

自定義 mdx 組件

上面代碼已經(jīng)實(shí)現(xiàn)了在 md 文檔中顯示組件和代碼,但我們想要的是類似于 ant design 那樣的效果,默認(rèn)代碼不展示,點(diǎn)擊可以收起和展開,這該怎么實(shí)現(xiàn)呢?

我們可以利用 mdx 的自定義組件來實(shí)現(xiàn)這個(gè)效果。

寫 mdx 的時(shí)候,在組件 <Login/>和代碼外層嵌套一個(gè)自定義組件DemoBlock

然后實(shí)現(xiàn)一個(gè)自定義一個(gè) DemoBlock 組件,提供給 MDXProvider,這樣所有的 mdx 文檔中,不需要 import 就可以使用組件。

import dynamic from "next/dynamic";
import { MDXProvider } from "@mdx-js/react";
const DemoBlock = ({ children }: any) => {
  console.log(children);
  return null
};
const components = {
  DemoBlock,
};
export default function Page({ slug }: Props) {
  const Content = dynamic(() => import(`packages/${slug}/docs/index.mdx`), {
    ssr: false,
  });
  return (
    <div>
      <MDXProvider components={components}>
        <Content />
      </MDXProvider>
    </div>
  );
}

我們先寫一個(gè)空組件,看下 children 的值。刷新頁(yè)面, 此時(shí) DemoBlock中的組件和代碼不會(huì)顯示,我們看一下打印出的 children 節(jié)點(diǎn)信息;

chilren 為 react 中的 vNode,現(xiàn)在我們就可以根據(jù) type 來判斷,返回不同的 jsx,這樣就可以實(shí)現(xiàn)DemoBlock組件了,代碼如下:

import React, { useState } from "react";
const DemoBlock = ({ children }: any) => {
  const [visible, setVisible] = useState(false);
  return (
    <div className="demo-block">
      {children.map((child: any) => {
        if (child.type === "pre") {
          return (
            <div key={child.key}>
              <div
                className="demo-block-button"
                onClick={() => setVisible(!visible)}
              >
                {!visible ? "顯示代碼" : "收起代碼"}
              </div>
              {visible && child}
            </div>
          );
        }
        return child;
      })}
    </div>
  );
};

再給組件添加一些樣式,給按鈕添加一個(gè) svg icon,一起來看下實(shí)現(xiàn)效果:

是不是有跟 antd 的 demo block 有些相似了呢? 若要顯示更多字段和描述,我們可以修改組件代碼,實(shí)現(xiàn)完全自定義。

優(yōu)化文檔界面

至此我們的文檔,還是有些簡(jiǎn)陋,我們得優(yōu)化下文檔界面,讓我們的界面顯示更美觀。

  • 安裝并且初始化 tailwindcss
pnpm install -Dw tailwindcss postcss autoprefixer @tailwindcss/typography
pnpx tailwindcss init -p

修改 globals.css 為 tailwindcss 默認(rèn)指令

@tailwind base;
@tailwind components;
@tailwind utilities;

修改 tailwind.config.js 配置文件,讓我們的應(yīng)用支持文章默認(rèn)樣式,并且在 md 和 mdx 文件中也可以寫 tailwindcss

const defaultTheme = require("tailwindcss/defaultTheme");
const colors = require("tailwindcss/colors");
/** @type {import("tailwindcss").Config } */
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,md,mdx}",
    "./components/**/*.{js,ts,jsx,tsx}",
    "./packages/**/*.{md,mdx}",
  ],
  darkMode: "class",
  plugins: [require("@tailwindcss/typography")],
};

在 MDX Content 組件 外層可以加一個(gè) prose class,這樣我們的文檔就有了默認(rèn)好看文章樣式了。

現(xiàn)在 md 文檔功能還很薄弱,我們需要讓它強(qiáng)大起來,我們先安裝一些 markdown 常用的包

pnpm install remark-gfm remark-footnotes remark-math rehype-katex rehype-slug rehype-autolink-headings rehype-prism-plus -w

remark-gfm 讓 md 支持 GitHub Flavored Markdown (自動(dòng)超鏈接鏈接文字、腳注、刪除線、表格、任務(wù)列表)

remark-math rehype-katex 支持?jǐn)?shù)學(xué)公式

rehype-slug rehype-autolink-headings 自動(dòng)給標(biāo)題加唯一 id

rehype-prism-plus 支持代碼高亮

修改 next.config.jsnext.config.mjs,并輸入以下代碼

// Remark packages
import remarkGfm from "remark-gfm";
import remarkFootnotes from "remark-footnotes";
import remarkMath from "remark-math";
// Rehype packages
import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypePrismPlus from "rehype-prism-plus";
import nextMDX from "@next/mdx";
const withMDX = nextMDX({
  extension: /\.mdx?$/,
  options: {
    remarkPlugins: [
      remarkMath,
      remarkGfm,
      [remarkFootnotes, { inlineNotes: true }],
    ],
    rehypePlugins: [
      rehypeSlug,
      rehypeAutolinkHeadings,
      [rehypePrismPlus, { ignoreMissing: true }],
    ],
  },
});
export default withMDX({
  pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
  reactStrictMode: true,
  swcMinify: true,
});

我們?cè)谶@里可以配置 remarkPlugins 和 rehypePlugins;

markdown 在編譯過程中會(huì)涉及 3 種 ast 抽象語(yǔ)法樹 , remark 負(fù)責(zé)轉(zhuǎn)換為 mdast,它可以操作 markdown 文件,比如讓 markdown 支持更多格式(比如:公式、腳注、任務(wù)列表等),需要使用 remark 插件; rehype 負(fù)責(zé)轉(zhuǎn)換為 hast ,它可以轉(zhuǎn)換 html,比如給 標(biāo)題加 id,給代碼高亮, 這一步是在操作 HTML 后完成的。因此我們也可以自己寫插件,具體寫什么插件,就要看插件在哪個(gè)階段運(yùn)行。

最后我們到 github prism-themes 中復(fù)制一份代碼高亮的樣式到我們的 css 文件中,一起來看下效果吧!

發(fā)布工作流

workspace 中的包版本管理是一個(gè)復(fù)雜的任務(wù),pnpm 目前也并未提供內(nèi)置的解決方案。pnpm 推薦了兩個(gè)開源的版本控制工具:changesets 和 rush,這里我采用了 changesets 來實(shí)現(xiàn)依賴包的管理。

配置

要在 pnpm 工作空間上配置 changesets,請(qǐng)將 changesets 作為開發(fā)依賴項(xiàng)安裝在工作空間的根目錄中:

pnpm add -Dw @changesets/cli

然后 changesets 的初始化命令:

pnpm changeset init

添加新的 changesets

要生成新的 changesets,請(qǐng)?jiān)趥}(cāng)庫(kù)的根目錄中執(zhí)行pnpm changeset。 .changeset 目錄中生成的 markdown 文件需要被提交到到倉(cāng)庫(kù)。

發(fā)布變更

為了方便所有包的發(fā)布過程,在工程根目錄下的 pacakge.json 的 scripts 中增加如下幾條腳本:

"compile": "pnpm --filter=@mastack/* run build",
"pub": "pnpm compile && pnpm --recursive --registry https://registry.npmjs.org/ publish --access public"

編譯階段,生成構(gòu)建產(chǎn)物

  • 運(yùn)行pnpm changeset version。 這將提高先前使用 pnpm changeset (以及它們的任何依賴項(xiàng))的版本,并更新變更日志文件。
  • 運(yùn)行 pnpm install。 這將更新鎖文件并重新構(gòu)建包。
  • 提交更改。
  • 運(yùn)行 pnpm pub。 此命令將發(fā)布所有包含被更新版本且尚未出現(xiàn)在包注冊(cè)源中的包。

部署

部署可以選擇 gitbub pages 或者 vercel 部署,他們都是免費(fèi)的,Github pages 只支持靜態(tài)網(wǎng)站,vercel 支持動(dòng)態(tài)的網(wǎng)站,它會(huì)將 nextjs page 中,單獨(dú)部署成函數(shù)的形式。我這里選擇使用 vercel,因?yàn)樗脑L問速度相對(duì)比 gitbub pages 要快很多。只需要使用 github 賬號(hào)登錄 vercel.com/ 導(dǎo)入項(xiàng)目,便會(huì)自動(dòng)部署,而且會(huì)自動(dòng)分配一個(gè) xxx.vercel.app/ 二級(jí)域名。

也可以使用命令行工具,在項(xiàng)目跟目錄下執(zhí)行,根據(jù)提示,選擇默認(rèn)即可

npx vercel

預(yù)覽地址:nextjs-components-docs.vercel.app/

小結(jié)

本文,我們從零開始,使用 Next.js 和 pnpm 搭建了一個(gè)組件庫(kù)文檔,主要使用 Next.js 動(dòng)態(tài)導(dǎo)入功能解決了開發(fā)服務(wù)緩慢的問題,使用 Next.js 的 SSG 模式來生成靜態(tài)文檔。最后我們使用 changesets 來管理包的 version 和生成 changelog。

以上就是Next.js搭建Monorepo組件庫(kù)文檔的詳細(xì)內(nèi)容,更多關(guān)于Next.js搭建Monorepo的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • react如何添加less環(huán)境配置

    react如何添加less環(huán)境配置

    這篇文章主要介紹了react如何添加less環(huán)境配置,本文給大家分享遇到問題及解決方案,結(jié)合示例代碼圖文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-05-05
  • react使用websocket實(shí)時(shí)通信方式

    react使用websocket實(shí)時(shí)通信方式

    這篇文章主要介紹了react使用websocket實(shí)時(shí)通信方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • React中Key屬性作用

    React中Key屬性作用

    react中的key屬性,它是一個(gè)特殊的屬性,它是出現(xiàn)不是給開發(fā)者用的,而是給React自己使用,有了key屬性后,就可以與組件建立了一種對(duì)應(yīng)關(guān)系,本文主要介紹了React中Key屬性作用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • React中路由參數(shù)如何改變頁(yè)面不刷新數(shù)據(jù)的情況

    React中路由參數(shù)如何改變頁(yè)面不刷新數(shù)據(jù)的情況

    這篇文章主要介紹了React中路由參數(shù)如何改變頁(yè)面不刷新數(shù)據(jù)的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 基于react hooks,zarm組件庫(kù)配置開發(fā)h5表單頁(yè)面的實(shí)例代碼

    基于react hooks,zarm組件庫(kù)配置開發(fā)h5表單頁(yè)面的實(shí)例代碼

    這篇文章主要介紹了基于react hooks,zarm組件庫(kù)配置開發(fā)h5表單頁(yè)面,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • React 性能優(yōu)化之非必要的渲染問題解決

    React 性能優(yōu)化之非必要的渲染問題解決

    本文主要介紹了React 性能優(yōu)化之非必要的渲染問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • React?forwardRef?用法案例分析

    React?forwardRef?用法案例分析

    這篇文章主要介紹了React forwardRef用法,forwardRef允許你的組件使用ref將一個(gè)DOM節(jié)點(diǎn)暴露給父組件,本文結(jié)合案例分析給大家講解的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • React Fiber源碼深入分析

    React Fiber源碼深入分析

    Fiber 可以理解為一個(gè)執(zhí)行單元,每次執(zhí)行完一個(gè)執(zhí)行單元,React Fiber就會(huì)檢查還剩多少時(shí)間,如果沒有時(shí)間則將控制權(quán)讓出去,然后由瀏覽器執(zhí)行渲染操作,這篇文章主要介紹了React Fiber架構(gòu)原理剖析,需要的朋友可以參考下
    2022-11-11
  • 關(guān)于React中的聲明式渲染框架問題

    關(guān)于React中的聲明式渲染框架問題

    這篇文章主要介紹了React中的聲明式渲染框架,我們先討論了命令式和聲明式這兩種范式的差異,其中命令式更加關(guān)注過程,而聲明式更加關(guān)注結(jié)果,對(duì)React渲染框架知識(shí)感興趣的朋友跟隨小編一起看看吧
    2022-06-06
  • 基于React路由跳轉(zhuǎn)的幾種方式

    基于React路由跳轉(zhuǎn)的幾種方式

    這篇文章主要介紹了React路由跳轉(zhuǎn)的幾種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07

最新評(píng)論