node的项目实战相关

1.node的优势

并发能力特别强

Node.js使用了非阻塞的方式运行。同样的一台服务器,并发量是其他传统语言的数倍

第二:开发简单,无需学习新的语言

Node.js开发后端,使用的框架叫做Express,它是一个非常轻量的,学习起来非常简单的框架。

Node.js所使用的就是JS语言,并没有什么新的语法,更不用学习其他新的语言。

2.创建express项目

常用的node环境

express

koa

全局安装express

js 复制代码
npm i -g express-generator@4

创建项目,进入项目,npm i安装依赖

js 复制代码
express --no-view clwy-api
//  --no-view参数,它的意思是不需要任何视图模板

进入项目

npm i

npm start

终端显示 node ./bin/www代表成功,此时访问localhost:3000,得到html格式,不是接口所需要的json格式

修改为JSON格式

修改routes/index.js

js 复制代码
router.get("/", function (req, res, next) {
  // res.render('index', { title: 'Express' });
  res.json({ message: "hello nodejs" })
})

删除public下的index.html

!!!重启启动项目

页面json格式优化:JSON handle插件

nodemon安装

npm i nodemon

!!注意在项目路径下

"scripts": { "start": "nodemon ./bin/www" }

重新启动即可

3.项目结构解析

项目结构解析

bin/www:项目启动文件

node_modules,package-lock.json:依赖包和锁包版本号,npm i都会重新生成

public:静态文件存放

重点关注:app.js和routes即可

router解析

js 复制代码
router.get("/hello", function (req, res, next) {
  // res.render('index', { title: 'Express' });
  res.json({ message: "hello nodejs2" })
})
// /hello表示接口的访问路径,现在要访问http://localhost:3000/hello才能访问了
// req参数
// res返回
// next回调多用于登录等场景

4.通过docker安装mysql

安装docker

js 复制代码
// docker的安装地址及汉化包配置(需梯子)
https://github.com/asxez/DockerDesktop-CN/releases

// 配置中国镜像(docker引擎中)(无下载问题可不装)
{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": false,
  "registry-mirrors": [
    "https://xelrug2w.mirror.aliyuncs.com"
  ]
}

// 其他问题根据clwy文档更正
https://clwy.cn/chapters/fullstack-node-mysql#section8

安装mysql

项目app.js同级目录下,新建docker-compose.yml

js 复制代码
// docker-compose.yml
services:
  mysql:
    image: mysql:8.3.0
    command: --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
    environment:
      - MYSQL_ROOT_PASSWORD=clwy1234
      - MYSQL_LOWER_CASE_TABLE_NAMES=0
    ports:
      - "3306:3306"
    volumes:
      - ./data/mysql:/var/lib/mysql

docker-compose up -d,此时docker中就显示有文件的mysql了

安装navicat

js 复制代码
// navicat下载地址
https://www.navicat.com.cn/products/navicat-premium-lite

// 连接navicat
连接名:mysql/账号:root/密码:clwy1234

// 双击打开连接

5.创建数据库和数据表

创建数据库

右键mysql,新建数据库

新建表

给表添加字段id title artile[注:id点上自动递增],保存表名为Articles

常用数据类型

6.sql新增\编辑\删除

新增

js 复制代码
// 第一种方法:手动输入,点下方的√或者ctrl+s保存


// 第二种方法:查询-新建查询-输入语句-运行-刷新Articles表
// 单行插入
INSERT INTO 表名 (列1, ...) VALUES (值1, ...)
// 例:
INSERT INTO `Articles` (`title`, `content`) VALUES ('行路难·其一', '长风破浪会有时,直挂云帆济沧海。');


// SQL语句规则说明
// **大小写**:INSERT INTO,WHERE都使用了大写字母,其实你改为小写也是一样可以正确运行的
// **反引号**:表的名字和字段的名字`title`\`content`使用了`号。这里不写这个符号也是可以运行的,但是有些情况下必须写,例如我们有一个字段刚好叫做`insert`

// 多行插入
// INSERT INTO 表名 (列1, ...) VALUES (值1, ...),(值1, ...)...;
// 例:
INSERT INTO `Articles` (`title`, `content`) VALUES ('将进酒', '天生我材必有用,千金散尽还复来。'), ('宣州谢朓楼饯别校书叔云', '抽刀断水水更流,举杯消愁愁更愁。'), ('梦游天姥吟留别', '安能摧眉折腰事权贵,使我不得开心颜!'), ('春夜宴从弟桃花园序', '天地者,万物之逆旅也;光阴者,百代之过客也。'), ('宣州谢朓楼饯别校书叔云', '弃我去者,昨日之日不可留;乱我心者,今日之日多烦忧。'), ('庐山谣寄卢侍御虚舟', '我本楚狂人,凤歌笑孔丘。手持绿玉杖,朝别黄鹤楼。'), ('行路难', '长风破浪会有时,直挂云帆济沧海'), ('将进酒', '人生得意须尽欢,莫使金樽空对月。天生我材必有用,千金散尽还复来。'), ('望庐山瀑布', '飞流直下三千尺,疑是银河落九天。'), ('访戴天山道士不遇', '树深时见鹿,溪午不闻钟。'), ('清平调', '云想衣裳花想容,春风拂槛露华浓。'), ('春夜洛城闻笛', '谁家玉笛暗飞声,散入春风满洛城。');

