响应式布局
媒体查询
输出页面最大可见宽度
@media screen and (min-width:100px) and (max-width: 300px) {
div {
background-color:lightblue;
}
}
弹性盒子
.fu{
display: flex;
flex-direction:row/column;主轴方向横向
justify-content:space-around;主轴对齐方式
align-items:center;交叉轴对齐方式
align-content:space-around;将所有子项看作一个整体交叉轴对齐方式
}
.zi{
flex:1;
}
九宫格布局
弹性盒子实现
<div class="box">
<div class="r">
<div class="c"></div>
<div class="c"></div>
<div class="c"></div>
</div>
<div class="r">
<div class="c"></div>
<div class="c"></div>
<div class="c"></div>
</div>
<div class="r">
<div class="c"></div>
<div class="c"></div>
<div class="c"></div>
</div>
</div>
.box {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.r {
flex: 1;
display: flex;
}
.c{
flex:1;
}
网格布局实现
<div id="container">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
#container{
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
}
web安全
xss攻击
往 Web 页面里插入恶意可执行网页脚本代码
解决方案:
- 将< >进行转义为
<
和>
- node.js中通过escape库解决。
escape(data)
sql注入
利用服务端拼接sql字符串,拼接处各种sql,在服务端执行。使服务端数据泄漏。一般出现在登录页面。
在密码框输入1'or'1'=='1'
select*from tablename where username=''or true
解决方案:sql预编译
CSRF攻击
跨站请求伪造,它利用用户已登录的身份,在用户毫不知情的情况下,以用户的名义完成非法操作。
解决方案:
- 使用post请求(不能完全解决)
- 加入验证码(确定为用户行为而不是黑客行为)
- 验证ReFerer(验证发起请求的网页的地址)
- 也不能完全解决,因为网页地址也可以篡改伪装
- 请求时附带验证信息Anti CSRF Token
http协议
HTTP
是一种无状态 协议(本身不会对请求保存),由于HTTP
是无状态协议,所以必须引入一些技术来记录管理状态,例如Cookie
。
http
请求消息
- 请求行
- 请求方法+空格+URL+空格+协议版本
- 请求头
- 头部字段名+
:
+值
- 头部字段名+
- 空行
- 请求数据
http
响应消息
- 状态行
- 协议版本+状态码+状态消息
- 消息报头
- 头部字段名+
:
+头部字段值
- 头部字段名+
- 空行
- 响应正文
常见状态码
1字头:信息,服务器收到请求,需要请求者继续执行操作
2字头:成功,操作被成功接收并处理
3字头:重定向,需要进一步的操作以完成请求
4字头:客户端错误,请求包含语法错误或无法完成请求
5字头:服务器错误,服务器在处理请求的过程中发生了错误
101:切换协议。
200:请求成功。一般用于GET与POST请求
203:非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204:无内容。服务器成功处理,但未返回内容。
301:永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。
302:临时移动。
304:未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。
305:使用代理。所请求的资源必须通过代理访问
307:临时重定向。
400:客户端请求的语法错误,服务器无法理解
404:服务器无法根据客户端的请求找到资源(网页)
405:客户端请求中的方法被禁止
500:服务器内部错误,无法完成请求
502:作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503:由于超载或系统维护,服务器暂时的无法处理客户端的请求。
505:服务器不支持请求的HTTP协议的版本,无法完成处理
http原理
从输入url到页面解析
排序算法
冒泡排序
function fun(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
// 把最小的放到最后面
if (arr[j] < arr[j + 1]) {
let temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
快速排序
function fun(arr) {
if (arr.length <= 1) {
return arr
}
let left = []
let right = []
let k = arr.splice(0, 1)[0]
for (let i = 0; i < arr.length; i++) {
if (arr[i] < k) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return fun(left).concat([k], fun(right))
}
Vue 初始化页面闪动问题如何解决?
出现该问题是因为在 Vue 代码尚未被解析之前,尚无法控制页面中 DOM 的显示,所以会看见模板字符串等代码。
解决方案是,在 css 代码中添加 [v-cloak] { display: none; }
规则,同时在待编译的标签上添加 v-cloak
属性:
<div v-cloak>
{{ message }}
</div>
双向数据绑定源码
/*
1.定义Vue的类
2.实现数据劫持
3.属性代理:将this.$data上面的所有的属性,代理到Vue实例上,方便取值,设置
4.模板编译:先把节点导入文档碎片中,文档碎片替换,将文档碎片放回去 【还没有处理替换操作】
5.文本替换:
{{}}的内容已经放上去了,但是,当vm上的数据变了,页面不会再次渲染。
6.发布订阅模式:data---》view 单向数据已经实现
1.Dep:收集订阅者
2.Watcher:订阅者
3.有个节点满足条件,就立刻实例化Watcher
4. Watcher构造函数中,取值,调用了getter,准备将watcher添加到subs中
5.实例化dep
6.getter添加订阅者
7.修改了值,会调用setter,发布订阅
8.在watcher的update中,取出新值,传给回调函数,进行更新
7.实现input的单向数据绑定
8.实现双向数据绑定
*/
// 1.定义Vue的类
class Vue {
constructor(options) {
//options={el:"#app",data:{ name:"妲己", age:20, info:{ a:"aa", b:"bb" }}}
this.$data = options.data;
// 2.调用数据劫持的函数
Observe(this.$data);
// 3.属性代理
Object.keys(this.$data).forEach((key) => {
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get() {
//this.name 返回:this.$data["name"]
return this.$data[key];
},
set(newValue) {
//this.name=123 this.$data.name=123
this.$data[key] = newValue;
},
});
});
// 4.调用模板编译的函数
Compile(options.el, this);
}
}
// 2.实现数据劫持
function Observe(obj) {
// obj={ name:"妲己", age:20, info:{ a:"aa", b:"bb" }}
//2.4终止递归
if (!obj || typeof obj !== "object") {
return;
}
// 6.5实例化dep
let dep = new Dep();
//2.1获取到所有的key值,循环,给所有的属性加getter和setter
Object.keys(obj).forEach((key) => {
let value = obj[key];
//2.3递归
Observe(value);
//2.2加getter和setter
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
// 6.6添加订阅者
Dep.target && dep.addSub(Dep.target);
return value;
},
set(newValue) {
value = newValue;
//2.4后面设置的新值,也需要加setter和getter
Observe(value);
//6.7发布订阅
dep.notify();
},
});
});
}
// 4.实现模板编译的函数
function Compile(el, vm) {
// 4.1 获取节点
vm.$el = document.querySelector(el);
// 4.2 创建文档碎片
let fragment = document.createDocumentFragment();
// 4.3 将节点放入文档碎片中
while ((childNode = vm.$el.firstChild)) {
fragment.appendChild(childNode);
}
// 4.4数据替换
// 5.调用文本替换函数
replace(fragment);
// 4.5 将文档碎片放回去
vm.$el.appendChild(fragment);
// 5.实现文本替换
function replace(node) {
// 5.1 插值表达式的正则 {{ name }} \s空格 *(0个或者多个) \S非空字符 +(1个或者多个)
let reg = /\{\{\s*(\S+)\s*\}\}/;
//纯文本节点判断
if (node.nodeType === 3) {
let text = node.textContent; // "" "name:{{name}}" "123"
let execResult = reg.exec(text); //如果不满足条件,返回null;满足,返回数组,而且数组的第二项是 "info.a"
//满足插值表达式
if (execResult) {
let value = execResult[1].split(".").reduce((obj, k) => obj[k], vm);
node.textContent = text.replace(reg, value);
//6.3实例化Watcher
new Watcher(vm, execResult[1], (newValue) => {
node.textContent = text.replace(reg, newValue);
});
}
return;
}
// 7.实现input的单向数据绑定
if (node.nodeType === 1 && node.nodeName === "INPUT") {
let attrs = Array.from(node.attributes);
let attrObj = attrs.find((item) => item.nodeName === "v-model");
if (attrObj) {
let key = attrObj.value; //"name" "info.a"
let value = key.split(".").reduce((obj, k) => obj[k], vm);
node.value = value;
//添加订阅者
new Watcher(vm,key,(newValue)=>{
node.value = newValue;
})
//8.实现双向数据绑定
node.addEventListener("input",(e)=>{
// e.target.value 123
//key:"info.a" -["info","a"] vm
//obj=vm["info"] obj["a"]=123
let arr=key.split(".");//["info","a"]
let obj=arr.slice(0,arr.length-1).reduce((val,k)=>val[k],vm)
obj[arr[arr.length-1]]=e.target.value;
},false)
}
return;
}
//查看node的子节点是不是文本
node.childNodes.forEach((item) => replace(item));
}
}
// 6.1 收集依赖的类
class Dep {
constructor() {
//存放订阅者
this.subs = [];
}
//添加订阅者
addSub(watcher) {
this.subs.push(watcher);
}
//发布订阅
notify() {
this.subs.forEach((watcher) => watcher.update());
}
}
// 6.2订阅者类
class Watcher {
//cb:更新的时候的回调
//key:关联的属性 name
//vm:所有的数据来源都是vm
constructor(vm, key, cb) {
this.vm = vm;
this.key = key;
this.cb = cb;
//非常难理解6.4 准备将watcher添加到subs中
Dep.target = this;
this.key.split(".").reduce((obj, k) => obj[k], this.vm); //这句话并不是真的要取值
Dep.target = null;
}
update() {
// 在watcher的update中,取出新值,传给回调函数,进行更新
let value = this.key.split(".").reduce((obj, k) => obj[k], this.vm);
this.cb(value);
}
}
EventBus原理
class EventBus {
constructor() {
this.subs = [];
}
$on(event, handle) {
this.subs.push({
event,
handle
});
return this;
}
$emit(event, param) {
this.subs.forEach(item => {
if (item.event === event) {
item.handle(param);
}
})
return this;
}
}
const eventBus = new EventBus();
// eventBus
// .$on('aaa', n => {
// console.log(n);
// })
// .$on('aaa', n => {
// alert(n);
// })
// .$on('bbb', n => {
// console.log(n);
// })
// .$on('bbb', n => {
// alert(n);
// });
// eventBus.$emit('aaa', 5);
// eventBus.$emit('bbb', 3);
js的继承方法
1.原型链继承
如果继承的属性是引用数据类型,一个实例化对象改变,则另一个实例化对象也会改变(缺点)
function Person(name) {
this.name = name
}
Person.prototype.sey = function () {
console.log("你好,我是" + this.name);
}
function Student() { }
Student.prototype = new Person("妲己")
let fun = new Student
fun.sey()
console.log(fun);
2.构造函数继承
原型链上的方法和属性不能继承
function Person(name) {
this.name = name
}
Person.prototype.sey = function () {
console.log("你好,我是" + this.name);
}
function Student(name) {
Person.call(this, name)
}
let fun = new Student("妲己")
console.log(fun);
3.组合继承
function Person(name) {
this.name = name
}
Person.prototype.sey = function () {
console.log("你好,我是" + this.name);
}
function Student(name) {
Person.call(this, name)
}
Student.prototype = Person.prototype
let fun = new Student("妲己")
fun.sey()
console.log(fun);
ES6定义一个类
class Student {
constructor(name, age) {
this.name = name;
this.age = age
}
sey() {
console.log(`你好,我叫${this.name},我${this.age}岁了`);
}
}
let student = new Student("小明", 18)
ES6继承一个类
// 提取共同属性name,封装成父类
class Person {
constructor(name) {
this.name = name
}
// 也可以继承方法
drink(){
console.log("喝水");
}
}
// 继承父类
class Student extends Person {
constructor(name, age) {
// 调用父类Person构造函数
super(name)
this.age = age
}
sey() {
console.log(`你好,我叫${this.name},我${this.age}岁了`);
}
}
let student = new Student("小明", 18)
console.log("student", student);
student.sey()
student.drink()
class Teacher extends Person {
constructor(name, age) {
super(name)
this.age = age
}
teach() {
console.log(`你好,我叫${this.name},我${this.age}岁了`);
}
}
let teacher = new Teacher("王老师", 46)
console.log("teacher", teacher);
teacher.teach()
teacher.drink()
vue异步组件
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})