技术标签: 初遇你 node.js templates npm mongodb
目录
一.MongoDB 数据库
1.数据库概述及环境搭建
- 数据库软件 包含多个数据仓库,每个 数据仓库 中包含多个数据集合,每个 数据集合 中包含多条 文档
术语
解释说明
database
数据库,mongoDB中可以建立多个数据库
collection
集合,一组数据的集合,可以理解为 JavaScript数组
document
文档,一条具体的数据,可以理解为 JavaScript对象
field
字段,文档中的属性名称,可以理解为 JavaScript对象属性
- 使用 Node.js 操作 MongoDB数据库 需要使用 npm install mongoose 命令下载 mongoose 第三方模块
- 数据库启动: net start mongoDB 开启 MongoDB服务,否则 MongoDB 将无法连接
- 数据库连接:引入 mongoose第三方模块,使用 connect()方法 连接:const mongoose = require('mongoose');
- mongoose.connect('mongodb://localhost/playground', { useNewUrlParser: true }) // 自动创建不存在的数据库
- useNewUrlParser: true:链接数据库的过程中 系统提示的代码 需要进行添加
- mongoose.connect().then().catch() ←这个格式说明,数据库连接是 异步函数,返回 promise对象
- 连接成功:.then(() => console.log('数据库连接成功'))
- 连接失败:.catch(err => console.log(err, '数据库连接失败'));
2.MongoDB 增删改查
2.1 创建集合
- 创建集合分两步:①设定集合规则 ②创建集合
- 设定集合规则:const courseSchema = new mongoose.Schema({ name: String });
- 使用集合规则创建集合:mongoose.model('集合名称(表名)',集合规则)
- const CourseA = mongoose.model('Course', courseSchema)
- 集合名称必须首字母大写,到数据库会自动把集合名称(表名)转换成小写复数 // courses
- Course是.js文件中给数据库的的表名,courses是数据库中展示的表名,CourseA 是这个表在.js文件中的称呼
2.2 创建文档
- 创建文档 = 向集合中插入数据
- 方法一:①创建集合实例 ②调用实例对象下的 save()方法,将数据保存到数据库中
- 创建集合实例(表中一条数据):const data1 = new CourseA({ name:'茶茶子'});
- 将数据保存到数据库:data1.save()
- 方法二:集合.create()方法插入文档,通过 回调函数 / promise对象方法 获得结果
- 回调函数:Course.create({ author: '茶茶子' }, (err, result) => {console.log(err);console.log(result)})
- promise对象:Course.create({ author: '茶茶子' }).then(result => {console.log(result)})
2.3 mongoDB 数据库导入数据
- 找到 mongodb数据库的安装目录,将安装目录下的 bin目录放置在 系统path环境变量中
- mongoimport -d 数据库名称 -c 集合名称 --file 要导入的数据文件(注意 file前面两个--)
- 直接在当前目录 cmd命令窗口 运行该命令会失败 (找不到命令) ,必须重新打开 cmd命令窗口
- 向本地 mongoDB导入JSON文件:mongoimport -d playground -c users --file user.json
2.4 查询文档
- .find():查询所有文档,返回 promise对象,可通过.then()查询结果
- 查询结果result 是个数组,包含很多对象,即 .find().then() 得到数组
- 查询结果始终用数组保存返回,没有查询到,就返回空数组
- 查询所有文档:User.find().then(result => console.log(result));
- .find({属性:}):通过 属性字段 查找文档
- User.find({_id: '5c09f267aeb04b22f8460968'}).then(result => console.log(result))
- .findOne():返回一条文档对象,不是返回数组,默认返回当前集合中的第一条文档
- User.findOne({name: '李四'}).then(result => console.log(result))
- .find({属性:{$gt:,$lt:}}):查询集合中 年龄字段 大于20并且小于40 的文档
- User.find({age: {$gt: 20, $lt: 40}}).then(result => console.log(result))
- .find({属性:{$in:['']}}):查询集合中 hobbies字段值包含足球的 文档
- User.find({hobbies: {$in: ['足球']}}).then(result => console.log(result))
- .find().select(''):选择要查询的字段 空格隔开 不想查的字段 在前面加- 如不想查询_id → -_id
- User.find().select('name email -_id').then(result => console.log(result))
- .find().sort('+属性'):根据字段进行 升序排列
- User.find().sort('age').then(result => console.log(result))
- .find().sort('-属性'):根据字段进行 降序排列
- User.find().sort('-age').then(result => console.log(result))
- .find().skip(2).limit(3):查询文档 跳过前两条结果 限制显示3条结果 用于分页
- User.find().skip(2).limit(3).then(result => console.log(result)
2.5 删除文档
- .findOneAndDelete({}):查找一条文档并删除 返回被删文档 如果查询条件匹配多个文档 删除第一个匹配文档
- User.findOneAndDelete({_id: '9...998'}).then(result => console.log(result))
- .deleteMany({}):删除多条文档 传入 空对象 表示删除全部文档,返回对象{n:删除文档个数}
- User.deleteMany({}).then(result => console.log(result)) // { n: 6, ok: 1, deletedCount: 6 }
2.6 更新文档
- .updateOne({},{}):更新一个文档,返回 promise对象,可以采取 .then() 获取结果,只更新第一个符合条件的
- User.updateOne({name: '李四'}, {age: 120, name: '李狗蛋'}).then(result => console.log(result))
- .updateMany({},{}):更新多个文档,空对象表示更新全部文档
- User.updateMany({}, {age: 300}).then(result => console.log(result)
2.7 mongoose验证
- 创建集合规则时,设置当前字段的验证规则,验证失败就插入失败
const postSchema = new mongoose.Schema({ // 创建集合规则 title: { type: String, required: [true, '请传入文章标题'], // 必选字段 minlength: [2, '文章长度不能小于2'],// 字符串的最小长度 maxlength: [5, '文章长度最大不能超过5'], // 字符串的最大长度 trim: true // 去除字符串两边的空格 }, age: { type: Number, min: 18, // 数字的最小范围 }, publishDate: { type: Date, // 时间类型 default: Date.now // 默认值 }, category: { type: String, enum: { // 枚举 列举出当前字段可以拥有的值 values: ['html', 'css', 'javascript', 'node.js'], message: '分类名称要在一定的范围内才可以' // 自定义错误信息 }}, author: { type: String, validate: { // 自定义验证器 validator: v => { // 返回布尔值 true 验证成功 false 验证失败 v 要验证的值 return v && v.length > 4 // 存在这个值 并且这个值大于4 就返回true }, message: '传入的值不符合验证规则' // 自定义错误信息 }}}); // 创建集合 const Post = mongoose.model('Post', postSchema); // 相机和中插入值 这是利用了 promise对象 .create({}).then().catch() Post.create({title:'tea', age: 21, category: 'java', author: 'teameow'}) .then(result => console.log(result)) .catch(error => { const err = error.errors; // 获取错误信息对象 for (var attr in err) { // 循环错误信息对象 console.log(err[attr]['message']); // 将错误信息打印到控制台中 }})
2.8 集合关联
- 文章的 作者author 存储的是 用户的 id,用到集合关联
- 创建用户集合、文章集合,用户集合包含 name字段,文章集合包含 title字段、author字段
- 文章集合中的 author字段 与用户集合的 _id字段 关联,所以使用 固定语法 定义 author字段的 type属性:
- type: mongoose.Schema.Types.ObjectId(集合规则里面书写) ref: 'User' // 实现 作者 和 用户id 关联
- 生成用户集合、文章集合、创建用户、创建文章
- 通过查询文章集合的 author属性 可以跳转获取到 用户全部信息,即查询关联集合,用到 populate() 方法
- Post.find().populate('author').then(result => console.log(result))
const userSchema = new mongoose.Schema({ // 用户集合规则 name: { type: String, required: true }}); const postSchema = new mongoose.Schema({ // 文章集合规则 title: { type: String }, author: { type: mongoose.Schema.Types.ObjectId, // 作者id 是关联 用户id的 这是固定写法 ref: 'User' // 关联的集合 就是用户集合 }}); const User = mongoose.model('User', userSchema); // 用户集合 const Post = mongoose.model('Post', postSchema); // 文章集合 User.create({name: '茶茶子'}).then(result => console.log(result)); // 创建用户 // 创建文章 这里的文章作者就要复制 用户id 在数据库中的编号(系统自动生成的) Post.create({titile: 'wenzhang', author: '5c0caae2...'}).then(result => console.log(result)); // 使用 populate()方法进行关联集合查询,展示Post.author的详细信息 Post.find().populate('author').then(result => console.log(result))
2.9 用户信息增删查改
- 搭建网站服务器,实现客户端与服务器端的通信
- 连接数据库,创建用户集合,向集合中插入文档
- 当用户访问 /list 时,将所有用户信息查询出来
- 将用户信息和表格html 进行拼接并将拼接结果响应回客户端
- 当用户访问 /add 时,呈现表单页面,并实现添加用户信息功能
- 当用户访问 /modify 时,呈现修改页面,并实现修改用户信息功能,修改用户信息分为两大步骤:
- 增加页面路由呈现页面:在点击修改按钮的时候 将用户ID传递到当前页面,从数据库中查询当前用户信息 将用户信息展示到页面中
- 实现用户修改功能:指定表单的提交地址以及请求方式,接受客户端传递过来的修改信息 找到用户 将用户信息更改为最新的
- 当用户访问 /delete 时,实现用户删除功能
- 将数据库连接文件 index.js + User集合创建文件 user.js 这两个和数据库有关的文件,放在 model文件夹下
- 在 app.js 中引入 第三方模块 index.js 和 user.js
- 通过 node app.js 在浏览器中输入 localhost:3000/list 即可访问主页
- 此方法存在问题:拼接字符串非常不友好
二.模板引擎 artTemplate
1.基础概念
- 模板引擎是第三方模块,以更加友好的方式拼接字符串,使项目代码易于维护
- art-template 模板引擎:
- 在命令行工具中使用 npm install art-template 命令进行下载
- 使用 const template = require('art-template') 引入模板引擎
- 告诉模板引擎要拼接的数据和模板在哪 const html = template( 模板路径 , { 数据 });
- 使用 模板语法 告诉模板引擎,模板与数据应该如何进行拼接
- 模板文件 .art一般放在 views文件夹中
const template = require('art-template'); const path = require('path'); const views = path.join(__dirname, 'views', '01.art'); const html = template(views, { name: '茶茶子', content: '<h1>我是标题</h1>' // 模板文件中无法直接解析<h2>标签 需要{ {@}} <%-%> }) console.log(html);
2.语法
2.1 模板语法
- art-template 同时支持两种模板语法:标准语法 和 原始语法
- 标准语法可以让模板更容易读写,原始语法具有强大的逻辑处理能力
- 标准语法: { { 数据 }}
- 原始语法:<%=数据 %>
2.2 输出
- 将某项数据输出在模板中,标准语法和原始语法如下:
- 标准语法:{ { 数据 }}
- 原始语法:<%=数据 %> 注意 = 表示输出
2.3 原文输出
- 如果数据中携带 HTML标签,默认模板引擎不会解析标签,会将其转义后输出
- 标准语法:{ {@ 数据 }}
- 原始语法:<%- 数据 %> 注意是 = 换成了 -
2.4 条件判断
<!-- 标准语法 --> { {if age > 18}} 年龄大于18 { {else if age < 15 }} 年龄小于15 { {else}} 年龄不符合要求 { {/if}} // 一定不要忘记这个 { {/if}}!!! <!-- 原始语法 --> <% if (age > 18) { %> // 此处因为没有输出 所以不需要写 = 年龄大于18 <% } else if (age < 15) { %> 年龄小于15 <% } else { %> 年龄不符合要求 <% } %>
2.5 循环
- 标准语法:{ {each 数据}} { { $value.xxxx }} { {/each}}
- 原始语法:<% for() { %> <%= user[i].xxxx><% } %>
// .js文件中 如果串了一个 对象数组 给模板 const html = template(views, { users: [{name: '张三', age: 20, sex: '男'}, {name: '李四', age: 30, sex: '男'}, {name: '玛丽', age: 15, sex: '女'}] }); <!-- 模板文件 遍历标准语法 --> <ul> { {each users}} <li> // users是对象数组名 { {$value.name}} // value是对象数组中的一个对象 不要忘记$ 这是遍历对象的各个属性 { {$value.age}} { {$value.sex}} </li> { {/each}} </ul> <!-- 模板文件 遍历原始语法 --> <ul> <% for (var i = 0; i < users.length; i++) { %> <li> <%=users[i].name %> <%=users[i].age %> <%=users[i].sex %> </li> <% } %> </ul>
2.6 子模板
- 使用子模板可以将网站公共区块(头部、底部)抽离到单独的文件中。
- 标准语法:{ {include '模板'}}
- 原始语法:<%include('模板') %>
- const html = template(views, { msg: '我是首页' }); // .js文件
- 原始语法:<% include('./common/header.art') %> // 我是头部 ,这里的 include 相当于方法
- 标准语法:{ { include './common/footer.art' }} // 我是底部 // .art文件,这里的 include 相当于关键字
2.7 模板继承
- 使用 模板继承 可以将 网站HTML骨架 抽离到 单独文件中,其他页面模板可以继承骨架文件
const html = template(views, { msg: '首页模板' }); // .js文件 开发人员要写的信息 <!DOCTYPE html> // ./common/layout.art 模板骨架文件 用{ {block ''}} { {/block}} 拆坑 <head> <meta charset="UTF-8"> { {block 'link'}} { {/block}} </head> <body> { {block 'content'}} { {/block}} </body> { {extend './common/layout.art'}} // index.art 首页art 继承了模板骨架 { {extend ''}} { {block 'content'}} <p> { { msg }} </p> { {/block}} // 用{ {block ''}} { {/block}} 填坑 { {block 'link'}} <link rel="stylesheet" type="text/css" href="./main.css"> { {/block}}
2.8 模板配置
- 向模板中导入变量 template.defaults.imports.变量名 = 变量值;
- ↑↑↑↑↑ 导入第三方模块中的方法,使得模板可以用 第三方模块的方法
- 设置模板根目录 template.defaults.root = 模板目录
- 设置模板默认后缀 template.defaults.extname = '.art'
const template = require('art-template'); // .js文件 const path = require('path'); const dateFormat = require('dateformat'); // 这是一个 格式化时间 的第三方模块 // 模板设置 template.defaults.root = path.join(__dirname, 'views'); // 设置模板的根目录 template.defaults.imports.dateFormat = dateFormat; // 导入模板变量(要用的第三方模块) template.defaults.extname = '.html'; // 配置模板的默认后缀 const html = template('06.art', { time: new Date() // 获取的时间 是不友好的时间,要在 模板.art文件中调用 第三方模块dateformat处理 }); console.log(template('07', {})); // 这里没写模板后缀,但是会自动添加我们设置的默认后缀 console.log(html); // dateFormat(.js文件获得的要处理的时间, 时间格式'yyyy-mm-dd') { { dateFormat(time, 'yyyy-mm-dd')}} // .art文件 在模板文件中本来不可以调用第三方模块语法,设置后可以
3.学生档案管理
3.1 概述
- HTTP请求响应、数据库、模板引擎、静态资源访问
- 制作流程:
- 建立项目文件夹并生成项目描述文件:
- 创建网站服务器实现客户端和服务器端通信(数据库文件)
- 连接数据库并根据需求设计学员信息表(数据库文件)
- 创建路由并实现页面模板呈递(路由文件)
- 实现静态资源访问(静态资源文件)
- 实现学生信息添加功能(路由文件)
- 实现学生信息展示功能(路由文件)
3.2 第三方模块 router 实现路由
- const serveStatic = require('serve-static') // 引入serve-static模块获取创建静态资源服务功能的方法
- const serve = serveStatic('public') // 通过模块方法 获取路由对象
- router.get('/add', (req, res) => { ...
3.3 第三方模块 serve-static 实现静态资源访问服务
- const serveStatic = require('serve-static') // 引入serve-static模块获取创建静态资源服务功能的方法
- const serve = serveStatic('public') // 调用方法 创建静态资源服务 并指定 静态资源目录
- app.on('request', (req, res) => { ...
3.4 添加学生信息功能步骤分析
- 在模板的表单中指定请求地址(action="/add")与请求方式(method="post")
- 为各表单项添加 name属性(这些 name属性 被 post中的 data接收,拼成 formData,转换为对象格式存入数据库)
- 添加实现学生信息功能路由(router.post('/add', (req, res) => {})
- 接收客户端传递过来的学生信息(req.on('data', param => {})
- 将学生信息添加到数据库中(req.on('end', async() => {})
- 将页面重定向到学生信息列表页面(res.writeHead(301, {Location: '/list'}))
3.5 学生信息列表页面分析
- 从数据库中将所有的学生信息查询出来(students = await Student.find())
- 通过模板引擎将学生信息和 HTML模板进行拼接(let html = template('list.art', { students: students})
- 将拼接好的 HTML模板响应给客户端
<th>{ {$value.sex == '0' ? '男' : '女'}}</th> // 此处value指的是对象数组中的各个学生 <th> { {each $value.hobbies}} // 此处value指的是对象数组中的各个学生 <span>{ {$value}}</span> // 此处value指的是 爱好这个学生对象属性的值 { {/each}} </th>
三.Express框架
1.Express框架简介及初体验
- Express是基于 Node平台的 Web框架,使用 npm install express 命令下载
- 框架特性:
- 提供了简洁的 路由定义方式
- 对 获取HTTP请求参数 进行了简化处理
- 对 模板引擎 支持程度高,方便渲染 动态 HTML页面
- 提供了 中间件机制 有效控制 HTTP请求
- 使用 Express框架 创建 Web服务器:引入express模块, const app = express()
- send():内部会检测响应内容的类型、自动设置http状态码、自动设置响应的内容类型及编码:
const express = require('express'); // 引入express框架 const app = express(); // 创建网站服务器 app.get('/' , (req, res) => { // 当客户端发来 get请求 res.send('Hello. Express'); }) app.listen(3000); // 监听端口 console.log('网站服务器启动成功');
2.Express中间件
2.1 中间件简介
- 中间件 = 一堆方法,可以接收客户端请求、对请求做出响应,也可以将请求继续交给下一个中间件处理(next)
- 可以针对同一个请求设置多个中间件,对同一个请求进行多次处理
- 默认情况下,请求从上到下依次匹配中间件,一旦匹配成功,终止匹配
- next()方法:将请求的控制权交给下一个中间件,直到遇到结束请求的中间件
app.get('/request', (req, res, next) => { // 用不到 next()时,可以省略不写 next参数 req.name = "张三"; next(); // 此中间件没有处理完,next()交给下一个中间件 继续处理请求并作出响应 }); app.get('/request', (req, res) => { res.send(req.name); // 处理完成,结束请求,给客户端发出响应 });
2.2 app.use 中间件用法
- app.use 匹配所有的请求方式,可以直接传入请求处理函数,代表接收所有的请求
- app.use 第一个参数也可以传入请求地址,代表不论什么请求方式,只要是这个请求地址就接收这个请求
// 接收所有请求的中间件 不需要跟请求路径 app.use((req, res, next) => { console.log('请求走了app.use中间件'); next() }) // 跟请求路径 当客户端访问 /request请求时 走当前中间件 app.use('/request', (req, res, next) => { console.log('请求走了app.use / request中间件') next() })
2.3 中间件应用
- 路由保护,客户端在访问需要登录的页面时,用中间件判断用户登录状态,未登录则拦截请求,并直接响应
- 网站维护公告,在所有路由的 最上面 定义接收所有请求的中间件,app.use((req, res, next) => {}
- 为客户端响应 404状态码 及 提示信息 res.status(404).send('')
// 1.网站公告 // app.use((req, res, next) => { // res.send('当前网站正在维护...')}) // 2.路由保护 app.use('/admin', (req, res, next) => { let isLogin = true; // 用户没有登录 if (isLogin) { // 如果用户登录 next()// 让请求继续向下执行 }else { // 如果用户没有登录 直接对客户端做出响应 res.send('您还没有登录 不能访问 /admin页面') }}) app.get('/admin', (req, res) => { res.send('您已登录 可以访问当前页面') }) // 3.响应 404状态码及提示信息 res.status(404).send('') app.use((req, res, next) => { res.status(404).send('当前访问的页面不存在') })
2.4 错误处理中间件
- 程序执行时,可能会出现无法预料的错误,比如文件读取失败,数据库连接失败,错误处理中间件集中处理错误
- 程序出错时,调用 next()方法,并且将 err 传递给 next()方法,触发错误处理中间件
app.get('/index', (req, res, next) => { // throw new Error('程序发生了未知错误') fs.readFile('./01.js', 'utf8', (err, result) => { if (err != null) { // 读取文件时,错误对象不为空 next(err) // 将错误作为 参数 传递给next(),触发错误处理中间件 }else { res.send(result) }})}) app.use((err, req, res, next) => { // 错误处理中间件 res.status(500).send(err.message); })
2.5 捕获异步函数的错误
- try{} catch(){}:捕获 异步函数及其他同步代码 在执行过程中发生的错误,不能捕获 其他类型的API 发生的错误
- const promisify = require('util').promisify; // 将函数 改造为 异步函数
- const readFile = promisify(fs.readFile); // 改造 fs模块的 readFile方法 为异步方法
const promisify = require('util').promisify; // 将函数 改造为 异步函数 const fs = require('fs'); const readFile = promisify(fs.readFile); // 改造 fs模块的 readFile方法 为异步方法 app.get('/index', async (req, res, next) => { try { // 尝试执行这些函数,如果没有错误,就跳过catch,继续向下执行 await readFile('./aaa.js') // 这个文件不存在 }catch (ex) { next(ex); // catch()捕获到错误,通过 next() 交给下面的 错误处理中间件 处理错误 }}) app.use((err, req, res, next) => { res.status(500).send(err.message); })
3.Express框架请求处理
3.1 构建模块化路由
3.2 GET参数获取
- Express框架中,使用 req.query 即可获取GET参数,框架内部会将 GET参数 转换为 对象 并返回
- 接收地址栏中问号后面的参数,例如: http://localhost:3000/?name=TeaMeow&age=30
- app.get('/', (req, res) => {res.send(req.query)});
- 浏览器输出:{"name": "zhangsan", "age": "30"}
3.3 POST参数获取
- Express框架中,需要借助 第三方包 body-parser 将 请求体 中的参数转换为对象格式,并存储在 req.body 中
- extended: false 方法内部使用 querystring模块 处理请求参数的格式(推荐)
- extended: true 方法内部使用 qs模块 处理请求参数的格式
- const bodyParser = require('body-parser'); // 接收 POST请求参数 所用的第三方包
- 处理请求体中的 post参数 app.use():app.use(bodyParser.urlencoded({extended: false}))
- 处理后的 post请求参数 存储在 req.body:app.post('/add', (req, res) => {res.send(req.body) })
2.4 Express路由参数
- use()方法:里面可以接受 对象格式的内容 做处理
- get路由请求参数: /路由/:XXX/:XXX,如:'/index/:id/:name/:age',注意冒号
- 获取多个请求参数的方法:res.send( req.params )
app.use(fn ({a: 2})) // fn()方法接受了一个对象参数 {a:2}是实参对象属性 function fn (obj) { return function (req, res, next) { if (obj.a == 1) { console.log(req.url) }else { console.log(req.method)} next() // use()方法处理完成后 交给下一个中间件继续处理请求 }} // get路由请求参数 /路由/:XXX/:XXX app.get('/index/:id/:name/:age', (req, res) => { res.send(req.params)// 接收多个路由请求参数 }) // localhost:3000/index/123456/TeaMeow/21 每个参数都不能少,不然访问不到页面
2.5 静态资源访问
- Express内置的 express.static() 托管静态文件,例如:img文件、css文件、js文件等
- 实现静态资源访问功能:app.use('/static',express.static(path.join(__dirname, 'public')))
- http://localhost:3000/static/images/kitten.jpg
4.express-art-template 模板引擎
- 模板引擎官方在 原 art-template模板引擎 的基础上封装了 express-art-template,方便二者配合
- 使用 npm install art-template express-art-template 命令进行安装(两个第三方模块)
- 模板配置:
- 告诉express框架 使用 x模板引擎 渲染 x后缀的模板文件:
- app.engine('art', require('express-art-template'))
- 告诉express框架 模板存放的位置 第一个views是拼接后的完整路径 第二个views指文件夹
- app.set('views', path.join(__dirname, 'views'))
- 告诉express框架 模板默认后缀
- app.set('view engine', 'art');
- 渲染模板:res.render('模板文件',{ 需要拼接的数据 }),这个自动对数据进行拼接处理,并给客户端作出响应
- app.get('/list', (req, res) => { res.render('list', {msg: 'list page'})})
- app.locals 对象:将公共变量设置到 程序入口文件 app.locals对象,这个数据在 所有模板中 都可以获取到
- app.locals.users=[{},{}.....]
活动简介那些积极探索技术边界并持续对社区做出贡献的开发者是真正的技术英雄,是开发者的学习榜样,也是各个技术社区发展的生命力!2020年即将结束,CSDN向所有技术社区发起“社区之星”成长故事的征集邀请活动,汇聚各大社区所推举出的社区之星(年度贡献者)共同讲述他们的成长故事!活动亮点参与流程参与说明参与名额20个(暂定)可参与对象所有技术社区报名时间即日起 至 2021年1月3日曝光时间2021年1月4日 至 2021年1月21日说明入选的技术社区需要提供给CSDN .
Android.app.ActivityActivity是用户主要也是唯一打交道的途径。基本上所有的Activity都是和用户打交道的,你可以通过setContentView(View)来创建一个窗口。Activity常常是以全窗口的模式展示的,当然也有其他的展示方式:浮动窗口(windowIsFloating)或则嵌入到其他Activity(ActivityGroup)。基本上所有子类都会重...
Autoware是一个城市无人车的开源软件。https://github.com/CPFL/Autoware可有一下功能:3D Localization3D MappingPath PlanningPath FollowingAccel/Brake/Steering ControlData LoggingCar/Pedestrian/Object DetectionTraffic
描述写一个程序完成以下命令:new id ——新建一个指定编号为id的序列(id<10000)add id num——向编号为id的序列加入整数nummerge id1 id2——合并序列id1和id2中的数,并将id2清空unique id——去掉序列id中重复的元素out id ——从小到大输出编号为id的序列中的元素,以空格隔开输入第一行一个数n,表示有多少个命令( n<=200000)。以后n行每行一个命令。输出按题目要求输出。样例输入16new 1new 2.
最让人头疼的就是配环境,现在把自己的成功安Tensorflow的步骤记录在这里,留着以后参考吧,万一有用呢目 录MacOS + Tensorflow CPU版 1. 安anaconda 2. 建一个 conda 计算环境 3. 激活环境,安装 TensorFlow 4. 运行一段代码看看Win10 + Tensorflow CPU版 1. 安anaconda ...
Java语言控制LED灯背景材料准备1.编排线路2.Java后端代码2.1 LED 开关代码2.2 LED闪烁 + 回写日志3.前端4.视频演示背景最近心血来潮突然对树莓派产生了浓厚的兴趣,网上查阅很多资料都没找到好文章,讲解Java如何控制LED,有人会问玩树莓派为什么不用C语言或者Python,本人认为Java是世界上最好的语言(笔者因为懒,不想学其他语言)材料准备树莓派4b面包板(1个)LED灯(1个)电阻(1个)杜邦线 (2根公对公)1.编排线路线路: GPIO05(BCM
Problem Description XYZ-26进制数是一个每位都是大写字母的数字。 A、B、C、…、X、Y、Z 分别依次代表一个0 ~ 25 的数字,一个 n 位的26进制数转化成是10进制的规则如下 A0A1A2A3…An-1 的每一位代表的数字为a0a1a2a3…an-1 ,则该XYZ-26进制数的10进制值就为 m = a0 * 26^(n-1) + a1 * 26^(n-2) +
/************************************************************************* File Name: uva439.cpp Author: yubo Mail: yuz[email protected] Created Time: 2014/5/18 23:15:55reference:htt
转自: https://blog.csdn.net/rlnote/article/details/80711095git 项目选择 Clone or download 的Https地址.配置小乌龟,git=&gt;远端 URL改SSH地址为Https地址.
#include #include #include#include #include #include #include #include #include using namespace std;using namespace cv;// 定义点云类型 typedef pcl::PointXYZRGBA PointT;typedef pcl::PointCloud PointCloud;// 相机内参 const double ca...
document.body.addEventListener('touchmove', function (event) { event.preventDefault();}, false);
想从jdk1.8换到1.7,修改了JAVA_HOME,之后不起作用。按照网上的方法删除C:\Windows\System32目录下的java.exe、javaw.exe、javaws.exe三个文件删除环境变量Path中C:\ProgramData\Oracle\Java\javapath的配置 发现,都做了后,cmd中java -version依然显示老版本。解决方案:c...