从零搭建react+ts组件库(封装antd)
为什么会有这样一篇文章?因为网上的教程/示例只说了怎么做,没有系统详细的介绍引入这些依赖、为什么要这样配置,甚至有些文章还是错的!迫于技术洁癖,我希望更多的开发小伙伴能够真正的理解一个项目搭建各个方面的细节,做到面对对于工程出现的错误能够做到有把握。
最近使用阿里低开引擎的时候,想要封装一套组件库作为物料给低开引擎引入。根据低开引擎的物料封层模式,我的诉求是做一套组件库,并且将该组件库以umd方式生成。当然,从零开始开发组件库也是一个比较耗时耗力的事情,所以我想到将antd组件封装,于是催生出了本篇文章。
在封装组件并生成umd代码过程中,踩了很多的坑,也更加系统的了解了babel。
整体需求
- react组件库,取名r-ui,能够导出r-ui.umd.js和r-ui.umd.css。
- 代码使用typescript进行开发。
- 样式使用less进行开发。
- 引入antd组件库作为底层原子组件库,并且r-ui.umd.js和r-ui.umd.css包含antd组件代码和样式代码。
- 依赖的react、react-dom模块以外部引用方式。
开发与打包工具选型
使用webpack作为打包工具
老牌而又经典的打包工具,广泛的使用,丰富的插件生态以及各种易得的样例。
使用babel来处理typescript代码
由于 TypeScript 和 Babel 团队官方合作了一年的项目:TypeScript plugin for Babel(
@babel/preset-typescript
),TypeScript 的使用变得比以往任何时候都容易。 —— 摘自《TypeScript With Babel: A Beautiful Marriage (TypeScript 和 Babel:美丽的结合)》
建议各位读者可以先阅读一下上面的文章(有中文翻译文章)。
使用less-loader、css-loader等处理样式代码
使用MiniCssExtractPlugin分离CSS
项目搭建思路
整体结构
- r-ui
|- src
|- components
|- button
|- index.tsx
|- index.tsx
方案思路
编写webpack.config.js配置文件,添加核心loader:
- babel-loader。接收ts文件,交给babel-core以及相关babel插件进行处理,得到js代码。
- less-loader。接收less样式文件,处理得到css样式代码。
- css-loader+MiniCssExtractPlugin.loader。接收css样式代码进行处理,并分离导出组件库样式文件。
项目搭建实施
初始化
初始化r-ui项目
mkdir r-ui && cd r-ui && npm init
# 配置项目基本信息(name、version......)
初始化git仓库,添加gitignore文件(后续所有命令非特殊情况,均相对于项目根目录)
git init
# .gitignore文件内容请直接查看项目内文件
# 完成后,初始提交:
# git add . && git commit -m "init"
安装webpack(包管理器使用yarn)
yarn add -D webpack webpack-cli webpack-dev-server
# 安装webpack-dev-server是为后续构建样例页面做准备,前期可以不安装。
diff --git a/package.json b/package.json
index e01c1b1..53dd9a3 100644
--- a/package.json
+++ b/package.json
@@ -8,5 +8,9 @@
},
"author": "",
"license": "MIT",
- "devDependencies": {}
+ "devDependencies": {
+ "webpack": "^5.72.1",
+ "webpack-cli": "^4.9.2",
+ "webpack-dev-server": "^4.9.0"
+ }
}
项目根目录添加webpack.config.js并进行初始配置
// webpack.config.js
const {resolve} = require("path");
module.exports = {
// 组件库的起点入口
entry: './src/index.tsx',
output: {
filename: "r-ui.umd.js", // 打包后的文件名
path: resolve(__dirname, 'dist'), // 打包后的文件目录:根目录/dist/
library: 'rui', // 导出的UMD js会在window挂rui,即可以访问window.rui
libraryTarget: 'umd' // 导出库为UMD形式
},
resolve: {
// webpack 默认只处理js、jsx等js代码
extensions: ['.js', '.jsx', '.ts', '.tsx']
},
externals: {},
// 模块
module: {
// 规则
rules: []
}
};
Babel引入
引入babel-loader以及相关babel库
yarn add -D babel-loader @babel/core @babel/preset-env @babel/preset-typescript @babel/preset-react @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread
diff --git a/package.json b/package.json
index 53dd9a3..33c32b6 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,13 @@
"author": "",
"license": "MIT",
"devDependencies": {
+ "@babel/core": "^7.18.2",
+ "@babel/plugin-proposal-class-properties": "^7.17.12",
+ "@babel/plugin-proposal-object-rest-spread": "^7.18.0",
+ "@babel/preset-env": "^7.18.2",
+ "@babel/preset-react": "^7.17.12",
+ "@babel/preset-typescript": "^7.17.12",
+ "babel-loader": "^8.2.5",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0"
(END)
了解Babel
如果对于babel不太熟悉,可能对这一堆的依赖感到恐惧,这里如果读者有时间,我推荐这篇深入了解babel的文章:一口(很长的)气了解 babel - 知乎 (zhihu.com)。当然,如果这口气憋不住(哈哈),我做一个简单摘抄:
babel 总共分为三个阶段:解析,转换,生成。
babel 本身不具有任何转化功能,它把转化的功能都分解到一个个 plugin 里面。因此当我们不配置任何插件时,经过 babel 的代码和输入是相同的。
插件总共分为两种:
- 当我们添加 语法插件 之后,在解析这一步就使得 babel 能够解析更多的语法。(顺带一提,babel 内部使用的解析类库叫做 babylon,并非 babel 自行开发)
举个简单的例子,当我们定义或者调用方法时,最后一个参数之后是不允许增加逗号的,如
callFoo(param1, param2,)
就是非法的。如果源码是这种写法,经过 babel 之后就会提示语法错误。但最近的 JS 提案中已经允许了这种新的写法(让代码 diff 更加清晰)。为了避免 babel 报错,就需要增加语法插件
babel-plugin-syntax-trailing-function-commas
- 当我们添加 转译插件 之后,在转换这一步把源码转换并输出。这也是我们使用 babel 最本质的需求。
比起语法插件,转译插件其实更好理解,比如箭头函数
(a) => a
就会转化为function (a) {return a}
。完成这个工作的插件叫做babel-plugin-transform-es2015-arrow-functions
。同一类语法可能同时存在语法插件版本和转译插件版本。如果我们使用了转译插件,就不用再使用语法插件了。
简单来讲,使用babel就像如下流程:
源代码 =babel=> 目标代码
如果没有使用任何插件,源代码和目标代码就没有任何差异。当我们引入各种插件的时候,就像如下流程一样:
源代码
|
进入babel
|
babel插件1处理代码:移除某些符号
|
babel插件2处理代码:将形如() => {}的箭头函数,转换成function xxx() {}
|
目标代码
因为babel的插件处理的力度很细,我们代码的语法、语义内容规范有很多,如果我们要处理这些语法,可能需要配置一大堆的插件,所以babel提出,将一堆插件组合成一个preset(预置插件包),这样,我们只需要引入一个插件组合包,就能处理代码的各种语法、语义。
所以,回到我们上述的那些@babel开头的npm包,再回首可能不会那么迷茫:
@babel/core
@babel/preset-env
@babel/preset-typescript
@babel/preset-react
@babel/plugin-proposal-class-properties
@babel/plugin-proposal-object-rest-spread
@babel/core
毋庸置疑,babel的核心模块,实现了上述的流程运转以及代码语法、语义分析的功能。
以plugin开头的就是插件,这里我们引入了两个:@babel/plugin-proposal-class-properties
(允许类具有属性)和@babel/plugin-proposal-object-rest-spread
(对象展开)。
以preset开头的就是预置组件包合集,其中@babel/preset-env
表示使用了可以根据实际的浏览器运行环境,会选择相关的转义插件包:
env 的核心目的是通过配置得知目标环境的特点,然后只做必要的转换。
如果不写任何配置项,env 等价于 latest,也等价于 es2015 + es2016 + es2017 三个相加(不包含 stage-x 中的插件)。
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
如上配置将考虑所有浏览器的最新2个版本(safari大于等于7.0的版本)的特性,将必要的代码进行转换。而这些版本已有的功能就不进行转化了。
@babel/preset-typescript
会处理所有ts的代码的语法和语义规则,并转换为js代码;@babel/preset-react
故名思义,可以帮助处理使用React相关特性,例如JSX标签语法等。
webpack的基于babel-loader的处理流程
讲了这么多,我们的打包工具webpack如何使用babel相关组件处理代码的呢?还记得我们安装过babel-loader吗?
实际上,我们通过配置webpack.config.js,使用babel-loader建立起webpack处理代码与babel处理代码的连接:
diff --git a/webpack.config.js b/webpack.config.js
index 8bfbb63..6767fd8 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -17,6 +17,11 @@ module.exports = {
// 模块
module: {
// 规则
- rules: []
+ rules: [
+ {
+ test: /\.tsx?$/,
+ use: 'babel-loader'
+ }
+ ]
}
};
(END)
这一步的配置,就是让webpack遇到ts或tsx的时候,将这些代码交给babel-loader,babel-loader作为桥接把代码交给内部引用的@babel/core相关API进行处理。
那么,@babel/core如何知道要使用我们安装的各种plugin插件和preset预置插件包的呢?通过.babelrc文件
(注:实际上还有其他配置方式,但个人倾向于.babelrc)。这里,我们在项目根目录创建.babelrc文件,并添加一下内容:
{
"presets": [
"@babel/preset-env",
"@babel/preset-typescript",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread"
]
}
这里的配置不难理解,plugins字段存放要使用的插件,presets字段存放预置插件包名称,具体的配置可以查阅官方文档。
总结一下,配置babel可以按照如下思路进行:
- xxx.ts(x)代码交给webpack打包;
- webpack遇到ts(x)结尾的代码文件,根据webpack.config.js配置,交给babel-loader;
- babel-loader交给@babel/core;
- @babel/core根据.babelrc配置交给相关的插件处理代码,转为js代码;
- webpack进行后续的打包操作。
引入React相关库(externals方式)
还记得我们的需求吗?
依赖的react、react-dom模块以外部引用方式。
什么是外部引用方式?简单来讲,我希望react、react-dom等组件库的包,不会被打入到组件库中,而是在html中引入(Add React to a Website – React (reactjs.org)):
<!-- ... other HTML ... -->
<!-- Load React. -->
<!-- Note: when deploying, replace "development.js" with "production.min.js". -->
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
<!-- 组件库JS -->
<script src="r-ui.js"></script>
</body>
要实现这样的效果,第一步是配置webapck.config.js:
diff --git a/webpack.config.js b/webpack.config.js
index 6767fd8..54fc0e5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -13,7 +13,13 @@ module.exports = {
// webpack 默认只处理js、jsx等js代码
extensions: ['.js', '.jsx', '.ts', '.tsx']
},
- externals: {},
+ externals: {
+ // 打包过程遇到以下依赖导入,不会打包对应库代码,而是调用window上的React和ReactDOM
+ // import React from 'react'
+ // import ReactDOM from 'react-dom'
+ 'react': 'React',
+ 'react-dom': 'ReactDOM'
+ },
// 模块
module: {
// 规则
(END)
第二部,在引入react相关库的时候,可以不用引入到dependencies运行时依赖,而只需要引入对应的类型定义到devDependencies开发依赖中:
yarn add -D @types/react@17.0.39 @types/react-dom@17.0.17
diff --git a/package.json b/package.json
index 33c32b6..bd17763 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,8 @@
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12",
"@babel/preset-typescript": "^7.17.12",
+ "@types/react": "17.0.39",
+ "@types/react-dom": "17.0.17",
"babel-loader": "^8.2.5",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
至此,我们已经完成了处理基于TypeScript的React项目的webpack配置,此时我们的项目结构如下:
- r-ui
|- .babelrc
|- package.json
|- webpack.config.js
阶段演示1:基于TypeScript的React组件项目的webpack配置可行性
编写组件代码
新增src目录,在src目录下添加index.tsx(用于将所有的组件导出)
src目录下添加components/button目录,并创建index.tsx文件。具体结构与目录如下:
- r-ui
|- src/components/button/index.tsx
|- src/index.tsx
|- ... ...
src/components/button/index.tsx
import * as React from 'react';
interface ButtonProps {
}
const Button: React.FC<ButtonProps> = (props) => {
const {children, ...rest} = props;
return <button {...rest} >{children}</button>
}
export default Button;
src/index.tsx
export {default as Button} from './components/button';
修改package.json
添加webpack处理脚本
diff --git a/package.json b/package.json
index bd17763..01565ad 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
+ "build": "webpack --config webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
(END)
编译打包组件库
yarn run build
打包完成后,在项目根目录/dist目录下,会生成一个r-ui.umd.js文件。
效果演示
想要查看效果,可以在dist目录下添加如下的html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>r-ui example</title>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 注意r-ui.umd.js的路径 -->
<script src="r-ui.umd.js"></script>
</head>
<body>
<div id="example"></div>
<script>
const onClick = () => {
alert('hello');
};
// window上存在rui,是因为我们将组件包导出为了umd包,取名为rui
// 使用React原生方法创建Button的react组件实例
// 等价于:
// <Button onClick={onClick}>hello, world</Button>
const button = React.createElement(rui.Button, {onClick}, 'hello, world');
// 调用ReactDOM方法,将button组件实例挂载到example DOM节点上
ReactDOM.render(button, document.getElementById('example'));
</script>
</body>
</html>
- r-ui
|- dist
|- index.html
|- r-ui.umd.js
|- ... ...
此时,可以直接使用浏览器打开index.html查看效果:
处理样式(less编译与css导出)
依赖引入
根据上述内容,我们已经搭建了基础的项目结构,但是目前来说我们还需要处理我们的less样式,并且能够支持导出r-ui.umd.css样式文件。基于此考虑,我们需要引入:
- less-loader。处理less样式代码,转为css;
- less。由于less-loader内部是调用了less模块进行less代码编译,故还需要引入less(模式和babel-loader内部使用@babel/core一样);
- css-loader。处理css样式代码,进行适当加工;
- mini-css-extract-plugin。MiniCssExtractPlugin的loader用于进一步处理css,并且该插件用于导出独立样式文件。
yarn add -D less-loader less css-loader mini-css-extract-plugin
diff --git a/package.json b/package.json
index 01565ad..3070d07 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,10 @@
"@types/react": "17.0.39",
"@types/react-dom": "17.0.17",
"babel-loader": "^8.2.5",
+ "css-loader": "^6.7.1",
+ "less": "^4.1.2",
+ "less-loader": "^11.0.0",
+ "mini-css-extract-plugin": "^2.6.0",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0"
配置webpack
根据上述依赖,我们可以知道需要less-loader、css-loader以及MiniCssExtractPlugin的内置loader来处理我们的样式代码。但是配置到webpack需要注意: webpack中的顺序是【从后向前】链式调用的,所以注意下面配置的代码中use数组的顺序:
diff --git a/webpack.config.js b/webpack.config.js
index 54fc0e5..9db43b8 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,5 +1,6 @@
// webpack.config.js
const {resolve} = require("path");
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// 组件库的起点入口
entry: './src/index.tsx',
@@ -27,7 +28,28 @@ module.exports = {
{
test: /\.tsx?$/,
use: 'babel-loader'
+ },
+ {
+ test: /\.less$/,
+ use: [
+ // webpack中的顺序是【从后向前】链式调用的
+ // 所以对于less先交给less-loader处理,转为css
+ // 再交给css-loader
+ // 最后导出css(MiniCssExtractPlugin.loader)
+ // 所以注意loader的配置顺序
+ {
+ loader: MiniCssExtractPlugin.loader,
+ },
+ 'css-loader',
+ 'less-loader'
+ ]
}
]
- }
+ },
+ plugins: [
+ // 插件用于最终的导出独立的css的工作
+ new MiniCssExtractPlugin({
+ filename: 'r-ui.umd.css'
+ }),
+ ]
};
阶段演示2:less样式处理配置可行性
编写样式代码
新增src/components/button/index.less
@color: #006fde;
.my-button {
color: @color;
}
修改src/components/button/index.tsx
import * as React from 'react';
+// 引入less样式
+import './index.less';
interface ButtonProps {
}
const Button: React.FC<ButtonProps> = (props) => {
const {children, ...rest} = props;
- return <button {...rest} >{children}</button>
+ // 使用my-button样式
+ return <button {...rest} className='my-button'>{children}</button>
}
export default Button;
编译组件库
再次打包组件库以后,dist目录下会额外生成文件:r-ui.umd.css。所以,我们需要在index.html中添加样式文件的引入:
<head>
<meta charset="UTF-8">
<title>r-ui example</title>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="r-ui.umd.js"></script>
+ <link href="r-ui.umd.css" rel="stylesheet"/>
</head>
效果演示
刷新页面后,可以看到按钮的文字颜色已经生效
引入AntDesign
根据我们的需求,我们希望将antd组件代码引用到我们组件内部进行封装,所以需要以dependencies方式引入:
yarn add antd
diff --git a/package.json b/package.json
index 3070d07..09ca792 100644
--- a/package.json
+++ b/package.json
@@ -26,5 +26,8 @@
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0"
+ },
+ "dependencies": {
+ "antd": "^4.20.6"
}
}
引用antd的button样式
src/components/button/index.less
-@color: #006fde;
-
-.my-button {
- color: @color;
-}
+@import "~antd/lib/button/style/index.css";
引用antd的button组件
import * as React from 'react';
+// 使用antd的Button和ButtonProps
+// 为了不和我们的Button冲突,需要改导出名
+import {Button as AntdButton, ButtonProps as AntdButtonProps} from 'antd';
// 引入less样式
import './index.less';
-interface ButtonProps {
+interface ButtonProps extends AntdButtonProps {
}
const Button: React.FC<ButtonProps> = (props) => {
const {children, ...rest} = props;
- // 使用my-button样式
- return <button {...rest} className='my-button'>{children}</button>
+ // 使用AntdButton
+ return <AntdButton {...rest}>{children}</AntdButton>
}
export default Button;
阶段演示3:antd组件引入可行性
通过上述的代码修改以后,我们直接进行编译,然后检查效果即可:
写在最后
实际上,代码开发过程中,还有很多可以辅助开发的模块、流程,例如eslint检查,热更新等。但是那些内容不在本文的讨论范围。后续会出相关的文章再进一步进行介绍。
本文所搭建的整个项目,我都按照文章一步一步进行了git提交,开发小伙伴可以边阅读文章边对照git提交一步一步来看。
github地址:w4ngzhen/r-ui (github.com)
从零搭建react+ts组件库(封装antd)的更多相关文章
- 前端如何搭建vue UI组件库/封装插件(从零到有)
需求 因之前是做外包项目居多,经常用到相同的组件,之前的办法是在一个项目中写一个组件,其他项目直接将compents下的组件复制,粘贴到项目中使用,缺点是维护起来,改一个项目,其他项目也需要修改,所以 ...
- beeshell —— 开源的 React Native 组件库
介绍 beeshell 是一个 React Native 应用的基础组件库,基于 0.53.3 版本,提供一整套开箱即用的高质量组件,包含 JavaScript(以下简称 JS)组件和复合组件(包含 ...
- react_app 项目开发 (4)_ React UI 组件库 ant-design 的基本使用
最流行的开源 React UI 组件库 material-ui 国外流行(安卓手机的界面效果)文档 ant-design 国内流行 (蚂蚁金服 设计,一套 PC.一套移动端的____下拉菜单.分页.. ...
- 从零开始封装React UI 组件库并发布到NPM
github 开源地址:zswui github 说明文档:wiki 1.新建目录wui (1)进入到 wui 目录 执行 npm init 命令初始化项目.更具提示信息填充将会生成的 package ...
- 从零搭建react hooks项目(github有源代码)
前言 首先这是一个react17的项目,包含项目中常用的路由.状态管理.less及全局变量配置.UI等等一系列的功能,开箱即用,是为了以后启动项目方便,特地做的基础框架,在这里分享出来. 这里写一下背 ...
- 基于Vue搭建自己的组件库(1)
本项目演示地址:https://husilang.github.io/zm-ui 项目参考文章:从零开始搭建Vue组件库 VV-UI 项目的初衷是学习怎么封装一个基于Vue的UI组件库,顺便记录每个步 ...
- react hooks 如何自定义组件(react函数组件的封装)
前言 这里写一下如何封装可复用组件.首先技术栈 react hooks + props-type + jsx封装纯函数组件.类组件和typeScript在这不做讨论,大家别白跑一趟. 接下来会说一下封 ...
- React UI 组件库uiw v1.2.8 发布
uiw 高品质的UI工具包,基于React 16+的组件库.
- 【万字长文】从零配置一个vue组件库
简介 本文会从零开始配置一个monorepo类型的组件库,包括规范化配置.打包配置.组件库文档配置及开发一些提升效率的脚本等,monorepo 不熟悉的话这里一句话介绍一下,就是在一个git仓库里包含 ...
随机推荐
- 干货,看微信小程序后台用户数据如何演变和递增
这几天发现附近小程序又多了好几家,其中有普通小程序和门店小程序,把它们做一个对比,门店小程序更多的像一张名片,只有基本的企业名称.地址.营业时间.电话和门店照片,和普通小程序相比显得逊色许多.楼下的水 ...
- FreeSql的各种工程demo上新啦
FreeSql的各种工程demo GitHub | Gitee console,winforms nf461,vb,wpf,webapi,workerSevice,signalIR xamarinFo ...
- 深度学习(三)之LSTM写诗
目录 数据预处理 构建数据集 模型结构 生成诗 根据上文生成诗 生成藏头诗 参考 根据前文生成诗: 机器学习业,圣贤不可求.临戎辞蜀计,忠信尽封疆.天子咨两相,建章应四方.自疑非俗态,谁复念鹪鹩. 生 ...
- springboot集成spring security实现登录和注销
文章目录 一.导入坐标 二.Users实体类及其数据库表的创建 三.controller,service,mapper层的实现 四.核心–编写配置文件 五.页面的实现 运行结果 一.导入坐标 < ...
- 还原lvm逻辑卷创建整个过程
很多情况入职的时候,系统可能已规划过的,但是有的信息也不是很完整,比如下面的lvm逻辑卷我们先不管对与错,利用一些工具来了解当前lvm逻辑卷的情况 系统采样: [root@fp-web-112 var ...
- linux磁盘之分区类型id
我们通过命令来查看一下linux系统定义的分区类型id及其意义(更改磁盘分区类型必须掌握)系统采样: [root@fp-web-130 ~]# cat /etc/redhat-release Cent ...
- 帝国CMS实现栏目批量修改:是否生成
帝国cms实现栏目批量修改 是否生成:不生成栏目页, 不生成内容页, 不生成JS调用, 标签不调用的修改方本文关键字词:栏目批量设置第一步./e/admin/SetMoreClass.php中查找: ...
- 鲜为人知帝国CMS内容页调用上一篇和下一篇的精华方法汇总
<span style="float:left">上一篇:[!--info.pre--]</span><span style="float: ...
- For-Each循环(增强型For循环)
public class Demo077 { public static void main(String[] args) { int[] array ={11,2}; System.out.prin ...
- OSPF 路由协议详解(一)
(文章目录) ospf 协议是网络层协议 此篇文章适合有一定网络基础的人 ospf 协议最多应用在企业网络中,针对于运营商网络并不是特别的适用,端口号为 89 通过 IP 进行数据的转发 ospf 基 ...