编辑

js 复制代码
// UPDATE 表名 SET 列1=值1, 列2=值2, ... WHERE 条件
// 例:
UPDATE `Articles` SET `title`='黄鹤楼送孟浩然之广陵', `content`='故人西辞黄鹤楼,烟花三月下扬州。' WHERE `id`=2;

删除

js 复制代码
// DELETE FROM 表名 WHERE 条件
// 例:
DELETE FROM `ARTICLES` WHERE `id`=5;

mysql教程

clwy.cn/documents/m...

7.sql查询

查询

js 复制代码
// 查询全部
SELECT * FROM `Articles`

// 查询id和title
SELECT `id`,`title` FROM `Articles`

条件查询

js 复制代码
SELECT * FROM 表名 WHERE 条件;

-- 例如:
SELECT * FROM `Articles` WHERE `id`=2;

-- 或者,想查询id大于2的文章:
SELECT * FROM `Articles` WHERE `id`>2;

升序降序

js 复制代码
// ASC升序
// DESC降序
SELECT `id`,`title` FROM `Articles` WHERE `id` > 2 ORDER BY `id` ASC

// 延伸:
// 其他查询:`聚合查询`、`分组查询`、`连接查询`

8.ORM

orm是什么?

ORM:在数据库和编程语言之间的一种映射关系

sequelize orm的使用

js 复制代码
// 先安装`sequelize`的命令行工具,需要全局安装
npm i -g sequelize-cli

// 安装当前项目所依赖的`sequelize`包和对数据库支持依赖的`mysql2`
npm i sequelize mysql2

// 初始化项目
sequelize init

// 生成四个文件夹
**config**:`sequelize`连接数据库的配置文件。

**models**:模型文件,当我们使用`sequelize`来执行增删改查时,就需要用这里的模型文件了。
每个模型都对应数据库中的一张表。

**migrations**:迁移,如果需要对数据库做新增表、修改字段、删除表等等操作,就需要在这里添加迁移文件了。
而不是像以前那样,使用客户端软件来直接操作数据库。

**seeders**,存放的种子文件。一般会将一些需要添加到数据表的测试数据存在这里。
只需要运行一个命令,数据表中就会自动填充进一些用来测试内容的了。
├── config
│   └── config.json
├── migrations
├── models
│   └── index.js
└── seeders

9.模型、迁移与种子

配置config/config.json

js 复制代码
{
  "development": {
    "username": "root", // 账号
    "password": "clwy1234",
    "database": "api_dev",
    "host": "127.0.0.1",
    "dialect": "mysql",// 密码
    "timezone": "+08:00" // 时区
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "api_dev_test",
    "host": "127.0.0.1",
    "dialect": "mysql",
    "timezone": "+08:00" // 时区
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "api_dev_production",
    "host": "127.0.0.1",
    "dialect": "mysql",
    "timezone": "+08:00" // 时区
  }
}

模型model和迁移文件migrations

先删除掉Articles表,重新生成

新建模型model和迁移文件migrations:

js 复制代码
sequelize model:generate --name Article --attributes title:string,content:text

// models下生成了两个文件article.js和index.js
// migrations文件夹里生成了一个由当前时间+create-article命名的文件

注意:

  • models/article.js是单数,表名Articles是复数

  • 20250609080105-create-article.js它的作用就是用来创建、修改表的

    go 复制代码
      在`up`中,通过`createTabel`,创建了一个叫做`Articles`的表
    
      在`down`中,删除表
    
      额外生成了createdAt,updatedAt两个时间字段

运行迁移

js 复制代码
sequelize db:migrate

// 刷新navicat,可以看到`Articles`表又神奇的出现了,而且还多了两个时间字段。这就是`迁移文件`的作用。

// 另外一张表`SequelizeMeta`是我们运行迁移命令时,自动生成的。
// 这张表里记录了当前已经跑过了哪些迁移,这样当你再次运行`sequelize db:migrate`时,
// 已经运行过的迁移文件,就不会重复再次执行了

种子文件seeders

生成seed

js 复制代码
sequelize seed:generate --name article

生成100条数据

