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教程
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报错的问题
点击提取
-提取到响应提示
即可
流程
主窗口-新建团队-新建目录[后台]-新建目录[新闻通知]-添加接口
-
下方将文档模式改为调试模式,输入http://localhost:3000/admin/articles 访问成功!
-
右上方配置环境变量
开发环境,前置URL:http://localhost:3000 注意:http://localhost:3000/ 可能会错误访问到html
-
删除掉接口栏中的前缀,改为
/admin/articles
,/admin/articles/{id}
两个接口并保存 -
可以点击分享以分享接口
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.大数相加
2.插入排序
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.字符串全排列
前端优化题
追加问题: 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.JS面试题
- 2.【JS春招面试题】浏览器的存储方式有什么?_哔哩哔哩_bilibili
1.AJAX
js
相关题:
是什么?原理?手写
2.本地存储,token
js
相关题:
三者的区别?
token登录流程
3.页面渲染
js
相关题:
渲染页面的过程
dom树和渲染树的区别
js
本笔记上方有浏览器渲染原理
4.精灵图和base64,svg
js
相关题:
精灵图和base64的区别
svg格式
js
精灵图多个小图拼接在一起
base64是字符串,缺点:低版本兼容性
3月9日
js
数据没有请求过来时怎么办
递归遇到过什么问题?