实战一

准备本篇的环境

虽然可以仅展示核心代码,但笔者认为在一个完整的环境中边看边做,举一反三,效果更佳。

这里的环境其实就是初步认识 webpack一文完整的示例,包含 webpack、devServer、处理css、生成 html。

项目结构如下:

webpack-example2
- src // 项目源码
- a.css
- b.js
- c.js
- index.html // 页面模板
- index.js // 入口
- package.json // 存放了项目依赖的包
- webpack.config.js // webpack配置文件

src中的代码如下:

// a.css
body{color:blue;} // b.js
import './c.js'
console.log('moduleB')
console.log('b2') // c.js
console.log('moduleC') // index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=`, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>请查看控制台</p>
</body>
</html> // index.js
import './b.js'
import './a.css'
console.log('moduleA')

package.json:

{
"name": "webpack-example2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"dev": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^5.2.4",
"html-webpack-plugin": "^4.5.2",
"style-loader": "^2.0.0",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2"
}
}

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"]
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
})
],
mode: 'development',
devServer: {
open: true,
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
},
};

在 webpack-example2 目录下运行项目:

// 安装项目依赖的包
> npm i
// 启动服务
> npm run dev

启动服务器后,浏览器会自动打开页面,如果看到蓝色文字”请查看控制台“,说明环境已准备就绪。

打包样式

处理 css 和 less

less 是一种 css 预处理语言,在 webpack 中要处理 less 需要使用 less-loader,用于将 less 转为 css。

首先安装依赖,然后修改配置文件:

// 安装包。版本8安装失败,所以降了一个版本
> npm i -D less-loader@7 // webpack.config.js
// 增加对 less 文件处理的loader
rules: [
// 需要保留,否则识别不了 css 文件
{
test: /\.css$/i,
use: ["style-loader", "css-loader"]
},
// +
{
test: /\.less$/i,
loader: [
// compiles Less to CSS
"style-loader",
"css-loader",
"less-loader",
],
},
],

然后增加 a.less 文件,在 index.js 中引入 a.less,重新启动服务进行测试:

// src/a.less
body{
p{
color:pink;
}
} // index.js
import './b.js'
import './a.css'
// +
import './a.less'
console.log('moduleA') // 启动服务
> npm run dev

在新开的页面中,看到粉色文字”请查看控制台“,说明 less 处理成功。

提取 css 成单独文件

通过浏览器我们发现现在 css 是嵌在页面内的,就像这样:

<head>
...
<style>body{color:blue;}</style>
<style>body p {
color: pink;
}
</style>
</head>

通常我们会通过 link 来引入 css 文件,所以接下来就将 css 取成单独的文件。这里需要使用 mini-css-extract-plugin 这个包。

我们只需要安装依赖包,修改配置文件即可:

// 安装依赖包
> npm i -D mini-css-extract-plugin@1
// 不在需要 style-loader,卸载
> npm r -D style-loader // webpack.config.js
// +
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...
module: {
rules: [
// 修改规则
{
test: /\.css$/i,
// 将 style-loader 改为 MiniCssExtractPlugin.loader
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.less$/i,
loader: [
// 将 style-loader 改为 MiniCssExtractPlugin.loader
MiniCssExtractPlugin.loader,
"css-loader",
"less-loader",
],
},
],
},
plugins: [
// +
new MiniCssExtractPlugin(),
...
], };

启动服务(npm run dev),在打开的页面中可以看到 css 已经改为 link 的方式引入,就是这样:

// 从 style 改为 link 方式
<link href="main.css" rel="stylesheet"> // 通过网络查看 main.css 的内容是:
body{color:blue;}
body p {
color: pink;
}

由于我们对 css 和 less 都使用了 MiniCssExtractPlugin.loader,所以 a.css 和 a.less 都被提取到 main.css 中。

Tip:如果通过npm run build打包,则可以看到 dist/main.css 文件。

使用 PostCSS