js 复制代码
"use strict"

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up(queryInterface, Sequelize) {
    const arts = []
    const counts = 100

    for (let i = 1; i <= counts; i++) {
      const article = {
        title: `文章的标题 ${i}`,
        content: `文章的内容 ${i}`,
        createdAt: new Date(),
        updatedAt: new Date(),
      }

      arts.push(article)
    }

    await queryInterface.bulkInsert("Articles", arts, {})
  },

  async down(queryInterface, Sequelize) {
    await queryInterface.bulkDelete("Articles", null, {})
  },
}

执行

js 复制代码
sequelize db:seed --seed 20250609091829-article

10.查询文章列表

建议使用cmd窗口起一下项目

新建routes/admin/articles.js,仿照其它路由引入express,在app.js中引入adminArticles路由

js 复制代码
// 引入express
const express = require("express") 
const router = express.Router()
// 引入Article表
const { Article } = require("../../models")
// async/await异步查询
// condition条件查询,按照id顺序倒序
// trycatch捕获错误
router.get("/", async function (req, res, next) {
  try {
    const condition = {
      order: [["id", "DESC"]],
    }
    const ats = await Article.findAll(condition)
    res.json({
      status: 200,
      data: ats,
      message: "查询文章列表成功!",
    })
  } catch (error) {
    console.error("Error fetching articles:", error)
    res.status(500).json({
      status: 500,
      message: "查询文章列表失败!",
      error: error.message,
    })
  }
})

module.exports = router
js 复制代码
// app.js中
// 核心1
const adminArticlesRouter = require("./routes/admin/articles")

// 核心2
app.use("/admin/articles", adminArticlesRouter)

访问localhost:3000/admin/articles得到数据,在窗口能看到findAll等语句被转化成了SELECT语句

11.查询单个文章

核心:使用动态路由+findByPk

注意:对空值或不存在的值作处理

js 复制代码
// articles.js中
router.get("/:id", async function (req, res, next) {
  try {
    const id = req.params.id
    if (!id) {
      return res.status(400).json({
        status: 400,
        message: "文章ID不能为空!",
      })
    }
    const article = await Article.findByPk(id)
    if (!article) {
      return res.status(404).json({
        status: 404,
        message: "文章未找到!",
      })
    }
    res.json({
      status: 200,
      data: article,
      message: "查询文章成功!",
    })
  } catch (error) {
    console.error("Error fetching articles:", error)
    res.status(500).json({
      status: 500,
      message: "查询文章列表失败!",
      error: error.message,
    })
  }
})

或者使用findOne方法(由ai生成)

js 复制代码
// articles.js中
router.get("/:id", async function (req, res, next) {
  try {
    const ats = await Article.findOne({
      where: {
        id: req.params.id,
      },
    })
    if (!ats) {
      return res.status(404).json({
        status: 404,
        message: "文章未找到!",
      })
    }
    res.json({
      status: 200,
      data: ats,
      message: "查询文章详情成功!",
    })
  } catch (error) {
    console.error("Error fetching article:", error)
    res.status(500).json({
      status: 500,
      message: "查询文章详情失败!",
      error: error.message,
    })
  }
})

12.apifox的使用

13.创建文章

apifox新建接口,使用查询文章列表的接口,改为post

处理接口

js 复制代码
router.post("/", async function (req, res, next) {
  try {
    const { title, content } = req.body
    if (!title || !content) {
      return res.status(400).json({
        status: 400,
        message: "标题、内容和作者不能为空!",
      })
    }
    const newArticle = await Article.create(req.body)
    res.status(201).json({
      status: 201,
      data: newArticle,
      message: "创建文章成功!",
    })
  } catch (error) {
    console.error("Error creating article:", error)
    res.status(500).json({
      status: 500,
      message: "创建文章失败!",
      error: error.message,
    })
  }
})

校验响应结果201报错的问题

点击提取-提取到响应提示即可

流程

主窗口-新建团队-新建目录[后台]-新建目录[新闻通知]-添加接口

13.删除文章

删除

js 复制代码
// 删除文章
router.delete("/:id", async function (req, res, next) {
  try {
    const article = await Article.findByPk(req.params.id)
    if (!article) {
      return res.status(404).json({
        status: 404,
        message: "文章未找到!",
      })
    }
    await article.destroy()
    res.json({
      status: 200,
      message: "删除文章成功!",
    })
  } catch (error) {
    console.error("Error deleting article:", error)
    res.status(500).json({
      status: 500,
      message: "删除文章失败!",
      error: error.message,
    })
  }
})

批量删除

