Vue
Vue3的一些基本配置
-
去掉ESLint校验:
package.json
中的extends去掉eslint:commends -
引入icon图标
// main.js中 import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } // vue页面使用 <el-icon><edit /></el-icon>
Vuex
- state: 存放数据
- mutations: 同步操作,写方法用于改变state里面的数据,vue可以监听数据的改变进行更新(相当于methods)
- actions:所有异步操作都放在这里,改变的数据不能够被vue监听
- getters: 进行逻辑处理(相当于computed属性)
- module:单一状态树,存放所有模块
-
新建store/index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state:{ count: 0 }, mutations:{ // state是必要参数 addCount(state, number){ state.count+=number } }, actions:{ setCount(content, payload){ //增加setCount方法,默认第一个参数是content,其值是复制一份store,第二个是自定义参数 return new Promise(resolve => { setTimeout(()=>{ content.commit('addCount', payload); resolve(); }, 1000); }) } }, getters:{ // 不能改变state里面的值 alertCount(state){ return `当前值${state.count}` } } }) export default store;
-
页面使用
<template> <div><b>count: {{this.$store.state.count}}</b></div> <button @click="addCount">count++</button> </template> <script> export default { name: 'HelloWorld', mounted() { console.log(this.$store.state.count) // 获取state值 console.log(this.$store.getters.alertCount) // 执行getters里的方法 }, methods:{ addCount() { this.$store.commit('addCount', 1) // 执行mutation里的方法 } } } </script>
简写vuex
- 采用解构方法
import {mapState, mapMutations, mapGetters} from "vuex";
<script> import {mapState, mapMutations, mapGetters} from "vuex"; export default { name: 'HelloWorld', mounted() { console.log(this.count) // 解构后,采用this.xx调用vuex的内容 console.log(this.name) }, methods:{ ...mapMutations(['addCount']), //mutations和actions要在methods里面解构 ...mapActions(['setCount']), btnAdd() { this.addCount(1) } }, computed:{ ...mapState(['count', 'name']), //state和getters要在computed里面解构 ...mapGetters(['alertCount']), } } </script>
- 采用解构方法
-
异步action在页面的使用
async mounted() { console.log(`旧值: ${this.count}`) await this.$store.dispatch('setCount', 3); // 默认写法 await this.setCount(3); // 解构写法 console.log(`新值:${this.count}`) },
v-model低层原理
<h2>v-model低层原理</h2>
<input placeholder="请输入数值" id="username" />
<p id="uName"></p>
<script>
let obj={}
Object.defineProperty(obj, 'username', {
// 取值
get:function (){
console.log("取值")
},
// 设置值
set(val) {
console.log(val)
document.getElementById('uName').innerText = val
}
document.getElementById('username').addEventListener('keyup', function (){
// 监听事件
obj.username = event.target.value;
// console.log(event)
})
</script>
vue理解
- 渐进式框架,声明式渲染,借鉴MVVM思想
- 采用虚拟dom(不会重新渲染所有页面,先进行对比,有更新的部分会进行重新渲染(render函数))
- 高内聚,低耦合,使用组件化开发
- computed属性:如果多次调用某个方法,computed里面只会计算一次,减少了资源消
vue2.0生命周期
- 创建前后:组件实例被创建
- 挂载前后 :组件挂载到实例
- 更新前后 :组件数据更新
- 销毁前后:组件实例销毁
vue性能优化
- 数据层级不能过深,合理设置响应数据
- v-if 和 v-show合理选择
- 使用数据缓存,不频繁取值、组件缓存(keep-alive)
- 分页、虚拟滚动
vue组件通信
-
props通信:子组件通过props接受来自父级组件的参数
-
$emit: 子组件传递数据给父组件
-
ref:父组件使用子组件的时候设置ref(ref = ‘foo’),通过 this.$refs.foo 获取子组件实例
-
EventBus: 兄弟组件传值
-
$parent 或 $root: 通过共同祖辈搭建通信桥梁
-
provide 与 inject
父级组件:provide(){ return { foo:'foo' } }
子组件:
inject:['foo'] // 获取到祖先组件传递过来的值
父组件向子组件动态传参
需求
- 父组件是文章管理列表,带有一个编辑按钮
- 子组件(editor)是一个内嵌的富文本编辑器:点击编辑按钮,弹出Dialog框,富文本编辑器的内容显示为当前文章内容(暂且设为content属性)
- 我们需要传递content属性到子组件(editor),但是子组件中v-model无法绑定props中的content,因此子组件中的文本内容绑定的是data中的html属性,我们需要将content传递并且赋值给html。
当前问题
-
按照props传参(子组件第一次可以接收content并且更新页面内容,当时当我们点击编辑其他文章,这时候content虽然有变化,但是无法更新到子组件html属性上面(相当于没有监听变化),导致页面无法更新到当前文章的content,仍然显示的是之前的content)
父组件
<el-form-item label="内容"> <WangEditor ref="editor" :content="formLabelAlign.content" /> </el-form-item>
子组件
<template> <Editor style="height: 500px; overflow-y: hidden;" v-model="html" :defaultConfig="editorConfig" :mode="mode" @onCreated="onCreated" /> </template> <script> import { Editor, Toolbar } from '@wangeditor/editor-for-vue' export default{ name: 'WangEditor', data() { return { html: this.content, } }, props:['content'], } </script>
解决办法
在子组件加上content的监听,当content变化,再给文本编辑器内容绑定的html属性赋值.
watch:{ content(newVal, oldVal){ console.log('content change') console.log(newVal) this.html = this.content } },
vue-router
1.路由守卫
A.全局路由
全局使用
-
router.beforeEach((to, from, next)=>{}):路由跳转前执行的操作
to:需要跳转的路径
from:当前页面路径
next:执行当前操作// 登录拦截器 router.beforeEach((to, from, next) =>{ if(to.path == '/login' || to.path == '/front/home'){ next(); }else{ // 不是登录页面 localStorage.getItem("user")?next():next("/login") } })
-
router.beforeResolve
-
router.afterEach((to, from) =>{}):路由跳转后
一般用于跳转后更改页面标题、声明页面等辅助功能
B.路由独享守卫
某个单页面渲染使用
-
boforeEnter(路由独享的守卫)
const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: (to, from) => { // reject the navigation return false }, }, ]
C.组件路由
组件渲染的时候使用
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
2.hash和history的区别
hash模式
-
带#号,改变URL时,页面不会重新加载
-
利用window.onhashchange事件实现,每次改变hash值,浏览器访问历史会新增一个记录
-
hash值是URL中#后面的值,不会传递给服务器
-
使用"后退"按钮,返回上一个页面
history模式
-
通过window.history实现页面无刷新跳转
-
路径会随http请求发送给服务器,因此前端URL必须和请求的后端URL一致,否则404
-
404举例
当我们把 history 项目部署到服务器中后,此时我们在浏览器输入一个网址(比如是 www.test.com ), 此时会经过 dns 解析,拿到 ip 地址后根据 ip 地址向该服务器发起请求,服务器接受到请求后,然后返回相应的结果(html,css,js)。如果我们在前端设置了重定向,此时页面会进行跳转到 www.test.com/home ,在前端会进行匹配对应的组件然后将其渲染到页面上。此时如果我们刷新页面的话,浏览器会发送新的请求 www.test.com/home, 如果后端服务器没有 /home 对应的接口,那么就会返回404。
computed和watch的区别
- computed是计算属性,函数内的变量可以缓存,除非变量发生变化,才会重新进行计算
- watch主要是监听,每次值变化就会立即进行回调。
v-show和v-if的区别
- v-show是控制display来让元素隐藏(会渲染到页面,不会触发生命周期)
- v-if显示隐藏时是把整个元素删除(为true才会渲染组件,会触发生命周期)
created和mounted请求数据,有什么区别
-
created:渲染前调用,先初始化属性,再做渲染
-
mounted:渲染完成之后,先初始化页面,再对元素节点进行操作 (这里请求数据,可能会出现闪屏问题)
vue中的修饰符
- 事件修饰符
- .stop 阻止冒泡
- .prevent 阻止默认行为
- .self 只有在event.target是当前元素触发
- .capture
- .once 事件只触发一次
- .passive 立即触发默认行为
- 按键修饰符
- .keyup .keydown
- 系统修饰符
- 鼠标修饰符
- 表单修饰符
keep-alive组件
用来缓存组件 ==》减少页面刷新、网络请求次数,提升性能
Vue el-upload上传图片 + express
思路:
- 以formData的数据格式上传图片
- 后端存储图片到文件夹(/images/)下,并且返回给前端图片名称(imgUrl)
- 前端通过’http://localhost:3000/images/imgUrl’的形式访问图片地址,用于展示
前端代码
<el-upload
class="avatar-uploader"
action="string"
method="post"
:http-request="uploadImage" // 自定义上传图片函数
>
<img v-if="formLabelAlign.imageUrl" :src="resource+formLabelAlign.imageUrl" class="avatar">
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
<script>
export default {
name: "Attractions",
data() {
return {
formLabelAlign: { title: '', date: '', content: '', imageUrl: '', filename: '', label: ''},
resource: 'http://localhost:3000/images/',
}
},
methods: {
uploadImage(params){
// 图片以formData格式上传,并且请求头需要设置'Content-Type': 'multipart/form-data'
let formData = new FormData()
formData = { file: params.file,}
const config = {
headers:{
'Content-Type': 'multipart/form-data'
}
};
this.request.post('/attractions/uploadImage', formData, config).then(res=>{
// console.log("upload: ", res)
this.formLabelAlign.imageUrl = res.data.data
})
}
},
}
</script>
后端代码(express)
const MsgCode = require("../config/MsgCode");
const multer = require('multer') // 引入multer:图片处理的工具包
const path = require('path')
const storage = multer.diskStorage({
// 配置文件上传后存储的路径
destination: function (req, file, cb) {
// NodeJS的两个全局变量
// console.log("__dirname: ", __dirname); //获取当前文件在服务器上的完整目录
// console.log("__filename: ", __filename); //获取当前文件在服务器上的完整路径
cb(null, path.join(__dirname, '../public/images'))
},
// 配置文件上传后存储的路径和文件名
filename: function (req, file, cb) {
let defineFilename = new Date().getTime() + file.originalname // 防止图片名重复
cb(null, defineFilename)
}
})
const upload = multer({
fileFilter(req, file, callback) {
// 解决中文名乱码的问题
file.originalname = Buffer.from(file.originalname, "latin1").toString("utf8");
callback(null, true);
},
storage: storage
})
// 上传首页图
router.post('/uploadImage',upload.single('file'), function (req, res, next){
// console.log(req.file)
MsgCode(res, req.file.filename, "") // 返回前端的自定义函数,读者可以自己使用res.end()进行数据传输
})
this.$nexttick
当数据被修改后,获取dom更新之后的数据,用于异步获取更新后的数据,性能优化
Object.defineProperty有什么缺点
- 无法监听到数组变化
- 无法检测对象属性的添加和删除
vue2和vue3区别
- 选项式API(vue2)和组合式API(vue3)
- vue3:多根节点
- 响应式原理:vue2(Obejct.defineProperty)vue3(proxy)
- Diff算法优化
- 虚拟dom增加patchFlag字段
MVC
view(视图层)-controller(控制器)-model(数据模型)
view主要用于UI界面展示和响应用户交互,controller用于监听数据改变和处理用户交互,model用于存放数据
MVVM
model-viewmodel-viewcontroller
在MVC的基础上,把contrler的数据和逻辑处理部分抽离出来,放到viewmodel中,是model与controller沟通的桥梁。
JS
JS组成三部分
- ECMAScript: JS的核心内容,描述了JS的基础语法(var for 数据类型…)
- 文档对象模型(DOM)
- 浏览器对象模型(BOM): 对浏览器窗口访问和操作
location对象
包含了当前url的属性,主要有
- protocol(‘http:’)协议
- host(‘www.xx.com’) 网址
- hostname(带了端口号)
- path (/home/list) 访问路径
- search (‘?id=123456&sort=discount’) 参数
- hash (‘#title’)
innerText 和 innerHTML的区别
- innerText不识别html标签,不保留空格和换行
- innerHTML识别html标签,保留空格和换行
和=区别
- ==: 比较的是值(如string类型和number类型比较,会进行隐士转化)
- ===:除了比较值,也比较数据类型(object类型比较的是地址)
构造函数
- 函数名首字母大写
- 不需要return就可以返回结果
- 调用函数必须使用关键字
new
// 构造函数
function Star(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
var dz = new Star('DZ', 18, 'male');
console.log(dz.age)
cookie sessionStorage localStorage的区别
- 三者均保存在本地浏览器
- cookie:存储量小,由服务器写入,若设置了固定的时间,时间过后失效
- session:页面关闭自动清除(主要用于检测用户刷新页面)
- localStorage:一直保存在本地(存储不易变动的数据),不会自动清除
token的登录流程
- 客户端用账号密码请求登录
- 服务器验证请求,成功之后,给客户端签发一个token,
- 客户端将token保存在本地(localstorage或者cookie中)
- 每次客户端发送请求,都要携带token
- 服务端收到请求,需要先验证token,验证成功,给客户端返回请求数据
Token存localStorage还是session里面?
- 存localStorage里面,每次请求接口都需要把它当做字段传到服务器(容易被XSS攻击)
- 存cookie中,会自动发送,不能跨域(CSRF攻击)
基本&引用数据类型(JS内置对象)
-
基本数据类型:string,number,undefined,null,boolean,symbol,bigint
保存在栈内存中,保存的是一个值 -
引用类型:object(普通对象)Array Function Date
保存在堆内存中,保存的是引用数据类型的地址(多个引用会同时改变)。例如下:let obj = { name: 'xm', age: 18 } let obj1 = obj obj1.name = 'change' console.log(obj) console.log(obj1)
常用的对象
- Math(abs, ceil, sqrt, max, min…)
- Date(getYear, getMonth…)
- Array(push, foreach)
- String(concat, length, slice, split)
操作数组的方法
-
push() pop() sort()
-
splice():数组删除,返回删除的元素,会改变原数组
-
slice:数组截取
-
unshift() shift() reverse() concat() join() map() filter() reduce() isArray() findIndex()
-
find 和 filter
find 返回第一个满足条件的内容
filter 返回一个新的数组let arr = [1, 2, 3, 4, 5, 5] let a = arr.find(item=>item>2) let b = arr.filter(item=>item>2) console.log(a) // 3 console.log(b) // [3, 4, 5, 5]
-
改变原数组的方法:push() pop() sort() reverse() unshift() shift() splice()
JS判断数组的方法
-
Array.isArray() 、instanceof Array() 、 constructor、prototype
let arr = [1, 2,3] let str = 'hello' console.log(Array.isArray(arr)) console.log(arr instanceof Array) console.log(Object.prototype.toString.call(arr).indexOf('Array') > -1) console.log(arr.constructor.toString().indexOf('Array') > -1)
数组去重
-
Set
-
indexOf
-
filter
let arr = [1, 2, 2, 3, 1, 4] // set去重 console.log(Array.from(new Set(arr))) // indexof去重 function unique(arr){ let brr = [] arr.forEach(item=>{ if(brr.indexOf(item) == -1){ brr.push(item) } }) return brr; } unique(arr) // 方式三:filter var arr2 = arr.filter((item, index,self)=>{ console.log(item, index, self) return self.indexOf(item)===index }) console.log(arr2)
多维数组最大值
let arr = [
[1, 2,3],
[1,3,5],
[95, 25, 36]
]
let brr = []
arr.forEach(item=>{
console.log(Math.max(...item))
brr.push(Math.max(...item))
})
数据类的检测方法
-
typeof() 判断基本数据类型
-
instanceof() 判断引用数据类型
-
constructor 基本可以判断基本数据类型和引用数据类型
console.log(('abc').constructor === String)
-
Object.prototype.toString.call() 可以判断所有数据类型
var opt = Object.prototype.toString console.log(opt.call(2)) // [object Number] console.log(opt.call('abc')) // [object String] console.log(opt.call([])) // [object Array] console.log(opt.call({})) // [object Object] console.log(opt.call(true)) // [object Boolean]
闭包
-
优点:可以重复利用内部变量,不会污染全局变量,一直保存在内存中,不会被垃圾机制回收
-
缺点:闭包较多的时候,会消耗内存,导致页面性能下降,在IE浏览器会导致内存泄漏
-
应用:防抖(防止重复点击,n秒后执行事件,在n秒内重复点击,从最后一次点击开始计时 — 点击事件)节流(n秒内重复点击,只有一次生效 – 滚动条)
-
function fn(){ let name = 'xiaoxie' function getName(){ alert(name) } getName() } fn()
防抖和节流的实现
// 防抖:n次内重复点击,执行最后一次
function FD_debounce(func, wait){
let timeout;
return function (){
let context = this; // 保存this指向,context指向当前对象
let args = arguments
clearTimeout(timeout)
timeout = setTimeout(function (){
func.apply(context, args);
console.log(context)
}, wait);
}
}
let func = function (){
console.log('我是func')
}
res = {
a: debounce(func, 2000)
}
res.a()
// 节流: n秒多次点击,只执行一次
function JL_debounce(func, wait){
let timeout;
return function (){
let context = this
if(!timeout){
timeout = setTimeout(function (){
func.apply(context)
timeout = null
}, wait);
}
}
}
内存泄漏
JS里已经分配内存地址对象,但由于长期没办法回收和清除释放,造成长期占有资源的情况,导师运行速度缓慢甚至崩溃。
因素
- 未清空的定时器
- 过度的闭包
- 一些元素没被清除
事件委托(事件代理)
原理:利用事件冒泡的机制实现,把子元素的事件绑定到父元素身上
父元素添加addEventListener('click', 函数名, true/false)
默认true, false代表阻止(子元素)冒泡
原型链
原型链:一个实例对象在调用属性和方法的时候,会以此从实例本身=》构造函数原型 =》原型的原型 去查找
以下例子的原型链:p1 -> Person -> Object -> null
原型就是一个普通的对象,它是为构造函数的实例共享属性和方法,所有实例中引用的原型都是同一个对象
<!--原型-->
function Person(){
this.say = function (){
console.log('sing')
}
}
let p1 = new Person()
let p2 = new Person()
p1.say()
p2.say()
例:say调用了两次,在内存中占用了两个(如果多次使用,会造成资源浪费),为了解决这个问题,原型:将方法挂载到prototype上,所有实例共享这个方法,减少了内存消耗。
__proto__
: 可以理解为指针,实例对象中的属性,指向了构造函数的原型(prototype)
p1.__proto__ === Person.prototype // 返回true
例如下:
<!--原型-->
function Person(){
this.say = function (){
console.log('sing')
}
}
Person.prototype.run = function (){
console.log('跑步')
}
let p1 = new Person()
let p2 = new Person()
//p1.say()
//p2.say()
p1.run();
p2.run();
new关键字
实现new
关键字
function newFun(Fun, ...args){
// 1.创建空对象
let obj = {} // 引用类型
// 2.把空对象和构造函数通过原型链进行连接
obj.__proto__ = Fun.prototype
// 3.把构造函数的this绑定到新的空对象身上
const res = Fun.apply(obj, args) // 值类型
// 4.根据构造函数返回的类型判断,如果是值类型,返回对象;是引用类型,返回这个引用类型
return res instanceof Object ? res:obj;
}
// 测试newFun
function Person(name){
this.name = name
}
Person.prototype.printName = function (){
console.log(this.name)
}
let p1 = newFun(Person, 'xym')
p1.printName()
JS的继承
-
原型链继承(子类继承父类,new出来的实例拥有父类相同的属性和方法)
缺点:无法向父类传参function Person(){ this.info={ name: '张三', age: 12 } } function Child(){} Child.prototype = new Person();
-
构造函数继承(使用call / apply 方法实现继承,在子类里面调用父类)
缺点:无法实现函数的复用function Person(){ this.info={ name: '张三', age: 12 } } function Child(){ Person.call(this) } let c1 = new Child(); c1.info.nick = 'nick name' console.log(c1.info)
-
组合继承(调用了两次父类构造函数)
function Person(gender){ this.info={ name: '张三', gender: gender, age: 19 } } Person.prototype.getInfo = function (){ console.log(this.info) } function Child(gender){ Person.call(this, gender) // 构造函数继承 } Child.prototype = new Person() // 原型链继承 let c1 = new Child('男') c1.info.nickname = '小张' c1.getInfo() let c2 = new Child('女') c2.info.nickname = '小红' // c1 和 c2 互不影响 c1.getInfo() c2.getInfo()
-
ES6使用class关键字继承父类
某些浏览器可能不支持class Animal{ constructor(kind) { this.kind = kind } getKind(){ return this.kind } } // 继承animal类 class Cat extends Animal{ constructor(name) { // 子类构造方法必须调用super方法 super('cat'); this.name = name } getCatInfo(){ console.log(this.name+' : '+this.getKind()) } } let cat = new Cat('buding') cat.getCatInfo()
## JS设计原理
- JS引擎
- 运行上下文
- 调用栈
- 事件循环
- 回调
## JS中的this指向
- 全局对象的this指向window
- 全局作用域/普通函数中this指向window
- this永远指向最后调用他的对象
- apply call bind可以改变this指向
- 箭头函数this指向箭头函数外的一层
## setTimeout和setInterval最小时间
- setTimeout 4ms
- setInterval 10ms
## promise
- 解决回调问题
```js
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 300);
});
myPromise.then(value => {
console.log(value)
})
跨域?如何解决跨域?
跨域:当前页面接口访问的 协议/域名/接口 不同,(浏览器的同源策略)。(浏览器能发出请求,服务器能响应数据,返回的时候被浏览器拦截了)
解决办法
- script调用js文件
- 服务器实现cors接口,前端正常访问就行。服务器收到前端请求,设置Access-Control-Allow-Origin
异步操作实现
-
回调函数callback
function f(x){ console.log(x) } function printTime(){ setInterval(function (){ console.log('print fn exe') c = 1 }, 3000) } var c = 0 printTime() f(c)
js内部执行不是按照顺序的,因此这里先打印0,再输出"print fn exe"。采用回调函数实现异步
<script type="text/javascript"> <!--回调函数,实现异步操作--> function f(x){ console.log(x) } function printTime(callback){ setInterval(function (){ console.log('print fn exe') c = 1 callback(c) }, 3000) } var c=0 //方法一 printTime(f) // 方法二 printTime(function (x){ console.log(x) }) </script>
-
async / await
function print(time){ setTimeout(function(){ console.log(time); return 1; },time) } async function fun(){ let a = await print(1000); // let b = await print(3000); // let c = print(2000); console.log(a); // console.log(1) setTimeout(()=>{ alert(a) }, 3000) } fun();
-
setTimeout(利用定时器实现异步操作)
ES6新特性有哪些?
- 新增了箭头函数
不能作为构造函数使用,不能new
没有arguments
不能用call,apply,bind改变this指向
箭头函数的this是在函数定义时就决定了,不可以修改:this指向定义时外层第一个普通函数的this - 新增块级作用域(let 和 const)
不存在变量提升(在变量声明之后才能使用)
存在暂时性死区问题
块级作用域的内容
不能在同一个作用域内重复声明 - Promise
解决回调地狱问题
自身有all,reject,resolve,race等方法
原型上有then,catch
把异步操作队列化
三种状态:pending-初始状态,fulfilled-操作成功,rejected-操作失败
async/await
同步代码做异步操作 - 解构赋值
从对象或者数组中取值,然后给变量赋值 - set和map
set:数组去重
map的key类型不受影响 - 定义类语法糖(class)
- 数据结构(symbol)
- 模块化(export,import)
call、aply、bind区别
- 都能改变this的指向
- call和aply功能类似,传参方法不同
- call传的是一个参数列表
- aply传的数组
实现深拷贝
深拷贝就是完全拷贝一个新的对象,会在堆内存开辟新空间,拷贝的对象被修改后,原对象不受影响
主要针对引用数据类型
-
扩展运算符
-
JSON.parse(JSON.stringify())
-
递归实现
// 深拷贝 let obj = { name: '张三', age: 18, say(){console.log('say name')}, arr:[[1, 2], 3, 4, 5] } // let obj1 = {...obj} // 方法一:扩展运算符 // obj1.name = 'xxx' // console.log(obj) // name : "张三" // console.log(obj1) // name : 'xxx' // 方法二:json.parse => 不能拷贝函数,即say没有拷贝进来 // let obj2 = JSON.parse(JSON.stringify(obj)) // obj2.name = 'aaa' // console.log(obj) // console.log(obj2) // 方法三 递归实现深拷贝 function exten(origin, deep){ let res = {} if(origin instanceof Array) res = [] // 判断数组, 更改返回类型 for(let key in origin){ let value = origin[key] res[key] = (deep && typeof value == 'object' && value != null) ? exten(value, deep):value } return res } let obj3 = exten(obj, true) obj3.name = 'bbb' obj3.arr[0].push('ccc') console.log(obj) console.log(obj3)
ajax
ajax:交互式网页开发技术,在不重新加载整个页面的情况下,与服务器交换数据并更新部分内容
通过XmlHttpRequest对象向服务器发送异步请求,然后从服务器拿到数据,最后通过js操作dom更新页面
- 创建XmlHttpRequest对象 xml
- xml.open()和服务器建立连接
- xml.send()发送数据
- xml.onreadystate() , xml.change()事件监听服务器和通信状态
- 接收并处理服务器响应的数据
- 更新数据到html页面
ajax/fetch/axios区别
- fetch是es6的一个用于网络请求的api
- axios是一个网络请求库
- ajax是异步js和xmlhttprequest
axios封装
vue2.0
npm install axios // 安装
-
创建service.js配置文件
import axios from 'axios' import { Message, Loading } from 'element-ui' const ConfigBaseURL = 'http://localhost:8080/' //默认路径,这里也可以使用env来判断环境 let loadingInstance = null //这里是loading //使用create方法创建axios实例 export const Service = axios.create({ timeout: 7000, // 请求超时时间 baseURL: ConfigBaseURL, method: 'post', headers: { 'Content-Type': 'application/json;charset=UTF-8' } }) // 添加请求拦截器(请求前执行) Service.interceptors.request.use(config => { loadingInstance = Loading.service({ lock: true, text: 'loading...' }) return config }) // 添加响应拦截器(服务器响应后执行) Service.interceptors.response.use(response => { loadingInstance.close() return response.data }, error => { console.log('TCL: error', error) const msg = error.Message !== undefined ? error.Message : '' Message({ message: '网络错误' + msg, type: 'error', duration: 3 * 1000 }) loadingInstance.close() return Promise.reject(error) })
-
main.js
使用import {Service} from '@/utils/service' Vue.prototype.request = Service //将axios放在vue的this上(vue2) app.config.globalProperties.request = Service //(vue3)
-
使用
// post方法 this.request.post(`/menu/delete?id=${val}`).then(res=>{ // 请求成功 }else{ // 请求失败 } }); // get方法 this.request.get("/echarts/getList").then(res =>{ console.log(res) })
vue3.0
-
创建
http.js
import axios from "axios";//创建一个axios的对象 //生成一个axios的实例 const http=axios.create({ baseURL:"http://localhost:8080/",// baseURL会在发送请求的时候拼接在url参数前面 timeout:3000,//请求超时 }); export default http;//导出
-
main.js
使用import axios from "axios"; const app = createApp(App) app.config.globalProperties.axios = axios
-
单页面使用
getWeather:function (){ this.axios.get(url).then(res =>{ // console.log(res) }) }
-
封装API的js文件使用(异步调用)
封装函数js文件import http from "./http"; export const getWeather = ()=>{ return new Promise((resolve, reject)=>{ http.get(url).then(res =>{ resolve(res) }) }) }
页面调用
// 1.引入 import {getWeather} from '../request/HomeHeader.js' // 2.获取返回值 let res = getWeather() // 方法一 采用promise实现异步 res.then((res)=>{ console.log(res) }) // 方法二 采用async/await实现异步 async created() { let res = await getWeather() console.log(res) }
get和post请求
- get是请求数据,post是提交数据
- get的请求参数放在url中,不安全,post请求参数放在body中
- get请求会缓存,post不会缓存
- 刷新或回退页面get不会重新请求,post会重新请求
- get请求只能进行url编码,post支持多种编码方式
promise和async/await的区别是什么
- 都是处理异步请求的方式
- async/await是基于promise实现的
- promise是返回对象,使用then,catch方法处理和捕获异步请求,链式书写,造成代码重叠,维护差
- async/await使用try,catch捕获异常
SVG格式
基于XML语法的图像格式,可缩放矢量图,本质是文本文件,无论缩放都不会失真
JWT
JSON Web Token 通过JSON形式作为在web应用中的令牌
JWT认证流程
- 前端发送登录请求给接口
- 服务器核对账号密码成功,把用户id等信息作为jwt负载,和他和头部进行base64编码,形成jwt
- 前端每次请求都会把jwt放在http请求头的Authorization字段内
- 服务器验证jwt的有效性,返回对应结果
JSON
JSON是一种纯字符串形式的数据,适合网络传输
- JSON.parse()
- JSON.stringfy()
数据没有请求过来时,该怎么做?
给数据设默认值,最后进行判断
无感登录
用户不需要重新登录,优化用户体验
方案如下
- 在响应器中拦截,判断token过期后,调用刷新token的接口
- 后端返回过期时间,前端判断token过期时间,去调用刷新token的接口
- 设置定时器,定时刷新token
大文件上传
采用分片上传思想
- 把文件按照一定规则分块,分割成相同大小的数据
- 初始化一个分块上传,返回本次上传唯一标识
- 按照一定规则把各个数据上传
- 服务器判断文件完整性,完整就把文件块合并
断点续传
JS为什么是单线程
为什么是单线程:比如当一个用户执行添加节点和删除节点的任务,如果不是单线程,则会出问题
JS微任务和宏任务
-
js是单线程的
-
js代码执行流程:同步执行完 ==》事件循环[微任务、宏任务] ==》微任务 ==》宏任务 ==》微任务 …
for(let i=0; i<3; i++){ setTimeout(function (){ console.log(i) }, 1000*i) } // 输入三个3
-
微任务:promise.then
-
宏任务:setTimeOut
JS作用域
-
除了函数外,js无块级作用域
-
作用域链:内部可以访问外部变量,但外部不能访问内部
注:如果内部有相同的变量,则优先访问内部的变量let a = 10; function fun(){ let a = 20; console.log(a) } fun();
-
js变量提升机制
function c(){ var b = 1; function a(){ console.log(b) //undifined var b = 2; console.log(b) // 2 } a() console.log(b) // 1 } c()
-
优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
JS对象
-
对象都是new构建的,所以对象之间不相等
console.log([1,2,3] === [1,2,3]) // false
-
对象的key都是string类型
-
原型链:对象本身obj ==》构造函数Fun =》对象的原型(
obj._proto_
) =》构造函数的原型(Fun.prototype) => Object => null
var let const的区别
-
let const 不存在变量提升,var有
-
var可以声明同一个变量多次,let const 只能声明一次同一个变量
-
const声明常量,一旦赋值不可以修改(const为object/数组,里面的值是可以修改的)
-
let const有块级作用域
if(true){ let a = 1; const b = 2; var c = 3; } console.log(c) // c能打印,a,b会报错 console.log(a) console.log(b)
object对象的合并
const a = {a:1, b:4}
const b = {b:2, c:3}
//方式1
let obj = Object.assign(a, b) // {1,2,3}
// 方式二
let obj2 = {...a, ...b} // {1,2,3}
websocket(vue2.0实现)
-
前端
登录页:输入用户信息,进入聊天室
聊天页:输入消息,点击按钮发送<template> <div> <ul> <!-- 消息展示 --> <li v-for="item in msgList" :key="item.id"> <div>用户:{{item.username}}</div> <div>时间:{{new Date(item.dateTime)}}</div> <p>消息:{{item.msg}}</p> </li> </ul> <input type="text" placeholder="请输入消息" v-model="msg"> <button @click="sendMsg">发送</button> </div> </template> <script> // npm install ws const ws = new WebSocket('ws://localhost:8080') export default { name: "Two", data(){ return{ msg: '', username: '', msgList: [] } }, methods:{ sendMsg(){ const msg = this.msg ws.send(JSON.stringify({ id: new Date().getTime(), username: this.username, dateTime: new Date().getTime(), msg: this.msg })) }, handleWsOpen(e){ console.log('websocket open', e) }, handleWsClose(e){ console.log('websocket close', e) }, handleWsError(e){ console.log('websocket error', e) }, // 接收消息 handleWsMessage(e){ // console.log('websocket message', e) let msg = JSON.parse(e.data) this.msgList.push(msg) console.log(msg) }, }, mounted() { this.username = localStorage.getItem('username') if(!this.username){ this.$router.push('/login') } // 事件绑定 ws.addEventListener('open', this.handleWsOpen.bind(this), false); ws.addEventListener('close', this.handleWsClose.bind(this), false); ws.addEventListener('error', this.handleWsError.bind(this), false); ws.addEventListener('message', this.handleWsMessage.bind(this), false); } } </script> <style scoped></style>
-
后端
新建index.js文件const Ws = require('ws') // 立即执行函数 ;((Ws)=>{ const server = new Ws.Server({port: 8080}); // 要和前端监听同一个端口 // 初始化函数 const init = ()=>{ bindEvent(); } function bindEvent(){ server.on('open', handleOpen); server.on('close', handleClose); server.on('error', handleError); server.on('connection', handleConnection); } function handleOpen(){ console.log('wb open') } function handleClose(){ console.log('wb close') } function handleError(){ console.log('wb error') } // 消息接收 function handleConnection(ws){ console.log('wb connection') ws.on('message', handelMsg) } // 消息处理 function handelMsg(msg){ // c : 表示每个客户端 let oMsg = msg.toString() server.clients.forEach((c)=>{ c.send(oMsg) }) } init() })(Ws);
HTTP协议以及浏览器原理
http协议
web端发送请求报文(请求头、URL、请求方法、请求数据、协议版本) ==》 服务器端返回数据(响应头、协议版本、响应数据、响应代码(success/error)、服务器信息)
http协议是无状态协议 导致了 cookies的产生(用于验证服务器的合法性)
http协议版本
- 1.0 只能
- 1.1
- 2.0
浏览器原理
- 输入网址
- DNS域名解析(有该网页则返回,没有返回错误信息)
- TCP三次握手(建立连接)
- HTTP请求(获取资源) =》 HTTP响应
- 浏览器渲染页面。HTML解析 =》 DOM树,css =》css规则树,两者结合渲染成render树,浏览器通过render对页面进行渲染布局
http缓存
- 强缓存(本地缓存)和协商缓存(弱缓存)
- 网页请求资源
- 没有缓存 =》 使用服务器资源 =》返回请求资源。
- 有缓存 =》强缓存命中 =》使用缓存内容 =》返回资源。
- 强缓存未命中 =》协商缓存命中,使用缓存内容;未命中 =》服务器资源 =》返回资源
浏览器如何渲染页面
- html解析成DOM树,CSS解析成CSS规则树
- 浏览器将CSS规则树附着在DOM树上,结合生成Render渲染树
- 生成布局:浏览器解析渲染树节点的位置和大小,在屏幕上渲染出所有节点(重排)
- 将布局绘制在屏幕上,生成整个页面(重绘)
Http请求头和响应头
- 请求头
- Accept(浏览器可接受服务器返回数据类型, text/html, ‘/’ 代表所有)
- Accept-Encoding(gzip, deflate 申明浏览器接受的编码方法)
- Connection(keep-alive / close , 当浏览器与服务器建立了TCP连接,keep-alive表示不断开,下次请求服务器使用相同的TCP连接)
- Host(服务器ip和端口)
- Referer(浏览器的地址)
- User-Agent(浏览器的操作系统和名称、版本)
- Cookie(存储用户信息)
- 响应头
- Content-Type(
Content-Type:text/html;charset=UTF-8
):资源文件的类型以及编码方式 - Content-Encoding: 编码格式
- Date:服务器时间
- Server:服务器信息
- Connection(keep-alive/ close)
- Access-Control-Allow-Origin:指定可以访问服务器的网址(
Access-Control-Allow-Origin: *
代表所有网站可以跨域资源共享) - Access-Control-Allow-Methods(GET, POST, DELETE, PUT)
- Access-Control-Allow-Credentials(是否允许发送cookie)
- Content-Type(
同源策略
协议(http)+域名(www.a.com)+端口号(8080)三者一致,其中一个不同就不是同源,需要跨域
跨域的方法
- JSONP(利用script标签不存在跨域的方式)
- CORS(服务器配置响应头
Access-Control-Allow-Origin
) - websocket
- 反向代理(中间件)
CSS
BFC(块级格式化上下文)
- bfc就是一个隔离容器,日期里面的子元素不会影响到外面的元素
- bfc的原则:如果一个元素具有bfc,那么内部元素任何操作都不会影响到外部元素
- 如何触发bfc(一般给内部元素加overflow:hidden):
float:none
overflow: visible
display:inline-block、table-cell
position: absolute、fixed
清除浮动有哪些方式
// li标签会自己浮动,没有包裹在ul标签里面
<style>
*{margin: 0; padding: 0}
ul{
list-style: none;
border: 10px solid red;
}
li{
width: 100px;
height: 100px;
background: yellow;
float: left;
}
</style>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</body>
// 方法1:触发bfc清楚浮动
ul{
overflow: hidden;
}
// 方法2:after方式
ul:after{
content: '';
display: block;
clear: both;
}
position有哪些值,分别根据什么定位
- static:默认值,代表没有定位
- fixed:固定定位,相对于浏览器窗口进行定位,(使用left,right, top,bottom)
- absolute:相对于第一个有relative父元素定位
- relative:相对定位,相对于自身定位,不脱离文档流(位置改变了,其原有位置不会被其他元素占据)
flex布局
flex 表示 flex-grow(设置扩展比例), flex-shrink(设置收缩比例), flex-basis(不伸缩的情况下子容器的原始尺寸)
flex:1
: (1, 1, 0%) 不管内容多少,都是平分空间
flex:auto
: (1, 1, auto) 根据内容的大小来分,不是均分的(除非内容都是一样,才均分)
flex:0
: (0, 1, 0%)可缩小,不可扩大
flex:none
:(0, 0, auto) 不可扩大 不可缩小
CSS样式优先级(权重)
- !import > 行内样式 > id选择器 > class选择器 > 标签选择器 > 通配符
盒子模型
在html页面中所以元素都可以看成是一个盒子
盒子组成
- 内容content
- 内边距padding
- 边框border
- 外边距margin
盒模型类型
- 标准盒模型 margin+border+padding+content
- IE盒模型 margin+content(padding + border)
控制盒模型的模式:box-sizing:content-box(默认值), border-box(IE盒模型)
隐藏元素的方法
- display: none(元素消失,不占空间)
- opacity: 0 (透明度,占空间)
- visibility: hidden(元素消失,占空间)
- position: absolute(将元素移除页面)
- clip-path(将元素剪掉)
px和rem区别
- px: 像素,绝对单位长度,每个显示器像素大小一样
- rem:相对单位,相对html根节点font-size的值改变,html的根节点默认16px :
font-size = 62.5%
=>1rem = 10px
(16px * 62.5% = 10px) - em: 相对父元素的font-size改变
重绘和重排(回流)
- 重排(回流):布局引擎根据所有样式计算出盒模型在页面的位置和大小
- 重绘:计算好盒模型在页面的位置和大小以及其他属性,然后根据每个盒模型的特性进行绘制
- 对DOM的大小、位置修改,重排
- 对DOM的样式修改,重绘
元素水平居中方式
- 定位+margin
- flex布局
- grid布局
- table布局
例:
<div id="app">
<div id="sub"></div>
</div>
<!-- 定位+margin -->
#app{
width: 400px;
height: 400px;
position: relative;
border: 3px solid royalblue;
}
#sub{
position: absolute;
width: 100px;
height: 100px;
background-color: red;
top: 0;
right: 0;
left: 0;
bottom: 0;
margin: auto;
}
H5C3新特性
H5新特性
- 语义化标签
- 新增音频视频
- 画布canvas
- localStorage和sessionStorage
- 表单控件email url search
- 拖拽释放api
css3的新特性
- 选择器:属性选择器、伪类选择器、伪元素选择器
- 增加了媒体查询
- 文字阴影
- 边框
- 盒子模型box-sizing
- 渐变
- 过度
- 自定义动画
- 2D和3D
CSS属性继承
css三大特性 继承(子元素继承父元素属性)、层叠、优先级
CSS可以继承的属性
- 文字:font
- 文本:line-height
- 元素可见性:visibility: hidden
- 列表属性: list-style
预处理器
css的预处理器增加了函数、变量等,便于维护
- SASS
- LESS
例:
<div id="app">
<span></span>
</div>
#app{
span{
// 这里写span的样式
}
}
@global: #eee; // 定义全局变量,利于维护使用
#app{
color: @global;
}
Node.js
入门
数据库连接,登录小案例
<form action="http://localhost:8080" method="post">
<input type="text" class="username" name="username" />
<input type="password" class="password" name="password"/>
<input type="submit" value="提交" />
</form>
const mysql = require('mysql')
const http = require('http')
const queryString = require('querystring')
const server = http.createServer((req, res)=>{
let postVal = "";
req.on("data", (chunk)=>{
postVal+=chunk;
})
req.on('end', ()=>{
let formVal = queryString.parse(postVal)
let username = formVal.username;
let pwd = formVal.password;
console.log(username, pwd)
// 连接数据库
const conn = mysql.createConnection({
host: 'localhost',
port: 3306,
user: 'root',
password: '116054',
database: 'test'
})
conn.connect();
conn.query('select * from user where username=? and password=?', [username, pwd], (err, result, fields)=>{
if(err) throw err;
console.log(result);
if(result.length>0){
res.writeHead(200, {'Content-Type': 'text/html;charset=utf8'});
res.write('登录成功')
res.end(); // 关闭http连接
}
});
conn.end(); // 关闭数据库连接
})
})
server.listen(8080) // 端口监听
express
处理404
// catch 404 and forward to error handler
// 方法一
var createError = require('http-errors');
app.use(function(req, res, next) {
next(createError(404));
});
// 方法二
// 所有路由之后配置处理404
app.use((req, res, next)=>{
console.log('错误', err)
res.status(404).send('404 Not Found')
})
Java
面向对象和面向过程
面向过程:更注重任务顺序(简单高效)
面向对象:更注重参与者(易于维护、拓展)
封装、继承、多态
-
封装:内部细节对外部调用透明,外部调用无需修改或关心内部实现
1.javabean的属性私有,提供getset对外访问数据
2.orm框架:不需要关心链接如何建立,只需要调用mybatis,调方法即可 -
继承:继承基类方法,进行拓展
-
多态:基于对象所属类的不同,外部对同一个方法调用,实际执行的逻辑不同
父类类型 变量名 = new 子类对象 变量名.方法名();
JDK、JRE、JVM
- jdk(java开发工具)
- jre(java运行环境)
- jvm(虚拟机)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VuzCPhs5-1690204973231)(C:\Users\11605\AppData\Roaming\Typora\typora-user-images\image-20230711192250851.png)]
==和equals
- ==对比的是栈中的值:基本数据类型的变量值,引用类型是堆内存对象的地址
- equals: 直接对比的值:value
final的作用
- 修饰类:类不可被继承
- 修饰方法:不可被子类覆盖,但可以重载
- 修饰变量:一旦被赋值不可被修改
String StringBuffer StringBuilder
- String是final修饰的,每次操作都会产生新对象(赋值变量会创建一个新的内存地址)
- string buffer和string builder都是在原对象操作
- buffer是线程安全的,builder是线程不安全的
- stringbuffer方法都是synchronized修饰的
- 性能:string builder > stringbuffer > string
- 场景:经常改变字符串就使用后两者,优先使用stringbuilder,多线程使用共享变量时使用string buffer
重载和重写的区别
-
重载:发生在同一个类中,方法名必须相同,参数类型、个数、顺序不同,方法返回值和访问修饰符可以不同
-
重写:发生在父子类中,方法名、参数列表必须相同,返回值范围小于父类,抛出的异常范围小于父类;父类方法修饰符是private,子类就不能重写该方法
public int add(int a, String b) public String add(int a, String b) // 编译报错
List和Set的区别
-
list: 有序,按对象进入顺序保存,可重复,允许多个null对象,可遍历,可使用get(int index)获取元素文章来源:https://www.toymoban.com/news/detail-601934.html
-
set: 无序,元素不可重复, 最多一个null对象,只能使用iterator获取元素文章来源地址https://www.toymoban.com/news/detail-601934.html
List<Object> list = new ArrayList<>();
hashCode与equals
ArrayList和LinkedList区别
- Arraylsi:基于动态数组,连续内存,下标(随机)访问,扩容机制:因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组
- LinkedList: 基于链表,存储分散在内存中,适合做数据插入和删除,不适合查询(一般不用)
到了这里,关于前端八股文的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!