1.1
node.js
入口文件
//根目录下入口文件
//导入express模块
const express = require('express')
//创建实例
const app = express()
const joi = require('joi')
//引入cors中间件 配置为全局中间件 支持跨域
const cors = require('cors')
app.use(cors())
//引入配置解析 `application/x-www-form-urlencoded` 格式的表单数据的中间件
//express的内置中间件
app.use(express.urlencoded({ extended: false }))
//为了简化res.send()代码 封装一个函数 声明一个中间件 在之后的路由中使用
app.use((req, res, next) => {
//定义函数
res.cc = (err,status=1) => {
res.send({
status,
message:err instanceof Error ? err.message:err
})
}
next()
})
//导入并注册路由模块
const userRouter = require('./router/user')
app.use('/api', userRouter)//api为访问前缀
//定义错误级别的中间件
app.use(function (err, req, res, next) {
// 数据验证失败
if (err instanceof joi.ValidationError) return res.cc(err)
// 未知错误
res.cc(err)
})
//监听启动
app.listen(3007, () => {
console.log('api server running at http://127.0.0.1:3007')
})
路由
//只存放客户端请求与处理函数之间的关系
//引入express
const express=require('express')
//创建路由对象
const router = express.Router()
//导入router_handler中的user.js
const userHandler = require('../router_handler/user.js')
//导入验证数据的中间件
const expressJoi = require('@escook/express-joi')
//导入需要验证的规则对象
const {text}=require('../schema/user.js')
//注册新用户 使用这个中间件
router.post('/reguser',expressJoi(text),userHandler.regUser )
//登录
router.post('/login',userHandler.login )
//路由对象共享
module.exports = router
路由函数
//抽离用户路由模块中的处理函数
//导入数据库模块
const db = require('../db/index.js')
//导入bcryptjs模块
const bcrypt=require('bcryptjs')
//注册模块
exports.regUser = (req, res) => {
//1.检测表单是否合法
//接收表单数据
const userinfo = req.body
//判断数据是否合法
// if (!userinfo.username || !userinfo.password) {
// return res.send({
// status: 1,
// message:'用户名或者密码不能为空'
// })
// }
//2.监测用户名是否被占用 需要导入数据库模块
const sql1 = 'select * from ev_users where username=?'
db.query(sql1, userinfo.username, (err,results) => {
if (err) {
// return res.send({status:1 ,message:err.message})
return res.cc(err)
}
if (results.length > 0) { //查询返回的是数组 如果数组里有数据 长度大于零 说明用户已存在
// return res.send({status:1, message:'用户名已存在 请更换'})
return res.cc('用户名已存在 请更换')
}
//给符合要求的用户密码加密
userinfo.password = bcrypt.hashSync(userinfo.password, 10)
//插入注册的新用户信息
const sql2 = 'insert into ev_users set ?'
db.query(sql2, { username: userinfo.username, password: userinfo.password }, (err, results) => {
if (err) {
// return res.send({status:1 ,message:err.message})
return res.cc(err)
}
//语句执行成功但是影响行数不唯一 也是失败
if (results.affectedRows !== 1) {
// return res.send({status:1, message:'注册用户失败 稍后再试'})
return res.cc('注册用户失败 稍后再试')
}
//注册成功
// res.send({ status: 0, message: '注册成功' })
res.cc('注册成功',0)
})
})
}
//登录模块
exports.login = (req,res) => {
res.send('login ok')
}
与数据库联系
//导入mysql模块
const mysql = require('mysql')
//与数据库联系
const db = mysql.createPool({
host: '127.0.0.1',
user: 'root',
password: 'admin123',
database:'my_db_01'
})
//暴露
module.exports = db
验证规则
//优化表单数据验证
//用户信息验证规则模块
const joi = require('joi')
//用户名验证规则
const username = joi.string().alphanum().min(1).max(10).required()
//密码验证规则
const password = joi.string().pattern(/^[\S]{6,12}$/).required() //非空字符6-12位
//暴露验证规则
exports.text = {
body: { //验证表单数据req.body上的username password
username,
password
},
}
user
//抽离用户路由模块中的处理函数
//导入数据库模块
const db = require('../db/index.js')
//导入bcryptjs模块
const bcrypt = require('bcryptjs')
//导入jsonwebtoken包
const jwt = require('jsonwebtoken')
//导入配置密匙的文件
const config=require('../config.js')
//注册模块
exports.regUser = (req, res) => {
//1.检测表单是否合法
//接收表单数据
const userinfo = req.body
//判断数据是否合法
// if (!userinfo.username || !userinfo.password) {
// return res.send({
// status: 1,
// message:'用户名或者密码不能为空'
// })
// }
//2.监测用户名是否被占用 需要导入数据库模块
const sql1 = 'select * from ev_users where username=?'
db.query(sql1, userinfo.username, (err,results) => {
if (err) {
// return res.send({status:1 ,message:err.message})
return res.cc(err)
}
if (results.length > 0) { //查询返回的是数组 如果数组里有数据 长度大于零 说明用户已存在
// return res.send({status:1, message:'用户名已存在 请更换'})
return res.cc('用户名已存在 请更换')
}
//给符合要求的用户密码加密
userinfo.password = bcrypt.hashSync(userinfo.password, 10)
//插入注册的新用户信息
const sql2 = 'insert into ev_users set ?'
db.query(sql2, { username: userinfo.username, password: userinfo.password }, (err, results) => {
if (err) {
// return res.send({status:1 ,message:err.message})
return res.cc(err)
}
//语句执行成功但是影响行数不唯一 也是失败
if (results.affectedRows !== 1) {
// return res.send({status:1, message:'注册用户失败 稍后再试'})
return res.cc('注册用户失败 稍后再试')
}
//注册成功
// res.send({ status: 0, message: '注册成功' })
res.cc('注册成功',0)
})
})
}
//登录模块
exports.login = (req, res) => {
//1.检测表单数据是否合法 在router 下user.js已经配置检测用户信息的中间件
//2.根据用户名查询用户数据
const userinfo=req.body
const sql = 'select * from ev_users where username=?'
db.query(sql, userinfo.username, (err,results) => {
if (err) {
return res.cc(err)
}
if (results.length !== 1) {
return res.cc(' 用户名不正确 登录失败')
}
//用户名正确 接下来判断输入的密码是否正确
//数据库密码与用户输入的密码进行对比 调用bcrypt.compareSync 返回布尔值
const compareResult=bcrypt.compareSync(userinfo.password,results[0].password)
if (!compareResult) {
return res.cc('登录失败')
}
//登录成功 服务器生成token字符串
//因为是保存在客户端的 所以要剔除密码 头像这两个敏感信息
const user = { ...results[0], password: '', user_pic: '' }
//生成token字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, {
expiresIn:'10h'
})
//将生成的token响应给客户端
res.send({
status: 0,
message: '登陆成功',
token:'Bearer '+tokenStr
})
})
}
userinfo
const db = require('../db/index.js')
const bcrypt = require('bcryptjs')
exports.getUserInfo = (req,res) => {
//获取用户基本信息 查询数据库是否有用户信息
//导入数据库模块
const db = require('../db/index.js')
const sql1='select id, username,nickname,email,user_pic from ev_users where id=?'
db.query(sql1, req.user.id, (err,results) => {
if (err) {
return res.cc(err)
}
if (results.length !== 1) {
return res.cc('获取用户失败')
}
res.send({
status: 0,
message: '获取用户信息成功',
data: results[0],
})
})
}
exports.updateUserInfo = (req,res) => {
const sql2 = 'update ev_users set ? where id=?'
db.query(sql2, [req.body, req.body.id], (err, results) => {
if (err) {
return res.cc(err)
}
if (results.affectedRows !== 1) {
return res.cc('更新失败')
}
return res.cc('更新成功',0)
})
}
exports.updatePassword = (req, res) => {
//查询用户是否存在
const sql3 = 'select * from ev_users where id=?'
db.query(sql3, req.user.id, (err, results) => {
if (err) {
return res.cc(err)
}
if (results.length !== 1) {
return res.cc('用户不存在')
}
//判断用户输入密码正确与否 需要引入bcrypt 模块
const compareResult=bcrypt.compareSync(req.body.oldPwd,results[0].password)
if (!compareResult) {
return res.cc('原密码错误')
}
//将新密码加密并且更新数据库密码
//加密
const newPwd = bcrypt.hashSync(req.body.newPwd, 10)
//更新
const sql4 = 'update ev_users set password=? where id=?'
db.query(sql4, [newPwd, req.user.id], (err, results) => {
if (err) {
return res.cc(err)
}
if (results.affectedRows !== 1) {
return res.cc('更换失败')
}
res.cc('更换成功',0)
})
})
}
配置userinfo路由
//此模块是用户登录后的个人中心
const express = require('express')
const router = express.Router()
const userinfo_handler = require('../router_handler/userinfo')
//配置更新的验证表单数据的模块
const expressJoi = require('@escook/express-joi')
const {update}=require('../schema/user.js')
const {update_pwd}=require('../schema/user')
router.get('/userinfo', userinfo_handler.getUserInfo)
//更新用户信息
router.post('/userinfo', expressJoi(update), userinfo_handler.updateUserInfo)
//重置密码路由
router.post('/updatepwd',expressJoi(update_pwd) ,userinfo_handler.updatePassword)
module.exports=router
验证
//优化表单数据验证
//用户信息验证规则模块
const joi = require('joi')
//用户名验证规则
const username = joi.string().alphanum().min(1).max(10).required()
//密码验证规则
const password = joi.string().pattern(/^[\S]{6,12}$/).required() //非空字符6-12位
//id验证规则
const id=joi.number().integer().min(1).required()
//nickname验证规则
const nickname=joi.string().required()
//email验证规则
const email=joi.string().email().required()
//暴露验证规则
exports.text = {
body: { //验证表单数据req.body上的username password
username,
password,
},
}
exports.update = {
body: {
id,
nickname,
email
}
}
exports.update_pwd = {
body: {
oldPwd: password,
newPwd:joi.not(joi.ref('oldPwd')).concat(password)
}
}
复盘node.js部分项目
1.初始化包管理配置文件 用来检测安装的第三方包的相关信息
2.使用npm i 安装所需要的第三方包 指定版本
3.配置入口文件app.js 启动服务器
4.配置路由模块(单纯的对应关系)(router.js)
5.抽离路由函数关系模块(router_handler.js)
6.创建与数据库的联系的文件(index.js)
7.验证规则文件(user.js)
8.单独定义密匙文件(config.js)
9.在入口文件引入所需的全局中间件
10.优化重复代码 定义全局中间件封装函数
11.注意配置中间件时的顺序问题 错误中间件在路由之后
复盘axios部分
1.使用json-server快速搭建一个假的服务器
2.复盘具体写法
<button>GET</button>
<button>POST</button>
<button>PUT</button>
<button>DELETE</button>
<script>
const btns=document.querySelectorAll('button')
btns[0].onclick=function(){
axios({
method:'GET',
url:'http://localhost:3000/posts/2',
}).then((response)=>{
console.log(response)
})
}
//往db.json里面添加数据
btns[1].onclick=function(){
axios({
method:'POST',
url:'http://localhost:3000/posts',//这里没有具体id
//请求体
data:{
title:'天气'
}
}).then((response)=>{
console.log(response)
})
}
//更新数据
btns[2].onclick=function(){
axios({
method:'PUT',
url:'http://localhost:3000/posts/3',
data:{
title:'季节'
}
}).then((response)=>{
console.log(response)
})
}
//删除数据
btns[3].onclick=function(){
axios({
method:'DELETE',
url:'http://localhost:3000/posts/6'
})
}
</script>
3,默认配置
axios.defaults.method='GET'
axios.defaults.baseURL='http://localhost:3000'
axios.defaults.params={id:100}//url参数
axios.defaults.timeout=3000 //三秒钟结果未返回取消请求
btns[0].onclick=function(){
axios({
url:'posts',
}).then((response)=>{
console.log(response)
})
}
4.拦截器以及取消请求
复习vue重点
数据代理原理解析(简单的例子)
<script>
//数据代理原理
let obj={x:90}
let obj2={y:0}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x=value
}
})
</script>
key的原理
<!--
虚拟DOM算法
旧虚拟DOM与新虚拟DOM对比 找到相同的key
用index作为key:对数据进行:逆序添加 删除等破坏顺序操作:会产生没有必要的DOM更新 效果没问题 效率低
如果有输入性DOM则会产生错误 界面有问题
建议使用唯一标识作为key 学号 id 等
-->
<body>
<div class="root1">
<ul>
<li v-for="(p,index) in persons" :key="p.id">{{p.name}}
<input type="text">
</li>
<button @click="add">点我向前添加一个成员信息</button>
</ul>
</div>
</body>
<script>new Vue({
el:'.root1',
data:{
persons:[
{ id:'001',name:'张三'},
{ id:'002',name:'李四'},
{ id:'003',name:'王五'}
]
},
methods:{
add(){
const p ={id:'004',name:'老刘'}
this.persons.unshift(p)//往前加 push在后加
}
}
})
</script>
监测数据改变原理
1.传入data
2.加工data
3.vm._data=data
属性值一变 调用seter 重新解析模板 生成新的虚拟DOM 进行算法对比 更新页面
1.vue会监视data中所有层次的数据
2.如何监测对象中的数据:
通过setter实现 并且要在new vue时就传入监测数据
(1)后追加的属性 vue不做响应式的处理
(2)如需要响应式 使用以下API:
Vue.set(target propertyName value) 或者 vm.$set()
3.监测数组中的数据
(1)调用原生对应的数组的方法
(2)重新解析模板 更新页面
4.修改数组元素方法
除了一些特定的API 还可以使用vue.set()
5.vue.set不能给vm和vm的根数据对象vm._data添加属性
组件注意点(以school为例)
1.school组件本身是一个VueComponent的构造函数 是Vue.extend生成的
2.写 <school/>标签 会解析创建school组件的实例对象
3.每次调用Vue.extend 返回的是全新的VueComponent
4.组件配置中 this指向是VueComponent实例对象(简称vc) new vue指向是vue实例对象(简称vm)
生命周期
beforeCreat:无法访问data数据和methods方法
初始化:数据代理 数据监测
created:可以访问data methods
vue开始解析 生成虚拟dom(内存中) 页面不显示解析好的内容
beforeMount:页面是未经vue解析的
将内存中的DOM转为真实的
mounted:页面是vue编译好的DOM
beforeUpdate:数据是新的 页面是旧的 数据页面不同步
beforeDestroy:清除定时器 解绑自定义事件 原生DOM还可以操作 可以访问数据 调用方法
但是对数据的修改不会触发更新
基本列表
<ul>
<li v-for="(p,index) in persons" :key="index">{{p.name}} {{index}}
</li>
</ul>
<ul>
<li v-for="(value,k) in car" :key="k">{{k}}--{{value}}</li>
</ul>
</div>
</body>
<script>new Vue({
el:'.root1',
data:{
//遍历数组
persons:[
//数据有顺序用数组存
//每个人是个对象
{ id:'001',name:'张三'},
{ id:'002',name:'张三'},
{ id:'003',name:'张三'}
],
//遍历对象
car:{
name:'aodi',
price:'1200',
color:'pink'
}
}
})
列表过滤
new Vue({
el:'.root1',
data:{
keyword:'',
persons:[
{ id:'001',name:'马冬梅',age:19},
{ id:'002',name:'周冬雨',age:19},
{ id:'003',name:'周杰伦',age:19},
{ id:'003',name:'温兆伦',age:19}
]
},
computed:{
filpersons(){
//将过滤出的数据返回
return this.persons.filter((p)=>{
//过滤出包含关键字的数据
return p.name.indexOf(this.keyword)!== -1
})
}
}
})
姓名案例
计算属性
<div class="root1">
姓: <input type="text" v-model="firstName">
名:<input type="text" v-model="lastName">
全名:{{fullName}}
</div>
</body>
<script>
new Vue({
el:'.root1',
data:{
firstName:'张',
lastName:'三'
},
computed:{
//不需要在数据里配置fullname属性 是计算出来的
//执行完往vm上放了个fullname的属性
fullName(){
//读取fullname时会调用get 初次读取还有数据被修改时被调用
//简写相当于fullname()变为了getter使用
return this.firstName+this.lastName
}
}
})
</script>
methods
<div class="root1">
姓: <input type="text" v-model="firstname">
名:<input type="text" v-model="lastname">
<!-- 必须加括号 是函数的返回值展示 -->
全名:{{fullname()}}
</div>
</body>
<script>
new Vue({
el:'.root1',
data:{
firstname:'张',
lastname:'三'
},
methods:{
//函数
fullname(){
//手动调用 插值语法 需要展示返回值 需要写return
return this.firstname+this.lastname
}
}
})
</script>
条件渲染
<div class="root1">
<h2>当前n的值为:{{n}}</h2>
<button @click="n++">点我n加一</button>
<div v-show="false"> 1234 </div>
<div v-if="false"> 1234 </div>
<div v-if="n==1">n的值是1</div>
<!--if执行完后不看else if 即使else-if条件为真-->
<div v-else-if="n==1">n的值是2</div>
<div v-else-if="n==3">n的值是3</div>
<!-- v-else 前面条件均不成立 则执行 不需要写条件-->
<div v-else></div>
<!-- v-if 只能和template配合使用 页面渲染的时候 template消失 不影响只有h2的结构-->
<template >
<h2>哈哈</h2>
<h2>哈哈</h2>
<h2>哈哈</h2>
</template>
</div>
</body>
<script>
new Vue({
el:'.root1',
data:{
name:'张三',
n:0
}
})
</script>
尚品汇
1-7
(1)准备
1.1:node + webpack + VScode + git
2)脚手架使用
2: vue init webpack 项目的名字
3|4:vue create 项目名称
脚手架目录:public + assets文件夹区别
node_modules:放置项目依赖的地方
public:一般放置一些共用的静态资源,打包上线的时候,public文件夹里面资源原封不动打包到dist文件夹里面
src:程序员源代码文件夹
-----assets文件夹:经常放置一些静态资源(图片),assets文件夹里面资源webpack会进行打包为一个模块(js文件夹里面)
-----components文件夹:一般放置非路由组件(或者项目共用的组件)
App.vue 唯一的根组件
main.js 入口文件【程序最先执行的文件】
babel.config.js:babel配置文件
package.json:看到项目描述、项目依赖、项目运行指令
README.md:项目说明文件
3)脚手架下载下来的项目稍微配置一下
3.1:浏览器自动打开
在package.json文件中
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
3.2关闭eslint校验工具
创建vue.config.js文件:需要对外暴露
module.exports = {
lintOnSave:false,
}
3.3 src文件夹的别名的设置
因为项目大的时候src(源代码文件夹):里面目录会很多,找文件不方便,设置src文件夹的别名的好处,找文件会方便一些
创建jsconfig.json文件
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
},
"exclude": [
"node_modules",
"dist"
]
4)路由的配置
vue-router
路由分为KV
前端路由:
K即为URL(网络资源定位符)
V即为相应的路由组件
(5)
5.1路由的一个分析
确定项目结构顺序:上中下 -----只有中间部分的V在发生变化,中间部分应该使用的是路由组件
2个非路由组件|四个路由组件
两个非路由组件:Header 、Footer
路由组件:Home、Search、Login(没有底部的Footer组件,带有二维码的)、Register(没有底部的Footer组件,带二维码的)
5.2安装路由
cnpm install --save vue-router
--save:可以让你安装的依赖,在package.json文件当中进行记录
5.3创建路由组件【一般放在views|pages文件夹】
5.4配置路由,配置完四个路由组件
6)创建非路由组件(2个:Header、Footer)
非路由组件使用分为几步:
第一步:定义
第二步:引入
第三步:注册
第四步:使用
项目采用的less样式,浏览器不识别less语法,需要一些loader进行处理,把less语法转换为CSS语法
1:安装less less-loader@6
2:需要在style标签的身上加上lang="less",不添加样式不生效