WebRTC音视频开发:React+Flutter+Go实战
上QQ阅读APP看书,第一时间看更新

3.2 新建示例工程

当我们准备好了开发工具及开发环境后,接下来需要建立一个HTML5示例工程来详细阐述WebRTC技术在Web上的应用。示例采用React作为UI展示,所以你需要了解以下基础知识。

·React:掌握React的状态控制、属性传递以及组件创建等。

·AntDesign:AntDesign为React的一个UI框架,这里主要用来进行界面布局及组件展示。

·Node:Node主要作为程序的运行环境,需要了解npm及package.json配置等。

·Webpack:React工程配置打包工具。

·JavaScript:React使用的编程语言,需要了解ES6语法及规范,如箭头函数的使用方法。

·CSS:界面布局及细节展示使用。

·HTML:HTML标签使用,尤其是Video、Audio的使用。

首先,新建一个工程目录,命名为h5-samples,按照如下所示创建文件及目录。


├── README.md(项目说明)
├── configs(配置文件目录)
│   ├──server.crt(证书文件)
│   └──server.key (证书Key)
├── dist(build后生成文件目录)
├── package.json(项目描述及库引用文件)
├── .bablelrc(bable配置)
├── src(示例源码)
│   ├── App.jsx(主组件)
│   ├── Camera.jsx(摄像头示例)
│   ├── Microphone.jsx(麦克风示例)
│   └── index.jsx(首页)
├── styles(样式目录)
│   └── css(css目录)
│       ├── canvas.scss(画板样式)
│       ├── record.scss(录制样式)
│       └── styles.scss(全局样式)
└── webpack.config.js(打包配置文件)

其中,src及styles/css目录为示例源码及样式目录,这里暂时不需要添加文件,随着示例的创建会逐步添加。

configs目录下的两个文件为证书相关文件,可以使用源码中提供的文件,正式发布项目需要申请商业证书。使用证书的原因是WebRTC技术需要使用HTTPS安全协议才能正常使用。

dist目录为存放打包后生成文件的目录,你可以使用npm build命令打包示例工程,然后把dist目录下的文件部署至Nginx下即可发布。开发测试阶段只需要使用npm start命令启动一个本地HTTPS服务即可。

3.2.1 package.json配置

首先,在工程目录下新建package.json文件,此文件的作用有以下几点:

·工程描述:包括工程名称、作者、版本、项目描述以及关键字等。

·项目脚本:如npm start表示启动项目。

·开发依赖库:项目开发中使用到的依赖库。

·生产依赖库:项目开发完成后发布使用到的依赖库。

完整的配置信息如下所示。


{
    //项目名称
    "name": "h5-samples",
    //版本号
    "version": "0.0.1",
    //项目描述
    "description": "h5 webrtc samples",
    "main": "src/index.jsx",//入口程序
    "scripts": {
        //可使用npm build命令构建打包程序
        "build": "webpack --mode=production --config webpack.config.js",
        //可使用npm start命令启动程序
       "start": "webpack-dev-server --config ./webpack.config.js --mode development 
          --open --https --cert ./configs/server.crt --key  ./configs/server.key"
    },
    //作者
    "author": "kangshaojun",
    //授权
    "license": "MIT",
    //开发库
    "devDependencies": {
        //语法转换库,如ES6转ES5
        "@babel/core": "^7.4.3",
        "@babel/plugin-proposal-class-properties": "^7.4.4",
        "@babel/plugin-transform-runtime": "^7.4.4",
        "@babel/preset-env": "^7.4.3",
        "@babel/preset-react": "^7.0.0",
        "@babel/runtime": "^7.4.4",
        "babel-loader": "^8.0.5",
        "babel-plugin-import": "^1.13.0",
        //CSS加载器
        "css-loader": "^3.2.0",
        //webpack文本插件
        "extract-text-webpack-plugin": "^4.0.0-beta.0",
        //node使用sass库
        "node-sass": "^4.9.2",
        //sass加载库
        "sass-loader": "^7.0.3",
        //样式加载器
        "style-loader": "^0.23.1",
        //打包工具
        "webpack": "^4.30.0",
        "webpack-cli": "^3.3.1",
        "webpack-dev-server": "^3.3.1",
        //文件拷贝插件
        "copy-webpack-plugin": "^5.0.5"
    },
    //引用库
    "dependencies": {
        //ant desgin组件
        "antd": "^4.1.1",
        //文件拷贝插件
        "copy-webpack-plugin": "^5.0.5",
        //react使用的mdi图标库
        "mdi-react": "^6.4.0",
        //react相关库
        "react": "^16.8.6",
        "react-dom": "^16.8.6",
        "react-mdi": "^0.5.7",
        "react-router-dom": "^5.1.2",
        "reactjs-localstorage": "0.0.8"
    },
    //关键字
    "keywords": [
        "h5",
        "webrtc",
        "js"
    ]
}

