Express框架 发表于 2023-04-20 更新于 2023-04-20
字数总计: 6k 阅读时长: 30分钟 阅读量:
QX-AI初始化中...
暂无预设简介,请点击下方生成AI简介按钮。
express框架 express 是一个web框架,也是npm的一个工具包,功能和http模块类似,但功能更加强大,开发服务端效率更高
基本使用步骤:
导入express框架
创建服务器实例对象
绑定事件
监听端口,启动服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const express = require ('express' );const app = express ();app.get ('/home' , (req, res ) => { res.end ('Welcome' ); }) app.listen (3000 , () => { console .log ('服务启动' ); });
路由 路由: 确定如何响应客户端对特定端点的请求
使用方法,由请求方法、路径、回调函数组成 1 app.<method>(path,(req, res) => {})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 app.get ('/home' , (req, res ) => { res.end ('Welcome /home' ); }) app.get ('/' , (req, res ) => { res.end ('Welcome /' ); }); app.post ('/login' , (req, res ) => { res.end ('Welcome /login' ); }); app.all ('/test' , (req, res )=> { res.end ('Welcome /test' ); }); app.all ('*' , (req, res )=> { res.end ('404' ); });
获取请求报文 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 app.get ('/' , (req, res ) => { console .log (req.method ); console .log (req.url ); console .log (req.httpVersion ); console .log (req.headers ); console .log (req.path ); console .log (req.query ); console .log (req.ip ); console .log (req.get ('host' )); res.send ('Welcome /' ); });
路由参数 路由参数指的是 URL 路径中的参数(数据),如多篇文章按1.html、2.html往后排,匹配路由参数从而无需写多个路由
获取路由参数:req.params.id
1 2 3 app.get ('/:id.html' ,(req, res ) => { res.send ('id为' + req.params .id ); });
路由参数练习:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 { "singers" : [ { "singer_name" : "周杰伦" , "singer_pic" : "http://y.gtimg.cn/music/photo_new/T001R150x150M0000025NhlN2yWrP4.webp" , "other_name" : "Jay Chou" , "singer_id" : 4558 , "id" : 1 } , { "singer_name" : "林俊杰" , "singer_pic" : "http://y.gtimg.cn/music/photo_new/T001R150x150M000001BLpXF2DyJe2.webp" , "other_name" : "JJ Lin" , "singer_id" : 4286 , "id" : 2 } , { "singer_name" : "G.E.M. 邓紫棋" , "singer_pic" : "http://y.gtimg.cn/music/photo_new/T001R150x150M000001fNHEf1SFEFN.webp" , "other_name" : "Gloria Tang" , "singer_id" : 13948 , "id" : 3 } ] }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 const express = require ('express' );const app = express ();const {singers} = require ('./JSON/singers.json' );app.get ('/singer/:id.html' ,(req, res ) => { let {id} = req.params ; let result = singers.find ((item )=> { if (item.id === Number (id)){ return true ; } }); if (!result){ res.send ("404" ); return ; } res.send (` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h2>${result.singer_name} </h2> <img src=${result.singer_pic} }></img> </body> </html> ` );}); app.listen (3000 , () => { console .log ('服务启动' ); })
响应设置 一般响应
1 2 3 4 5 6 7 8 9 10 11 12 13 app.get ('/' , (req, res )=> { res.statusCode = 200 ; res.statusMessage = 'OK' ; res.setHeader ('Content-Type' , 'text/html;charset=UTF-8' ); res.status (200 ); res.set ('Content-Type' , 'text/html' ); res.send ('你好' ); res.status (200 ).set ('xxx' ,'yyy' ).send ('你好' ); });
其它响应,一般不能多个同时成功响应
1 2 3 4 5 6 7 8 9 10 11 12 13 app.get ('/' , (req, res )=> { res.redirect ('https://www.baidu.com/' ); res.download ('../3.mp4' ); res.json ({ a :1 }); res.sendFile (__dirname + '/package.json' ); });
中间件 中间件(Middleware)本质是一个回调函数
中间件函数可以像路由回调一样访问请求对象(request),响应对象(response)
作用: 使用函数封装公共操作,简化代码
类型: 全局中间件、路由中间件
每一个请求到达服务端后都会执行全局中间件函数 满足某个路由规则的请求会触发指定路由中间件函数
声明中间件函数 1 2 3 4 5 6 function middleware (req,res,next ){next ();}
全局中间件 每一个请求到达服务端后都会执行全局中间件函数
app.use是专门解析中间件函数的方法,使用之后express会将http请求响应对象交给中间件函数,中间件函数处理完通过next()方法传递给下一中间件函数,直到返回响应数据
可以使用 app.use() 定义多个全局中间件
案例:
利用中间件对所有路由记录访问路径和用户ip 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function middleware1 (req, res, next ) { let {url, ip} = req; fs.appendFileSync (path.resolve (__dirname + '/1.log' ), `${url} ${ip} \r\n` ); next (); } function middleware2 (req, res, next ) { let {url, ip} = req; console .log (url, ip); next (); } app.use (middleware1); app.use (middleware2); app.get ('/home' , (req, res )=> { res.send ('首页' ); }); app.get ('/admin' , (req, res )=> { res.send ('后台' ); });
路由中间件 满足某个路由规则的请求会触发指定路由中间件函数
只需要对某一些路由进行功能封装,则使用路由中间件,可以设置多个中间件函数
格式 1 app.get ('/路径' ,`中间件函数1` ,`中间件函数2` ,(req,res )=> {});
案例:
访问后台和设置页的请求必须携带code为123的参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function checkCode (req, res, next ){ if (req.query .code === '123' ){ next (); }else { res.send ('code参数错误' ); } } app.get ('/home' , (req, res )=> { res.send ('首页' ); }); app.get ('/admin' , checkCode, (req, res )=> { res.send ('后台' ); }); app.get ('/setting' , checkCode, (req, res )=> { res.send ('设置' ); });
静态资源中间件 express内置处理静态资源的中间件
1 app.use (express.static ('<静态资源目录>' ));
设置后,所有静态资源都会根据静态资源目录去寻找,index.html、index.css等为默认打开的资源
果静态资源与路由规则同时匹配,从上而下谁先匹配谁就响应
一般用路由响应动态资源,如搜索结果等,用静态资源中间件响应静态资源,如html、css等
1 2 app.use (express.static ('../public-test' ));
获取请求体 express 可以使用 body-parser 包处理请求体,该包内置许多中间件来处理请求体,需要先npm安装npm i body-parser
包内常用中间件,当中间件解析完请求后会向 req 对象上添加 body 属性,里面包含了请求体对象
1 2 3 4 var jsonParser = bodyParser.json ()var urlencodedParser = bodyParser.urlencoded ({ extended : false })
案例:
获取表单post提交的用户名和密码,get请求则显示登录页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const bodyParser = require ('body-parser' );app.get ('/login' , (req, res )=> { res.sendFile (__dirname + '/login.html' ); }); var jsonParser = bodyParser.json () var urlencodedParser = bodyParser.urlencoded ({ extended : false })app.post ('/login' , urlencodedParser, (req, res ) => { console .log (req.body ); res.send ('登录成功!' ); });
防盗链 请求报文中的 referer 会携带当前访问资源的域名和端口,限制 referer 的值即可实现防盗链
全局中间件实现防盗链 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 app.use ((req, res, next ) => { let referer = req.get ('referer' ); if (referer) { let url = new URL (referer); if (url.hostname !== '127.0.0.1' ) { res.status (404 ).send ('404 Not Found' ); return ; } } next (); });
路由模块化Router Router 是一个完整的中间件和路由系统,相当于一个小型的 app 对象。
声明Router和app 1 2 const router = express.Router ();const app = express ();
home.js一个完整的路由 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const express = require ('express' );const router = express.Router ();router.get ('/home' , (req, res )=> { res.send ('首页' ); }); router.get ('/search' , (req, res )=> { res.send ('搜索' ); }); module .exports = router;
在主文件需要导入路由并使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const express = require ('express' );const homeRouter = require ('./home.js' );const adminRuter = require ('./admin.js' );const app = express ();app.use (homeRouter); app.use (adminRuter); app.all ('*' , (req, res ) => { res.send ('404 Not Found' ); }); app.listen (3000 , ()=> { console .log ('服务启动' ); });
app.use可以添加一个路径参数,来给路由添加前缀
1 2 app.use ('/' , indexRouter); app.use ('/users' , usersRouter);
模板引擎ejs 模板引擎是分离用户界面 和业务数据 的一种技术,在许多语言都有的一种通用技术
EJS 是一个高效的 Javascript 的模板引擎,用于分离html和服务端js
1 2 3 4 5 6 7 8 9 10 11 const ejs = require ('ejs' );let str1 = 'qx' ;let str2 = `${str1} chuckle` ;console .log (str2);let str = '<%= str %>chuckle' ;let result = ejs.render (str, {str :str1});console .log (result);
将 ejs 模板写在单独的文件再由fs读入
1 2 3 4 let str = fs.readFileSync ('./01.html' ).toString ();let [name,age] = ['chuckle' ,19 ]let result = ejs.render (str, {name,age});console .log (result);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > </head > <body > <h2 > <%= name %></h2 > <h3 > <%= age %></h3 > </body > </html >
ejs列表渲染:
原生js, js和html耦合在一起 1 2 3 4 5 6 7 8 9 const arr = [1 ,2 ,3 ,4 ];let html = '<ul>' ;arr.forEach (item => { html += `<li>${item} </li>` ; }) html += '</ul>' ; console .log (html);
使用ejs, 分离html和服务端js 1 2 3 4 5 6 7 8 9 10 const arr = [1 ,2 ,3 ,4 ];let html = `<ul> <% arr.forEach(item => { %> <li><%= item %></li> <% }) %> </ul>` ;let result = ejs.render (html, {arr});console .log (result);
ejs条件渲染
1 2 3 4 5 6 7 8 9 10 let isLogin = true ;let html = `<% if(isLogin) { %> <span>登陆</span> <%}else{ %> <span>注册</span> <%} %>` ;let result = ejs.render (html, {isLogin});console .log (result);
express使用ejs 使用 app.set() 设置express使用的模板引擎、设置模板文件存放位置
1 2 3 4 app.set ('view engine' , 'ejs' ); app.set ('views' , path.resolve (__dirname +'/ejs' ));
然后就可以用res调用render方法响应渲染好的页面
1 2 3 4 let name = 'chuckle' ;res.render ('home' , {name});
完整案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const express = require ('express' );const path = require ('path' );const app = express ();app.set ('view engine' , 'ejs' ); app.set ('views' , path.resolve (__dirname +'/ejs' )); app.get ('/' , (req, res ) => { let name = 'chuckle' ; res.render ('home' , {name}); }); app.listen (3000 , ()=> { console .log ('服务启动' ); });
express-generator express-generator是一个node的自动化创建项目工具,类似于vue-cli,能快速构建express项目标准骨架
安装后会暴露一个全局命令:express
安装 1 2 npm install -g express-generator express -v
构建项目:
app.js是项目主文件;
views目录用于存放页面文件;
routes目录用于存放路由文件;
public用于存放静态文件;
bin中的www是项目的启动文件;
文件上传 在index.js中添加两个路由,get显示表单,post接收上传的数据
1 2 3 4 5 6 7 8 9 10 router.get ('/portrait' , (req,res ) => { res.render ('portrait' ); }); router.post ('/portrait' , (req, res )=> { res.send ('上传成功' ); });
新建 portrait.ejs 创建表单页面,对于文件上传,form表单必须添加 enctype=”multipart/form-data” 属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h3>文件上传</h3> <form action="/portrait" method="post" enctype="multipart/form-data"> 用户名:<input type="text" name="username"><br /> 头像:<input type="file" name="portrait"><br /> <button>提交</button> </form> </body> </html>
处理文件上传需要使用 formidable 包,它是用于解析表单数据的 Node.js 模块,尤其是文件上传。
完整的index.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 var express = require ('express' );var router = express.Router ();const formidable = require ('formidable' );router.get ('/' , function (req, res, next ) { res.render ('index' , { title : 'Express' }); }); router.get ('/portrait' , (req,res ) => { res.render ('portrait' ); }); router.post ('/portrait' , (req, res )=> { const form = formidable ({ multiples : true , uploadDir : __dirname + '/../public/images' , keepExtensions : true }); form.parse (req, (err, fields, files ) => { if (err) { next (err); return ; } console .log (fields); console .log (files); let url = '/images/' + files.portrait .newFilename ; res.send (url); }); }); module .exports = router;
lowdb包 在保存一些简单数据时可以使用 lowdb 包,在json中进行增删改查数据,推荐使用1.0.0版本
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 const low = require ('lowdb' )const FileSync = require ('lowdb/adapters/FileSync' )const adapter = new FileSync ('db.json' )const db = low (adapter)db.defaults ({ posts : [], user : {} }).write () db.get ('posts' ) .push ({ id : 1 , title : 'lowdb is awesome' }) .write () db.get ('posts' ) .unshift ({ id : 2 , title : 'lowdb is awesome' }) .write () let value = db.get ('posts' ).value ();console .log (value);let single = db.get ('posts' ).find ({id : 1 }).value ();console .log (single);db.get ('posts' ).remove ({id : 1 }).write (); db.get ('posts' ).find ({id : 2 }).assign ({title : 'chuckle' }).write ();
案例-记账本
主要的一些文件
主要路由:
/routes/index.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 var express = require ('express' );var router = express.Router ();const low = require ('lowdb' )const FileSync = require ('lowdb/adapters/FileSync' )const adapter = new FileSync (__dirname + '/../data/db.json' )const db = low (adapter)const shortid = require ('shortid' );router.get ('/' , function (req, res, next ) { let content = db.get ('content' ).value (); res.render ('index' , {content}); }); router.get ('/add' , function (req, res, next ) { res.render ('add' ); }); router.post ('/add' , function (req, res, next ) { let id = shortid.generate (); db.get ('content' ).unshift ({id :id, ...req.body }).write (); res.render ('tip' ,{msg : '添加成功!' , url : '/' }); }); router.get ('/delete/:id' ,(req, res ) => { let id = req.params .id ; db.get ('content' ).remove ({id}).write (); res.render ('tip' ,{msg : '删除成功!' , url : '/' }); }); module .exports = router;
页面ejs:
/views/index.ejs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > 记账本</title > <link rel ="stylesheet" href ="/css/index.css" /> </head > <body > <div class ="content" > <div class ="content-box" > <div class ="title" > <h2 > 记账本</h2 > </div > <div class ="add" > <a href ="/add" > 添加</a > </div > <div class ="record-box" > <% content.forEach(item=>{%> <div class ="record-item" > <div class ="date" > <%= item.date %></div > <div class ="type <%= item.type==='支出'?'expenditure':'income'%>" > <%= item.type %> </div > <div class ="record-body" > <div class ="record-content" > <div class ="matter" > <%= item.matter %></div > <div class ="account" > <%= item.account %> 元</div > </div > <a href ="/delete/<%= item.id %>" class ="delete" > 删除</a > </div > </div > <%})%> </div > </div > </div > </body > </html >
/views/add.ejs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > 添加记录</title > <link rel ="stylesheet" href ="/css/add.css" /> </head > <body > <div class ="content" > <div class ="content-box" > <div class ="title" > <h2 > 添加记录</h2 > </div > <div class ="home" > <a href ="/" > 账单列表</a > </div > <form action ="/add" method ="post" > <div class ="form-item" > <label for ="matter" > 事项</label > <input class ="control" type ="text" name ="matter" id ="matter" /> </div > <div class ="form-item" > <label for ="date" > 时间</label > <input class ="control" type ="date" name ="date" id ="date" /> </div > <div class ="form-item" > <label for ="type" > 类型</label > <select class ="control" name ="type" id ="type" > <option selected ="" > 支出</option > <option > 收入</option > </select > </div > <div class ="form-item" > <label for ="account" > 金额</label > <input class ="control" type ="text" name ="account" id ="account" /> </div > <div class ="form-item" > <label for ="remark" > 备注</label > <textarea class ="control" name ="remark" id ="remark" > </textarea > </div > <hr /> <button > 添加</button > </form > </div > </div > </body > </html >
/views/tip.ejs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Document</title > <link rel ="stylesheet" href ="/css/tip.css" /> </head > <body > <div class ="content" > <h3 > <%= msg %></h3 > <a href ="<%= url %>" > 返回</a > </div > </body > </html >
css:
/public/css/index.css 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 body { font-family :"Microsoft YaHei" ,微软雅黑; color : #363636 ; } *{ padding : 0 ; margin : 0 ; box-sizing : border-box; text-decoration : none; } .content { max-width : 600px ; margin : 0 auto; } .content-box { display : flex; flex-direction : column; flex-wrap : wrap; align-content : center; margin : 0 10px ; } .content-box >*{ width : 100% ; } .title { padding : 20px 0 ; border-bottom : 1px solid rgb (220 , 220 , 220 ); } .title h2 { font-size : 30px ; font-weight : 500 ; } .add { margin-top : 10px ; padding-left : 20px ; } .add a { text-decoration : none; } .record-box { margin-top : 10px ; border-bottom : 1px solid rgb (220 , 220 , 220 ); } .record-item { margin-bottom : 15px ; border : 1px solid rgb (226 , 226 , 226 ); border-radius : 6px ; -webkit-border-radius : 6px ; -moz-border-radius : 6px ; -ms-border-radius : 6px ; -o-border-radius : 6px ; position : relative; } .record-item >.date { height : 34px ; background : rgb (174 , 209 , 237 ); padding : 6px 12px ; font-size : 14px ; line-height : 22px ; } .record-body { display : flex; flex-direction : row; flex-wrap : wrap; padding : 10px 20px ; justify-content : space-between; align-content : center; } .record-content { display : flex; flex-direction : row; flex-wrap : wrap; justify-content : space-between; flex : 1 ; } .record-body >.delete { padding-left : 10px ; margin-left : 10px ; border-left : 1px solid rgb (201 , 201 , 201 ); color : rgb (33 , 70 , 181 ); } .record-item >.type { position : absolute; top : 5px ; right : 10px ; font-size : 14px ; padding : 0 6px ; height : 22px ; line-height : 22px ; color : #fff ; border-radius : 4px ; -webkit-border-radius : 4px ; -moz-border-radius : 4px ; -ms-border-radius : 4px ; -o-border-radius : 4px ; } .type .expenditure { background : rgb (241 , 141 , 158 ); } .type .income { background : rgb (85 , 194 , 134 ); }
/public/css/add.css 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 body { font-family :"Microsoft YaHei" ,微软雅黑; color : #363636 ; } *{ padding : 0 ; margin : 0 ; box-sizing : border-box; text-decoration : none; } hr{ margin : 20px auto; border : 0 ; border-top : 1px solid rgb (220 , 220 , 220 ); } .content { max-width : 600px ; margin : 0 auto; } .content-box { display : flex; flex-direction : column; flex-wrap : wrap; align-content : center; margin : 0 10px ; } .content-box >*{ width : 100% ; } .home { margin-top : 10px ; padding-left : 10px ; } .title { padding : 20px 0 ; border-bottom : 1px solid rgb (220 , 220 , 220 ); } .title h2 { font-size : 30px ; font-weight : 500 ; } .content-box form { margin-top : 10px ; } .form-item { margin-bottom : 15px ; } .form-item label { display : block; height : 32px ; line-height : 32px ; font-size : 18px ; } .form-item >*{ width : 100% ; } .form-item >.control { padding : 6px 12px ; height : 36px ; font-size : 16px ; line-height : 36px ; color : #363636 ; border : 1px solid #ccc ; border-radius : 4px ; -webkit-border-radius : 4px ; -moz-border-radius : 4px ; -ms-border-radius : 4px ; -o-border-radius : 4px ; transition : all 0.3s ; -webkit-transition : all 0.3s ; -moz-transition : all 0.3s ; -ms-transition : all 0.3s ; -o-transition : all 0.3s ; outline : none; font-family :"Microsoft YaHei" ,微软雅黑; } .form-item >.control :focus { border-color : #66afe9 ; -webkit-box-shadow : inset 0 1px 1px rgba (0 ,0 ,0 ,.075 ), 0 0 8px rgba (102 , 175 , 233 , .6 ); box-shadow : inset 0 1px 1px rgba (0 ,0 ,0 ,.075 ), 0 0 8px rgba (102 , 175 , 233 , .6 ); } .form-item >textarea .control { padding : 8px 12px ; height : 100px ; line-height : 1 ; resize : none; } .content-box form >button { width : 100% ; padding : 6px 12px ; margin-bottom : 15px ; border : 1px solid rgb (57 , 162 , 204 ); background : rgb (37 , 173 , 204 ); border-radius : 4px ; -webkit-border-radius : 4px ; -moz-border-radius : 4px ; -ms-border-radius : 4px ; -o-border-radius : 4px ; font-size : 17px ; color : #fff ; transition : all 0.3s ; -webkit-transition : all 0.3s ; -moz-transition : all 0.3s ; -ms-transition : all 0.3s ; -o-transition : all 0.3s ; } .content-box form >button :hover { background : rgb (32 , 153 , 190 ); }
/public/css/tip.css 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 body { font-family :"Microsoft YaHei" ,微软雅黑; color : #363636 ; } *{ padding : 0 ; margin : 0 ; box-sizing : border-box; text-decoration : none; } .content { margin : 0 auto; max-width : 300px ; padding : 20px 30px ; background : rgba (77 , 190 , 215 , 0.6 ); margin-top : 20px ; display : flex; align-items : center; flex-direction : row; flex-wrap : wrap; justify-content : space-between; border-radius : 4px ; -webkit-border-radius : 4px ; -moz-border-radius : 4px ; -ms-border-radius : 4px ; -o-border-radius : 4px ; }