合集:2023年最全前端面试题考点HTML5+CSS3+JS+Vue3+React18+八股文+手写+项目+笔试_参宿7的博客-CSDN博客
*表示回顾基础知识
项目为二面三面,面试官基本就是照着简历里面的项目技术点切入然后深入展开。
为了简洁,相关文章参考链接在标题里
目录
一个模块是实现一个特定功能的一组方法。
这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题。
但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适。
AMD是预加载,CMD是懒加载。
AMD是提前执行,CMD是延迟执行。
AMD在对应的加载之前导入,CMD在用的时候导入
(Load On Demand)延迟加载、按需加载
可视化区域之外的图片不会进行加载,在滚动屏幕时才加载。这样使得网页的加载速度更快,减少了服务器的负载。懒加载适用于图片较多,页面列表较长(长列表)的场景中。
scroll 事件,对其进行事件监听。//节流
window.addEventListener('scroll', throttle(lazyload, 200)) <html lang="en">
<head>
<meta charset="UTF-8">
<title>Lazyload</title>
<style>
.image-item {
display: block;
margin-bottom: 50px;
height: 200px;//一定记得设置图片高度
}
</style>
</head>
<body>
<img src="./img/default.png" data-src="./img/1.jpg" />
...
<img src="./img/default.png" data-src="./img/10.jpg" />
<script>
function lazyload() {
let viewHeight = document.body.clientHeight //获取可视区高度
//用属性选择器返回属性名为data-src的img元素列表
let imgs = document.querySelectorAll('img[data-src]')
imgs.forEach((item, index) => {
if (item.dataset.src === '') return
// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
let rect = item.getBoundingClientRect()
if (rect.bottom >= 0 && rect.top < viewHeight) {
item.src = item.dataset.src
item.removeAttribute('data-src')//移除属性,下次不再遍历
}
})
}
lazyload()//刚开始还没滚动屏幕时,要先触发一次函数,初始化首页的页面图片
document.addEventListener("scroll",lazyload)
</script>
</body>
</html>
IntersectionObserver 版IntersectionObserver "交叉观察器"。可见(visible)本质是,目标元素与视口产生一个交叉区
callback 一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)
//callback 是可见性变化时的回调函数,option 是配置对象(该参数可选)
var io = new IntersectionObserver(callback, option)
// 开始观察
io.observe(document.getElementById('example'))
// 停止观察
io.unobserve(element)
// 关闭观察器
io.disconnect()
callback 函数的参数(entries)是一个数组,每个成员都是一个 IntersectionObserverEntry 对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries 数组就会有两个成员。
getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回 nullintersectionRect 占 boundingClientRect 的比例,完全可见时为 1,完全不可见时小于等于 0const config = {
rootMargin: '0px',
threshold: 0,
}
let observer = new IntersectionObserver((entries, self) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
let img = entry.target
let src = img.dataset.src
if (src) {
img.src = src
img.removeAttribute('data-src')
}
// 解除观察
self.unobserve(entry.target)
}
})
}, config)
imgs.forEach((image) => {
observer.observe(image)
})
require/import
// CommonJS 的写法
const moduleA = require('moduleA');
const func1 = moduleA.func1;
const func2 = moduleA.func2;
// ES6 的写法
import { func1, func2 } from 'moduleA';
module.exports/export
// commonJS 的写法
var React = require('react');
var Breadcrumbs = React.createClass({
render() {
return <nav />;
}
});
module.exports = Breadcrumbs;
// ES6 的写法
import React from 'react';
class Breadcrumbs extends React.Component {
render() {
return <nav />;
}
};
export default Breadcrumbs;
脚本语言需要一个解析器才能运行,每一种解析器都是一个运行环境
JavaScript是脚本语言,在不同的位置有不一样的解析器,
写入html的js语言,浏览器是它的解析器角色。
浏览器中的js的用途是操作DOM,浏览器就提供了document之类的内置对象。
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,独立于浏览器的运行环境
nodejs中的js的用途是操作磁盘文件或搭建http服务器,nodejs就相应提供了fs,http等内置对象。