js 复制代码
// 扩展:批量删除文章
router.delete("/", async function (req, res, next) {
  try {
    const { ids } = req.body
    if (!ids || !Array.isArray(ids) || ids.length === 0) {
      return res.status(400).json({
        status: 400,
        message: "请提供要删除的文章ID数组!",
      })
    }
    const deletedCount = await Article.destroy({
      where: {
        id: ids,
      },
    })
    res.json({
      status: 200,
      message: `成功删除 ${deletedCount} 篇文章`,
    })
  } catch (error) {
    console.error("Error deleting articles:", error)
    res.status(500).json({
      status: 500,
      message: "删除文章失败!",
      error: error.message,
    })
  }
})

// apifox操作
body-json-{"ids":[]}

14.更新文章

接口处理

js 复制代码
// 更新文章
router.put("/:id", async function (req, res, next) {
  try {
    const article = await Article.findByPk(req.params.id)
    if (!article) {
      return res.status(404).json({
        status: 404,
        message: "文章未找到!",
      })
    }
    const updatedArticle = await article.update(req.body)
    res.json({
      status: 200,
      data: updatedArticle,
      message: "更新文章成功!",
    })
  } catch (error) {
    console.error("Error updating article:", error)
    res.status(500).json({
      status: 500,
      message: "更新文章失败!",
      error: error.message,
    })
  }
})

apifox

注:body中是需要修改的对象属性

js 复制代码
Executing (default): SELECT `id`, `title`, `content`, `createdAt`, `updatedAt` 
FROM `Articles` AS `Article` WHERE `Article`.`id` = '2';

Executing (default): UPDATE `Articles` SET `title`=?,`content`=?,`updatedAt`=? WHERE `id` = ?
UPDATE `Articles` SET `title`=?,`content`=?,`updatedAt`=? WHERE `id` = ?

15.模糊查询

模糊查询的核心-mysql语法

目标:查询到数据库中标题中含有'标题 10'的数据(注意空格)

语法:

js 复制代码
SELECT * FROM `Articles` WHERE `title` LIKE `%标题10%`

接口

基于查询的接口进行改造

js 复制代码
// 模糊查询
router.get("/", async function (req, res, next) {
  try {
    const { title } = req.query
    const condition = {
      order: [["id", "DESC"]],
    }
    if (title) {
      condition.where = {
        title: {
          [Op.like]: `%${title}%`,
        },
      }
    }

    const ats = await Article.findAll(condition)
    res.json({
      status: 200,
      data: ats,
      message: "查询文章列表成功!",
    })
  } catch (error) {
    console.error("Error fetching articles:", error)
    res.status(500).json({
      status: 500,
      message: "查询文章列表失败!",
      error: error.message,
    })
  }
})

// 对应的mysql
SELECT `id`, `title`, `content`, `createdAt`, `updatedAt` 
FROM `Articles` 
AS `Article` WHERE `Article`.`title` LIKE '%10%' ORDER BY `Article`.`id` DESC

// 注意
// Op需要引入;控制台上的报错信息
const { Op } = require("sequelize")

扩展:多个条件的查询

js 复制代码
// 模糊查询
// 多个条件的,通过[Op.or]\[Op.and]控制
router.get("/", async function (req, res, next) {
  try {
    const { title, content } = req.query
    const condition = {
      order: [["id", "DESC"]],
    }
    if (title || content) {
      condition.where = {
        [Op.or]: [
          {
            title: {
              [Op.like]: `%${title}%`,
            },
          },
          {
            content: {
              [Op.like]: `%${content}%`,
            },
          },
        ],
      }
    }
    const articles = await Article.findAll(condition)
    res.json({
      status: 200,
      data: articles,
      message: "查询文章成功!",
    })
  } catch (error) {
    console.error("Error searching articles:", error)
    res.status(500).json({
      status: 500,
      message: "查询文章失败!",
      error: error.message,
    })
  }
})

16.分页

js 复制代码
// 模糊查询
router.get("/", async function (req, res, next) {
  try {
    const { title = "", page = 1, pageSize = 10 } = req.query

    const offset = (page - 1) * pageSize
    const totalCount = await Article.count({
      where: {
        title: {
          [Op.like]: `%${title}%`,
        },
      },
    })
    const condition = {
      order: [["id", "DESC"]],
    }
    if (title) {
      condition.where = {
        title: {
          [Op.like]: `%${title}%`,
        },
      }
    }

    const ats = await Article.findAll({
      offset,
      limit: parseInt(pageSize),
      ...condition,
    })
    res.json({
      status: 200,
      data: ats,
      message: "查询文章列表成功!",
      total: totalCount,
    })
  } catch (error) {
    console.error("Error fetching articles:", error)
    res.status(500).json({
      status: 500,
      message: "查询文章列表失败!",
      error: error.message,
    })
  }
})

17.白名单

表单的新建\更新如果传入的id也一并处理的话,会有错误的,所有要把需要的属性筛出来