PostCSS - 使用JavaScript转换CSS的工具。

可以将 postcss 当作一个平台,下面我们通过 postcss 做两件事:

  • 增加代码可读性(或增加前缀)
:fullscreen {
} // 转为 :-webkit-full-screen {
}
:-ms-fullscreen {
}
:fullscreen {
}
  • 立即使用明天的CSS
body {
color: lch(53 105 40);
} // 转为 body {
color: rgb(250, 0, 4);
}

webpack 可以通过 postcss-loader 来使用 postcss。

由于 postcss 只是一个平台,具体功能需要通过插件来实现,这里我们使用 postcss-preset-env

postcss-preset-env 可以将现代CSS转换为大多数浏览器可以理解的内容,并根据目标浏览器或运行时环境确定所需的polyfill。而且它包含自动前缀。

首先安装相关依赖,并修改配置文件:

> npm i -D postcss-loader@4 postcss-preset-env@6

// webpack.config.js
// +
const postcssPresetEnv = require('postcss-preset-env');
// +
const postcssLoader = {
loader: 'postcss-loader',
options: {
// postcss 只是个平台,具体功能需要使用插件
// Set PostCSS options and plugins
postcssOptions:{
plugins:[
// 配置插件 postcss-preset-env
[
"postcss-preset-env",
{
// 自动前缀。默认是true
// autoprefixer: true,
// 根据您所支持的浏览器来确定需要哪些polyfill。这里仅做演示
browsers: 'ie >= 8, chrome > 10',
// stage 默认是 2
// stage:2
},
],
]
}
}
} module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
// + 放在css-loader后面
postcssLoader
]
},
{
test: /\.less$/i,
loader: [
MiniCssExtractPlugin.loader,
"css-loader",
// +
postcssLoader,
"less-loader",
],
},
]
},
};

接着修改 a.css 和 a.less,重新启动服务器:

// a.css
body{
color: lch(53 105 40);
} // a.less
body{
p{
transform: scale(1, 2);
}
} // 启动服务
> npm run dev

在新开的页面中,我们看到红色文字”请查看控制台“,而且文字纵向拉长了一倍。通过浏览器查看 main.css 源码如下:

body{
color: rgb(250, 0, 4);
}
body p {
-webkit-transform: scale(1, 2);
-ms-transform: scale(1, 2);
transform: scale(1, 2);
}

至此,增加前缀以及立即使用明天的CSS都已经完成。

Tip:stage(阶段)可以是0(实验)到4(稳定),默认是2,如果我们改为3或4,重新打包,lch(53 105 40);则不会转为 rgb(250, 0, 4);将plugins换成下面的写法效果相同。

plugins:[
postcssPresetEnv({
browsers: 'ie >= 8, chrome > 10',
})
]

postcss-preset-env 支持任何标准的 browserslist 配置,可以是 .browserslistrc 文件,package.json 中的browserslist 键或 browserslist 环境变量。

如果将 browsers: 'ie >= 8, chrome > 10', 注释,browsers 将使用默认的 browserslist 查询(即> 0.5%, last 2 versions, Firefox ESR, not dead),重新构建,则不会添加前缀。

如果不想在 browsers 中写,在 package.json 中的 browserslist 中配置也是可以的:

// package.json
{
...
"browserslist": [
"ie >= 8",
"chrome > 10"
]
}

:package.json 不能写注释,本文在 package.json 中的注释仅作说明。

如果觉得 package.json 写的内容太多,我们甚至可以将这部分提取到一个单独的文件中来写:

// .browserslistrc
// from github browserslist
# Browsers that we support ie >= 8
chrome > 10

最后,如果我们针对开发环境和生成环境做不同的处理,比如开发环境支持 ie8+,而生产环境支持 chrom10+,我们可以这么写:

// .browserslistrc
# Browsers that we support [production]
chrome > 10 [development]
ie >= 8

然后在配置文件中通过 process.env 来指定环境:

