1.单线程:
JavaScript 引擎是单线程的。这意味着,在同一时间,只有一个任务能够被执行。 这种单线程的特性使得 JavaScript 简单且易于实现,但同时也带来了一些挑战,如事件循环和异步编程。
2.事件循环:
JavaScript 中的事件循环负责管理任务队列。 当引擎启动时,它会创建一个事件循环,然后等待任务被添加到队列中。 任务可以分为两种:宏任务(macro tasks)和微任务(micro tasks)。 宏任务是由宿主(如浏览器)管理的,例如 setTimeout 和 setInterval。微任务则是在 JavaScript 引擎内部管理的,例如 Promise 的回调。 事件循环会不断地检查任务队列。如果存在微任务,则优先处理微任务。如果没有微任务,则处理宏任务。
3.异步编程:
由于 JavaScript 是单线程的,因此异步编程成为处理高延迟操作(如 I/O 操作)的关键。 JavaScript 提供了 Promise、async/await 和 setTimeout 等方法来处理异步操作。 使用 Promise 和 async/await,开发者可以更容易地编写和处理异步代码。
4.阻塞与非阻塞:
阻塞操作会阻止 JavaScript 引擎直到操作完成。非阻塞操作则不会。 在浏览器中,setTimeout 和 setInterval 是非阻塞的,因为它们将任务添加到事件循环中,而不需要等待它们完成。 阻塞操作的例子包括 alert 和 console.log,因为它们会暂停 JavaScript 引擎直到它们完成。
5.任务队列:
任务队列是一个 First In First Out (FIFO) 的队列,它存储了需要执行的任务。 宏任务和微任务都存储在任务队列中,但它们有不同的优先级。
6.执行上下文:
当 JavaScript 执行代码时,它会创建一个执行上下文栈。 执行上下文包含了当前代码的变量、对象和函数。 每当引擎执行一个函数,它会创建一个新的执行上下文并将其压入栈顶。当函数执行完毕,执行上下文会弹出栈顶并被垃圾回收。
1.宏任务(Macro Tasks):
宏任务是由宿主(如浏览器)管理的任务。 常见的宏任务包括: setTimeout setInterval setImmediate requestAnimationFrame 事件监听器(例如 addEventListener) 网络请求(例如 XMLHttpRequest) 当你在宏任务中调用 async 函数时,该函数会返回一个 Promise 对象。
2.微任务(Micro Tasks):
微任务是在 JavaScript 引擎内部管理的任务。 常见的微任务包括: Promise 的 then 和 catch 回调 async 函数中的 await 表达式 queueMicrotask 当你在微任务中调用 async 函数时,该函数会立即执行,并且不会返回一个 Promise 对象。
原型链继承:
是最基本的继承方式。 原理是利用原型(prototype)属性的链式查找机制。 子类原型继承自父类原型的基础上,添加或覆盖父类原型的方法。 可以通过设置 proto 属性来实现。
构造函数继承:
子类构造函数调用父类构造函数,通过 call 或 apply 方法。 子类原型是自己的,而不是父类的。 这种方法继承了父类的属性,但无法继承原型链。
组合继承(原型链继承 + 构造函数继承):
结合了原型链继承和构造函数继承的优点。 子类原型继承自父类原型的基础上,通过 call 或 apply 方法继承父类构造函数的属性。 这种方法可以继承父类的属性和原型链。
寄生组合继承:
类似于组合继承,但优化了内存占用。 子类原型直接指向父类原型的一个副本,而不是直接继承父类原型。 这种方法避免了在子类原型上创建不必要的、多余的属性。
ES6 的 extends 关键字继承:
这是 ECMAScript 6 引入的新特性。 使用 extends 关键字,子类可以继承父类的属性和方法。 这种方法基于原型链继承,但也支持添加或覆盖父类的方法。
1 原理:
原型链继承:
利用了 JavaScript 的原型链机制。每个对象都有一个与之关联的原型对象,当试图访问一个对象的属性或方法时,JavaScript 会首先在该对象本身查找,如果找不到,它会沿着原型链向上查找。
构造函数继承:
通过在子类构造函数中调用父类构造函数,将父类实例的属性“复制”到子类实例上。
2 属性继承:
原型链继承:
子类原型继承自父类原型的基础上,可以继承父类原型的所有属性。
构造函数继承:
子类仅继承父类实例的属性,不继承原型链上的属性。
3 方法继承:
原型链继承:
子类原型继承自父类原型的基础上,可以继承父类原型的所有方法。
构造函数继承:
子类仅继承父类实例的方法,不继承原型链上的方法。
4 内存占用:
原型链继承:
由于所有子类实例都共享同一个原型对象,因此内存占用较少。
构造函数继承:
每个子类实例都有自己的一份属性副本,因此内存占用较大。
5 多态性:
原型链继承:
支持多态性,因为可以通过修改原型对象的方法实现不同子类之间的不同行为。
构造函数继承:
不支持多态性,因为子类实例的方法都是直接从父类实例“复制”的。
6 使用场景:
原型链继承:
适用于方法或属性较少,且不需严格控制实例属性的场景。
构造函数继承:
适用于需要严格控制实例属性的场景。
1 性能问题:由于原型链的查找是线性搜索,所以在大型对象图中,查找一个属性可能会非常慢。 2 可读性和维护性:原型链继承可能导致代码难以理解和维护。特别是当对象继承层次复杂时,开发者可能需要花费更多的时间来追踪继承关系。 3 内存泄漏:在某些情况下,原型链可能会导致内存泄漏。如果一个对象被另一个对象的原型引用,而这两个对象又都被其他对象引用,那么垃圾回收器就很难释放这些对象的内存。 4 属性名冲突:由于原型链上的属性是共享的,所以如果多个对象有相同的属性名,可能会导致意外的结果。 不够灵活:原型链继承不支持多重继承、函数覆盖等高级功能。。
所有的变量和函数声明都会被解释器移到其所在的代码块的顶部,无论它们在代码中实际的位置如何。这意味着你可以在变量或函数声明之前使用它们,而不会出现引用错误。
第一个是传参方式不同: call 和 bind 是列表传参,apply 是数组或伪数组传参
第二个是执行机制不同:call 和 apply 是立即执行,bind 不会立即执行而是生成一个修改 this 之后的新函数
1 使用 split() 方法:
这是最常用的方法。使用一个特定的分隔符,将字符串分割为数组。
const str = "hello world"; const arr = str.split(" "); // ["hello", "world"]2 使用 Array.from() 方法:
这种方法基于可迭代对象,例如字符串,并将其转换为数组。
const str = "hello world"; const arr = Array.from(str); ?// ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]使用 join()将数组转化为字符串
Set和Map是ECMAScript 6(ES6)中引入的两种新的原生对象。它们都是用于存储数据的集合,但它们的设计目的和使用方式有所不同。 Set是一种无序的唯一值集合。这意味着它只存储唯一的值,并且不会存储重复的值。Set对象可以存储任何可比较值,包括对象引用。Set具有以下特点:
集合 是由一堆无序的、相关联的,且不重复的内存结构【数学中称为元素】组成的组合
字典 是一些元素的集合。每个元素有一个称作key 的域,不同元素的key 各不相同
Set可以存储任何可比较值。
存储的值是唯一的,重复的值会被自动去重。
Set对象是有序的,但这个顺序并不是确定的,即Set中的元素顺序可能会改变。
Set对象提供了许多有用的方法,如add()、delete()、has()、clear()、size等。
Map是一种键值对的集合。与对象不同,Map的键可以是任何可比较的值,而不仅仅是字符串或符号。这意味着可以使用非字符串值作为键,例如数字或对象。Map对象可以存储任何类型的值作为值。Map具有以下特点:
存储的键和值可以是任何可比较的值。
Map对象是有序的,保留了元素插入的顺序。
Map对象提供了许多有用的方法,如set()、get()、delete()、clear()、size等。 区别:
共同点:集合、字典都可以存储不重复的值
不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储
下面是一个使用Set和Map的示例:
使用Set去重 const arr = [1, 2, 2, 3, 3, 3]; const uniqueArr = [...new Set(arr)]; // [1, 2, 3] console.log(uniqueArr); // 使用Map存储键值对 const map = new Map(); map.set("key1", "value1"); map.set("key2", "value2"); console.log(map.get("key1")); // "value1" console.log(map.has("key2")); // true map.delete("key3"); console.log(map.size); // 2总之,Set和Map都是用于存储数据的集合,但Set用于存储唯一的值,而Map用于存储键值对。根据实际需求选择合适的数据结构可以提高代码的可读性和性能。
通过数字索引访问元素。
具有长度属性,表示对象中元素的数量。
类数组对象不一定是数组,但是它们可以像数组一样使用。例如,可以使用循环遍历类数组对象的元素,或者使用数组的方法来处理它们。
Promise是一种用于处理异步操作的对象。它表示一个未完成的异步操作,并在该操作完成时提供结果或拒绝。
Promise有三种状态:
Pending(进行中):初始状态,表示异步操作还未完成。
Fulfilled(已完成):表示异步操作已成功完成,并且有结果。
Rejected(已拒绝):表示异步操作已失败,并且有错误信息。
使用Promise可以更加方便地处理异步操作,避免回调地狱。常见的Promise方法包括:
then(): 当Promise状态变为Fulfilled时调用该方法,并传递操作成功的结果。
catch(): 当Promise状态变为Rejected时调用该方法,并传递操作失败的错误信息。
finally(): 当Promise状态变为Fulfilled或Rejected时都会调用该方法。
Promise的链式调用是通过then方法实现的。then方法返回一个Promise对象,该Promise对象由传入的回调函数的返回值决定状态( Pending -> Fulfilled 或 R拒)。
具体实现原理如下:
当调用then方法时,会创建一个新的Promise对象,作为原始Promise对象的后续操作。
将传入的回调函数存储到新的Promise对象中。
当原始Promise对象状态发生变化时,通过链式调用将该状态传递给新的Promise对象。
当新的Promise对象状态为Fulfilled时,执行回调函数的第一个参数;当新的Promise对象状态为Rejected时,执行回调函数的第二个参数。
通过这种方式,可以实现Promise的链式调用。每个then方法都返回一个新的Promise对象,可以链式调用下一个then方法,形成一个Promise链。当链中任意一个Promise对象状态发生变化时,会通过链式调用将该状态传递给下一个Promise对象,直到最终的回调函数被调用。
下面是一个简单的示例,说明Promise的链式调用的实现原理:
function delay(time) { ?return new Promise(function(resolve, reject) { ? ?setTimeout(function() { ? ? ?resolve(time); ? }, time); }); } ? delay(2000) .then(function(result) { ? ?console.log('2秒后输出:', result); ? ?return result + 1; }) .then(function(result) { ? ?console.log('结果加1:', result); }) .catch(function(error) { ? ?console.error('发生错误:', error); });在上面的示例中,首先调用delay函数返回一个Promise对象,表示延迟2秒后输出结果。然后通过链式调用then方法,依次执行回调函数,最终输出结果加1。如果发生错误,则通过链式调用catch方法捕获错误并输出。在每次回调函数中,返回新的Promise对象,通过链式调用将状态传递给下一个Promise对象,实现了Promise的链式调用。
使用 catch 方法:在 then 方法中添加 catch 方法用于捕获错误。
myPromise .then(function(result) { ? ?// some code }) .catch(function(error) { ? ?// handle error });
使用 .catch 方法:直接使用 .catch 方法来捕获错误。
myPromise .catch(function(error) { ? ?// handle error });
在添加 then 方法时直接处理错误:可以在添加 then 方法时直接处理错误。
myPromise .then(function(result) { ? ?// some code ? ?if (someConditionIsNotMet) { ? ? ?throw new Error('Something went wrong!'); ? } ? ?return result; }) .catch(function(error) { ? ?// handle error });以上是 Promise 错误捕获的几种常见方式
Promise.all和Promise.race都是Promise对象的静态方法,用于创建一个新的Promise对象。
Promise.all接收一个Promise对象数组作为参数,返回一个新的Promise对象。这个新的Promise对象在所有输入的Promise对象的状态变为fulfilled时,状态也是fulfilled,且返回一个值为输入的Promise对象的结果的数组。如果有任一输入的Promise对象的状态变为rejected,则新的Promise对象的状态也是rejected,且返回该 rejected 的Promise对象的结果。
语法如下:
Promise.all(promises)其中promises是一个包含了多个Promise对象的数组。
Promise.race接收一个Promise对象数组作为参数,返回一个新的Promise对象。这个新的Promise对象在任何一个输入的Promise对象率先改变状态时,其状态和值都会被改变为率先改变的Promise对象的状态和值。
语法如下:
Promise.race(promises)其中promises是一个包含了多个Promise对象的数组。
Promise.all等待所有Promise都结束(都成功或者都失败),而Promise.race等待第一个Promise结束(第一个成功或者第一个失败)。
watch是用户手动去监听属性的变化并作出响应,而computed是自动监听属性的变化并计算出一个值。
computed属性的值会自动被缓存,只有在相关依赖属性发生变化时,才重新计算属性值,而watch没有自动缓存机制。
computed属性的值只能通过计算其他属性得到,而watch的值可以是任意值。
computed属性的值会响应式的更新,而watch的回调函数会执行两次,先执行一次watch的回调函数,再执行一次computed的回调函数。
总之,computed适用于计算属性的情况,可以提高性能;watch适用于需要手动作出响应的情况
在定义一个computed属性时,Vue会同时跟踪这个属性的所有依赖属性,以及这个属性的缓存值。
当这个computed属性的值需要被获取时,Vue会首先检查缓存值是否存在,如果存在则直接返回缓存值。
如果缓存值不存在或者相关依赖属性值发生变化,Vue会计算这个computed属性的值,并将计算结果缓存起来。
下一次获取这个computed属性的值时,Vue会直接返回缓存值,而不是重新计算。
通过这种缓存的实现,computed属性的值只有在相关依赖属性发生变化时才会重新计算,避免了不必要的计算,提高了性能。
watch支持异步是因为在初始化时,Vue实例会创建一个Watcher对象,该对象会包含所有监听的属性和对应的getter/setter。当属性的值发生变化时,Vue会调用对应的getter,而如果该getter是异步的,Vue会将它放入一个队列中,等待所有的同步操作完成后,再依次执行队列中的异步操作。
在执行异步操作时,Vue会使用Promise来处理,将异步操作封装成一个Promise对象,并将这个Promise对象缓存起来。当下一次属性的值发生变化时,如果对应的getter是异步的,Vue会先检查之前缓存的Promise对象是否已经解决,如果已经解决,则直接返回解决值;如果没有解决,则等待Promise对象解决,并缓存解决值。
这样,通过使用Promise来处理异步操作,可以保证在属性的值发生变化时,对应的getter只会被调用一次,并且可以正确返回最终的值,而不是中间的某个状态值。
watch支持异步是因为Vue会将异步操作封装成Promise对象,并在属性的值发生变化时,通过Promise来处理异程操作,保证getter只会被调用一次,并且可以正确返回最终的值。
<keep-alive>
组件的属性如下:
include
:一个字符串或正则表达式,用于指定组件的名称,只有名称匹配的组件才会被缓存。代码示例:
<keep-alive :include="includeVal"> ?<component :is="compName"></component> </keep-alive> ? data() { ?return { ? ?includeVal: /^Hello$/, }; }, computed: { ?compName() { ? ?return this.$route.name === 'Hello' ? HelloWorld : null; }, },
exclude
:一个字符串或正则表达式,用于指定不被缓存的组件的名称。代码示例:
<keep-alive :exclude="excludeVal"> ?<component :is="compName"></component> </keep-alive> ? data() { ?return { ? ?excludeVal: /^Hello$/, }; }, computed: { ?compName() { ? ?return this.$route.name === 'Hello' ? HelloWorld : null; }, },
max
:一个数字,用于指定缓存的最大数量,默认为Infinity。代码示例:
<keep-alive :max="maxVal"> ?<component :is="compName"></component> </keep-alive> ? data() { ?return { ? ?maxVal: 3, }; }, computed: { ?compName() { ? ?return this.$route.name === 'Hello' ? HelloWorld : null; }, },
timeout
:一个数字,用于指定组件从 inactive 状态转换到 active 状态,或者从 active 状态转换到 inactive 状态所允许的最长超时时间(以毫秒为单位)。代码示例:
<keep-alive :timeout="timeoutVal"> ?<component :is="compName"></component> </keep-alive> ? data() { ?return { ? ?timeoutVal: 5000, }; }, computed: { ?compName() { ? ?return this.$route.name === 'Hello' ? HelloWorld : null; }, },
在微信小程序中打开Vue页面,可能会遇到以下一些限制:
直接使用<script>标签引入Vue.js库可能会失败,因为微信小程序自带了一套UI库和运行环境,不支持外部引入的库。解决方法是将Vue.js代码打包成微信小程序支持的格式,或者使用微信小程序提供的虚拟DOM库。
Vue模板语法在微信小程序中不被支持,因此需要将Vue模板编译成微信小程序的JS语法。可以使用微信小程序提供的小程序编译器或者手动编译。
微信小程序的生命周期函数和Vue的生命周期函数有所不同,需要进行适配和调整。
微信小程序的API和Vue的API也有很大的差异,需要根据微信小程序的API进行开发。
在 Vue.js 中,有多种方法可以实现路由跳转。以下是一些常见的方法: 使用 <router-link> 标签: <router-link> 是 Vue Router 提供的用于创建链接的组件。它允许你使用简单的 HTML 标签来创建链接,并自动处理导航。
<router-link to="/">Home</router-link> <router-link to="/about">About</router-link>使用 router.push() 方法: router.push() 方法允许你通过代码控制路由导航。你可以在 Vue 组件的方法或生命周期钩子中使用它。
this.$router.push("/");使用 router.replace() 方法: router.replace() 方法类似于 router.push(),但它不会在浏览器历史中添加新的记录。这意味着,一旦用户导航到一个新的页面,他们不能使用浏览器的后退按钮返回到之前的页面。
this.$router.replace("/");使用 router.go() 方法: router.go() 方法允许你向前或向后导航一个指定的页面。
this.$router.go(-1); // 返回上一页 this.$router.go(1); ?// 前进到下一页使用 router.back() 方法: router.back() 方法类似于 router.go(-1),但它会触发一个 beforeRouteLeave 钩子,允许你在导航离开当前路由之前执行一些操作。
this.$router.back();
Vue路由传参有多种方式,常用的有以下几种:
query传参:在URL后面附带参数,参数以?分隔,参数之间以&分隔。这种方法适用于参数较少且不需要刷新页面时的传值。
// 路由配置 { ?path: '/example', ?name: 'example', ?query: { ? ?param1: null, ? ?param2: null }, ?// ... } ? // 路由调用 this.$router.push({ ?path: '/example', ?query: { ? ?param1: '123', ? ?param2: '345' } });
hash传参:通过在URL中使用哈希(#)符号来进行传参,参数写在哈希符号下,常用于单页面应用。
// 路由配置 { ?path: '/example', ?name: 'example', ?hash: { ? ?param1: null, ? ?param2: null }, ?// ... } ? // 路路上调用 this.$router.push({ ?path: '/example', ?hash: { ? ?param1: '123', ? ?param2: '345' } });
常规参数传参:在path中直接写参数,适用于参数不多的路由。
// 路由配置 { ?path: '/example/:id', ?name: 'example', ?params: { ? ?id: null }, ?// ... } ? // 路由调用 this.$router.push({ ?path: '/example/123', ?params: { ? ?id: '123' } });
全局路由传参:通过在main.js中设置全局参数,然后在其他路由中使用this.$route.params获取参数。
// main.js import Router from 'vue-router' import Home from './views/Home.vue' ? Router.beforeEach((to, from, next) => { ?// 在这里设置全局参数 ?to.meta.globalParams = 'global' ?next() }) ? export default new Router({ ?mode: 'history', ?base: process.env.BASE_URL, ?routes: [ ? { ? ? ?path: '/home', ? ? ?name: 'home', ? ? ?component: Home, ? ? ?meta: { ? ? ? ?// 在路由中使用this.$route.meta获取参数 ? ? ? ?params: 'home' ? ? } ? } ] })
HTTP缓存是一种机制,用于提高Web页面加载速度。当用户第一次访问一个网页时,网页的所有资源(如图片、脚本、样式表等)都会被下载到用户的浏览器中。接下来,当用户再次访问相同的网页时,浏览器可以从缓存中获取这些资源,而不需要重新下载,这样可以显著提高网页的加载速度。 HTTP缓存通常分为两类:客户端缓存和服务器端缓存。
客户端缓存是指资源保存在用户的浏览器中。当用户第一次访问一个网页时,浏览器会向服务器发送请求并下载资源。然后,浏览器会将资源保存在本地缓存中。当用户再次访问相同的网页时,浏览器会先检查本地缓存中是否有资源的副本。如果有,则直接从缓存中加载资源,而不需要再次向服务器发送请求。
服务器端缓存是指资源保存在服务器上。服务器可以配置一些缓存控制头(如Expires、Cache-Control、Last-Modified等),以指示浏览器是否可以从缓存中加载资源,以及缓存资源的有效期。如果浏览器中的资源已经保存在服务器的缓存中,并且缓存还没有过期,则服务器会直接返回缓存资源给浏览器,而不需要从数据库中重新获取资源。
HTTP缓存可以有效地减少网络带宽的使用,提高网页的加载速度,减少服务器的负载。同时,它也给网站所有者带来一些控制权,可以通过缓存控制头来控制资源的缓存行为。但是,HTTP缓存也有一些缺点,比如可能会导致浏览器中的资源版本过旧,影响用户体验。因此,在使用HTTP缓存时需要注意平衡缓存带来的好处和可能的缺点。
TCP(Transmission Control Protocol,传输控制协议)和UDP(User Datagram Protocol,用户数据报协议)是两种常用的传输层协议,它们有以下几个主要的区别:
连接方式:TCP是面向连接的协议,而UDP是无连接的协议。也就是说,使用TCP之前必须先建立一个连接,使用完毕后才能断开连接;而使用UDP时则不需要建立连接,直接发送数据包即可。
可靠性:TCP协议通过确认、重传、流量控制和拥塞控制等机制来保证数据的可靠性传输,即发送方会等待接收方的确认,如果接收方没有回应,发送方会重新发送数据。UDP则没有这些机制,发送方不会等待接收方的确认,也不会重新发送数据,因此UDP协议不适合传输可靠性要求高的数据。
速度:由于TCP协议需要进行确认、重传等操作,会带来一些额外的开销,导致传输速度较UDP慢。而UDP没有这些开销,因此在一些对速度要求较高的应用中被广泛使用。
适用场景:TCP协议适用于对数据传输可靠性要求较高的应用场景,如网页浏览、电子邮件、文件传输等;UDP协议适用于对传输速度要求较高但对数据传输可靠性要求不高的应用场景,如在线游戏、视频通话、实时音乐等。
物理层(Physical Layer):负责传输比特流,即对数据进行“0”和“1”的二进制编码和解码。
数据链路层(Data Link Layer):负责将物理层传输的比特流组装成帧(Frame),并提供数据传输的错误检测和纠正功能。
网络层(Network Layer):负责寻址和路由选择,实现不同网络之间的互联,常用的协议有IP协议等。
传输层(Transport Layer):负责端到端的数据传输,提供可靠的数据传输服务,常用的协议有TCP协议和UDP协议等。
会话层(Session Layer):负责建立、管理和终止会话,协调应用程序之间的通信。
表示层(Presentation Layer):负责数据的格式化和加密解密,使应用程序能够理解数据的含义。
应用层(Application Layer):直接面向用户,提供各种网络应用服务,如HTTP、FTP、SMTP等。
同源策略是浏览器的一道安全措施。它用于限制一个文档或脚本如何与外部资源进行交互,以防止恶意网站通过窃取用户数据或执行其他恶意行为来侵犯用户隐私。 同源策略要求浏览器中访问资源的文档或脚器与资源必须来自同一个源。源是指协议(比如 HTTP、HTTPS)、域名(包括子域名)和端口的组合。如果资源的源与文档或脚本的源不同,浏览器将阻止对资源的访问。 HTTP协议是基于TCP/IP协议的应用层协议,它负责在客户端和服务器之间传输网页内容,但是它本身不会对跨域访问进行限制。因此,浏览器需要同源策略来限制对HTTP资源的访问,以确保Web安全。
两个服务端之间不会产生跨域问题。 跨域是指浏览器中的JavaScript代码访问的资源来自不同的域名、端口或协议,而两个服务端之间访问的资源都是在服务端进行的,不会受到浏览器同源策略的限制。
CORS(Cross-Origin Resource Sharing)是一种用于解决跨域问题的机制。它通过在HTTP头部添加一些特定的响应头来告知浏览器哪些源可以访问资源。 当浏览器向一个不同的源发送跨域请求时,服务器会根据请求中的Origin头字段来判断这个请求是否来自可信的源。如果服务器信任这个源,就会在响应中添加一些CORS响应头,告诉浏览器允许这个源访问资源。 一些常用的CORS响应头包括Access-Control-Allow-Origin、Access-Control-Expose-Headers、Access-Control-Allow-Methods等。这些头字段提供了很多配置选项,用于控制哪些源可以访问资源以及可以进行哪些类型的跨域请求。 通过使用CORS,服务器可以更加精细地控制不同源对资源的访问权限,从而增强跨域请求的安全性。
常用的HTTP响应头如下:
Content-Type:指定返回内容的类型,如text/html、application/json等。
Content-Length:指定返回内容的长度。
Cache-Control:指定缓存控制策略,如no-cache、max-age等。
Expires:指定响应过期时间。
Set-Cookie:用于向客户端发送cookie。
Location:重定向到其他URL。
Status:HTTP状态码,如200 OK、404 Not Found等。
Server:服务器标识信息。
Pragma:指定缓存控制策略。
Last-Modified:指定资源最后修改时间。
ETag:指定资源的唯一标识。
Accept-Ranges:指定服务器接受的范围请求类型,如bytes等。
Connection:指定连接方式,如close、keep-alive等。
Transfer-Encoding:指定返回内容的编码方式,如chunked等。
请求行(Request Line)或响应行(Response Line):由 HTTP 方法、请求或响应的 URI 和 HTTP 版本组成。
请求头部(Request Headers)或响应头部(Response Headers):由请求头字段和值组成,用于传递请求或响应的附加信息。
空行(Blank Line):请求行或响应行和请求头部或响应头部之间的空行。
请求体(Request Body)或响应体(Response Body):包含请求或响应的具体内容,如 POST 请求中的表单数据或上传文件。
通过 wx.login() 获取到用户的code判断用户是否授权读取用户信息,调用wx.getUserInfo 读取用户数据
由于小程序后台授权域名无法授权微信的域名,所以需要自身后端调用微信服务器获取用户信息
通过 wx.request() 方法请求业务方服务器,后端把 appid , appsecret 和 code 一起发送到微信服务器。 appid 和 appsecret 都是微信提供的,可以在管理员后台找到
微信服务器返回了 openid 及本次登录的会话密钥 session_key
后端从数据库中查找 openid ,如果没有查到记录,说明该用户没有注册,如果有记录,则继续往下走
session_key 是对用户数据进行加密签名的密钥。为了自身应用安全,session_key 不应该在网络上传输
然后生成 session并返回给小程序
小程序把 session 存到 storage 里面
下次请求时,先从 storage 里面读取,然后带给服务端
服务端对比 session 对应的记录,然后校验有效期
在微信小程序中,openid是用于标识用户的唯一标识符,通过验证登录的openid可以判断当前用户是否已经登录过,以及确保用户数据的安全性和隐私保护。通过验证openid,可以防止用户信息被恶意盗用或篡改,同时也可以保证用户的登录状态的一致性。openid是用户授权后,微信服务器返回给小程序的参数,包含了用户在微信中的唯一标识信息,可用于实现用户登录态维持和用户身份认证等功能。
微信小程序中,appId指的是微信小程序的AppId,用于标识一个小程序的唯一性。在用户登录时验证appId可以确保用户登录的是正确的小程序,同时也可以防止恶意攻击者伪造小程序进行诈骗等行为。通过验证appId,可以提高小程序的安全性和可信度,保障用户的权益。
uniid 是一个自动生成的全局唯一标识符。它可以用于标识某个元素、某个节点或者某个对象,以便在代码中进行引用和操作。uniid 是由字母和数字组成的字符串,具有唯一性,可以保证在小程序中的唯一性。通过使用 uniid,可以方便地进行小程序的开发和调试。
通过使用 wx.navigateWeb 函数来内嵌一个 web 页面。使用该函数需要传入一个 web 地址作为参数,示例代码如下:
wx.navigateWeb({ ?url: 'http://www.example.com' })此外,还可以使用 wx.redirectTo 函数来内嵌一个 web 页面。使用该函数需要传入一个对象作为参数,示例代码如下:
wx.redirectTo({ ?url: 'http://www.example.com' })需要注意的是,微信小程序对于内嵌的 web 页面有一定的限制和管控,例如可能会进行 CSP 的限制等。在使用内嵌 web 页面时需要注意相关限制并进行相应的处理