你可以查询此项目中用到的开发库的作用是什么,尽量做到精简,如果依赖的库过多,会增加项目的复杂度。

package.json文件的本质就是一个json文件,不是代码文件,所以不能在上面添加“//”注释,这里添加注释的目的是解释每一个配置项的作用,实际使用时需要去掉这些注释。

如果想增加一个库,以react为例,可以使用如下命令进行安装。其中@16.8.6为react的版本,如果不指定,则安装的是最新版本。--save表示要保存至package.json中。


npm install react@16.8.6 --save

如果想卸载一个库,以react为例,可以使用如下命令进行卸载。--save表示将react库的引用从package.json中去除。


npm uninstall react@16.8.6 --save

当准备好package.json文件后,使用npm install命令安装所有依赖库,安装完成后会在项目根目录下生成一个node_modules文件夹,如图3-4所示。

图3-4 node_modules文件夹

一定不要在node_modules里手动添加或删除库文件,而是要使用npm命令来维护项目依赖。如果手动添加库文件,当使用npm install命令后会把手动添加的库文件移除。

scripts配置下有两个配置项作用,如下所示。

·build:使用npm build命令可以打包构建应用程序,打包后可以进行程序发布。

·start:使用npm start命令可以启动一个HTTPS服务,方便程序调试开发。

其中,npm build命令调用了webpack,关于webpack的配置及使用后面会有详细介绍。npm start命令调用了webpack-dev-server,可以指定参数启动一个HTTPS的服务,复制如下两个文件至configs文件夹中。


./configs/cert.pem 
./configs/key.pem

3.2.2 babel支持

babel是一种JavaScript语法编译器,在前端开发过程中,由于浏览器的版本和兼容性问题,很多JavaScript的新方法和特性都受到了使用限制。使用babel可以将代码中JavaScript代码编译成兼容大多数主流浏览器的代码。

进入项目根目录添加.babelrc文件,添加如下代码即可。注意文件名前需要添加一个点。


{
    //预制套件
    "presets": [
        //处理转译需求
        "@babel/preset-env",
        //处理react
        "@babel/preset-react"
    ],
    //插件
    "plugins": [
        "@babel/plugin-transform-runtime",
        "@babel/plugin-proposal-class-properties",
        ["import", {
            "libraryName": "antd",
            "libraryDirectory": "es",
            "style": "css"
        }]
    ]
}

3.2.3 webpack配置

webpack本质上是一个现代JavaScript应用程序的静态模块打包器(bundler)。webpack处理应用程序时,会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或多个bundler。webpack的核心概念及配置项请参考https://www.webpackjs.com/concepts/文档。

进入项目根目录后,添加webpack.config.js文件,添加如下完整配置。


//引用webpack
const webpack = require('webpack');
//模块导出
module.exports = {
    //入口文件
    entry: './src/index.jsx',
    //开发调试时可以看到源码
    devtool: 'source-map',
    //模块
    module: {
        //规则
        rules: [
            //加载js|jsx源码文件
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: ['babel-loader']
            }, 
            //加载scss|less|css等样式文件
            {
                test: /\.(scss|less|css)$/,
                use: ['style-loader', 'css-loader', 'sass-loader']
            },
        ]
    },
    //配置如何寻找模块所对应的文件
    resolve: {
        //寻找所有js、jsx文件
        extensions: ['*', '.js', '.jsx']
    },
    //输出文件配置
    output: {
        //输出路径 __dirname表示当前目录
        path: __dirname + '/dist',
        //公共路径为项目根目录
        publicPath: '/',
        //打包后输出文件名
        filename: 'samples.js'
    },
    //webpack插件
    plugins: [
        //热加载插件
        new webpack.HotModuleReplacementPlugin(),
    ],
    //开发服务器配置
    devServer: {
        //加载内容目录
        contentBase: './dist',
        //是否热加载
        hot: true,
        //加载IP地址
        host: '0.0.0.0',
    }
};

其中,entry:'./src/index.jsx'为项目的入口文件,一定要命名为index.jsx。dist目录为项目打包生成的文件存放的目录。打包后输出的文件名为samples.js,这个文件和dist/index.html里引用的js文件是一一对应的。另外,注意devtool:'source-map',加上此配置的目的是当我们需要在浏览器里调试React程序时可以看到其源码,从而可以进行断点调试。如图3-5所示,在Chrome浏览器的程序调试窗口里,代码的第26行被打上了断点。

图3-5 Chrome断点示意图

3.2.4 首页模板文件

配置好webpack后就可以打包应用了,打包后的文件为一个js文件。需要添加一个首页把它引入才能生效。打开项目根目录下的dist目录添加index.html文件。添加如下代码。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>H5 WebRTC Samples</title>
</head>