js 复制代码
function filterBody(body) {
  const allowedFields = ["title", "content"]
  const filteredBody = {}
  for (const field of allowedFields) {
    if (body[field] !== undefined) {
      filteredBody[field] = body[field]
    }
  }
  return filteredBody
}

// 处理优化新建文章\更新文章接口
//#region 新增文章
router.post("/", async function (req, res, next) {
  try {
    const body = filterBody(req.body)
    const { title, content } = body

    if (!title || !content) {
      return res.status(400).json({
        status: 400,
        message: "标题、内容和作者不能为空!",
      })
    }
    const newArticle = await Article.create(body)
    res.status(201).json({
      status: 201,
      data: newArticle,
      message: "创建文章成功!",
    })
  } catch (error) {
    console.error("Error creating article:", error)
    res.status(500).json({
      status: 500,
      message: "创建文章失败!",
      error: error.message,
    })
  }
})

// 更新文章
router.put("/:id", async function (req, res, next) {
  try {
    const article = await Article.findByPk(req.params.id)
    if (!article) {
      return res.status(404).json({
        status: 404,
        message: "文章未找到!",
      })
    }

    const body = filterBody(req.body)
    const { title, content } = body
    if (!title || !content) {
      return res.status(400).json({
        status: 400,
        message: "标题、内容不能为空!",
      })
    }
    const updatedArticle = await article.update(body)
    res.json({
      status: 200,
      data: updatedArticle,
      message: "更新文章成功!",
    })
  } catch (error) {
    console.error("Error updating article:", error)
    res.status(500).json({
      status: 500,
      message: "更新文章失败!",
      error: error.message,
    })
  }
})

18.表单验证

问题:怎么验证title字段同名问题?

修改models/articles.js

js 复制代码
  Article.init(
    {
      title: {
        type: DataTypes.STRING,
        allowNull: false,
        validate: {
          notNull: {
            msg: "标题必须存在",
          },
          notEmpty: {
            msg: "标题不能为空",
          },
          len: {
            args: [2, 50],
            msg: "标题长度必须在2-50之间",
          },
        },
      },
      content: DataTypes.TEXT,
    },
    {
      sequelize,
      modelName: "Article",
    }
  )

优化routes/admin/articles.js中新增和更新接口

js 复制代码
//#region 新增文章
router.post("/", async function (req, res, next) {
  try {
    const body = filterBody(req.body)

    const newArticle = await Article.create(body)
    res.status(201).json({
      status: 201,
      data: newArticle,
      message: "创建文章成功!",
    })
  } catch (error) {
    if (error.name === "SequelizeValidationError") {
      return res.status(400).json({
        status: 400,
        message: "创建文章失败!",
        error: error.errors.map((err) => err.message),
      })
    } else {
      console.error("Error creating article:", error)
      res.status(500).json({
        status: 500,
        message: "创建文章失败!",
        error: [error.message],
      })
    }
  }
})

//#region 更新文章
router.put("/:id", async function (req, res, next) {
  try {
    const article = await Article.findByPk(req.params.id)
    if (!article) {
      return res.status(404).json({
        status: 404,
        message: "文章未找到!",
      })
    }
    const body = filterBody(req.body)
    const updatedArticle = await article.update(body)
    res.json({
      status: 200,
      data: updatedArticle,
      message: "更新文章成功!",
    })
  } catch (error) {
    if (error.name === "SequelizeValidationError") {
      return res.status(400).json({
        status: 400,
        message: "更新文章失败!",
        error: error.errors.map((err) => err.message),
      })
    } else {
      console.error("Error updating article:", error)
      res.status(500).json({
        status: 500,
        message: "更新文章失败!",
        error: error.message,
      })
    }
  }
})

// 注:
可以在catcherror中通过res.json(error)打印error查看错误信息

19.封装响应,优化代码

js 复制代码

Express简介

路由(Route):Route是什么、Route的定义、Route的实现、Route的实例、Route的运行流程、Request对象、Response对象

中间件:中间件简介、中间件功能、中间件的分类、中间件实例

中间件实例:Router是什么、为什么使用Router、Router的使用

EJS模板:EJS是什么、EJS的使用、EJS语法

7,node.js怎么分辨登录状态

12/21/23更新

sass颜色函数:lighten darken

js 复制代码
问题:在项目中一个btn的hover/active/disabled状态往往是难以维护的

原先代码:
<div>
  <button class="btn type1">按钮</button>
</div>

.btn {
    padding: 20px;
}
.btn.type1 {
    background: #409eff;
    color: #fff;
}

.btn.type1:hover {
    background: #73b8ff;
}
.btn.type1:active {
    background: #0d84ff;
}
.btn.type1:disabled {
    background: #a6d2ff;
}
js 复制代码
解决方法:使用sass颜色函数
.btn {
    padding: 40px;
}