Node Package Manager ,即:node包管理器
是nodeJS的一个程序包管理和分发的管理工具,npm完全用 JavaScript 写成
允许用户从NPM服务器下载安装上传包
整个项目不再使用class组件,统一使用函数式组件,并且全面用Hooks;
所有的函数式组件,为了避免不必要的渲染,全部使用memo进行包裹;
组件内部的状态,使用useState、业务数据全部放在redux中管理;
函数组件内部逻辑代码基本按照如下顺序编写代码:
组件内部state管理;
redux的hooks代码;
其他hooks相关代码(比如自定义hooks);
其他逻辑代码;
返回JSX代码;
redux代码规范如下:
redux目前我们学习了两种模式, 根据自己的情况选择普通模式还是rtk模式
每个模块有自己独立的reducer或者slice,之后合并在一起;
redux中会存在共享的状态、从服务器获取到的数据状态;
网络请求采用axios
对axios进行二次封装;
所有的模块请求会放到一个请求文件中单独管理;
项目使用AntDesign或者MUI(Material UI)
其他规范在项目中根据实际情况决定和编写
npm run start 来运行启动项目并打开页面
npx create-react-app my-app //创建项目
cd my-app
npm start
安装命令:
npx create-react-app 项目名称 --template typescript
主要开发代码在src目录下。App.js为根组件,index.js为入口模块,index.css为全局样式文件。