// +
// process.env属性返回一个包含用户环境的对象
process.env.NODE_ENV = 'development' // or production

Browserslist将根据BROWSERSLIST_ENV或NODE_ENV变量选择,所以设置 process.env.BROWSERSLIST_ENV 也是可以的。

再次打包 npm run build,则只会针对 ie,生成的 main.css 内容如下:

body{
color: rgb(250, 0, 4);
}
body p {
-ms-transform: scale(1, 2);
transform: scale(1, 2);
}

压缩 css

如果我们需要压缩 css 代码,可以使用 optimize-css-assets-webpack-plugin,用于优化或最小化 css。

首先安装依赖,然后修改配置:

> npm i -D optimize-css-assets-webpack-plugin@5

// webpack.config.js
// +
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new MiniCssExtractPlugin(),
// +
new OptimizeCssAssetsPlugin()
],

重新打包,原来的 main.css 则变成一行,请看:

> npm run build

// main.css(优化前)
body{
/* 注释 */
color: rgb(250, 0, 4);
}
body p {
-ms-transform: scale(1, 2);
transform: scale(1, 2);
} // main.css(优化后)
body{color:#fa0004}body p{-ms-transform:scaleY(2);transform:scaleY(2)}

优化后,css 变成了一行,注释也删除了。

打包图片

前端资源通常有图片,由于 webpack 只识别 javascript,所以需要 loader 来帮们识别图片。

我们使用 url-loader,能将图片转为 base64。

首先安装依赖,并修改配置文件:

> npm i -D url-loader@4

// webpack.config.js
module: {
rules: [
...
// +
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
// 指定文件的最大大小(以字节为单位)
limit: 1024*7,
},
},
],
},
]
},

接着引入图片,启动服务:

// 引入图片。src/6.68kb.png

// a.less
body{
p{
transform: scale(1, 2);
}
.m-box{display:block;width:100px;height:100px;}
.img-from-less{background:url(./6.68kb.png) no-repeat;background-size:100% 100%;}
} // index.html
...
<body>
<p>请查看控制台</p>
<span class='m-box img-from-less'></span>
</body>
... // 启动服务
> npm run dev

Tip: 笔者的图片大小为 6.68kb,上面的 limit 只需要大于6.68kb即可

在新开的页面中,我们在”请查看控制台“文字下面看见了我们设置的图片。通过检查元素会发现这张图片是 base64。

body .img-from-less{
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA... no-repeat;
...
}

如果将 limit: 1024*7 修改为 limit: 1024*6(也就是将 limit 设置的比图片的 size 更小),再次运行 npm run dev,会发现报错了。还会提示找不到 file-loader。这是因为这张图片(6.68kb.png)大于 1024*6,所以就不会被打包成 base64,所以需要 file-loader 来处理。

安装依赖包 npm i -D file-loader@6,再次启动服务器,页面上又看到我们的图片,而且这次不再是 base64,而是直接生成了一张图片。

body .img-from-less{
background: url(26bd867dd65e26dbc77d1e151ffd36e0.png) no-repeat;
...
}

图片除了在 css 中使用,我们也会通过 img 元素引用,于是我们在 index.html 中新增 <img class='m-box' src="./6.68kb.png" alt=""> 再次启动服务,在打开的浏览器页面中发现 img 引用的图片没生效,而且源码也没变化。

这里需要使用 html-loader 这个包,它能让每个被加载的属性(例如:<img src="data:image.png")能被引入(imported)。

安装依赖包,修改配置:

> npm i -D html-loader@1

// webpack.config.js
module: {
rules: [
...
// +
{
test: /\.html$/i,
loader: 'html-loader',
},
]
},

再次启动服务,就能看到两张一样的图片了。img 的代码变为 <img class="m-box" src="26bd867dd65e26dbc77d1e151ffd36e0.png" alt="">

如果再次将 limit: 1024*6 修改为 limit: 1024*7,启动服务你会发现这两处图片都变为 base64。