.btn.type1 {
    $color: #409eff;
    background: $color;
    color: #fff;

    &:hover {
        background: lighten($color, 10%); // 变淡
    }

    &:active {
        background: darken($color, 10%); // 变深
    }

    &:disabled {
        background: lighten($color, 30%); // 变淡
    }
}


注意:
1,scss文件不能直接link引入html文件,需要通过vscode插件编译成css文件才可以
js 复制代码
扩展:循环得到多个不同颜色的btn

<div>
  <button class="btn type1">按钮1</button>
  <button class="btn type2">按钮2</button>
  <button class="btn type3">按钮3</button>
</div>


index.scss中:
$btnColor: #409eff,
#67c23a,
#f54343;

@for $i from 1 through length($btnColor) {
    .btn {
        padding: 40px;
    }

    .btn.type#{$i} {
        $color: nth($btnColor, $i);
        background: $color;
        color: #fff;

        &:hover {
            background: lighten($color, 10%); // 变淡
        }

        &:active {
            background: darken($color, 10%); // 变深
        }

        &:disabled {
            background: lighten($color, 30%); // 变淡
        }
    }
}

24年,1月2日更新

eventloop相关

相关知识点: 1,进程线程 2,异步 3,eventloop

js 复制代码
1,进程线程

什么是异步?
    重点:JS是单线程语言


JS阻碍渲染?
    
    
任务优先级?
    重点1:任务没有优先级,任务队列有
    重点2:根据w3c的说法,随着浏览器的复杂度越来越高,每个任务都会有一个任务类型,但是不管怎样,微队列永远是优先级最高的
    重点3:微队列》交互队列》延时队列
    
    
    
以上可以总结为:JS的事件循环


JS中的计时器能做到精确计时吗?
    重点1:不能
    重点2:多层循环有延迟,操作系统函数有误差,事件循环队列交互>延时,没有原子钟等等

1月3日更新

浏览器渲染原理

js 复制代码
渲染的本质:
html字符串==>像素信息



面试题:

渲染
 1,解析html/parseHTML
 
 解析html-DOM树
     document object model
 解析css-CSSOM树
     css object model
     样式表:
         <style>
         <link ...>
         <div style=""> // 内联
         浏览器默认样式表
         
     注:除了浏览器默认样式表都是可以改变的,内联用dom.style改变,其它的可以用document.styleSheets,选择适当的元素addRule('div','border:1px solid red !important')改变
     
     
HTML 解析过程中遇到 CSS 代码怎么办?
    重点:预解析线程,下载解析css
    重点:css不会阻塞解析html
HTML 解析过程中遇到 JS 代码怎么办?
    重点:遇到js代码必须暂停
    重点:JS执行可能回修改当前的DOM树,∴会阻塞解析html的根本原因
    
2,样式计算
        让DOM树的每一个节点计算出最终样式
        css属性值的计算过程:
            层叠
            继承
        视觉格式化模型:
            盒模型
            包含块
    
3,布局Layout
        DOM 树 和 Layout 树不⼀定是⼀⼀对应的
        原因1:display:none的节点没有几何信息
        原因2:伪元素选择器::before在dom树中不存在伪元素节点,但是拥有集合信息,∴会生成到布局树中
        原因3:匿名行盒、匿名块盒(内容必须在行盒中,行盒和块盒不能相邻)
        
4,分层
        重点1:好处提高效率
        重点2: 滚动条、堆叠上下文、opacity、transform都会影响,或者用will-change:transform希望分层 
        


5,绘制paint
6,分块tiling
    在合成线程分块+合成
    
7,光栅化Raster
    GPU加速
    优先处理靠近视口的块
    
8,画draw
    画完交给gpu最终呈现
    
    问:为什么要交给gpu?
    答:gpu是浏览器端的,渲染流程在沙盒里的涉及到安全问题
    

总结:浏览器的渲染
渲染主线程中:
    解析html+样式计算+布局layout+分层layer+绘制painter
合成线程中:
    分块tiling+光栅化raster+画draw

2.reflow回流/repaint重绘

js 复制代码
例:改变宽度width:300px
重新计算样式、布局等等

 影响效率
∴在代码运行过程中,尽量少的影响几何信息,如margin、padding、宽高、字体大小


重点:是异步完成的
∵可能会有多个连续的操作,浏览器会合并这些操作,统一计算
随之而来的问题:如果在这些操作后,获取最新的布局信息如clientWidth可能会有问题,∴遇到这种问题浏览器会立即reflow获取最新数据
js 复制代码
例:改变字体颜色等
重新分层+绘制,没有样式计算

