自我总结前端vue笔记

三阶段笔记

node基础(书写简单api接口)

  • 什么是node.js

    • javascript的运行时环境

    • javascript运行在浏览器的时候:操作DOM,操作BOM,语法ECMAScript

    • javascript运行在node的时候:

      • 操作文件

      • 操作数据库

      • 开启web服务 ...

      • 不能操作DOM,不能操作BOM

  • node.js的安装

    • 版本识别: 16.14.2 node -v

    • LTS: 稳定版本,建议安装 Current: 最新版本

    • 主版本号.子版本号.修订版本号 v16.14.2

      • 子版本号为偶数的是稳定版本,为奇数的是非稳定版本

      • 修订版本号变化是是内部bug的一些处理

      • 建议安装LTS的偶数版本

  • 在node环境中运行js代码

    • 方法一:(不推荐)

      • 直接在终端运行: node+回车

      • 进入js编辑环境,就可以输入js代码

    • 方法二:

      • 把你要在node环境中运行的js代码写在一个js文件

      • 在终端运行: node js文件路径

    • node的版本管理

      • 我在公司要管理多个node项目

        • 项目一: node 10.16.0

        • 项目二: node 14.12.2

        • 就需要在电脑上安装多个不同的node版本,随时切换 就可以使用nvm这个node的版本管理工具

  • API

    • path模块(路径模块)

      • **path.join()**

        • 将所有给定的路径拼接成一个完整的路径

        • 规范化生成的路径

        • 语法:path.join('/目录1','目录2','目录3/目录4','目录5')

        • 返回值:/目录1/目录2/目录3/目录4/目录5

      • **path.resolve()**

        • 同path.join()

        • 区别:会将路径或者路径片段转换成绝对路径

        • path.resolve(__dirname,'static')

    • url模块

      • **url.parse()**

        • 作用:把网址解析成对象

        • 语法: url.parse(urlString[,parseQueryString, slashesDenoteHost])

        • urlString:表示url地址

        • parseQueryString:布尔值,如果为true,就和解析查询字符串,否则不解析

        • slashesDenoteHost:如果为true表示//后面,/前面是主机名(如果是完整的url地址不影响)

        • 返回值:url对象

      • **url.format()**

        • 作用: 把url对象解析成url地址字符串

        • 语法: url.format(url对象)

        • 返回值: url地址字符串

      • **url.resolve()**

        • 作用:把两段url片段,组成一个完整的url

        • 返回值: url地址字符串

    • querystring模块(对查询字符串进行更强大的解析)

      • ** querystring.stringify() **

        • 作用:把对象转换成查询字符串

        • 语法:querystring.stringify(要转换的对象[,自定义分隔符,自定义键值对之间的连接符])

        • 默认使用&进行分隔(由第二个参数决定分隔)

          • var o1 = querystring.stringify({
                name:'pipi',
                course:[11,22,33]
            },',')
            //name=pipi,course=11,course=22,course=33
        • 键值用=连接(由第三个参数决定连接)

          • var o1 = querystring.stringify({
                name:'pipi',
                course:[11,22,33]
            },',',':')
            //name:pipi,course:11,course:22,course:33

        • 返回值:查询字符串

        • var o1 = querystring.stringify({
              name:'kimi',
              course:['nodejs','vue','react']
          })
          //name=kimi&course=nodejs&course=vue&course=react

      • **querystring.parse()**

        • 作用:把查询字符串转换成对象

        • 语法:querystring.parse(要转换的查询字符串[,自定义分隔符,自定义键值

        • 返回值:对象

        • var o1 = querystring.parse("name=kiki&pwd=1234")
          // { name: 'kiki', pwd: '1234' }
      • ** querystring.escape()**

        • 把字符串进行url编码

        • var o1 = querystring.escape("http://www.baidu.com/search?wd=千锋教育");
          //http%3A%2F%2Fwww.baidu.com%2Fsearch%3Fwd%3D%E5%8D%83%E9%94%8B%E6%95%99%E8%82%B2
      • ** querystirng.unescape()**

        • 对url进行解码

        • var o1 = querystring.unescape('http://www.baidu.com/search?wd=千锋教育')
          //http://www.baidu.com/search?wd=千锋教育

    • fs模块

      • __filename:当前文件的绝对路径

      • __dirname: 当前文件所在目录的绝对路径

      • fs.stat(获取文件/文件夹信息)

        • fs.stat(__filename,(err,stats)=>{
            // err:是错误信息
            // stats:获取到的文件信息
            console.log(stats) ;// 对象
            // 判断是不是文件
            console.log(stats.isFile());
            // 判断是不是文件夹
            console.log(stats.isDirectory());
          })
      • fs.mkdir(创建文件夹)

        • fs.mkdir('./public',(err)=>{
            if(!err){
              // 如果没有错误信息
              console.log('创建文件夹成功')
            }
          })
          
      • fs.writeFile(向文件写入内容 异步)

        • fs.writeFile(filePath,text,(err)=>{
            if(!err){
              console.log('文件写入成功')
            }
          })
          
      • fs.appendFile (向文件追加内容 异步)

        • fs.appendFile(filePath,text,(err)=>{
            if(!err){
              console.log('成功写入文件')
            }
          })
          
      • fs.readFile(读取文件)

        • //方法一
          fs.readFile(filePath,(err,data)=>{
            if(!err){
              // 编码格式转换ss
              console.log(data.toString())ss
            }
          })
          
        • //方法二
          fs.readFile(filePath,'utf-8',(err,data)=>{
            if(!err){
              console.log(data)
            }
          })
          
      • fs.readdir(读取文件目录)

        • const dirPath = path.resolve(__dirname,'public')
          // 读取文件目录
          fs.readdir(dirPath,(err,files)=>{
            // err:错误
            // files:是一个数组,里面是读取到的文件夹里面的子文件/子文件夹名称
            if(!err){
              console.log(files)
            }
          })
          //返回一个s
          
      • fs.rename(文件名修改)

        • fs.rename("要重命名的文件地址","新名字",(err)=>{
            if(!err){
              console.log('文件名修改成功')
            }
          })
          
        • const fs = require('fs')
          const path = require('path')
          
          const filePath = path.resolve(__dirname,'public','js/a.js')
          const newName = path.resolve(__dirname,'public','js/index.js')
          
          fs.rename(filePath,newName,(err)=>{
            if(!err){
              console.log('文件名修改成功')
            }
          })
          
      • fs.rm(删除文件)

        • fs.rm(filePath,(err)=>{
            if(!err){
              console.log('文件删除成功')
            }
          })
          
      • fs.rm(删除文件夹)

        • s.rm(dirPath,{recursive:true},(err)=>{
            if(!err){
              console.log('文件夹删除成功')
            }
          })
          
        • 注:需要添加{recursive:true},表示递归删除文件夹里面的内容再删除文件夹

    • events模块

      • 创建事件触发器(第一步)

        • const EventEmitter = require('events');

        • const emitter = new EventEmitter()

      • 定义事件监听器(第二步)

        • emitter.on(可触发多次)

        • emitter.on('many',()=>{
            console.log('many事件发生了')
          })
          
        • emitter.once(只触发一次)

        • emitter.once('one',()=>{
            console.log('one事件发生了')
          })
          
        • 触发(emit)

          • emitter.emit('many')
            emitter.emit('one')
            
    • stream模块(流模块)

      • fs.createReadStream(fromPath) 文件读取流(inp)

      • fs.createWriteStream(toPath) 文件写入流(out)

      • 读取资源的相关事件

        • once

          • inp.once('data',(chunk)=>{
            	//chunk获取到的数据流
            })
            
        • on

          • inp.on('data',(chunk)=>{
            	//chunk获取到的数据流
            })
            
          • inp.on('end',()=>{
              console.log('数据读取完成');
              out.end()
            })
            
          • inp.on('error',()=>{
              console.log('数据写入错误')
            })
            
        • pipe

          • inp.pipe(out) 
            //文件读取流(inp)与 文件写入流(out)之间建立一个管道
            
    • http模块

      • http.request()

        • 语法:

          • 第一步: 书写请求行

            • const req = http.request(url,callback) const req = http.request(options,callback)

          • 第二步: 书写请求体

            • req.write()

          • 第三步: 结束请求

            • req.end()

        • 例子

          • //方法一
            const https = require('https')
            const url = "https://www.lagou.com/"
            let html = "";
            const req = https.request(url,(res)=>{
              // 设置响应的编码集
              res.setEncoding('utf-8');
              // 当有数据可以读取的时候触发data事件
              res.on('data',(chunk)=>{
                html += chunk;
              })
              // 当数据读取完成的时候触发end事件
              res.on('end',()=>{
                console.log(html)
              })
              // 当有数据读取错误的时候触发error事件
              res.on('error',(err)=>{
                console.log(err)
              })
            })
            req.end()
            
          • 方式二
            const req = https.request({
              host:"www.lagou.com",
              ports:"443",
              method:'get',
              path:'/',
              headers:{}
            },(res)=>{
              // 设置响应的编码集
              res.setEncoding('utf-8');
              // 当有数据可以读取的时候触发data事件
              res.on('data',(chunk)=>{
                html += chunk;
              })
              // 当数据读取完成的时候触发end事件
              res.on('end',()=>{
                console.log(html)
              })
              // 当有数据读取错误的时候触发error事件
              res.on('error',(err)=>{
                console.log(err)
              })
            })
            req.end();
            

      • http.get()

        • 语法:同http.request()

        • 区别:

          • 不用设置method,是get请求

          • 不用书写req.write(),get请求没有请求主体

          • 不用调用req.end(),会自动调用

        • 例子:

          • https.get(url,(res)=>{
              // 设置响应的编码集
              res.setEncoding('utf-8');
              // 当有数据可以读取的时候触发data事件
              res.on('data',(chunk)=>{
                html += chunk;
              })
              // 当数据读取完成的时候触发end事件
              res.on('end',()=>{
                console.log(html)
              })
              // 当有数据读取错误的时候触发error事件
              res.on('error',(err)=>{
                console.log(err)
              })
            })
            

      • http.createServer()

        • 作用:开启服务器

        • nodejs实现webServer

          • 原理:根据req.url来区分客户的请求路径,根据不同的访问路径,给用户响应不同的资源

          • const http = require('http');
            const fs = require('fs')
            const path = require('path')
            let num = 0;
            const server = http.createServer((req,res)=>{
              // 只要有客户端请求,就会执行这个函数
              // req:请求对象
              // res:响应对象
              // 获取请求路径
              let url = req.url;
              if(url!="/favicon.ico"){
                if(url=="/"){
                  url = "/index.html"
                }
                // 设置响应头
                res.setHeader('content-type',"text/html;charset=utf-8")
            
                // 通过请求路径,拼接出文件的本地地址
                const  filePath = path.resolve(__dirname,'public'+url)
                fs.stat(filePath,(err,stats)=>{
                  // 文件路径不存在
                  if(err){
                    res.statusCode = 404;
                    res.end(`${url} 不存在`)
                  }
                  // 如果是一个文件
                  else if(stats.isFile()){
                    res.statusCode = 200;
                    fs.createReadStream(filePath).pipe(res)
                  }
                  // 如果是一个文件夹
                  else if(stats.isDirectory()){
                    fs.readdir(filePath,(err,files)=>{
                      res.statusCode = 200;
                      res.end(files.join('/'));
                    })
                  }
                })
              }
              
            })
            
            server.listen(8080,()=>{
              console.log('服务器开启在:http://10.20.158.132:8080')
            })
            
        • 特别注意: 没有http.post方法

    • cheerio(第三方模块)

      • 安装:npm install cheerio -D

      • 使用方法和jquery类型

      • jquery操作的是dom节点

      • cheerio操作html字符串

      • 可用于爬虫(具体看E:\2022资料\tree-grup\资料\01node-api\源代码\spider.js)

    • express(web 开发框架)

      • 下载:npm install express -S

      • 使用三步走

        • //导入:
          const express = require('express');
          const path = require('path')
          const app = express()
          //静态资源中间件函数: express自己写好的,你可以直接使用
          //express.static(root, [options])
          app.use(express.static(path.resolve(__dirname,'文件夹名')))
          //监听
          app.listen(9090,()=>{
              console.log("http://10.20.158.121:9090")
          })
          
          • [利用 Express 托管静态文件 - Express 中文文档 | Express 中文网 (expressjs.com.cn)](https://www.expressjs.com.cn/starter/static-files.html)

      • app.use('路由',中间件函数)

        • 如果不写路由,就可以匹配所有的请求

        • 如果写路由,就必须以路由开头的才能匹配

        • app.use('/abc',(req,res)=>{
              //只要有请求,就会执行这个函数是经过express封装的请求对象,可以使用原生node的方法,也有自己的方法
          	//req:是经过express封装的请求对象,可以使用原生node的方法,也有自己的方法
              //res:响应对象,可以使用原生node的方法,也有自己的方法
              res.end('all')
          })
          
      • 路由中间件

        • 如何实现?

        • 一个主js文件(main.js)

          • const express = require('express');
            // 这个包是为了解析请求主体(要安装)
            const bodyParser = require('body-parser')
            const app = express()
            
            app.use(bodyParser.json()) // 解析 application/json 格式的请求主体
            app.use(bodyParser.urlencoded({ extended: true })) // 解析 application/x-www-form-urlencoded 格式的请求主体
            
            
            app.use('/user',require('./routers/user.js'))
            
            
            app.listen(8080)
            

        • 一个专门存储接口api的文件夹(routers),里面放接口文件(user.js)

          • 根据需求写接口

            • // 用户相关接口
                // /user/login?un=xxx&pw=xxx
                // /user/register?un=xxx&pw=xxx
              
          • // 路由中间件
            const express = require('express');
            const router = express.Router();
            
            // 登录接口
            router.get('/login',(req,res)=>{
              // 获取请求的查询字符串
              console.log(req.query);
              res.json(req.query)
            })
            
            // 注册接口
            router.post('/register',(req,res)=>{
              // 如果要使用req.body获取请求主体
              // 需要安装: npm install body-parser -S
              // 获取请求的请求主体
              console.log(req.body)
              res.json(req.body)
            })
            
            注意:导出
            module.exports = router;
            
      • app.get('路由',中间件函数)

        • 匹配get请求,路由要求精确匹配

        • 如果有写*,表示匹配所有路由

        • app.get('/login',(req,res)=>{
            // express封装的res上的方法
            // res.send('字符串')
            res.send('hello world')
          
            // res.json(对象)
            res.json({a:1,b:2})
          })
          
        • 响应:(选其一)

          • res.send('字符串')

          • res.json(对象)

      • app.post('路由',中间件函数)

        • 匹配post请求,路由要求精确匹配

        • 如果有写*,表示匹配所有路由

        • app.post('/api',(req,res)=>{
            res.json({path:'/api',message:"post"})
          })
          

      • app.all('路由',中间件函数)

        • 匹配所有请求,路由要求精确匹配

        • 如果有写*,表示匹配所有路由

        • 专门用于处理404的

        • app.all('*',(req,res)=>{ 
            res.json({code:404})
          })
          
      • 模板中间件(后端把html拼接好,直接返回给前端可以使用ejs模板引擎进行文件渲染)

        • 下载: npm install ejs -S

        • 配置:(在当前这js文件的同级下有一个views的文件夹,里面存储list.ejs文件用来写模板中间件)

          • app.set('views',path.resolve(__dirname,'views'));告诉app,模板文件所在的根目录

          • 告诉app,模板文件用哪个包解析 app.set('view engine','ejs');

          • res.render('在设置的模板文件目录中的模板文件路径',要在模板文件中使用的变量集合)

          • const express = require('express');
            const path = require('path')
            const app = express();
            
            // 后端把html拼接好,直接返回给前端
            // 可以使用ejs模板引擎进行文件渲染
            // 下载: npm install ejs -S
            // 配置:
            // 告诉app,模板文件所在的根目录
            app.set('views',path.resolve(__dirname,'views'));
            // 告诉app,模板文件用哪个包解析
            app.set('view engine','ejs');
            
            app.get('/data',(req,res)=>{
              // 在配置了模板引擎以后,res就多了一个方法 : 
              // res.render('在设置的模板文件目录中的模板文件路径',要在模板文件中使用的变量集合)
              // 在views/list.ejs中可以使用变量:age和arr和count
              // 把解析完的结果返回
              res.render('list',{
                age:"<h1>hello world</h1>",
                arr:[
                  {name:'吴波',task:'纪律委员'},
                  {name:'海文',task:'学习委员'},
                  {name:'小磊',task:'班级班长'},
                ],
                count:100
              })
            })
            app.listen(9090)
            
    • 路由

      • 运行跨域请求

        • 下载: npm install cors -S

        • 导入: const cors = require('cors')

        • 使用:app.use(cors())

      • 解析请求主体

        • npm install body-parser

        • app.use(bodyParser.json()) // 解析 application/json 格式的请求主体
          app.use(bodyParser.urlencoded({ extended: true })) // 解析 application/x-www-form-urlencoded 格式的请求主体
          
    • mongodb

      • mysql:关系型数据库 -- 表结构

      • mongodb:非关系型数据库 -- 数据库里面是一个一个的json文件

      • 在术语上

        • 数据库: database

        • mysql => 数据库里面有表: table

        • mongodb => 数据库里面有集合: collection

        • mysql => 表里面有数据行: row

        • mongodb => 集合里面有文档: document

      • 常用的shell命令:

        • 1、帮助命令

          -->help

          -->db.help()

          2、数据库操作命令

          -->show dbs

          -->use dbname 切换数据库

          -->db / db.getName() 查看当前数据库名称

          -->db.stats() 显示当前DB的状态

          -->db.version() 查看当前DB的版本

          -->db.getMongo() 查看当前DB的连接的主机地址

          -->db.dropDatabase() 删除当前DB

          3、创建数据库和集合

          -->use project 不存在就创建,存在就切换至

          -->db.createCollection('user') // 创建user集合

          -->show dbs

          -->show collections / db.getCollectionNames()

          -->db.getCollection('music') 获取指定集合

          -->db.printCollectionStats() 打印指定集合的状态

          4、集合中的文档操作:

          -->db.user.insertOne({}) 向集合中插入文档

          -->db.user.insertMany([{},{}])

          -->db.user.save({})

          -->db.user.updateOne({"name":"cyr"}, {$set:{"age":100}})

          -->db.user.updateMany({},{$set:{}})

          -->db.user.deleteOne({"name":"jiaming"})

          -->db.user.deleteMany({})

          -->db.user.remove({}) // 要指出删除的条件

          -->save和insert的区别:

          + 新增的数据中存在主键,则再次插入相同的主键时insert() 会提示错误

          + 而save() 则更改原来的内容为新内容

          + 没有saveMany命令

          5、聚集集合查询

          -->db.集合名.find({查询条件对象},{显示对象})

          -->db.user.find() 查询所有记录

          -->db.user.find({age:22}) 查询age=22的记录

          -->db.user.find({age:{$gt: 22}}) 查询age>22的记录

          -->db.user.find({age:{$lt: 22}}) 查询age<22的记录

          -->db.user.find({age:{$gte: 22}}) 查询age>=22的记录

          -->db.user.find({age:{$lte: 22}}) 查询age<=22的记录

          -->db.user.find({age:{$gte:20, $lte:30}}) 查询age>=20 && age<=30的记录

          -->db.user.find({name:/cyr/}) 查询name中包含'cyr'的记录

          -->db.user.find({name:/^cyr/}) 查询name以'cyr'开头的记录

          -->db.user.find({},{name:1,age:1}) 查询所有记录,只返回name和age字段(1-显示 0-不显示)

          -->db.user.find({age:{$gt:20}},{name:1,age:1}) 查询age>20的记录,只返回name和age字段

          -->db.user.find().sort({age:1}) 按age进行升序排列

          -->db.user.find().sort({age:-1}) 按age进行降序排列

          -->db.user.find({},{name:1,age:1,_id:0}).sort({age:1})

          -->db.user.find({name:'cyr',age:22}) 查询name='cyr' && age=22的记录

          -->db.user.find().limit(5) 只查询前5条记录

          -->db.user.find().skip(10) 查询10条以后的所有数据

          -->db.user.find().skip(5).limit(5) 查询第6~10条记录

          -->db.user.find({$or:[{age:20},{age:25}]}) 查询age=20或者age=25的记录

          -->db.user.findOne() 查询满足条件的第一条记录

          -->db.user.find({age:{$gte:25}}).count() 查询满足条件的记录的总条数

        • 常用:

          • use 数据库名

          • db.createCollection('user') 创建集合

          • db.user.insertOne({}) 向集合中插入文档

    • node操作mongodb数据库

      • 使用第三方模块: mongoose

      • 下载:npm install mongoose -S

      • 入门创建五步骤

        • 导入 const mongoose = require('mongoose');

        • 链接数据库 mongodb://localhost/数据库名 mongoose.connect('mongodb://localhost/qf');

        • 创建表(第一个参数:表名尽量用复数,第二个参数:定义表里面的字段)mongoose.model('表名',表结构

          • const Users = mongoose.model('users', {
              name:String,
              age:Number,
              pw:String,
              create_time:Number
            });
            
        • 创建一条数据

          • const student = new Users({ 
              name: '程磊',
              age:12,
              pw:'123456',
              create_time: Date.now()
            });
            
        • 把创建好的数据插入集合中

          • student.save().then((doc)=>{
              console.log('文档插入集合成功')
              console.log(doc)
            })
            
      • 链接数据库

        • // 1 导入mongoose模块
          const mongoose = require('mongoose');
          
          // 2 链接数据库,pinxixi是要链接的数据库名称
          mongoose.connect('mongodb://127.0.0.1:27017/pinxixi')
          
          // 3 获取数据库链接
          const db = mongoose.connection;
          
          // 4 事件监听,判断是否链接成功
          db.once('open',()=>{
            console.log('数据库链接成功')
          })
          
          db.on('error',(err)=>{
            console.log(err)
          })
          
          
      • 创建数据集合

        • require('./01');
          
          // 导入mongoose模块
          const mongoose = require('mongoose');
          // 创建集合的字段名和数据类型的规范
          const articleSchema = mongoose.Schema({
            title:String,
            content:String,
            createTime:Number,
            author:String
          })
          // 根据上面的规范创建一个articles表
          const articles = mongoose.model('articles',articleSchema)
          
          // 导出创建好的articles表
          module.exports = articles;
          
          
      • 插入数据

        • //导入要操作的表
          const articlesModel = require('./02')
          articlesModel.insertMany([
              {
                  title:'tree',
                  content:'mongoose 进行插入操作指南3',
                  createTime:Date.now(),
                  author:'多多'
              },
              {
                  title:'four',
                  content:'mongoose 进行插入操作指南4',
                  createTime:Date.now(),
                  author:'次次'
              }
          ]).then((doc)=>{
              console.log(doc);
          })
          
          

      • 查询数据

        • //导入要操作的表
          const articlesModel = require('./03')
          //查询title:'tree'的数据
          articlesModel.find({title:'tree'}).then((doc)=>{
              console.log(doc);
          })
          //查询时间在2022/5/11 13:23:00之前的数据
          articlesModel.find({createTime:{$lt:new Date('2022/5/11 13:23:00').getTime()}}).then((doc)=>{
              console.log(doc);
          })
          //查询 id
          articlesModel.findById('627b46c08f5a4300fc74fe7c').then((doc)=>{
              console.log(doc);
          })
          
      • 更新数据

        • //更新一条
          articlesModel.updateOne({author:"张三"},{$set:{title:"1111"}}).then(result=>{
            console.log(result);
            // 查看更新以后的数据
            articlesModel.find({}).then(doc=>{
              console.log(doc)
            })
          })
          
        • //更新多条
          articlesModel.updateMany({author:'次次'},{$set:{title:'danger'}}).then(res=>{
              console.log(res);
              articlesModel.find({}).then(res=>{
                  console.log(res);
              })
          })
          

      • 删除数据

        • //删除一条
          articlesModel.deleteOne({title:'tree'}).then(res=>{
              console.log(res);
              articlesModel.find({}).then(doc=>{
                  console.log(doc);
              })
          })
          
        • //删除多条
          articlesModel.deleteMany({title:'four'}).then(res=>{
              console.log(res);
              articlesModel.find({}).then(doc=>{
                  console.log(doc);
              })
          })
          

    • 注意:

      • 使用模块请先导入

      • 如果一个请求经过多个中间件,多个中间的req和res是共享的

npm包管理工具

  • 什么是npm

    • 是基于commonjs规范的包管理工具

    • node安装完成以后,npm同步安装 npm -v

    • nrm可以切换仓库镜像源,建议使用淘宝镜像

  • 管理模块

    • npm init 生成package.json文件

    • 作用: 便于模块管理 便于代码转移

    • 项目依赖 和 开发时依赖的区别

  • 模块安装

    • 查看指定包信息: npm info name

    • 查看安装了哪些包: npm list

    • 全局安装: npm install name --global

      • 简写: npm i name -g

    • 本地安装项目依赖: npm install name --save

      • 简写: npm i name -S

    • 本地安装开发时依赖: npm install name --save-dev

      • 简写: npm i name -D

    • 安装指定版本

      • npm install name@版本号 --save

    • 模块安装完成,在node_modules文件夹里面

      • 这个文件夹一般不随项目转移

    • 模块卸载

      • npm uninstall name -g

      • npm uninstall name -S

  • yarn:另一个包管理工具

    • 安装yarn: npm install yarn -g

    • yarn和npm二选一,即可

重要概念

  • BSR 客户端渲染(可异步渲染)

    • 前端利用ajax等数据交互方式向后端请求数据

    • 把数据变成html字符串,进行页面的局部刷新

    • 方法:ajax/jsonp/fetch -> 获取数据 -> html节点/html字符串 -> 插入页面

    • 优点:灵活,前后端分离,方便前后端更新维护

    • 缺点:对SEO不友好,增加http请求次数,减缓了页面加载速度

  • SSR 服务端渲染

    • 在后端把数据从数据库取出来以后,直接通过模板引擎渲染成html字符串

    • 客户端请求页面的时候,直接返回这个页面拼接好的html字符串

    • 也就是说后端可以把数据直接插入html字符串

    • 优点:对SEO友好,减少http请求次数,加快了页面加载速度

    • 缺点:不灵活,前后端耦合性太高

  • Rest风格的api设计

    • 每一个url代表一种资源

    • 客户端使用GET,POST,DELETE,PUT,PATCH等方式请求资源

    • 从客户端到服务端的每个请求必须包含理解请求所必要的信息

      • get类型 /user 获取所有用户信息

      • post类型 /user 插入用户信息

      • delete类型 /user/1 删除id=1用户信息

      • put类型 /user/1 更新id=1用户的所有信息

      • patch类型 /user/1 更新id=1用户的部分信息

    • 安装:npm install -g json-server

      • 创建一个db.json文件,并写入一个接收的数组

        • {
          “user”:[]
          }
          
      • json-server --watch db.json

      • 使用postman访问(http://localhost:3000/user

        • post请求即为添加数据 参数写在body中

跨域问题

  • 出现:协议/端口/域名有一个不同就是跨域

  • 解决

    • cors跨域

      • 在目标服务器设置跨域请求头

        • res.header("Access-Control-Allow-Origin", "*");
          res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,== OPTIONS");
          res.header("X-Powered-By",' 3.2.1')
          res.header("Content-Type", "application/json;charset=utf-8");
          
      • 一般不自己写,引入一个cors中间件(给目标文件引入)

        • npm install cors -S

        • const cors = require('cors')

        • app.use(cors())

    • jsonp

      • 不是xhr请求,是利用script的src可以跨域的特点

      • 提前准备一个函数,通过src请求后端接口返回这个函数的调用,实参就是需要的数据,函数名通过callback传递: callback=函数名

      • 如果a文件需要访问b中的数据

        • a文件中需要准备一个返回的函数,并一个script标签连接请求地址

        • function getList(data){
            console.log(data)
          }
          插入一个script标签请求后端接口,后端返回一个函数调用
          通过callback参数告诉后端要函数名
          <script src="http://localhost:9090/list?callback=getList"></script> 
          
        • b文件则调用

        • const express = require('express');
          const app = express();
          app.get('/list',(req,res)=>{
          res.jsonp({host:"b server",port:9090})
          }
          
    • 代理

      • 引入包 http-proxy-middleware

      • 使用:

        • 导入:const express = require('express');

          const { createProxyMiddleware } = require('http-proxy-middleware');

        • 设置 app.use('路径',createProxyMiddleware({

          }))

          • app.use("/list",createProxyMiddleware({
              target:'http://localhost:9090/',
              changeOrigin:true
            }))
            

token

  • token引入

    • 客户端频繁向服务器发送请求,大量的接口需要权限验证,就需要大量的数据库查询用户名和密码是有有效,服务器压力大,有没有一种办法可以不用频繁进行数据库验证,token就是为此产生的

  • token长什么样?

    • LKJjLHhlHwerJlkhl324HyiyHhIUYiyGkgIUYG234kgIUYTGKgIUYiuyG

  • token的目的

    • 减轻服务器的压力

    • 减少频繁的查询数据库

  • token从哪里来

    • token是在服务器产生的,当客户端登录或者调用接口的时候,会返回token,客户端用户收到token以后,保存在前端(比如保存在localStorage中),之后请求其他有访问权限的后端接口时,需要把token携带上传递给后端进行验证

  • 如何把token传递给后端呢?

    • 这要根据后端的需要,通常会把token放在headers中进行传递

  • 使用:

    • 下载包: npm install jsonwebtoken

    • [jsonwebtoken - npm (npmjs.com)](https://www.npmjs.com/package/jsonwebtoken)

WebSocket

  • 是什么

    • 用于建立服务器和客户端的双向交互通信,打开一条通道,客户端可以主动给服务器发消息,服务器也可以主动给客户端发消息,这个链接的一端就被称为socket

  • 主要功能

    • 向对方发送消息

    • 基于事件驱动接收对方发送的消息

  • WebSocket和socket.io之间的区别

    • socket.io是一个开源库,他对WebSocket进行封装

    • 增强了浏览器的兼容性

    • 使用起来更方便功能更强大

    • 使用on绑定事件监听

    • 如果绑定的是自定义事件,用emit触

  • 案例(E:\2022资料\tree-grup\test\04-day\socketserver)

    • 聊天室开发

      • socket服务器开发(socket底层是基于net模块的)

        == 接收客户端的消息(基于事件驱动的)

        == 群发消息给所有客户

      • socket客户端开发

        == 接收服务端的消息(基于事件驱动的),要渲染在页面上

        == 把消息发送给服务器

      • 解释

        • socket:就是链接到socket服务器的另一端

        • 通过io可以群发

        • 通过socket可以私聊

      • 例子:

        • // const socket = io('http://10.20.158.121:9090')
                  const socket = io('http://192.168.0.106:9090')
                  let count = 0
                  socket.on('message-from-server',(message)=>{
                      count++
                      $('#top').append('<div class="row">'+message+'</div>')
                      $('#top').scrollTop(count*35)
                  })
                  
                  $('#send').click(()=>{
                      let val = $('#message').val().trim()
                      socket.emit('message-from-client',val)
                      $('#message').val('')
                  })
          
                  $('#message').keyup((e)=>{
                    if(e.keyCode == 13){
                        let val = $('#message').val().trim();
                        socket.emit('message-from-client',val)
                      $('#message').val('')
                    }
                  })
          
  • pm2部署项目

    • 管理多个node项目

    • 安装:npm install -g pm2

    • pm2管理单个node项目

      • 启动: pm2 start 启动服务器的入口js文件

      • 列出所有应用: pm2 list

      • 结束应用: pm2 stop id

      • 删除进程: pm2 delete id

    • pm2管理多个node项目

      • 使用命令PM2 ecosystem 或者 pm2 init,初始化配置文件ecosystem.config.js

      • 配置ecosystem.config.js文件

        • name 指定项目名称

        • script 指node项目的入口启动文件

      • 启动|重启|停止|删除多个node服务器

      • pm2 [start|restart|stop|delete] ecosystem.config.js

      • pm2 list

      • 下载两个包 npm i socket.io -s nom i socket socketserver public index.html socket.io.js(nodemoudel中的) app.js

        配置路由 并检测postman 写接口

vue2

  • vue: 渐进式JavaScript 框架

  • 选择vue的原因

    • 超快的虚拟dom,很多省心的优化

    • 繁荣的生态系统

    • vue@2+vue-cli+vue-router@3+vuex@3+element ui+vue-i18n@8+...

    • vue@3+vue-cli+vue-router@4+vuex@4+element pluse+vue-i18n@9+.. == 对开发人员友好 == 兼容性: Vue 不支持 IE8 及以下版本 == 我们使用的v版本2.6.14

  • 选项

    • data html标签中可以使用data里定义的变量,使用插值语法{{表达式}},可以通过vue实例访问里面的data选项

      • data:{}

      • data:function(){return {}}

    • el 选项指定vue实例接管的根标签,书写根标签的css的选择器

      • methods 里面定义在html标签中可以使用的方法名

    • $mount方法 挂载

    • template选项 指定要替换el指定的标签的html字符串

      • template的优先级高于el指定标签本身的

      • vue实例在编译html字符串的时候如果现象有template, 就用template选项里面的html字符串,如果没有就用el指定标签本身的html字符串

      • 实际工作用一般都是用template,写在body里面的属性和标签名,遵循html标准,不区分大小写,写在template里面的属性和标签名,区分大小写

    • computed选项(计算属性)

      • 在computed里定义的函数,使用的时候直接写函数名,不要写函数名(),内部会自动执行这个函数,显示返回值

      • 计算属性的语法:

        • 属性名: 用于获取变量值的函数

        • 属性名: {get:获取变量值的函数,set:设置变量值的函数}

  • 指令:

    • 文本类指令

      • v-text

      • v-html

      • v-pre 加了这个指令的标签及其子元素不会被编译

      • v-once 加了这个指令的标签及其子元素只会被编译一次,后续就当静态内容

      • v-cloak vue编译完成以后,会把所有标签上看到的v-cloak移除(隐藏未编译的 Mustache 标签直到实例准备完毕)

        • [v-cloak]{ display:none; }配用

    • v-on

      • 语法

        • v-on:事件类型="事件处理函数名"

        • v-on:事件类型="事件处理函数名()"

        • v-on:事件类型="事件处理函数名(参数1,参数2,...)"

          • 有一个特别的参数$event

        • v-on:可以简写成: @

      • 事件修饰符

        • .prevent: 阻止事件默认行为

        • .stop: 阻止冒泡

        • .self: 只有事件的target是我自己,才触发事件,冒泡上来不触发 (大都用于父类)

        • .once:只发生一次

        • .enter v-on:keyup.enter="addTask"

        • .right

        • v-on:keydown.alt.67=""

        • v-on:mousedown.left=""

    • v-bind

      • 语法: v-bind:属性名="js表达式"

      • 简写::属性名="js表达式"

      • 使用:

        • 图片:src上 <img :src="imgSrc" alt="/>

        • class动态属性

          • 动态属性原始语法

            • <div v-bind:class="className"></div>

          • class数组语法

            • <div v-bind:class="['bg','border']"></div>

          • class对象语法

            • <div :class="{bg:false,border:true,r:false}"></div>

          • class数组语法和对象语言一起使用

            • <div :class="['border',{bg:false},{r:true}]"></div>

        • style动态属性

          • 动态属性原始语法

            • <div :style="style"></div>

          • style的对象语法

            • <div :style="{background:'red',border:'10px solid #ccc'}"></div'

          • style的数组语法

            • <div :style="[{background:'red'},{border:'10px solid #ccc'}]"></div>

    • 条件渲染指令: v-if

      • 通过控制节点的插入和销毁来控制显示隐藏

      • 语法: v-if="值为true就显示,值为false就隐藏"

      • 如果要切换显示隐藏的有多个元素可以把他们放在一起

        vue里面有一个标签标签叫做template类似js里面的()

    • v-show 通过控制节点的样式的display来显示和隐藏

      • 语法: v-show="值为true就显示,值为false就隐藏

    • v-for循环渲染

      • 语法:

        • v-for="item in arr"

        • v-for="(item,index) in arr"

        • v-for="(val,key) in obj"

        • v-for="(val,key,index) in obj"

      • v-for和v-if强烈不推荐在同一个标签上使用

        • vue@2 : v-for 的优先级比 v-if 更高

        • vue@3 : v-if 的优先级比 v-for 更高

      • v-for推荐配合key属性使用

        • key必须是不能重复的

        • key建议使用字符串或者number

        • key属性是在比较不同的虚拟dom的时候起作用

        • 如果key相同,就是原来的那个节点

        • 如果key不相同,就不是同一个节点,就需要销毁不存在的key对应的节点

    • v-model

      • 双向绑定(text变量的变化会导致界面的更新,界面上文本框或者文本域的value的变化会导致text这个变量的更新)

      • 修饰符

        • .lazy

        • .trim

        • .number

      • 复选框:

        • <input type="checkbox" value="football" v-model="flag">

        • 如果flag初值是布尔值:复选框把选中的状态双向绑定到flag这个变量上

        • 如果flag初值是数组:把复选框选中元素的value同步到这个数组里面

      • v-model用于组件元素上

        • <son v-model="text"></son>等价于<son v-bind:value="text" @input="inputHandler"></son>

      • 总结:

        • v-model

          == 还可以使用在组件上

          == 用于双向数据绑定

          == 数据的变化导致界面更新

          == 界面更新导致数据的更新

          == v-model默认把变量绑定到value属性上

          == 变量值的变化会导致元素value的变化

          == v-model默认监听input事件

          == 把元素的value值更新到变量上

  • render选项

    • 优先级: render渲染函数>template模板字符串>el元素的html字符串

    • render(h){
                     return h('p',{
                         class:{
                             a:true,
                             b:true
                         },
                         style:{
                             fontSize:'100px'
                         },
                         attrs:{
                             id:'box',
                             index:123
                         }
                     },[
                     h('h3',"我是p里面的h3标签")
                     ])
                 }   
      

  • 过滤器

    • 过滤器可以在{{}}和 v-bind:属性名="" 里面使用

    • 使用方法:

      • {{变量|过滤器|过滤器(实参1,实参2,....)}} 可接多个

      • v-bind:属性名="变量|过滤器|过滤器(实参1,实参2,....)"

    • 全局过滤器

      • 语法:Vue.filter('过滤器名称',function(val){})

        • val就是使用过滤器 | 前面的变量值

        • return 返回值就是经过处理以后新值

    • 局部过滤器

      • 语法:

        • new Vue({
                filters:{
                 "过滤器名称":function(val,arg1,arg2,...){
                   // val就是使用过滤器 | 前面的变量值
                  // arg1,arg2,...给过滤器函数传递的其他参数
                 }
                }
              })
          

  • 侦听器(watch)

    • 定义变量的侦听器,一旦被侦听的变量的值发生改变,就会触发侦听器

    • 语法:

      • {要侦听的变量名: 数据变化的处理函数}

      • {要侦听的变量名:{

        handler:数据变化的处理函数,

        deep:是否深度监听,

        immediate:定义函数的时候立即执行一次

        }}

    • 对象类型如何侦听?

      • 当侦听的变量为一个对象(复杂数据类型)时,直接改变对象中的值,由于监听的对象,比较的是地址,地址没有变化无法监听到,需要监听地址里面内容的变化,需要深度侦听

      • 如果对象list的地址变了,那这个对象的值就变化(也可以侦听到)

        • this.list = {
                     ...this.list,
                     name:this.newName
                   }
          

  • 自定义指令

    • 指令都是v-开头的,所以我们自定义指令的时候,不用写v-, 用的时候要加上

    • 全局自定义指令 - 所有vue实例都能使用

      • 语法:

        • Vue.directive('指令名',{
          
          ​     // 当被绑定的元素插入到 DOM 中时执行inserted这个钩子函数
          
          ​     inserted:()=>{},
          
          ​     // 只调用一次,指令第一次绑定到元素时调用
          
          ​     bind:()=>{},
          
          ​     // 所在组件的 VNode 更新时调用
          
          ​     update:()=>{}
          
          ​    })
          
      • 简写语法:

        • Vue.directive('指令名',(el,binding)=>{
                    // bind和update的时候会执行
                  })
          
    • 局部自定义指令 - 只有定义指令的vue实例可以使用

      • new Vue({
                 directives:{
                   '指令名': {钩子函数},
                   '指令名': 钩子函数简写
                 }
               })
        
      • 例子:

        • Vue.directive('pos',(el,binding)=>{
               // bind和update的时候会执行
               console.log(el);// 指令所在的节点
               console.log(binding);// 书写的修饰符的细节信息
               console.log(binding.value);// =后面表达式的值101
               console.log(binding.arg);// :后面的left
               console.log(binding.modifiers);// 指令的修饰符集合
               // pos这个指令可以设置元素的定位
               // el.style.left = '100px'
               el.style[binding.arg] = binding.value+"px"
             })
          
  • 混入:mixin

    • 定义一个对象,这个对象包含一些vue选项

    • 全局混入

      • Vue.mixin(要混入的对象)

      • 所有实例都能使用混入的对象里面的vue选项

    • 如果使用局部混入:

      • new Vue({mixins:[要混入的对象1,要混入的对象2,...]})

      • 只有定义混入的vue实例可以使用混入的vue选项

    • 注意:

      • 混入对象的选项和实例选项有冲突,用实例的选项的

      • 全局混入:慎用,一般用于做插件

      • vue@2 在mixin合并data的时候是深合并

      • vue@3 在mixin合并data的时候是浅合并

      • 生命周期函数在混入的时候,不会冲突,都执行

  • 中央事件总线

    • 即借助vue的一个实例去提供一个’电信公司‘,其他组件可借助这个vue的实例来通信

      • 实现

        • let bus = new Vue()

        • bus.$emit('其他组件自定义函数名',要传的数据) (通过事件 发送消息的人)

        • mounted(){ bus.$on('自定义函数名',(data)=>{ console.log(data) }) } (接收信息的人,即去bus这个电信公司办卡的人)

  • $refs

    • 使用(通常在mounted中获取)

      • 如果给一个普通的标签添加 ref=’xxx‘属性,使用vm.$refs.xxx 获取其真实DOM节点

      • 如果v-for循环出的标签有ref='xx'属性,我就可以通过 vm.$refs.xx 获取这个真实的dom节点的集合

      • 如果一个组件有ref='xx'属性,我就可以通过 vm.$refs.xx 获取这个组件实例对象

  • provide与inject选项

    • 在祖先实例中通过provide选项定义的变量,可以在其后代中通过inject选项引入来进行使用

    • 使用:

      • 祖先实例中: provide:{ num:100 }

      • 子孙组件中:inject:[ ’num‘ ] 直接{{ num }}使用

  • axios https://unpkg.com/axios/dist/axios.min.js

    • 基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中

    • 请求方法的简写方法

      • axios.get(url[, config])

      • axios.post(url[, data[, config]])

    • 请求方法完整

      • axios(config)

      • aixos(url,config)

    • axios的实例

      • 在实例中设置配置,后面直接用实例发请求,就所有的请求的baseURL都是设置好的,发请求的时候如果用server发请求,和axios发请求的方法是一样的,只不过用server发就都设置好了baseURL和responseType

      • const server = axios.create({
              // axios的config怎么写,这里的配置就怎么写
              baseURL:"http://localhost:8888",
              responseType:'json',
              headers:{
                token:localStorage.getItem('token')||''
              }
            })
        
    • 请求响应拦截器

      • 请求拦截器(给axios实例添加请求拦截器)

        • erver.interceptors.request.use(function(config){
                // 请求发送成功
                // config请求的配置项目,是一个对象
                // 你对config做一些事情,做完以后给回去
                config.headers.token = localStorage.getItem('token')||"";
                return config;
              },function(error){
                // 请求发送失败
                return Promise.reject(error);
              })
          
      • 响应拦截器(给axios实例添加响应拦截器)

        • server.interceptors.response.use(function(res){
                // 响应成功返回
                // 在这个函数里面对返回的结果做一些事情,做完以后给回去
                return res.data;
              },function(error){
                // 响应失败
                return Promise.reject(error);
              })
          
    • 取消请求

      • 生成取消令牌的函数 const CancelToken = axios.CancelToken;

      • 生成取消令牌对象 const source = CancelToken.source();

      • 在请求中添加 cancelToken:source.token

        • axios.get('http://localhost:8888/articles/list?page=1&size=10',{
                  cancelToken:source.token
                })
          
      • 取消发送请求 source.cancel('不要发了')

  • 组件

    • 组件是一个Vue实例,有名字的,组件的概念类似自定义html标签,组件自带HTML结构,自带css样式,自带js交互,用来像html标签,可以是双标签<xxx></xxx>,可以是单标签<xxx />

    • 组件的选项和vue实例的选项一样,就是data一定要是函数写法,组件中的data一般叫做组件的状态

    • 全局组件 在所有vue实例里面都可以使用

    • 局部组件 在定义vue实例的template选项中使用

    • 插件 elementui

    • 组件的生命周期

      • beforeCreate

      • created 响应式数据初始化完成以后调用,data中的数据可获取

      • beforeMount

      • mounted 会在替换完el中的节点之后调用

      • beforeUpdate

      • updated

      • beforeDestroy

      • destroyed

      • 其他补充:

        • vm.$destroy() 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。

    • 父子组件

      • 父子组件的生命周期

        • 先父组件再子组件,当父组件执行到beforemount时候,即父组件挂载之前(mounted)先挂载子组件,再挂载父组件。

      • 父组件给子组件传值

        • 在子组件上加自定义属性 自定义属性名=‘父组件属性名’,并在子组件里添加props:[]来接收父组件传过来的属性(即接收子组件上的自定义属性名),子组件使用{{自定义属性名}}来使用

        • props验证

          • props:['msg']

          • props:{ msg:String }

          • msg:{ type:String, required:true }

          • 带有默认值的对象

            • props:{
              	msg: {
                type: Object,
                // 对象或数组默认值必须从一个工厂函数获取
                default:function(){
                  return {a:1,b:2}
                }
              },
              }
              

            • 自定义验证函数,返回值true表示通过验证

              • props:{
                 msg:{
                          validator:function(val){
                            // val就是给msg注入的具体的值
                            return true;
                          }
                        }
                }
                

      • 子组件将数据给父组件

        • 使用 vm.$emit('要触发的事件类型','给事件处理函数的实参1','给事件处理函数的实参2',...)

        • 在子组件上定义一个自定义事件,@自定义事件名f=‘自定义函数名’,当子组件中的一个事件触发时(点击子组件按钮触发事件a),在事件a中使用this.$emit('自定义事件名f',子组件要传递的参数(多个参数用,分隔)),父组件调用自定义函数进行接收子组件传来的值

      • sync修饰符(父子组件传值的语法糖)

        • <son :msg="title" @update:msg="msgHandel"></son>

        • 上述可改写为: <son :msg.sync="title"></son>

    • 异步组件

      • Vue.component('child2',function(resolve,reject){
              setTimeout(()=>{
                resolve({
                  template:`<h1>hello child2</h1>`
                })
              },3000)
            })
        
    • 动态组件

      • 语法: <component is='组件名称'></component>

      • 通常与keep-alive一起用

      • keep-alive 包裹动态组件,会"缓存"不活动的组件实例,而不是销毁他们

      • <keep-alive>
              <component :is="which"></component>
            </keep-alive>
        
    • 把回调延迟到下次dom更新完成执行

      + Vue.nextTick()

      + vm.$nextTick()

       clickHandler(){
                this.msg = "哭唧唧";
                this.$nextTick(function(){
                  console.log(document.querySelector('h1').innerHTML)
                })
              }
      
  • 插槽v-slot指令

    • 组件的outerHTML一般会被丢弃, 除非组件里面有可扩展的插槽,组件通过<slot>标签可以开辟一个插槽

    • <slot></slot> 是匿名插槽,名字是default,所有内容都可以放到这个插槽里面

    • <slot name='xxxxx'></slot> 是具名插槽,只有指定这个插槽名才能插入

    • 指定插槽名字插入内容的语法 <template v-slot:插槽名字>要插入插槽的内容</template>

    • 给插槽里面的内容传值

      • 组件里面定义的插槽 :<slot a='xxx' b='xxx' c='xx' name='插槽名'></slot>

      • 使用: v-slot:插槽名 = "变量名" 等价: var 变量名 = {a:'xxx',b:'xxx',c:'xx'}

      • v-slot: 可以简写成 #

  • 插件机制

    • 定义插件:export default {install:function(Vue,options){}}

    • 例子:

      • export default {
          install:function(Vue,options){
            console.log(Vue);
            console.log(options)
            console.log("Vue.use的时候其实是调用obj的install方法,并传入第一个实参是Vue")
          }
        }
        
    • 使用插件:

      • import MyPlugin from 'xxx.js'

      • Vue.use(MyPlugin)

      • 例子

        • <script type="module">
          	//导入
          	import myPlugin from "./plugin.js";
          	//注册插件
              Vue.use(myPlugin,{banji:'sz2202',number:66})
          <script/>
          
        • //导入的是通过export导出的变量
          import {aButton,bButton} from "./myElement.js
          //注册需要的全局组件
          Vue.component('a-button',aButton)
          
  • 过渡

    • 在插入、更新或者移除 DOM 时,只要把要动画的元素放到transition标签就就会添加动效果,过渡的名字默认是: v

      • 开始插入: v-enter

      • 插入结束: v-enter-to

      • 插入过程: v-enter-active

      • 开始移除: v-leave

      • 移除结束: v-leave-to

      • 移除过程: v-leave-active

      • 例子(常用)

        方案一:
        .v-enter,.v-leave-to{
              transform: translateX(200px);
              opacity: 0;
            }
            .v-enter-to,.v-leave{
              transform: translateX(0);
              opacity: 1;
            }
            .v-enter-active,.v-leave-active{
              transition:all 1s;
            }
        
      • 方案二:
        .fade-enter-active{
              animation: move 1s;
            }
            .fade-leave-active{
              animation: move 1s reverse;
            }
            @keyframes move {
              from{
                transform: translateY(200px);
                opacity: 0;
              }
              to{
                transform: translateY(0);
                opacity: 1;
              }
            }
        

    • 通过给transiton设置name可以修改名字 name='xxx',原来类名中的 v- 就变成 xxx-

    • 元素之间的过渡可以通过mode设置元素进入和移除的顺序

      • mode='out-in'先出后进

      • mode='in-out' 先进后出

    • 多元素过渡

      • 可以通过mode设置进入和移除的顺序

      • 需要给每个过渡的元素一个唯一的key

      • transition里面只能一个节点

    • 初始过渡

      • 给transition添加appear属性,初始就有过渡效果

    • 如果过渡的元素有多个,那么要写在标签 transition-group中

  • vue的特点

    • 数据的改变会导致界面的更新

  • 其他:

    • el和$mount区别:

      • 都是一个作用: 让vue实例接管页面上的标签

      • el的优先级高于$mount,vue实例看有没有el选项,如果有就不调用$mount,只有没有el选项,才调用$mount

  • 扩展:

    • 扩展:vue3版本中没有$mount方法了

    • contentmenu

    • better-scroll插件 https://better-scroll.github.io/docs/zh-CN/guide/#%E8%B5%B7%E6%AD%A5

    • 面试题:

      • methods 和 computed有什么区别?

        • methods没有缓存,只要数据改变就会调用

        • computed有缓存,只有参与当前计算属性的data里面的变量变化的时候,才会调用,否则直接使用上次缓存的值

      • v-if vs v-show

        == 条件渲染 — Vue.js

    • methods和computed的定义函数不要写箭头函数

vuecli

  • 如何使用:

    • 下载:npm install -g @vue/cli

    • 创建项目:vue create hello-world

  • router

    • 如何配置路由

      • 参考 : https://router.vuejs.org/zh/guide/#javascript

    • 路由跳转的方法

      • this.$router.go(-1);

      • this.$router.push('配置的路由地址')

    • 动态路由

      • 例子

        • http://localhost:8080/detail/:id/:num

        • http://localhost:8080/detail/1/200

      • 配置: path:'detail/:id',

      • 访问:xxx/detail/xx

      • 获取:可在created的钩子中 使用 $route获取

        • console.log(this.$route.params.id)

        • 匹配以后,里面的 this.$route.params = {id:1,num:200}

        查询字符串

        • http://localhost:8080/login?a=1&b=2

        • a=1&b=2就是查询字符串

        • 当匹配路由组件以后,里面的:this.$route.query = {a:1,b:2}

    • 嵌套路由

      const routes = [
       {
        path:'/user',
        component:Layout,
        children:[
         {
       //path:"/user/manage", 或者
       path:'manage',
       component:UserManage
         }]
       }]
      
  • Vue Router

    • 路由守卫

      • router.beforeEach((to,from,next)=>{})

        • to: 要进入的的路由对象

        • from: 从哪里来的那个路由对象

        • 调用next(),就可以进入路由组件了

    • 方法

      • addRoute#

        添加一条新的路由记录作为现有路由的子路由。如果路由有一个 name,并且已经有一个与之名字相同的路由,它会先删除之前的路由。

    • 用户体验不佳时可用:

      • 路由的命名视图 ​ <router-view name='aaa'></router-view> ​ 在路由添加 ​ compons:{ 'aaa':()=>import() } ​ props:{ aaa:true }

    • 动态路由如何接收参数(如id)

      • 方法一:path:'detail/:id',

        • 接收:this.$route.params.id

      • 方法二: 如果设置props:true,那当前动态路由参数会作为props传入匹配的组件,匹配的组件需要设置同名的props来接收,就可以直接使用

        • path:'detail/:id','detail/:num'

        • props:true

        • 组件中接收 props:['num']

  • multi-page 模式下(多页面模式)

    • 需要给vue.config.js配置相关信息

      • 参考[配置参考 | Vue CLI (vuejs.org)](https://cli.vuejs.org/zh/config/#pages)

      • 案例:

        • pages:{
             index:"src/index/main.js",
             sz:{
               // page 的入口
               entry: 'src/sz/main.js',
               // 模板来源
               template: 'public/sz.html',
               // 在 dist/index.html 的输出
               filename: 'sz.html',
               // 当使用 title 选项时,
               // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
               title: 'SSSZZZ',
               // 在这个页面中包含的块,默认情况下会包含
               // 提取出来的通用 chunk 和 vendor chunk。
               chunks: ['chunk-vendors', 'chunk-common', 'sz']
             }
           },
          
  • 如何设置代理:

    • 参考文档:[配置参考 | Vue CLI (vuejs.org)](https://cli.vuejs.org/zh/config/#devserver-proxy)

    • 语法:

      • 方法一:proxy:'跨域地址'

      • 方法二:proxy:{ '/users':target:'跨域地址',changeOrigin:true,pathRewrite:{'/users':''} }

    • 案例:

      • devServer:{
           // 所有的请求都代理到一个服务器
           // proxy:"http://10.20.158.132:9090"
           // 所有的请求不是代理到一个服务器
           proxy:{
             // 如果请求自己服务器的地址有含有/api90
             "/api90":{
               // 代理到的目标服务器
               target:"http://10.20.158.132:9090",
               // 是否修改主机名
               changeOrigin:true,
               // 是否要修改请求路径
               pathRewrite:{
                   /访问地址是否包含用以区分的地址'/api90'
                 "^/api90":""
                 // 原始请求地址如果是:http://localhost:8080/api90/user/login
                 // 代理以后变成:http://10.20.158.132:9090/user/login
               }
             },
             "/api":{
               target:"https://mm.cxbiao.cn",
               changeOrigin:true
               // 原始请求地址如果是:http://localhost:8080/api/search?wd=xxx
               // 代理以后变成:https://mm.cxbiao.cn/api/search?wd=xxx
               }
             }
          }
        
    • 切换生产环境与上线环境的地址

      • publicPath:process.env.NODE_ENV==='production'?'/muti/':'/',

  • 关于项目打包的问题(npm run build)

    • 打包之后如何处理缓存(因为请求地址一致,导致页面不更新)

    • 实验:

      • 打包之后删除,再修改一些代码,再次打包

      • 代码的hash:代码发生变化,js中的文件名发生改变 只要项目的内容发生改变,再次打包代码的hash会发生改变 两种:

      • chunk hash:只和当前代码块有关

      • hash:从整个项目代码生成hash(一般用这个)

  • 多个组件都需要使用token,如何解决?

    • 方法一:存储在本地存储(不推荐) token发生变化时候,页面上使用的token不能同步更新token

    • 方法二:使用原型(不可取) main.js :this.$proptype.token = '123' 使用:token:this.token

    • 方法三:(vuex 状态管理工具) 创建了一个全局响应变量 vue2 => vuex@3 src/store/index.js 导入 => 注册 => 使用 在main.js注入 数据更新导致页面的更新 修改:

      • 方法一:直接修改devtool不会记录 this.$store.state.token = token(不可用)

      • 方法二:修改state里面的值只能调用mutations中的方法,会被devtool记录

  • vue的ssr(vue实例的服务端渲染)

    • 如何使用ssr

    • 第一步:创建一个vue实例

      • const app = new Vue({
          template:`
          <h1>hello {{name}},i am {{age}} years old!</h1>
          `,
          data(){
            return{
              name:'JC-T',
              age:12
            }
          }
        })
        
    • 第二步:创建一个renderer(需下载一个包) npm i vue-server-renderer -S

      • const renderer = require('vue-server-renderer').createRenderer()
        
    • 第三步: 将vue实例编译成html字符串

      • renderer.renderToString(app,(err,html)=>{
          if(err) throw(err);
          console.log(html)
        })
        
    • 参考文档: http://nuxtjs.cn(做vue的服务器渲染)

  • 辅助函数

    • 导入import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'

    • mapState

      • 对应:state

        • 访问: this.$store.state.XXX

      • 放在计算属性中(computed)

        • ...mapState(['要使用的state内的响应数据假设为token'])

      • 访问:this.要使用的state内的响应数据(token)

      • 模块怎么访问:...mapState('模块名',['name(模块理state内的数据)'])

    • mapGetters

      • 对应:getters

        • 访问: this.$store.getters.XXX

      • 放在计算属性中(computed)

        • ...mapGetters(['要使用的getters内的数据假设为count'])

      • 访问:this.要使用的getters内的数据假设为count

      • 模块怎么访问:...mapGetters('模块名',['count(模块理getters内的数据)'])

    • mapMutations

      • 对应:mutations

        • 使用: this.$store.commit('mutations内的方法名',payload(要传递的参数),)

      • 放在方法中使用(methods)

        • ...mapMutations(['mutations内的方法名'])

      • 模块怎么访问:...mapMutations('模块名',['SET_NAME'])

    • mapActions

      • 对应:actions

        • 使用: this.$store.commit('mutations内的方法名',payload(要传递的参数),)

      • 放在方法中使用(methods)

        • ...mapMutations(['mutations内的方法名'])

      • 模块怎么访问:

        • ...mapMutations('demo',['SET_NAME'])

        • 可以重命名,把数组写成对象

          • ...mapMutations('demo',{a:SET_NAME})

      • 用的时候就是: this.a

  • vuex

    • state: 所有组件都可以使用的状态

    • mutations:定义修改state里面的值的方法

      • mutations:{
           // mutations里面的方法不能有异步
           'SET_TOKEN':function(state,payload){
             // state就是当前的 store.state
             // payload就是你调用方法传入的参数
             state.token = payload.token
           }
         }
        
      • 调用mutations里面的方法

        • 语法1:store.commit('mutations里面的方法名','参数')

        • 语法2:store.commit({type:'mutations里面的方法名','参数'})

  • vue2响应的底层原理

    • 数据的改变会导致界面的更新,需要能监听到数据的变化,从而在变化的时候执行一次界面的更新

    • **Object.defineProperty**能监听对象的属性的操作

      • 获取属性值

      • 设置属性值

      • Object.defineProperty(obj,attrName,{})

        • 第一个参数: 要监听的对象

        • 第二个参数: 要监听的对象的属性名

        • 第三个参数: 是一个对象,定义赋值和读取的时候要执行的方法

          • set:function(){}
            get:function(){}
            
    • mvvm的模型

      • model:数据模型

      • view: 视图

      • vm: vue实例

    • 发布订阅模式

  • vue3的响应式原理

    • Proxy对象

      • 用于创建一个对象的代理

      • 实现基本操作的拦截和自定义(如属性的获取和赋值)

      • 语法: const p = new Proxy(要代理的目标对象,{})

        • 第二个参数为一个对象

        • {
              set:function(target,prop,val){
              // 当通过代理设置对象的属性值的时候,会触发
                      // target:原始对象
                      // prop:你通过代理要操作的那个属性名
                      // val:你通过代理要设置的属性值
                      console.log(target,prop,val)
                      // 把属性值设置给原始对象
                      target[prop] = val;
           }
               get:function(target,prop){
                  // 当通过代理获取对象的属性值的时候,会触发
                  // target:原始对象
                  // prop:你通过代理要获取的那个属性的名称
                  console.log(target,prop)
                  return target[prop]
                }
          }
          
  • vue3

    • vite(vue3使用的框架)

      • 内部使用rollup打包你的代码

      • 创建项目: npm init vite projectName -- --template vue

      • 开启服务:npm run dev

      • 打包压缩文件: npm run build

      • 常用配置: vite.config.js

      • 配置@别名

        • const path = require('path')
                   {
                     resolve:{
                       alias:{
                         '@':path.resolve(__dirname,'src')
                       }
                     }
                   }
          
      • .vue扩展名不能省略

    • 回顾:

      • vue-cli

        • 内部使用webpack打包你的代码

        • 安装:npm install -g @vue/cli

        • 创建项目:vue create projectName

        • 开启服务:npm run serve

        • 打包压缩文件:npm run build

        • 常用配置: proxy, alias(@ => src)

    • 一些语法的小变化

      • template里面可以有多个根节点

      • 挂载不会替换原来的#app

      • 自定义事件要预先说明

      • v-bind:abc.sync='xxx' => v-model:abc="xxx"

      • this.$emit('update:abc')

      • 单文件组件的setup语法糖(单文件组件的组合式API的语法糖 setup里面this不是组件实例 定义在setup里面的顶层的变量,函数,组件都可以直接使用)

      • 顶层可直接 使用awite(异步语法)

        • 顶层的变量,函数,组件可以使用使用

        • 定义响应式变量: ref(xx) reactive(xxx)

        • 定义计算属性: computed(()=>{})

        • 定义侦听器:

          • watch(响应变量,()=>{})

          • watchEffect(()=>{})

        • 定义生命周期:

          • onMounted(()=>{})

          • 没有onBeforeCreate和onCreated

        • 定义自定义事件

          • const emits = defineEmits(['自定义事件名称1','自定义事件名称2'])

          • vue3

            • const emits = defineEmits(['自定义事件名'])

            • emits('自定义事件名',参数)

          • 回顾vue2

            • this.$emit('事件名',参数)

        • 定义props

          • 1 获取外部传入的props: const props = defineProps(['接收的属性1','接收的属性2'])

      • vue3的组件生命周期

        • beforeCreate created

        • beforeMount mounted

        • beforeUpdate updated

        • beforeUnmount unmounted

    • 响应式变量:

      • 使用前要导入 import { ref,reactive }

      • ref (针对非对象类型)

        • let title = ref("");

      • reactive (针对对象类型)

        • let person = reactive({name:"lucy",age:12})

    • 组合式API

      • 在script组件上加上setup

        • 是单文件组件的组合式API的语法糖

        • setup里面this不是组件实例

        • 定义在setup里面的顶层的变量,函数,组件都可以直接使用

      • 定义计算属性

        • 导入 import { computed } from ‘vue’

        • 定义:

          • const count = computed(()=>{
            return xxxx
            })
            
      • 定义监听器

        • 导入 import { watch } from ‘vue’

        • 定义:

          • watch(监听的变量,()=>{
            
            })
            `{要侦听的变量名:{`
            
                ` handler:数据变化的处理函数,`
            
                `deep:是否深度监听,`
            
                `immediate:定义函数的时候立即执行一次`
            
               ` }}`
            
        • 定义(watchEffect会立即执行一遍,相当于有immediate功能)

          • watchEffect(()=>{})
            
    • vue-Router(vue3)

      • 下载: npm i vue-router -S

      • 在文件夹/src/router/index.js内写路由

        • 第一:导入: import {createRouter,createWebHashHistory} from 'vue-router' }

        • 第二:定义路由地址

          • const routes = [
            	{
                    path:'/login/:id',
                    component:()=>import('../xxxx')
            	}
            ]
            
        • 配置

          • const router = createRouter({
            	history:createWebHashHistory(),
            	routes
            })
            
        • 导出:export default router

      • 在main.js中导入,挂载

        • import router from "./router"
          createApp(App)
          .use(router)
          .mount('#app')
          
      • 在组件中使用:

        • 导入:import {useRouter,useRoute} from 'vue-router'

        • 使用:

          • const router = useRouter() //可以通过useRouter来获取router对象

          • 在函数在调用跳转方法router.push('/dashboard')

          • const route = useRoute() //可以通过useRoute来获取route对象

          • 在模板中使用获取动态路由的id{{route.params.id}}

          • 匹配所有或404

            • path:"/:pathMatch(.*)*",
                  component:()=>import('../views/404.vue')
              

    • vuex(vue3)

      • 使用了vite框架

      • 下载 npm i vuex -S

      • 在文件夹/src/store/index.js内写状态管理

        • 第一:导入: import {createStore} from 'vuex'

        • 第二:定义状态管理

          • const store = createStore({
              state:{
                num:10
              },
              mutations:{
                set_num:function(state,payload){
                  state.num = payload
                }
              }
            })
            
          • 导出:export default store

          • 在main.js中导入,挂载

            • import store from './store'
              createApp(App)
              .use(store)
              .mount('#app')
              
          • 在组件中如何使用

            • 导入 import {useStore} from 'vuex'

            • 使用:

              • const store = useStore()

              • {{store.state.xxx}}

    • 其他

      • nrm test查看镜像资源

      • nrm use 名称切换镜像资源

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值