打包 javascript

js 语法检查

有时我们希望团队成员写的 javascript 代码风格一致。

我们可以使用 eslint,它能查找并修复JavaScript代码中的问题;可以自定义 eslint,使其完全按照项目所需的方式工作。代码风格,笔者选用 airbnb,一个流行的 javascript 风格指南(此刻是第 6 名(topics javascript))。

在 webpack 中使用 eslint,需要使用 eslint-webpack-plugin( eslint-loader废弃了),而 eslint-webpack-plugin 依赖于 eslint

eslint-config-airbnb 默认导出包含我们所有的ESLint规则,包括ECMAScript 6+和React,而 我们不需要使用 react,所以使用 eslint-config-airbnb-base 即可。

首先安装依赖包,修改配置:

// 没有引入 eslint-plugin-import
> npm i -D eslint@7 eslint-webpack-plugin@2 eslint-config-airbnb-base@14 // webpack.config.js
// +
const ESLintPlugin = require('eslint-webpack-plugin'); module.exports = {
// ...
plugins: [
new ESLintPlugin({
// 将启用ESLint自动修复功能。此选项将更改源文件
fix: true
}) ],
// ...
}; // package.json
{
// +
"eslintConfig": {
"extends": "airbnb-base"
}
}

重新打包 npm run build,出现了一些警告和错误,核心信息如下:

WARNING in 

webpack-example2\src\index.js
6:1 warning Unexpected console statement no-console 6 problems (2 errors, 4 warnings) ERROR in webpack-example2\src\index.js
1:8 error Unexpected use of file extension "js" for "./b.js" import/extensions 5 problems (2 errors, 3 warnings)

错误(import/extensions)是不希望使用 js 扩展名,将 ./b.js 改为 ./b 就好了,可参考issues:import/extensions

警告(no-console)是因为不能出现 console.log。可以通过配置将这个告警关闭:

// package.json
{
"eslintConfig": {
"extends": "airbnb-base",
// +
"rules": {
"no-console": "off",
}
}
}

import/extensions修复,并将警告关闭,重新打包 npm run build则不会出现警告和错误。

Tip:打包后,源码也会自动修复,比如 src/index.js 中的 sum() 方法,a, 后面是多个空格,打包后会合并成一个空格:

function sum(a,    b) {
return a + b;
}
// 需要调用 sum() 方法
// 否则报错:error 'sum' is defined but never used no-unused-vars
console.log(sum(1, 100)); // 修复后 function sum(a, b) {
return a + b;
}

如果在 js 文件中使用 window ,再次打包会报错,就像这样:

// index.js
// +
setTimeout(() => {
window.location = 'https://www.baidu.com/';
}, 1000); // 打包
> npm run build
...
error 'window' is not defined no-undef

可以在配置文件中指定环境来解决这个问题。就像这样:

// package.json
"eslintConfig": {
// +
"env": {
"browser": true
}
}

如果不想写到 package.json,也可以配置到单独的文件(.eslintrc.js)中:

// .eslintrc.js
module.exports = {
"extends": "airbnb-base",
"rules": {
"no-console": "off"
},
"env": {
"browser": true
}
}

js 兼容性处理

我们想使用 es6 来编写代码,但有的浏览器支持的不够全面,所以我们会将 es6 转成 es5。

接着上面的例子进行,重写 index.js,放入一个箭头函数,再次打包,你会发现 webpack 不会对 es6 语法做处理,const 还是 const,而不是 var:

// src/index.js
const sum = (a, b) => (a + b);
console.log(sum(1, 10)); // sum 还是我们的箭头函数
eval("const sum = (a, b) => (a + b);\nconsole.log(sum(1, 10));\n\n\n//# sourceURL=webpack:///./src/index.js?");

Babel 是一个 JavaScript 编译器。通过它可以让我们使用下一代的 JavaScript 语法编程。