∴reflow一定会引起repaint

3.transform为什么效率高?

js 复制代码
∵transform既不影响布局也不影响绘制指令,只影响draw画的操作

1月13日更新

面试题相关

1,移动端1px问题

js 复制代码
问题产生:由于移动设备的dpr问题导致,css里的1px在物理像素里的显示不同

解决方法:(常见)
    1,transform:scale,可以用在伪元素,媒体查询中
    2,viewport视口,也是flexible.js库的原理,改变initial-scale的值

2.渐进增强、优雅降级

js 复制代码
两种开发策略,一种是向前兼容,一种是向后兼容

渐进增强:先构建最基本的功能,然后根据浏览器情况追加功能、样式
向后兼容:构建后完整功能,再根据低版本兼容

4.进程、线程的概念

js 复制代码
----->大师课第一节

6.title和h1、b和strong、i em

js 复制代码
title:html标题
h1:一级标题

b是加粗文本
strong是h5标签,强调

i是斜体
em是h5标签,强调

7.TLS/SSL的工作原理---->HTTP知识相关

js 复制代码
用于保护网络通信的加密协议
TLS是SSL的后继

过程/工作原理:
对称加密+密钥加密
握手阶段:
    客户端给服务器发送hello,告诉服务器TLS版本,随机数,加密套件,服务器给客户端加密套件、随机数、公钥,双方根据预主密钥生成会话密钥,进行对称加密

HTTPS是什么?加密原理和证书。SSL/TLS握手过程_哔哩哔哩_bilibili

7-2.防御XSS攻击---->HTTP知识相关,网络安全

js 复制代码
后端中间件、组件转译

HTTPOnly防止恶意截取cookie

HTTPencode转译,有一个xssFilter库

css中用encodeForCss方法函数

8.&& ||的返回值

js 复制代码
&&与 ||或都可以进行短路操作

||或有一个真则为真,∴
第一个数求值为false则返回第二个数
第一个数为对象则返回第一个数
都为undefined返回undefined
都为null返回null
都为NaN返回NaN

&&全为真才为真,第一个操作数为false第二个操作数就不执行 ,∴
第一个数求值为false则返回第一个数
第一个数为对象则返回第一个数
都为undefined返回undefined
都为null返回null
都为NaN返回NaN

11.margin\padding的使用场景?

js 复制代码
margin是容器之间的距离,边框外;padding是容器内元素和元素的距离,边框内

margin可能会有重叠问题,此时可以用padding结局--->跳转问题3

3.from memory cache \ from dist cache 的区别,性能对比,怎么选择

3.service worker

看视频

3.webpack优化性能

3.node相关

js 复制代码
   eventloop和浏览器的区别
    process.nextTick执行顺序

算法题

1.大数相加

JS实现两个大数相加_js二进制相加代码-CSDN博客

2.插入排序

js 常用排序算法_js排序-CSDN博客

js 复制代码
const arr = [5, 2, 4, 6, 1, 3]

const insertFn = (arr) => {
  for (let i = 1; i < arr.length; i++) {
    const cur = arr[i]

    let j = i - 1
    while (j >= 0 && arr[j] > cur) {
      arr[j + 1] = arr[j]
      j--
    }
    arr[j + 1] = cur
  }
  return arr
}
insertFn(arr)
3.字符串全排列

js 字符串的全排序_js对字符串排序-CSDN博客

前端优化题

追加问题: grid布局 工程化 前端监控?

手写题

1月14日,周日更新

12.console.log()

js 复制代码
是同步的,但是打印引用类型比如说对象的属性的时候,打印的是快照
但是查看对象的时候,看的是地址里的值

解决方法:JSON序列化

13.数据类型题相关

js 复制代码
基础类型:
    num
    string
    boo
    undefined
    null
    bigint
    symbol

引用类型:(即object)
    []
    {}
    

延伸:
    typeof null ====>object

13-2. 1.0.1+0.2为什么不=0.3,双精度浮点数,解决方法

js 复制代码
回答核心:
    ∵计算机是二进制的,0.1+0.2会转换成二进制科学计数法计算再转回十进制,0.1转换二进制会是一个无限循环,导致产生误差结果为0.3000000000004

JS的存储方式:双精度浮点数

解决方法:
    1.小数转换成整数计算
    2.Number.EPSILON代表极小数来比较

15.深拷贝/浅拷贝

15-1.浅拷贝

js 复制代码
// 浅拷贝:(精确拷贝)
// 基本数据类型直接拷贝值,引用数据类型只能拷贝引用地址
let a = 1;
let b = a
a = 2
console.log(b); // 1


let a = { name: "qc" };
let b = a
a.name = 'zch'
console.log(b); // {name:'zch'}