<body>
    <!-- 此处一定要指定id,react组件渲染在App这个div里 -->
    <div id="app" style="height:100%"></div>
    <!-- 引入打包后的js文件 -->
    <script src="samples.js"></script>
</body>

</html>

这里的id为App的div,一定要添加,react的根组件是渲染在这个div里,如果没有它,react的内容显示不出来。另外,samples.js是webpack打包后生成的js文件,需要引入,这个文件名和webpack.config.js里输出的文件名一一对应。这时还不能使用npm build命令来生成samples.js文件,因为react的入口文件还没有添加,执行命令会报错,导致打包不成功。

3.2.5 全局样式

全局样式中提供了一些默认样式,如视频的默认大小、背景色等。打开根目录下的styles/css/styles.scss文件,添加如下代码。


//html样式
html {
    //垂直方向滚动
    overflow-y: scroll;
}
//body样式
body {
    //字体加粗
    font-weight: 300;
    //文字自动换行
    word-break: break-word;
}
//标题1
h1 {
    border-bottom: 1px solid #ccc;
    font-family: 'Roboto', sans-serif;
    font-weight: 500;
    font-size: 22px;
}
//标题2
h2 {
    color: #444;
    font-weight: 500;
}
//标题3
h3 {
    border-top: 1px solid #eee;
    color: #666;
    font-weight: 500;
    margin: 10px 0 10px 0;
    white-space: nowrap;
}
//主容器
.container {
    //左右居中
    margin: 0 auto 0 auto;
    //最大宽度
    max-width: 600px;
    padding: 10px 0px 10px 0px;
}
//视频默认样式
.video {
    //背景色
    background: #222;
    margin: 0 0 20px 0;
    //宽高值,宽高比为4:3
    width: 640px;
    height: 480px;
}
//画布默认样式
.canvas {
    background-color: #ccc;
    //宽高值,宽高比为4:3
    width: 640px;
    height: 480px;
}
//错误警告样式
.warning {
    color: red;
    fontv-weight: 400;
}

视频界面和画布默认大小是640×480,这是一个4:3的宽高比,还可以采用16:9的宽屏比例,如640×360。

样式采用scss的原因是当我们在开发调试时,每次修改文件后保存会立即刷新样式,而不需要每次都从首页点进来测试,从而提高开发效率。

3.2.6 入口文件

入口文件为React程序的第一个jsx文件,主要作用是引入全局样式、主组件以及将主组件渲染至首页里。打开项目根目录下的src目录添加index.jsx文件。添加如下代码。


import React from "react";
import ReactDOM from "react-dom";
//导入主组件
import App from "./App";
//导入antd样式
import "antd/dist/antd.css";
//导入全局样式
import "../styles/css/styles.scss";

//将根组件App渲染至首页div里
ReactDOM.render( <App />, document.getElementById("app"));

这里引入的是全局的样式,当某个组件里如果引入了局部样式,则会覆盖全局样式。

3.2.7 主组件及路由

打开src目录,添加App.jsx及Samples.jsx文件。首先来看App.jsx的作用,它就是用程序的根组件,里面主要用来配置全局路由,如摄像头示例的路由、麦克风示例的路由等。添加代码,如下所示。


import React from "react";
import { HashRouter as Router,Route,} from 'react-router-dom';
//导入示例
import Samples from './Samples';

//主组件
class App extends React.Component {

    render() {
        //路由配置
        return <Router>
            <div>
                {/* 首页 */}
                <Route exact path="/" component={Samples} />
            </div>
        </Router>
    }
}
//导出主组件
export default App;

其中,根路径是直接路由向Samples页面跳转的,Samples页面里放了WebRTC的所有示例。所以此页面需要添加一个列表来展示所有示例,代码如下所示。


import React from "react";
import {List} from "antd";
import {Link} from 'react-router-dom';
//标题和路径
const data = [
  {title:'首页',path:'/'},
];
//示例组件
class Samples extends React.Component {

    render() {
    
        return <div>
            {/* 示例列表 */}
            <List
                header={<div>WebRTC示例</div>}
                footer={<div>Footer</div>}
                bordered
                //数据源
                dataSource={data}
                //列表项
                renderItem={item => (
                    <List.Item>
                        {/* 链接 */}
                        <Link to={item['path']}>{item['title']}</Link>
                    </List.Item>
                )}
            />
        </div>
    }
}
//导出示例组件
export default Samples;

完成上述步骤后,就可以启动应用程序了。进入项目根目录,输入npm start命令。控制台输出内容如图3-6所示。

图3-6 启动示例程序

当出现Compiled successfully提示时表示编译并启动成功。这时在浏览器地址栏中输入https://0.0.0.0:8080/#/即可访问页面,如图3-7所示。

图3-7 首次运行效果图

值得注意的是,地址栏里提示是不安全的链接,这是因为使用的是自定义签名,正式发布应用程序后使用正式证书即可。