在 webpack 中要使用 babel 就得用 label-loader。用法(Usage)如下:

// 安装依赖包。没有使用 @babel/core
> npm i -D babel-loader@8 @babel/preset-env@7 // webpack.config.js
module: {
rules: [
// +
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
}
]
}

重新打包,箭头函数就变成普通函数:

eval("var sum = function sum(a, b) {\n  return a + b;\n};\n\nconsole.log(sum(1, 10));\n\n//# sourceURL=webpack:///./src/index.js?");

如果我们在 js 中使用 Promise,重新打包后 Promise 还是 Promise,而且在不识别 Promise 语法的浏览器中(比如 ie11)运行会报错。

// index.js
Promise.resolve('aaron').then((v) => {
console.log(v);
}); // 重新打包,Promise 还是 Promise
eval("Promise.resolve('aaron').then(function (v) {\n console.log(v);\n});\n\n//# sourceURL=webpack:///./src/index.js?");

babel 官网提到,使用@babel/polyfill,就可以使用新的内置函数(例如Promise或WeakMap),静态方法(例如Array.from或Object.assign),实例方法(例如Array.prototype.includes)等等。所以这个 polyfill 是我们的解决方案。

但是 @babel/polyfill 废弃了。而 @babel/polyfill 包含 regenerator runtime 和 core-js。

core-js,包括适用于2021年前ECMAScript的polyfill,而且仅加载必需的功能。

在 useBuiltIns 参数中也提到:由于在7.4.0中已弃用@ babel/polyfill,因此我们建议直接添加core-js并通过corejs选项设置版本。

于是我们知道 core-js 能解决 Promise 这类问题。

如何使用 core-js ?我们先来介绍一下插件预设

babel 通过将插件(或预设)应用于配置文件来启用Babel的代码转换。比如插件列表中的es2015,这是一个集合,包含了箭头函数(arrow-functions)、类(classes)等插件;

而预设(presets)其实是多个插件(plugin)的集合。比如 @babel/preset-env 这种预设则包含了 es2015、es2016、es2017等最新的插件。

最后根据babel-preset-env中的介绍,我们将 core-js 应用上:

// 安装依赖
> npm i -D core-js@3.11 // webpack.config.js
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
// +
{
// 配置处理polyfill的方式
useBuiltIns: "usage",
// 版本与我们下载的版本保持一致
corejs: { version: "3.11"},
"targets": "> 0.25%, not dead"
}
]
]
}
}

重新打包,dist/main.js 的Size变成 109 Kib,而之前还不到 4kiB。

启动服务,在不支持 Promise 语法的浏览器中,比如 ie11,也能在控制台输入 aaron。

至此 javascript 的兼容处理完毕。

webpack 快速入门 系列 —— 实战一的更多相关文章

  1. webpack 快速入门 系列 - 自定义 wepack 上

    其他章节请看: webpack 快速入门 系列 自定义 wepack 上 通过"初步认识webpack"和"实战一"这 2 篇文章,我们已经学习了 webpac ...

  2. webpack 快速入门 系列 —— 性能

    其他章节请看: webpack 快速入门 系列 性能 本篇主要介绍 webpack 中的一些常用性能,包括热模块替换.source map.oneOf.缓存.tree shaking.代码分割.懒加载 ...

  3. webpack 快速入门 系列 —— 初步认识 webpack

    初步认识 webpack webpack 是一种构建工具 webpack 是构建工具中的一种. 所谓构建,就是将资源转成浏览器可以识别的.比如我们用 less.es6 写代码,浏览器不能识别 less ...

  4. vue 快速入门 系列 —— vue loader 上

    其他章节请看: vue 快速入门 系列 vue loader 上 通过前面"webpack 系列"的学习,我们知道如何用 webpack 实现一个不成熟的脚手架,比如提供开发环境和 ...

  5. vue 快速入门 系列 —— vue loader 下

    其他章节请看: vue 快速入门 系列 vue loader 下 CSS Modules CSS Modules 是一个流行的,用于模块化和组合 CSS 的系统.vue-loader 提供了与 CSS ...

  6. vue 快速入门 系列 —— vue-cli 下

    其他章节请看: vue 快速入门 系列 Vue CLI 4.x 下 在 vue loader 一文中我们已经学会从零搭建一个简单的,用于单文件组件开发的脚手架:本篇,我们将全面学习 vue-cli 这 ...

  7. 快速入门系列--WebAPI--03框架你值得拥有

    接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...

  8. 前端学习 node 快速入门 系列 —— 初步认识 node

    其他章节请看: 前端学习 node 快速入门 系列 初步认识 node node 是什么 node(或者称node.js)是 javaScript(以下简称js) 运行时的一个环境.不是一门语言. 以 ...

  9. 前端学习 node 快速入门 系列 —— npm

    其他章节请看: 前端学习 node 快速入门 系列 npm npm 是什么 npm 是 node 的包管理器,绝大多数 javascript 相关的包都放在 npm 上. 所谓包,就是别人提供出来供他 ...

