去掉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>
新建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;
页面使用
count: {{this.$store.state.count}}
简写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}`)
},
<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>
props通信:子组件通过props接受来自父级组件的参数
$emit: 子组件传递数据给父组件
ref:父组件使用子组件的时候设置ref(ref = ‘foo’),通过 this.$refs.foo 获取子组件实例
EventBus: 兄弟组件传值
$parent 或 $root: 通过共同祖辈搭建通信桥梁
provide 与 inject
父级组件:
provide(){
return {
foo:'foo'
}
}
子组件:
inject:['foo'] // 获取到祖先组件传递过来的值
需求
当前问题
按照props传参(子组件第一次可以接收content并且更新页面内容,当时当我们点击编辑其他文章,这时候content虽然有变化,但是无法更新到子组件html属性上面(相当于没有监听变化),导致页面无法更新到当前文章的content,仍然显示的是之前的content)
父组件
子组件
解决办法
在子组件加上content的监听,当content变化,再给文本编辑器内容绑定的html属性赋值.
watch:{
content(newVal, oldVal){
console.log('content change')
console.log(newVal)
this.html = this.content
}
},
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.组件路由
组件渲染的时候使用
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。
// 默认写法(会覆盖当前页面)
this.$router.push({
path: '/news',
query:{
a_id: val.a_id, title: '旅游资讯', sub_title: val.title, date: val.date
}
})
// 打开新页面的写法(使用resolve + window.open)
let routeData = this.$router.resolve({
name: 'news',
query: { a_id: val.a_id, title: '旅游资讯', sub_title: val.title, date: val.date }
})
window.open(routeData.href, '_blank')
created:渲染前调用,先初始化属性,再做渲染
mounted:渲染完成之后,先初始化页面,再对元素节点进行操作 (这里请求数据,可能会出现闪屏问题)
view(视图层)-controller(控制器)-model(数据模型)
view主要用于UI界面展示和响应用户交互,controller用于监听数据改变和处理用户交互,model用于存放数据
model-viewmodel-viewcontroller
在MVC的基础上,把contrler的数据和逻辑处理部分抽离出来,放到viewmodel中,是model与controller沟通的桥梁。
包含了当前url的属性,主要有
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)
基本数据类型: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)
常用的对象
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()
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
关键字
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()
原型链继承(子类继承父类,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)
})
跨域:当前页面接口访问的 协议/域名/接口 不同,(浏览器的同源策略)。(浏览器能发出请求,服务器能响应数据,返回的时候被浏览器拦截了)
解决办法
回调函数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(利用定时器实现异步操作)
深拷贝就是完全拷贝一个新的对象,会在堆内存开辟新空间,拷贝的对象被修改后,原对象不受影响
主要针对引用数据类型
扩展运算符
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:交互式网页开发技术,在不重新加载整个页面的情况下,与服务器交换数据并更新部分内容
通过XmlHttpRequest对象向服务器发送异步请求,然后从服务器拿到数据,最后通过js操作dom更新页面
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)
}
基于XML语法的图像格式,可缩放矢量图,本质是文本文件,无论缩放都不会失真
JSON Web Token 通过JSON形式作为在web应用中的令牌
JWT认证流程
JSON是一种纯字符串形式的数据,适合网络传输
给数据设默认值,最后进行判断
用户不需要重新登录,优化用户体验
方案如下
采用分片上传思想
断点续传
前端
登录页:输入用户信息,进入聊天室
聊天页:输入消息,点击按钮发送
-
用户:{{item.username}}
时间:{{new Date(item.dateTime)}}
消息:{{item.msg}}
后端
新建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);
web端发送请求报文(请求头、URL、请求方法、请求数据、协议版本) ==》 服务器端返回数据(响应头、协议版本、响应数据、响应代码(success/error)、服务器信息)
http协议是无状态协议 导致了 cookies的产生(用于验证服务器的合法性)
http协议版本
Content-Type:text/html;charset=UTF-8
):资源文件的类型以及编码方式Access-Control-Allow-Origin: *
代表所有网站可以跨域资源共享)协议(http)+域名(www.a.com)+端口号(8080)三者一致,其中一个不同就不是同源,需要跨域
跨域的方法
Access-Control-Allow-Origin
)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) 不可扩大 不可缩小
在html页面中所以元素都可以看成是一个盒子
盒子组成
盒模型类型
控制盒模型的模式:box-sizing:content-box(默认值), border-box(IE盒模型)
font-size = 62.5%
=> 1rem = 10px
(16px * 62.5% = 10px)例:
<div id="app">
<div id="sub">div>
div>
#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;
}
H5新特性
css3的新特性
css三大特性 继承(子元素继承父元素属性)、层叠、优先级
CSS可以继承的属性
css的预处理器增加了函数、变量等,便于维护
例:
#app{
span{
// 这里写span的样式
}
}
@global: #eee; // 定义全局变量,利于维护使用
#app{
color: @global;
}
<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) // 端口监听
// 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')
})