配置jsconfig.json:这个文件在Vue通过脚手架创建项目时自动生成, React是没有自动生成
package.json相当于说明书,其重点字段:
{
"name": "react-ts-app",
"version": "0.1.0",
"private": true,//是否私有,设置为 true 时,npm 拒绝发布
"dependencies": {//生产环境下,项目运行所需依赖
"@ant-design/icons": "^4.8.0",
...
"@types/node": "^16.18.6",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"antd": "^5.0.4",
"axios": "^1.2.1",
"classnames": "^2.3.2",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"react-router-dom": "^6.4.4",
"react-scripts": "5.0.1",
"redux": "^4.2.0",
"redux-persist": "^6.0.0",
"sass": "^1.56.1",
"typescript": "^4.9.3",
"web-vitals": "^2.1.4"
},
"scripts": {//执行npm脚本命令简写,比如“start" : "react-scripts start",执行npm start就是运行“react-scripts start"
"start": "SET PORT=8080 && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
....
"devDependencies": {//开发环境下,项目所需依赖
"@types/lodash": "^4.14.191"
}
}
用一句话来概括很简单,就是锁定安装时的包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致。

是安装node后用来存放用包管理工具下载安装的包的文件夹。比如webpack、gulp、grunt这些工具。
git init 初始化git仓库
git status 查看文件状态
git add 文件列表 追踪文件
git commit -m 提交信息 向仓库中提交代码
git log 查看提交记录
1.分支明细
(1)主分支(master):第一次向 git 仓库中提交更新记录时自动产生的一个分支。
(2)开发分支(develop):作为开发的分支,基于 master 分支创建。
(3)功能分支(feature):作为开发具体功能的分支,基于开发分支创建
2.分支命令
(1)git branch 查看分支
(2)git branch 分支名称 创建分支
(3)git checkout 分支名称 切换分支
(4)git merge 来源分支 合并分支 (备注:必须在master分支上才能合并develop分支)
(5)git branch -d 分支名称 删除分支(分支被合并后才允许删除)(-D 强制删除)
是当前修改是左箭头方向,传入的是右箭头的方向,
中间用等于号分割,等号上边是当前修改(本地),下边是传入的修改(线上的代码)。
两人同时提交可能会出现冲突,解决办法是手动修改冲突
存储临时改动:git stash(藏匿)
恢复改动:git stash pop
对于多人并行开发,维护同一仓库工作场景,经常会出现文件合并冲突的情况
能够将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录。
git add
只是把文件加到 git 版本控制里
压缩js,css,js分包,优化图片(webp),开启gzip(后端开启),配置缓存(强制缓存协商缓存),使用cdn,
webpack分包,减少重绘回流
它将根据模块的依赖关系进行静态分析,然后将这些模块( js、css、less )按照指定的规则生成对应的静态资源,减少了页面的请求。Webpack是以公共JS的形式来书写脚本的,方便旧项目进行代码迁移。

Webpack通过一个给定的主文件(如:index.js)开始找到项目的所有依赖文件,
使用loaders处理它们,plugin可以压缩代码和图片,
把所有依赖打包成一个 或多个bundle.js文件(捆bundle)浏览器可识别的JavaScript文件。
JavaScript 编译器
将es6、es7、es8等语法转换成浏览器可识别的es5或es3语法,即浏览器兼容的语法,比如将箭头函数转换为普通函数
将jsx转换成浏览器认的js
webpack只认识JS和JSON,所以Loader相当于翻译官,将其他类型资源进行预处理,最终变为js代码。
Plugin解决loader 无法实现的事情,比如打包优化和代码压缩等。

运行时机
1.loader运行在编译阶段
2.plugins 在整个周期都起作用
浏览器热更新:开发时能够在浏览器页面中实时看到我们代码的变化产生效果的流程。
热加载是通过内置的 HotModuleReplacementPlugin 实现的

一般面试官不会考察,但是你可以写在简历项目或者技能中,面试时主动提出使用了ts,解释下为什么要使用ts及ts与js,java(类成员可见性,多的类型(any,void,泛型,断言等))的区别,然后引导面试官提问,回答完其问题再自己拓展一些相关,是不错的加分项。
但既然是加分项,一般不会问太深,毕竟还有更关键的要问你,所以会试探一下你是否真的用过,还是纯背。
面试官一般是看你简历问的,没有问的才会问八股文之类。
面试一定要把握主动权,不要干等着面试官问,尽量展示自己所会的,并且回答不必太急,最后能条理清晰。
// let a: string -> 类型推断
let a = 'hello'
a = 123; // error
类型强弱是针对类型转换是否显示来区分,静态和动态类型是针对类型检查的时机来区分。
let a = 123。但在TS中还存在类型声明空间,可通过类型注解结合这两个空间
可以通过type关键字来定义类型声明空间,type在TS中叫做类型别名。为了能够更好的区分两个空间,所以人为规定类型空间定义的名字首字母要大写,例如:type A = number
TS与java不同,TS默认隐式公共的,TS符合 JS 的便利性,java默认private,符合安全性
接口是一系列抽象方法的声明,是一些方法特征的集合。
接口跟类型别名类似都是用来定义类型注解的,但接口只能操作对象,且有继承,合并
interface A {
username: string;
age: number;
}
let a: A = {
username: 'xiaoming',
age: 20
}
接口可以进行合并操作。
interface A {
username: string;
}
interface A {
age: number;
}
let a: A = {
username: 'xiaoming',
age: 20
}
接口具备继承能力。
interface A {
username: string
}
interface B extends A {
age: number
}
let b: B = {
username: 'xiaoming',
age: 20
}
never类型表示永不存在的值的类型,当一个值不存在的时候就会被自动类型推断成never类型。
在出问题的时候会被自动转成never。
any类型表示任意类型,而unknown类型表示为未知类型,是any类型对应的安全类型。
当 TypeScript 推断出来类型并不满足需求,需要手动指定一个类型,强制说明类型,让TS编译不报错
let a: unknown = 'hello';
a = 123;
(a as []).map(()=>{}) // success
因为b可能是字符串也可能是undefined,所以b.length的时候就会报错,这样我们可以采用非空断言来告诉TS,这个b肯定不是undefined,所以b只能是字符串,那么b.length就不会报错了。
let b: string|undefined = undefined;
b!.length // success
function foo(n: number, m?: string): number{
return 123;
}
foo(123, 'hello');
foo(123); // success
当函数没有return和return undefined的时候返回void类型。
let foo = function(){ // void
}let foo = function(): undefined{ // undefined 不能不写return的
} // error
泛型是指在定义函数、接口、类时,未指定其参数类型,只有在运行时传入才能确定。泛型简单来说就是对类型进行传参处理。
类型注解:把变量空间与类型空间结合在一起
type关键字+类型别名
let a: string = 'hello'
//也可以通过类型别名进行连接
type A = string
let a: A = 'hello'
let arr: Array<number> = [1, 2, 3];
//自定义MyArray实现
type MyArray<T> = T[];
let arr2: MyArray<number> = [1, 2, 3];
function foo(n: number, m?: string): number{
return 123;
}
foo(123, 'hello');
foo(123); // success
function foo<T>(n: T){
}
foo<string>('hello');
foo(123); // 泛型会自动类型推断
class Foo {
//第一种写法
//username: string = 'xiaoming';
//第二种写法
// username: string;
// constructor(){
// this.username = 'xiaoming';
// }
//第三种写法
username: string;
constructor(username: string){
this.username = username;
}
}
interface A {
username: string
age: number
showName(n: string): string
}
class Foo implements A {
username: string = 'xiaoming'
age: number = 20
gender: string = 'male'
showName = (n: string): string => {
return n
}
}
class Foo {
...
showAge = (n: number): number => {
return n;
}
}
class Foo<T> {
username!: T; //不给初始值可通过非空断言
}
let f = new Foo<string>();
f.username = 'hello';
class Foo<T> {
username!: T
}
class Baz extends Foo<string> {}
let f = new Baz()
f.username = 'hello'
is 让 ts 分辨类型
function toUpperCase(x: unknown) {
if(isString(x)) {
x.toUpperCase(); // ⚡️ x is still of type unknown
}
}
但是由于这个检验函数(isString)被包裹在 toUpperCase()函数中,ts 在判断的时候还是抛出了错误提示
使用 is ,这里让我们主动明确的告诉 ts ,在 isString() 这个函数的参数是一个 string。
// !!! 使用 is 来确认参数 s 是一个 string 类型
function isString(s): s is string {
return typeof s === 'string';
}
function pipsAreValid(pips: number) {
// we check for every discrete value, as number can
// be something between 1 and 2 as well.
return pips === 1 || pips === 2 || pips === 3 ||
pips === 4 || pips === 5 || pips === 6;
}
function evalThrow(count: number) {
if (pipsAreValid(count)) {
// my types are lying 😢
switch (count) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log('Not today');
break;
case 6:
console.log('Won!');
break;
case 7:
// TypeScript does not complain here, even though
// it's impossible for count to be 7
console.log('This does not work!');
break;
}
}
}
count 是一个 number 类型,输入的参数是没有问题的。
在我们校验它的时候,count不再是一个 number 类型,而是变成了一个 1-6 number 的特殊类型。
ts 为了防止类型溢出,使用了联合类型把原来 number 类型变成 1-6的数字(缩小了范围)
改变处
// 主动使用联合类型确保我们的输入是 1-6的数字
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
function pipsAreValid(pips: number): pips is Dice {
改变处
return pips === 1 || pips === 2 || pips === 3 ||
pips === 4 || pips === 5 || pips === 6;
}
function evalThrow(count: number) {
if (pipsAreValid(count)) {
// count is now of type Dice 😎
switch (count) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log('Not today');
break;
case 6:
console.log('Won!');
break;
case 7:
// TypeScript errors here. 7 is not in the union type of
// Dice
console.log('This does not work!');
break;
}
}
}
less, sass, scss都是css预处理语言(也是对应的文件后缀名)。
CSS 预处理器为 CSS 增加一些编程的特性,无需考虑浏览器的兼容性问题,
可以在 CSS 中使用变量、简单的逻辑程序、函数(如变量$main-color)等等在编程语言中的一些基本特性,可以让 CSS 更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处。
开发时用预处理语言,在打包上线时,用webpack再配合loader工具给转成css给浏览器使用。
在实际开发过程中,scss是常用写法,scss还是越直观越好,这种运算类型的特别是map类型的,尽量不要在实际项目中使用,后续维护成本很高的。
Sass 和 SCSS 其实是同一种东西,我们平时都称之为 Sass,
后缀扩展名:
语法书写方式:

#css
nav a {
color:red;
}
header nav a {
color:green;
}
#scss
nav {
a {
color: red;
header & {
color:green;
}
}
}
复制代码
#css
.box {
border-top: 1px solid red;
border-bottom: 1px solid green;
}
#scss
.box {
border: {
top: 1px solid red;
bottom: 1px solid green;
}
}
.clearfix{
&:after {
clear:both;
overflow: hidden;
}
}
在选择器、函数、混合宏内定义的变量
em {
$color: red;//定义局部变量
a {
color: $color;//调用局部变量
}
}
$btn-primary-color : #fff !default;
Vue中叫它混入指令,可以设置参数,复用重复代码块。但会生成冗余的代码块。比如在不同的地方调用一个相同的混合宏时,不能将两个合成并集形式。
@mixin border-radius{
border-radius: 5px;
}
# 带值参数
@mixin border-radius($radius:5px){
border-radius: $radius;
}
# 不带值参数
@mixin border-radius($radius){
border-radius: $radius;
}
# 带多个参数
@mixin center($width,$height){
width: $width;
height: $height;
margin-top: -($height) / 2;
margin-left: -($width) / 2;
}
当混合宏传的参数过多之时,可以使用“…”来替代
# 带特别多参数
@mixin box-shadow($shadows...){
@if length($shadows) >= 1 {
-webkit-box-shadow: $shadows;
box-shadow: $shadows;
} @else {
$shadows: 0 0 2px rgba(#000,.25);
-webkit-box-shadow: $shadow;
box-shadow: $shadow;
}
}
@include关键词“@include”来调用声明好的混合宏
button {
@include border-radius;
}
.box {
@include border-radius(3px);
}
.box-center {
@include center(500px,300px);
}
.box {
@include box-shadow(0 0 1px rgba(#000,.5),0 0 2px rgba(#000,.2));
}
继承已存在的类样式块,从而实现代码的继承
.btn {
border: 1px solid #ccc;
padding: 6px 10px;
font-size: 14px;
}
.btn, .btn-primary, .btn-second {
border: 1px solid #ccc;
padding: 6px 10px;
font-size: 14px;
}
.btn-primary {
background-color: #f36;
color: #fff;
}
.btn-second {
background-clor: orange;
color: #fff;
}
写成继承的形式,而且编译出来的 CSS 会将选择器合并在一起,形成组合选择器.
.btn-primary {
background-color: #f36;
color: #fff;
@extend .btn;
}
.btn-second {
background-color: orange;
color: #fff;
@extend .btn;
}
%声明的代码,如果不被 @extend 调用的话,不会产生任何代码
%mt5 {
margin-top: 5px;
}
.btn {
@extend %mt5;
}
.block {
@extend %mt5;
}
通过 @extend 调用的占位符,编译出来的代码会将相同的代码合并在一起.
.btn, .block {
margin-top: 5px;
}

(1)构建一个选择器
@mixin generate-sizes($class, $small, $medium, $big) {
.#{$class}-small { font-size: $small; }
.#{$class}-medium { font-size: $medium; }
.#{$class}-big { font-size: $big; }
}
@include generate-sizes("header-text", 12px, 20px, 40px);
(2) 属性变量
$properties: (margin, padding);
@mixin set-value($side, $value) {
@each $prop in $properties {
#{$prop}-#{$side}: $value;
}
}
.login-box {
@include set-value(top, 14px);
}
@mixin中插值不能作为赋值语句的值部分,只能用做属性名定义或者选择器构建时@include中不能使用插值
1、类似 CSS 的注释方式,使用 ”/* ”开头,结属使用 ”*/ ”
2、类似 JS的注释方式,使用“//” 两者区别,
前者会在编译出来的 CSS 显示,后者在编译出来的 CSS 中不会显示
加减乘除连带单位一起计算
在变量或属性中都可以做加法运算,但对于携带不同类型的单位时,在 Sass 中计算会报错
.content {
width: $full-width - $sidebar-width;
}
字符串拼接
div {
cursor: e + -resize;
}
编译后
div {cursor: e-resize;}
当一个单位同时声明两个值时会有问题 只能有一个值带单位(比如 em ,px , %)
# 编译的时候报“20px*px isn't a valid CSS value.”错误信息。
.box {
width:10px * 2px;
}
# 正确的写法
.box {
width: 10px * 2;
}
如果数值或它的任意部分是存储在一个变量中或是函数的返回值。
• 如果数值被圆括号包围。
• 如果数值是另一个数学表达式的一部分
在除法运算时,如果两个值带有相同的单位值时,除法运算之后会得到一个不带单位的数值
.box {
width: (1000px / 100px);
}
编译后
.box {
width: 10;
}
@else if ,@else条件@mixin blockOrHidden($boolean:true) {
@if $boolean {
display: block;
}
@else {
display: none;
}
}
.block {
@include blockOrHidden;
}
.hidden{
@include blockOrHidden(false);
}
@for $i from <start> through <end>
@for $i from <start> to <end>
关键字 through 表示包括 end ,而 to 则不包括 end
@for $i from 1 through 3 {
.item-#{$i} { width: 2em * $i; }
}
@while $types > 0 {
.while-#{$types} {
width: $type-width + $types;
}
$types: $types - 1;
}
@each 循环就是去遍历一个列表,然后从列表中取出对应的值
@each $var in <list>
$list: adam john wynn mason kuroir;
@mixin author-images {
@each $author in $list {
.photo-#{$author} {
background: url("/images/avatars/#{$author}.png") no-repeat;
}
}
}
.author-bio {
@include author-images;
}
Sass 扩展了 CSS 的 @import 规则,让它能够引入 SCSS 和 Sass 文件。 所有引入的 SCSS 和 Sass 文件都会被合并并输出一个单一
描述:页面元素的宽度按照屏幕分辨率进行适配调整,但整体布局不变。主要特征是像瀑布一样往下流,有规律的无限遍历模块。(阿里、字节考过)

给图片添加样式让图片等宽并同行显示。
1、首先我们定义一个container容器来装所有图片,在这个容器中用box容器装box-img容器再装入每张图片,这样方便之后样式的编写。
2、使图片同行显示--给box容器使用float:left;属性。
3、让图片等宽显示--给box-img容器设置width:150px;,img标签设置width:100%;继承父容器box-img高度的100%
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>waterFall</title>
<script src="./index.js"></script>
</head>
<style>
<!--清除浏览器自带样式-->
*{
margin: 0;
padding: 0;
}
<!--overflow: hidden触发BFC-->
.container {
overflow: hidden;
position: relative;
}
<!--float: left同行显示-->
.box{
float: left;
padding: 5px;
}
<!--图片等宽-->
.box-img{
width: 150px;
padding: 5px;
border: 1px solid #484848;
box-shadow: 0 0 5px #484848;
}
<!--图片占满父容器-->
.box-img img{
width: 100%;
}
</style>
<body>
<div id="container">
<div class="box">
<div class="box-img">
<img src="./img/1.jpg" alt="">
</div>
</div>
/*.......后面接39个box,此处省略*/
</div>
</body>
</html>
1、首先用window.οnlοad=function(){}来实现页面加载完毕后立即执行的功能
调用imgLocation('container','box')函数来呈现最终效果,
传入的实参是父容器'container',装图片的子容器'box'。
window.onload=function() {
imgLocation('container','box')
}
2、实现imgLocation()函数功能
1)首先我们得 获取所有要摆放的图片,并将其存入一个数组中
function imgLocation(parent,content){
//得到父容器
var cparent=document.getElementById(parent)
//cparent下的所有的第一层的子容器 box
var ccontent=getChildElement(cparent,content)//数组,40个div
}
//取到父容器中的某一层子容器
function getChildElement(parent,content){
var contentArr=[]
var allContent=parent.getElementsByTagName('*')//通过标签来选中得到一个数组
//遍历allContent把其中类名为content的容器都存到contentArr数组中
for(var i=0;i<allContent.length;i++){
if(allContent[i].className==content){ //当前这个容器的类名是否为content
contentArr.push(allContent[i])
}
}
return contentArr;
}
2)得到这个数组后,找出从谁开始是需要被摆放位置的
首先获取视窗的宽度和每张图片的宽度,
将两者相除并向下取整可得到第一行可以放置图片的数量,
自然也就知道了我们需要操作的那张图片的序号。
//从谁开始是需要被摆放位置的
var winWidth=document.documentElement.clientWidth;//视窗宽度
var imgWidth=ccontent[0].offsetWidth;//图片宽度
var num=Math.floor(winWidth/imgWidth)//第一行能放几张图
3)得到需要被摆放位置的图片序号后,确定其摆放位置
定义一个存储高度的数组,对前一行元素的高度进行遍历并存入数组,
当遍历到需要被摆放位置的图片时,
用Math.min()方法获取前一行高度最矮的元素高度,并用indexOf()方法获取到其下标。
再对我们所操作的这个图片容器的样式调整:
position:absolute;绝对定位,
top值设置为前一行高度最矮的图片高度minHeight,
left值设置为单张图片宽度乘这张图片的下标minIndex。
最后,摆放好图片后,还要更新摆放的那一列的高度
//操作num+1张图
var BoxHeightArr=[]
for(var i=0;i<ccontent.length;i++){
//前num张只要计算高度
if(i<num){
BoxHeightArr[i]=ccontent[i].offsetHeight
}
else{
//我们要操作的box :ccontent[i]
var minHeight=Math.min.apply(null,BoxHeightArr)//apply:把最小值这个方法借给它用
var minIndex=BoxHeightArr.indexOf(minHeight)//返回数组下标
ccontent[i].style.position='absolute'//style设置样式
ccontent[i].style.top=minHeight+'px'
ccontent[i].style.left=imgWidth*minIndex+'px'
//更新最矮的那一列的高度
BoxHeightArr[minIndex]+=ccontent[i].offsetHeight
}
}
复用和业务逻辑拆分
说到媒体查询和多个样式表
React如何实现动画的(数字不断变大的动画)
最后是用库实现的
打卡考勤项目
Origin字段用来说名本次请求来自哪个源,服务器根据这个值,决定是否同意这次请求。
如果Origin指定的源不在允许范围之内,服务器就会返回一个正常的HTTP回应,然后浏览器发现头信息中没有包含Access-Control-Allow-Origin 字段,就知道出错啦,然后抛出错误
// 加载Express模块
const express = require('express');
// 加载MySQL模块
const mysql = require('mysql');
...
// 创建MySQL连接池
const pool = mysql.createPool({
host: '127.0.0.1', //MySQL服务器地址
port: 3306, //MySQL服务器端口号
user: 'root', //数据库用户的用户名
password: '', //数据库用户密码
database: 'reg_log', //数据库名称
connectionLimit: 20, //最大连接数
charset: 'utf8' //数据库服务器的编码方式
});
// 创建服务器对象
const server = express();
// 加载CORS模块
const cors = require('cors');
// 使用CORS中间件
server.use(cors({
origin: ['http://localhost:8080', 'http://127.0.0.1:8080']
}));
| 网络需求 | 数据传输 | 访问 | 性质 | |
|---|---|---|---|---|
| localhost | 不联网 | 不使用网卡,不受防火墙和网卡限制 | 本机访问 | 域名,默认是指向 127.0.0.1 |
| 127.0.0.1 | 不联网 | 网卡传输,受防火墙和网卡限制 | 本机访问 | 环回地址 |
| 本机IP | 联网 | 网卡传输 ,受防火墙和网卡限制 | 本机or外部访问 | 本机对外放开访问的 IP 地址 |
localhost是个域名,而不是一个ip地址。可修改。
用于指代 this computer 或者 this host,可以用它来获取运行在本机上的网络服务。
在大多数系统中,localhost被指向了 IPV4 的 127.0.0.1 和 IPV6 的 ::1,这就是把localhost与127.0.0.1等同的原因。
本机地址,主要用于测试
(127.x.x.x)是本机回送地址(Loopback Address),即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,不进行任何网络传输。
示例都使用端口3000作为HTTP服务器的默认监听端口。
3000 是整数。 0 ~ 1023 (常用端口)49152 ~ 65535 号端口是动态端口
//用户注册接口
server.post('/register', (req, res) => {
//console.log(md5('12345678'));
// 获取用户名和密码信息
let username = req.body.username;
let password = req.body.password;
//以username为条件进行查找操作,以保证用户名的唯一性
let sql = 'SELECT COUNT(id) AS count FROM reg_log WHERE username=?';
pool.query(sql, [username], (error, results) => {
if (error) throw error;
let count = results[0].count;
if (count == 0) {
// 将用户的相关信息插入到数据表
sql = 'INSERT reg_log(username,password) VALUES(?,MD5(?))';
pool.query(sql, [username, password], (error, results) => {
if (error) throw error;
res.send({
message: 'ok',
code: 200
});
})
} else {
res.send({
message: 'user exists',
code: 201
});
}
});
});
// 用户登录接口
server.post('/login', (req, res) => {
//获取用户名和密码信息
let username = req.body.username;
let password = req.body.password;
// SQL语句
let sql = 'SELECT id,username FROM reg_log WHERE username=? AND password=MD5(?)';
pool.query(sql, [username, password], (error, results) => {
if (error) throw error;
if (results.length == 0) { //登录失败
res.send({
message: 'login failed',
code: 201
});
} else { //登录成功
res.send({
message: 'ok',
code: 200,
result: results[0]
});
}
});
// 指定服务器对象监听的端口号
server.listen(3000, () => {
console.log('server is running...');
})
注册页,使用token,能避免CSRF攻击Cross-site request forgery。JSON Web Token(JWT)
//注册
checkForm() {
// 点击注册按钮后调用此方法,验证用户名、密码、二次密码是否均正确,正确则发送axios请求
if (this.checkName() && this.checkPwd() && this.checkrePwd()) {
console.log(`验证成功,执行注册业务......`);
// 发送注册(post)请求
this.axios
.post("/register", `username=${this.name}&password=${this.pwd}`)
.then((result) => {
console.log(result);
if (result.data.code == 200) {
// 弹窗提示注册成功
...
// 注册成功后直接跳转到登录页
this.$router.push("/login");
} else if (result.data.code == 201) {
...
}
});
}
//登陆
checkForm() {
// 点击登录按钮后调用此方法,同时验证用户名和密码
if (this.checkName() && this.checkPwd()) {
// 发送登录(post)请求
this.axios
.post("/login", `username=${this.name}&password=${this.pwd}`)
.then((result) => {
console.log(result);
if (result.data.code == 200) {
// 弹窗提示登录成功
this.$toast({
message: `欢迎您 ${this.name}`,
position: "bottom",
duration: 3000,
});
...
} else {
this.$toast({
message: "登录失败,请检查您的用户名和密码",
position: "bottom",
duration: 3000,
});
}
});
}
currentPage(当前页码)、total(总条数)、pageSize(每页展示的数据量)
把所有的数据请求回后,通过arr.slice(开始索引,结束索引)来进行截取每一页的数据;
假设当前页是currentPage = 1,pageSize = 5,那么应该从(currentPage-1)*pageSize开始截取,到currentPage*pageSize结束
.modal {
display: none; /* 默认隐藏 */
position: fixed; /* 固定定位 */
z-index: 1; /* 设置在顶层 */
...
overflow: auto;
}
// 点击按钮打开弹窗
btn.onclick = function() {
modal.style.display = "block";
}
事件委托,往上遍历target的父元素如果有弹窗的id就不做反应,如果没有就触发消失
主要目的是为了巩固基础知识,和适应大型项目的需求
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g
关于如何使用git设置类似Dropbox的服务,您有什么建议吗?您认为git是解决此问题的合适工具吗?我在考虑使用git+rush解决方案,你觉得怎么样? 最佳答案 检查这个开源项目:https://github.com/hbons/SparkleShare来自项目的自述文件:Howdoesitwork?SparkleSharecreatesaspecialfolderonyourcomputer.Youcanaddremotelyhostedfolders(or"projects")tothisfolder.Theseprojec
我编写了一个非常简单的“部署”脚本,作为我的裸git存储库中的post-updateHook运行。变量如下livedomain=~/mydomain.comstagingdomain=~/stage.mydomain.comgitrepolocation=~/git.mydomain.com/thisrepo.git(bare)core=~/git.mydomain.com/thisrepo.gitcore==addedremoteintoeachlive&stagegitslive和stage都初始化了gitrepos(非裸),我已经将我的裸仓库作为远程添加到它们中的每一个(名为co
我正在安装gitlabhq,并且在Gemfile中有对某些资源的“git://...”的引用。但是,我在公司防火墙后面,所以我必须使用http://。我可以手动编辑Gemfile,但我想知道是否有另一种方法告诉bundler使用http://作为git存储库? 最佳答案 您可以通过运行gitconfig--globalurl."https://".insteadOfgit://或通过将以下内容添加到~/.gitconfig:[url"https://"]insteadOf=git://
Activeadmingem已添加到我的rails项目中,但每次我尝试安装railsgactive_admin:install时,我都会收到类似的错误git://github.com/activeadmin/activeadmin.git(atmaster)isnotyetcheckedout.Runbundleinstallfirst.我肯定在运行“railsgactive_admin:install”之前运行了bundle。运行“bundleshow”后,我看到我已将“*activeadmin(1.0.0.pre3f916d6)”添加到我的项目中,但不断收到此错误消息。我的gem文
当我刚刚运行middleman时服务,all.css编译得很好,只包含对+box-shadow(none)的调用:/*line1,/home/yang/asdf/source/stylesheets/content.css.sass*/div{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}但是当我构建网站时,我得到了这个Sass/Compass错误:$middlemanbuildSlim::EmbeddedEngineisdeprecated,itiscalledSlim::EmbeddedinSlim2.0
所有题目均有五种语言实现。C实现目录、C++实现目录、Python实现目录、Java实现目录、JavaScript实现目录题目n行m列的矩阵,每个位置上有一个元素你可以上下左右行走,代价是前后两个位置元素值差的绝对值.另外,你最多可以使用一次传送阵(只能从一个数跳到另外一个相同的数)求从走上角走到右下角最少需要多少时间。输入描述:第一行两个整数n,m,分别代表矩阵的行和列。后面n行,每行m个整数,分别代表矩阵中的元素。输出描述:一个整数,表示最少需要多少时间。
我已经安装了最新版本的compass、sass和susy。但我仍然收到此错误:Unabletoactivatesusy-2.1.1,becausesass-3.2.17conflictswithsass(~>3.3.0)有人知道这个Ruby是如何工作的吗?这是我安装的gem的列表:***LOCALGEMS***CFPropertyList(2.2.0)chunky_png(1.3.0)compass(0.12.4)compass-core(1.0.0.alpha.19)compass-import-once(1.0.4)compass-rails(1.1.3)fssm(0.2.10)l