随机推荐

  1. Go语言中利用append巧妙的删除slice切片中的元素

    package main import ( "fmt" ) //删除函数 func remove(s []string, i int) []string { return appe ...

  2. ECharts系列 (01):地图三级下钻

    前言 最近项目中用到了地图下钻功能,GitHub上找到了一个轮子 - echarts3-chinese-map-drill-down,启动项目看了一下Demo,动画衔接的很流畅,感觉做的非常棒,膜拜大 ...

  3. Hdu 4821 (字符串hash+map)

    题目链接https://vjudge.net/problem/HDU-4821 题意:给定字符串S ,询问用几个子串满足 : 1.长度为n*len  . 2. n个子串都不相同. 题解:倒序hash将 ...

  4. P1055_ISBN号码(JAVA语言)

    题目描述 每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括9位数字.1位识别码和3位分隔符, 其规定格式如x-xxx-xxxxx-x,其中符号-就是分隔符(键盘上的减号), 最后一位是 ...

  5. 比Django官方实现更好的分页组件+Bootstrap整合

    前言 Django全家桶自带的分页组件只能说能满足分页这个功能,但是没那么好用就是了 Django的分页效果 django-pure-pagination分页效果 使用方法 首先安装: pip ins ...

  6. 前端 | vxe-table 翻页保留复选框状态

    0 前言 在前端开发过程中时常会遇到表格相关的显示与处理.组件库通常都会提供表格组件,对于展示.简单操作这些常用功能通常也够用:但如果需要更多的定制或进行比较复杂的操作,组件库自带的组件可能会捉襟见肘 ...

  7. 谷歌SRE运维模式解读

    谷歌SRE运维模式解读 前面我和你分享了一些关于运维组织架构和协作模式转型的内容,为了便于我们更加全面地了解先进的运维模式,今天我们再来谈一下谷歌的SRE(Site Reliability Engin ...

  8. Python面向对象练习题

    1.模拟栈操作原理 先进后出 ​1.初始化(创建一个存储数据的列表) 2.进栈使用列表保存数据 ​ 3.出栈 使用列表删除数据 ​ 4.查看栈顶元素 切片获取列表最后一位数据 ​5.判断是否为空栈 6 ...

  9. 201871030107-常雅伦 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告

    项目 内容 课程班级博客链接 班级博客 这个作业要求链接 作业要求 我的课程学习目标 1.体验软件项目开发中的两人合作,练习结对编程(Pair programming).2.掌握Github协作开发程 ...

  10. .NET Core3.1 Dotnetty实战系列视频

    一.概要 由于在.net的环境当中对dotnetty相关资料相对较少,所以这里主要分享一个dotnetty使用教程希望能帮助到正在使用这套框架的开发者们.虽然这套框架已微软官方已经不在维护,但是这套框 ...