// 实现
function shallowClone(obj) {
  const newObj = {}
  for (const key in object) {
    if (Object.hasOwnProperty(key)) {
      newObj[key] = obj[key]
    }
  }
  return newObj
}


// 存在浅拷贝现象的
// 1.Object.assign
// 第一层深拷贝了,但是第二层的引用类型还是浅拷贝
const obA = {
  name: 'a',
  nature: [1, 2, 3],
  age: {
    a1: 1,
    a2: 2
  },
  fn: function () {
    console.log('obA');
  }
}

const obB = Object.assign({}, obA)

obA.age.a1 = 3;

console.log(obB); // 跟着obA一起改变


// 2.Array.prototype.slice
const arr1 = [1, 2, 3]
const arr2 = arr1.slice(0)
arr1[0] = 2
console.log(arr1) // [2,2,3]
console.log(arr2) // [2,2,3]


// 3.Array.prototype.concat
// 第一层深拷贝了,但是第二层的引用类型还是浅拷贝
const arr1 = [1, 2, 3, [4, 5]]
const arr2 = arr1.concat()

arr1[3][0] = 5
console.log(arr1) // 相同,发生了浅拷贝
console.log(arr2)


// 4,...拓展运算符
// 第一层深拷贝了,但是第二层的引用类型还是浅拷贝
const arr1 = [1, 2, 3, [4, 5]]
const arr2 = arr1.concat()

arr1[3][0] = 5
console.log(arr1) // 相同,发生了浅拷贝
console.log(arr2)

15-2.深拷贝

js 复制代码
// 1.lodash库的_cloneDeep

// 2.JSON序列化
// 有弊端,见15-3
// 函数、undefined、symbol都会丢失等


// 3.循环递归
见例1
js 复制代码
// 例1
function cloneDeep(params, hash = new WeakMap()) {
  if (params === null) return params
  if (params instanceof RegExp) return new RegExp(params)
  if (params instanceof Date) return new Date(params)
  if (typeof params != 'object') return params
  if (hash.get(params)) return hash.get(params)

  let cloneObj = new params.constructor()
  hash.set(params, cloneObj)
  for (const key in params) {
    if (params.hasOwnProperty(key)) {
      cloneObj[key] = cloneDeep(params[key], hash)

    }
  }

  return cloneObj
}

15-3.JSON序列化的弊端

js 复制代码
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式  
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;  
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;  
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null  
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;  
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;

15.严格模式,有什么好处

15.函数作用域,词法作用域,匿名函数,函数表达式和函数声明

15。实现一个源生ajax,fetch

15.前端解析二进制,流媒体、图片二进制怎么渲染到页面

15.实现一个简单的模版引擎

15.函数记忆?什么情况下用?实现一个具有记忆功能的函数

15.lazyMan

15。parseQuery实现

15。乱序算法

15。数组去重,考虑到NAN object

1月15日笔记 1、JSON序列化的弊端需要加深

2月29日更新 10,反转链表,

未知的 2,千分位----还没掌握

3月7日更新

参考链接:

1.AJAX

js 复制代码
相关题:
是什么?原理?手写

2.本地存储,token

js 复制代码
相关题:
三者的区别?

token登录流程

3.页面渲染

js 复制代码
相关题:
渲染页面的过程
dom树和渲染树的区别
js 复制代码
本笔记上方有浏览器渲染原理

4.精灵图和base64,svg

js 复制代码
相关题:
精灵图和base64的区别
svg格式
js 复制代码
精灵图多个小图拼接在一起
base64是字符串,缺点:低版本兼容性

3月9日

js 复制代码
数据没有请求过来时怎么办
递归遇到过什么问题?
相关推荐
彤银浦23 分钟前
Web学习笔记3
前端·笔记·学习·html5
江城开朗的豌豆28 分钟前
退出登录后头像还在?这个缓存问题坑过多少前端!
前端·javascript·vue.js
江城开朗的豌豆35 分钟前
Vue的'读心术':它怎么知道数据偷偷变了?
前端·javascript·vue.js
江城开朗的豌豆42 分钟前
手把手教你造一个自己的v-model:原来双向绑定这么简单!
前端·javascript·vue.js
我在北京coding1 小时前
el-tree 懒加载 loadNode
前端·vue.js·elementui
江城开朗的豌豆1 小时前
v-for中key值的作用:为什么我总被要求加这个'没用的'属性?
前端·javascript·vue.js
angen20181 小时前
Ruby如何采集直播数据源地址
前端·chrome
goldenocean1 小时前
React之旅-05 List Key
前端·javascript·react.js
亮学长1 小时前
lodash不支持 Tree Shaking 而 lodash-es可以
大数据·前端·elasticsearch
rzl022 小时前
HTML/JOSN复习总结
前端·html