MVC理解
- MVC(Model View Controller)是前端开发中的一种设计模式。
- M即model,模型层主要负责数据逻辑部分,按照功能将代码分块;
- V即view,视图层负责数据显示的部分;
- C即controller,控制层主要负责数据交互的部分。
- MVC开发模式可以简化为以下几步:
- 1.model层与服务器进行交互,从服务器请求数据,得到数据后进行封装。
- 2.controller层调用model层的数据,然后进行逻辑处理,然后model层接收controller层返回的数据。
- 3.model层对view层进行监听,让视图进行更新,视图更新完毕后通知model层。
MVC设计模式简化了开发,降低代码的耦合性。
var model = {
data: null,
init(){}
fetch(){}
save(){}
update(){}
delete(){}
}
view = {
init() {}
template: '<h1>hi</h1'>
}
controller = {
view: null,
model: null,
init(view, model){
this.view = view
this.model = model
this.bindEvents()
}
render(){
this.view.querySelector('name').innerText = this.model.data.name
},
bindEvents(){}
}
MVVM的理解
MVVM 由 Model、View、 ViewModel 三部分构成
- Model 代表数据模型,也可以在 Model 中定义数据修改和业务逻辑
- View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来
- ViewModel 是一个同步View 和 Model的对象
MVP理解
MVP 模式将 Controller 改名为 Presenter(呈现),同时改变了通信方向。它切断View跟Model之间的联系,由Presenter充当桥梁,做到View-Model之间通信的完全隔离,而其他各部分的的通信是双向的。
vue中MVVM原理及其实现
- 1、理解MVVM
- MVVM - Model View ViewModel:数据,视图,视图模型。
- 三者与 Vue 的对应:view 对应 template,vm 对应 new Vue({…}),model 对应 data。
- 三者的关系:view 可以通过事件绑定的方式影响 model,model 可以通过数据绑定的形式影响到view,
viewModel是把 model 和 view 连起来的连接器
。
- 2、
cookie、sessionStorage和localStorage的区别和使用
1.相同点是都是保存在浏览器端、且同源的。
2.cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递
,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存
。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。
3.存储大小限制也不同,cookie数据不能超过4K
,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。这里需要注意的是cookie的数据大小是指单个cookie的大小,而localStorage的大小是单个域名下localStorage所占用的大小总和。
4.数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效; localStorage:始终有效,窗口或浏览器关闭也—直保存,因此用作持久数据,除非用户主动删除; cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭。
5.作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面; localstorage在所有同源窗口中都是共享的; cookie也是在所有同源窗口中都是共享的。
浏览器为什么要有同源策略?
所谓的“同源策略”,最早是由 Netscape公司提出的一个安全策略,后来这就成为了浏览器最核心也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略。
在同源策略中,要求 域名、协议、端口 3部分都要相同!!!
什么是跨域并且怎么解决
跨域:从一个网页去请求另一个网页的资源,只要协议、域名、端口其中一个不同,就被当作是跨域
在请求时,如果出现了以下情况中的任意一种,那么它就是跨域请求:
1.协议不同,如 http 和 https;
2.域名不同;
3.端口不同。
解决方法
1、Proxy(代理) vue.config.js
可配置多个不同的proxy
devServer: {
proxy: {
'/api': {//代理标识,一般是每个接口前的相同部分
target: 'http://23.15.11.15:8000', // 这里写的是访问接口的域名和端口号
changeOrigin: true, // 允许跨域请求
pathRewrite: { // 重写路径,替换请求地址中的指定路径
'^/api': '/user'
}
},
'/login': {
target: 'http://23.15.11.15:8000',
changeOrigin: true,
pathRewrite:{
'^/login':'' //替换成空
}
}
}
},
2、jsonp处理跨域
简单说说你对 SPA单页面的理解,它的优缺点分别是什么?
单页 Web 应用 (single-page application 简称为 SPA) 是一种特殊的Web 应用。它将所有的活动局限于一个 Web 页面中
,仅在该 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成了,SPA 不会因为用户的操作而进行页面的重新加载或跳转。取而代之的是利用 JavaScript 动态的变换 HTML 的内容
,从而实现 UI 与用户的交互。由于避免了页面的重新加载, SPA 可以提供较为流畅的用户体验。得益于 ajax,我们可以实现无跳转刷新,又多亏了浏览器的 histroy 机制,我们可以用 hash 的变化来推动界面变化,从而模拟元素客户端的单页面切换效果。
- (1)单页面应用
只有一个WEB主页面的应用,公共资源(js、css等)仅需加载一次,所有的内容都包含在主页面,对每一个功能模块组件化。单页应用跳转,就是切换相关组件,仅刷新局部资源。常用于PC端官网、购物等网站。
优点: 页面切换快
缺点: 首屏时间稍慢,SEO差
常用于PC端官网、购物等网站。
- (2)多页面应用
有多个独立的页面的应用,每个公共资源(js、css等)需选择性重新加载,多页面跳转刷新所有资源。常用于 app 或 客户端等。
页面跳转: 返回HTML
优点: 首屏时间快,SEO效果好
缺点: 页面切换慢
常用于 app 或 客户端等
当然任何东西都有两面性,以下是总结目前 SPA 的一些优缺点:
它的优点有三点:
(1)良好的交互式体验,内容的改变不需要重新加载整个页面
,避免了不必要的跳转和重复渲染
;
(2)基于上面所说,SPA 对服务器的压力小
;
(3)前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理。
它的缺点有四点:
(1)初次加载耗时多
,为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载(可以使用路由懒加载解决)
(2)由于单页应用在一个页面中显示,所以不可以使用浏览器自带的前进后退功能
,想要实现页面切换需要自己进行管理
(3)SEO 难度较大
,由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
(4)有兼容性问题,部分平台不支持pushstate
(注:需了解History.pushState())
什么是浮动,如何清除浮动?
浮动的定义: 非IE浏览器下,容器不设高度且子元素浮动时,容器高度不能被内容撑开。 此时,内容会溢出到容器外面而影响布局。这种现象被称为浮动(溢出)。
-
浮动的工作原理:
浮动元素脱离文档流
,不占据空间(引起“高度塌陷”现象)
浮动元素碰到包含它的边框或者其他浮动元素的边框停留
浮动元素可以左右移动,直到遇到另一个浮动元素或者遇到它外边缘的包含框。浮动框不属于文档流中的普通流,当元素浮动之后,不会影响块级元素的布局,只会影响内联元素布局。此时文档流中的普通流就会表现得该浮动框不存在一样的布局模式。当包含框的高度小于浮动框的时候,此时就会出现“高度塌陷”。 -
浮动元素引起的问题?
父元素的高度无法被撑开,影响与父元素同级的元素
与浮动元素同级的非浮动元素会跟随其后
若浮动的元素不是第一个元素,则该元素之前的元素也要浮动,否则会影响页面的显示结构
清除浮动的方式如下: -
清除浮动
给父级div定义height属性
最后一个浮动元素之后添加一个空的div标签,并添加clear:both样式
包含浮动元素的父级标签
添加overflow:hidden
或者overflow:auto
使用 :after 伪元素。由于IE6-7不支持 :after,使用 zoom:1 触发 hasLayout**
如何创建块级格式化上下文BFC,BFC 有什么用?
- 如何创建 若一个元素具备以下任一特性,则可创建一个新的BFC:
根元素
float不为none,即 float:left 或 float:right
position不为static,relative,即 position:absolute,position:fixed
overflow不为visible,即 overflow:auto,overflow:hidden,overflow:scroll
display:inline-block,display:flex,display:inline-flex,display:table-caption,display:table-cell - 作用
创建BFC,从而消除垂直外边距叠加的现象
创建BFC,清除浮动带来的影响
创建BFC,构建自适应两列布局
页面怎么渲染的
- 1、根据html文件构建DOM树和CSSOM树。构建DOM树期间,如果遇到JS,阻塞DOM树及CSSOM树的构建,优先加载JS文件,加载完毕,再继续构建DOM树及CSSOM树。
- 2、构建渲染树(Render Tree)。
- 3、页面的重绘(repaint)与重排(reflow,也有称回流)。页面渲染完成后,若JS操作了DOM节点,根据JS对DOM操作动作的大小,浏览器对页面进行重绘或是重排。
最常见报错的状态码有哪些?
- 304:有缓存, 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
- 400:请求内容有误,服务器不理解请求的语法。一般请求的内容写错或者格式不正确可能会出现这种错误
- 404:服务器上无法找到请求的资源,除此之外,也可以在服务器拒绝请求但不想给拒绝原因时使用;
- 405:请求的方法不正确,例如定义的是post方式,请求写的是get 就会报这种错
- 500:服务器内部错误,服务器遇到错误,无法完成请求。
- 502:错误网关,我一般遇见这个会出现在ngnix没有启动或者代理写错的时候
axios
- 1、axios的特点有哪些?
- axios是基于promise的http库,支持promise所有的API
- 它可以拦截请求和响应
- 它可以转换请求数据和响应数据,并对响应回来的内容自动转换成JSON类型的数据
- 安全性更高,客户端支持防御XSRF
- 2、axios有哪些常用方法?
- axios.get(url[, config]) //get请求用于列表和信息查询
- axios.delete(url[, config]) //删除
- axios.post(url[, data[, config]]) //post请求用于信息的添加
- axios.put(url[, data[, config]]) //更新操作
vue项目中使用axios上传图片等文件
通过new 一个 FormData() form对象
import axios from 'axios'
// 添加请求头
update (e) { // 上传照片
let self = this
let file = e.target.files[0]
/* eslint-disable no-undef */
let param = new FormData() // 创建form对象
param.append('file', file) // 通过append向form对象添加数据
param.append('chunk', '0') // 添加form表单中其他数据
console.log(param.get('file')) // FormData私有类对象,访问不到,可以通过get判断值是否传进去
let config = {
headers: {'Content-Type': 'multipart/form-data'}
}
// 添加请求头
axios.post('http://172.19.26.60:8081/rest/user/headurl', param, config)
.then(res => {
if (res.data.code === 0) {
self.ImgUrl = res.data.data
}
console.log(res.data)
})
}
什么是ajax
AJAX 是一种用于创建快速动态网页的技术。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
优点
:减轻服务器的负担,按需取数据,最大程度的减少冗余请求,局部刷新页面,减少用户心理和实际的等待时间,带来更好的用户体验。
缺点
:AJAX大量的使用了javascript和ajax引擎,这些取决于浏览器的支持.在编写的时候考虑对浏览器的兼容性。”
css
块级元素和行内元素有什么区别
行内元素和块级元素的区别:行内元素不会自动换行,设置宽高无效;对margin设置左右方向有效,而上下无效,padding设置都无效。块级元素可以自动换行,可以设置宽高;设置margin和padding都有效。
HTML CSS 语义化
语义化的含义就是用正确的标签做正确的事情,html语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;
在没有样式CCS情况下也以一种文档格式显示,并且是容易阅读的。
搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于 SEO。
使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
- 简单来说:就是用正确的标签做正确的事。比如:
- 头部:header
- 导航:nav
- 主体内容:main
- 标题:h1 ~ h6
- 段落:p
- 侧边栏:aside
- 页脚:footer
这样,整篇HTML结构非常清晰,好看。
HTML语义化有什么好处呢?
- 网页加载慢导致CSS文件还未加载时(没有CSS),页面仍然清晰、可读、好看。
- 提升用户体验,例如title、alt可用于解释名词或解释图片信息。
- 有利于SEO:和搜索引擎建立良好沟通,有助于爬虫抓取更多的有效信息。
- 方便其他设备(如屏幕阅读器、盲人阅读器、移动设备)更好的解析页面。
- 使代码更具可读性,便于团队开发和维护。
w3c标准是什么?
- web标准简单来说可以分为结构、表现和行为
- 结构 主要是有HTML标签组成
- 表现 即指css样式表
- 行为 主要是有js、dom组成
- web标准一般是将该三部分独立分开,使其更具有模块化。但一般产生行为时,就会有结构或者表现的变化,也使这三者的界限并不那么清晰。
- W3C对于WEB标准提出了规范化的要求
1)标签和属性名字母要小写
2)标签要闭合
3)标签不允许随意嵌套
4)尽量使用外链css样式表和js脚本。让结构、表现和行为分为三块,符合规范。同时提高页面渲染速度,提高用户的体验。
5)样式尽量少用行间样式表,使结构与表现分离
6)标签的id和class等属性命名要做到见文知义,更利于seo,代码便于维护
简述一下 src 与 href 的区别?
- src 是 source 的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当 前标签所在位置
- href 是 Hypertext Reference 的缩写,指向网络资源所在位置,建立和当前元 素(锚点)或当前文档(链接)之间的链接
浏览器页面有哪三层构成,分别是什么,作用是什么?
构成: 结构层(HTML)、表示层(CSS)、行为层(JavaScript)
作用:HTML实现页面结构、CSS实现页面样式、JS实现业务逻辑功能
js放在html的< body >和< head >有什么区别?
js 放在 中,如果不添加 async 或者 defer 时,当浏览器遇到 script 时,会阻塞 DOM 树的构建,进而影响页面的加载。当 js 文件较多时,页面白屏的时间也会变长。
在这个过程中,如果解析器遇到了一个脚本(script),它就会停下来,并且执行这个脚本,然后才会继续解析 HTML。如果遇到了一个引用外部资源的脚本(script),它就必须停下来等待这个脚本资源的下载,而这个行为会导致一个或者多个的网络往返,并且会延迟页面的首次渲染时间。
把 js 放到 里(一般在 的上面)时,由于 DOM 时顺序解析的,因此 js 不会阻塞 DOM 的解析。对于必须要在 DOM 解析前就要加载的 js,我们需要放在 中。
CSS
< img >的 title 和 alt 有什么区别?
alt : 图片加载失败时,显示在网页上的替代文字
title :鼠标悬浮在图片上显示的文字
alt是必要属性,title非必要
css的样式规则是什么样的
css的样式规则是:由
选择器和声明块
两个基本部分组成的。选择器决定为哪些元素应用样式;声明块定义相应的样式,它包含在一对花括号内,有一条或多条声明组成,而每一条声明则由一个属性和一个值组成,中间用冒号隔开
CSS里常见选择器有几种?
- 属性选择器
- id选择器
- 类选择器
- 多元素选择器,用 , 分隔,用于同时选择多个元素
- 后代选择器,用空格分隔,如div p { }用于匹配div元素的后代p元素
- 子元素选择器,用 > 分隔,用于匹配父元素的所有直接子元素
- 伪类选择器,常用的有:link , :hover , :visited , :active , :focus,其中:active要在:hover之后才能实现其样式效果
- 伪类元素选择器,常用的有 :first-link用于匹配元素第一行,:first-letter 用于匹配元素第一个字母,:before在元素之前插入生成的内容,:after 在元素之后插入生成的内容
伪元素和伪类的区别?
伪类:将特殊的效果添加到特定选择器上。它是已有元素上添加类别的,不会产生新的元素。例如:
a:hover{color:#FF00FF;}
伪元素:在内容元素的前后插入额外的元素或样式,但是这些元素实际上并不在文档中生成。它们只在外部显示可见,但不会在文档的源代码中找到它们。例如:
p::before{content:"第一章";}
总结:伪类是用过在元素选择器上加入伪类改变元素状态,而伪元素通过对元素的操作进行元素的改变。
选择器的优先级是怎样的?
- 在属性后面使用!important是最高优先级,可以覆盖任何位置定义的属性
- 作为style属性写在元素标签上的内联样式
- id选择器
- 类选择器
- 伪类选择器
- 属性选择器
- 标签选择器
- 通配符选择器
position定位
- 1、static
- position的默认值,没有定位,元素出现在正常的文档流中
- 2、realtive
- 相对定位,相对于正常位置定位,不会脱离文档流
- 3、absolute
- 绝对定位,相对于最近的定位不为static的父元素进行定位。脱离了文档流
- 4、fixed
- 固定定位,相对于浏览器窗口定位,不会跟随屏幕滚动。生成绝对定位,脱离文档流
- 5、sticky
- 粘性定位,它的行为就像 position:relative和position:fixed的合体,当页面滚动超出目标区域时,它会固定在设定位置。
后面几种是不常用
什么是盒子模型
盒子模型是css中的一种思维模型,即把网页内容看成一个盒子,这个盒子由外到内包括margin、border、padding、content。根据浏览器计算方式不同分为两种模型:标准模型(w3c模型)和怪异模型(又称IE模型)。
主要区别:宽高的定义不同
标准盒模型:box-sizing:content-box;
(盒子的宽高不包括margin和padding)
怪异盒模型:内容的宽 = 设置的宽 - padding的宽 - border的宽box-sizing:border-box; (盒子的宽高包括margin和padding)
display有哪些属性?
display 属性规定元素应该生成的框的类型。使段落生出行内框,常用于页面布局,比较常见的属性值包括none、block、inline和inline-block
flex布局
2.flex-direction设置主轴的方向
1.主轴与侧轴
2.属性值
1.row
2.row-reverse
3.column
4.column-reverse
3.justify-content设置主轴的子元素排列方式
属性值:
1.flex-start
2.flex-end
3.center
4.space-around
5.space-between
6.space-evenly
4.align-items设置侧轴上的子元素排列方式(单行)
1.flex-start
2.flex-end
3.center
4.stretch
5.flex-wrap设置子元素是否换行
1.nowrap
2.wrap
6.align-contnet设置侧轴上的子元素的排列方式(多行)
1.flex-star
2.flex-end
3.center
4.space-around
5.space-between
6.stretch
7.space-evenly
vue
监听data变化的核心api是什么?
vue2.0核心api是Object.defineProperty,vue3.0是启用provy实现响应式
vue 实现数据双向绑定的原理 并简单实现
- Vue 主要通过以下 4 个步骤来实现数据双向绑定的:
- 核心
- 对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
- 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持 可实现双向
- push方法 (在数组的末尾添加一个或多个元素,并返回新的长度)
- pop方法 (删除数组中的最后一个元素并返回数组的最后一个元素)
- shift方法 (删除数组中的第一个元素并返回第一个元素的值)
- unshift方法 (在数组最前面添加一个或更多元素并返回新的长度)
- splice方法 (删除/替换/新增数组内某一个或者几个值然后返回被删除的项目)
- sort方法 (按照默认的排序方式进行排序)
- reverse方法 (颠倒数组中的元素的顺序)
- 核心
1、实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。并且可以拿到最新值通知订阅者。
2、实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
3、实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
4、实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
- 实现 通过
Object.defineProperty
let obj = {};
let data='default';
//第一个参数:定义属性的对象。
//第二个参数:要定义或修改的属性的名称。
//第三个参数:将被定义或修改的属性描述符。
Object.defineProperty(obj, "data", {
//获取值
get: function () {
console.log('调用了一次get');
return data;
},
//设置值
set: function (val) {
data = val;
console.log(val);
document.getElementById('result').innerHTML=val;
}
})
// 获取值调用get
document.getElementById('result').innerHTML = obj.data;
//赋值调用set
let flag = true;
document.getElementById("inputing").addEventListener('keyup',function(){
if(flag) obj.data = this.value;
})
// 中文输入法下,仅在选词后触发input事件
document.getElementById("inputing").addEventListener('compositionstart', function(e) {
flag = false;
});
document.getElementById("inputing").addEventListener('compositionend', function(e) {
flag = true;
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>forvue</title>
</head>
<body>
<input type="text" id="textInput">
输入:<span id="textSpan"></span>
<script>
var obj = {},
textInput = document.querySelector('#textInput'),
textSpan = document.querySelector('#textSpan');
Object.defineProperty(obj, 'foo', {
set: function (newValue) {
textInput.value = newValue;
textSpan.innerHTML = newValue;
}
});
textInput.addEventListener('keyup', function (e) {
obj.foo = e.target.value;
});
</script>
</body>
</html>
v-show 与 v-if 有什么区别?
相同点:控制元素的 显示/隐藏
不同点:
(1)原理不同:
v-if 原理是动态设置元素 新增/删除
v-show 原理是设置元素样式,display
(2)编译区别
v-if 满足条件才编译
v-show 一定会编译
(3)性能不同
频繁切换 v-show 性能高于 v-if
keep-alive的作用是什么? 使用它的目的是什么?
作用:keep-alive可以在组件切换时,保存其包裹的组件的状态,使其不被销毁,防止多次渲染。
目的:可以使被包含的组件保留状态,或避免重新渲染。
怎样理解 Vue 的单向数据流?
顾名思义,单向数据流就是从一个组件单方向将数据流向它的内部组件
,也就是父组件的数据流向子组件中,但子组件不能将这个数据修改掉,只能请求父组件修改数据再传给子组件
,如要返回到父组件中修改然后重新流向子组件,从而达到更新数据的原理
说说你对vue虚拟DOM的理解
虚拟dom
其实就是一个普通的JavaScript对象
,用来描叙试图上有哪些界面结构,并不生成界面
,我们可以在生命周期【mounted阶段】打印this._vnode,如下:
它描叙了该阶段是div,有 哪些子节点,哪些属性,它是采用一个js对象来描叙这些,但是它并不会显示在页面上。
- 虚拟DOM的优势
- 可以针对不同的终端平台 输出不同的页面展示节点
比如:网页、微信小程序、原生应用- 在生成的时候只需要修改render方法渲染出不同的节点标签即可
- 为什么要用虚拟DOM?
浏览器在生成dom树的时候,非常消耗资源,因此引入虚拟dom概念通过一定的算法优化之后能够非常快捷的根据数据生成真实的html节点现在vue和react都是使用的虚拟dom
v-model 原理
- v-bind绑定响应式数据;
- 通过oninput触发事件获取当前$event.target.value,然后赋值给当前变量。
v-model主要提供了两个功能:view层输入值影响data的属性值;data属性值发生改变会更新view层的数值变化。
其核心就是,一方面modal层通过defineProperty来劫持每个属性,一旦监听到变化通过相关的页面元素更新。另一方面通过编译模板文件,为控件的v-model绑定input事件,从而页面输入能实时更新相关data属性值。
- v-model是什么:
v-model就是vue的双向绑定的指令,能将页面上控件输入的值同步更新到相关绑定的data属性,也会在更新data绑定属性时候,更新页面上输入控件的值。 - 为什么使用v-model
v-model作为双向绑定指令也是vue两大核心功能之一,使用非常方便,提高前端开发效率。在view层,model层相互需要数据交互,即可使用v-model。
vue2响应式和vue3响应式的区别
- vue2中Object.defineProperty:
- 深度监听在初始化时就一次性递归监听好了
- 无法跟踪到对象增加新值,删除属性
- 数组需要额外处理
请用 vnode 描述一个 DOM 结构
vnode 就是一个普通对象 对象属性代表着一些dom结构比如
tag代办是的当前的标签名
,data代表的是蛋清标签上的属性
,key 代办的是唯一 用户可以传递
function $createElement(tag,data,...children){
let key = data.key;
delete data.key;
children = children.map(child=>{
if(typeof child === 'object'){
return child
}else{
return vnode(undefined,undefined,undefined,undefined,child)
}
})
return vnode(tag,props,key,children);
}
export function vnode(tag,data,key,children,text){
return {
tag, // 表示的是当前的标签名
data, // 表示的是当前标签上的属性
key, // 唯一表示用户可能传递
children,
text
}
}
computed和watch区别以及运用场景
computed:计算属性
支持缓存,只有依赖数据发生变化时,才会重新计算函数。
不支持异步操作
自动监听依赖值的变化,从而动态返回内容。
watch:侦听属性
不支持缓存,只要数据变化,就会执行侦听函数
支持异步操作
侦听属性的值可以是一个对象,接受handler回调,deep,immediate三个属性
watch: {
isHot: {
// 设置为true时会立刻执行以表达式的当前值触发回调
inmediate: true,
handler接收两个参数(newVal:新值,oldVal:旧值
handler(newvalue, oldvalue) {
console.log('修改了', newvalue, oldvalue);
},
//deep设置为true时会监听对象内部值的变化
deep:true
}
}
- 执行过程不同
在依赖的data属性变化后,computed并不会重新计算新的值,而是等到访问的时候再判断,如果依赖的data有改动则重新计算并返回结果,如果依赖的data没有改动,就不计算,直接返回当前结果。
依赖的数据变化后就会执行watch的回调。 - 使用场景
computed:(一个数据受多个数据影响)多个数据进行相应操作影响一个,进行数值计算时利用其缓存的特性,避免每次获取值都重新计算。computed的值一般被用在渲染中。
例如:购物车商品结算的时候
watch:(一个数据影响多个数据)一个数据执行相应操作会影响多个数据,当需要在数据变化时候执行异步操作或者开销较大的操作时使用。
例如:搜索数据
为什么v-for和v-if不建议用在一起
原因
v_for渲染的优先级比v-if 要高
所以会导致每次渲染都会先循环再进行条件判断
如果避免出现这种情况
,则在外层嵌套template
(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环。
Vue 组件间通信有哪几种方式?
父子组件通信
、隔代组件通信
、兄弟组件通信
(1)props / $emit适用 父子组件通信
- 这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
(2)ref与$parent / $children适用 父子组件通信
- ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
- $parent / $children:访问父 / 子实例
(3)EventBus ($emit / $on)适用于 父子、隔代、兄弟组件通信
- 这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
(4) a t t r s / attrs/ attrs/listeners适用于 隔代组件通信 - a t t r s :包含了父作用域中不被 p r o p 所识别 ( 且获取 ) 的特性绑定 ( c l a s s 和 s t y l e 除外 ) 。当一个组件没有声明任何 p r o p 时,这里会包含所有父作用域的绑定 ( c l a s s 和 s t y l e 除外 ) ,并且可以通过 v − b i n d = " attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind=" attrs:包含了父作用域中不被prop所识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind="attrs"传入内部组件。通常配合 inheritAttrs 选项一起使用。
-
l
i
s
t
e
n
e
r
s
:包含了父作用域中的
(
不含
.
n
a
t
i
v
e
修饰器的
)
v
−
o
n
事件监听器。它可以通过
v
−
o
n
=
"
listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="
listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−on="listeners"传入内部组件
(5)provide / inject适用于 隔代组件通信 - 祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(6)Vuex适用于 父子、隔代、兄弟组件通信 - Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
Vue 的父组件和子组件生命周期钩子函数执行顺序?
加载渲染过程:
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
父组件更新过程:
父 beforeUpdate -> 父 updated
子组件更新过程:
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
销毁过程:
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
总结:子的生命周期都会被先结束,父的才结束。先由父到子,再从子到父
。
Vue 模板编译原理
- 概念
平时使用模板时,可以在模板中使用变量、表达式或者指令等,这些语法在html中是不存在的,那vue中为什么可以实现?这就归功于模板编译功能。
模板编译的作用是生成渲染函数,通过执行渲染函数生成最新的vnode,最后根据vnode进行渲染
。那么,如何将模板编译成渲染函数?
- 将模板编译成渲染函数
此过程可以分成两个步骤:先将模板解析成AST(abstract syntax tree,抽象语法树),然后使用AST生成渲染函数。 由于静态节点不需要总是重新渲染,所以生成AST之后,生成渲染函数之前这个阶段,需要做一个优化操作:遍历一遍AST,给所有静态节点做一个标记,这样在虚拟DOM中更新节点时,如果发现这个节点有这个标记,就不会重新渲染它。 所以,在大体逻辑上,模板编译分三部分内容: 1、将模板解析成AST 2、遍历AST标记静态节点 3、使用AST生成渲染函数 这三部分内容在模板编译中分别抽象出三个模块实现各自的功能:解析器、优化器和代码生成器。
谈谈对vue生命周期的理解及每个阶段做的事?
-
beforeCreate:通常用于插件开发中执行一些初始化任务
-
create:组件初始化完毕,可以访问各种数据和获取接口数据
-
mounted:dom已创建可用以访问数据和dom元素,访问子组件
-
beforeUpdate:此时view层还未更新,用于获取更新前各种状态
-
update:完成view更新,更新后所有状态已经是最新
-
beforeunmounted:实例被销毁钱调用,用于取消定时器或者订阅
-
unmanned:销毁实例,他可以清理与实例的链接解绑的全部指令和事件监听
在哪些生命周期内调用异步请求?
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是推荐在 created 钩子函数中调用异步请求
,因为在 created 钩子函数中调用异步请求有以下优点:
能更快获取到服务端数据,减少页面 loading 时间;
ssr渲染模式的话 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
第一次页面加载会发哪几个钩子?
beforeCreate, created, beforeMount, mounted
vue-router 有哪几种导航钩子?
总共分为三钟:全局导航钩子、路由独享钩子、组件内的导航钩子
- 1、
全局导航钩子
router.beforeEach(to, from, next): 路由改变前的钩子
const router = new Router({ ... })
router.beforeEach((to, from, next) => {
//
})
to: 即将要进入的目标路由对象
from: 当前正要离开的路由,也是一个路由对象
next: 一定要调用该方法来resolve这个钩子
next 方法必须要调用,否则钩子函数无法 resolved
router.beforeResolve : 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,该钩子函数就被调用
router.afterEach : 路由改变后的钩子
- 2、
路由独享钩子
可以在路由配置上直接定义 beforeEnter
cont router = new Router({
routes: [
{
path: '/file',
component: File,
beforeEnter: (to, from ,next) => {
// do someting
}
}
]
});
3、组件内的导航钩子
有三种:
beforeRouteEnter 在进入当前组件对应的路由前调用
beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用
beforeRouteLeave 在离开当前组件对应的路由前调用
export default {
data() { ... },
beforeRouteEnter(to, from, next) {
... ...
}
}
注意:
beforeRouteEnter 不能获取组件实例 this,因为当守卫执行前,组件实例被没有被创建出来,剩下两个钩子则可以正常获取组件实例 this
vue-loader的理解
- vue-loader主要用来处理vue组件文件,配合 webpack 以及相关 loader,来进行编译模版、scoped CSS 和热重载。
vue-loader作用
解析和转换.vue文件。提取出其中的逻辑代码 script,样式代码style,以及HTML 模板template,再分别把他们交给对应的loader去处理
- 1.vue-loader是webpack的加载器,允许以单文件组件(SFC)的格式创作Vue组件
- 2.允许对Vue组件的每个部分使用其他webpack加载器
- 3.允许.vue文件中的自定义块可以应用自定义加载程序链,简单来说就是可以解析.vue文件
- 4.处理在模块依赖项中引用的静态资源
- 5.模拟每个组件的范围CSS
- 6.在开发过程中保持热加载
vue性能优化
- 编码优化
- v-for的dom都需要注册差不多的事件的时候可以考虑给元素绑定事件代理
- 尽可能拆分出组件,提高复用性,维护性
- key值要保证唯一
- 数据持久化使用尽量用防抖和节流优化
- 加载优化
- ui组件按需加载
- 内容懒加载
- 图片懒加载
- 用户体验
- 骨架屏
- loading提示
- SEO优化
- 预渲染
- ssr服务端渲染
- 打包优化
- CDN形式加载第三方模块
- 多线程打包
- 抽离出公共文件
写出 vue-router 路由模式有几种,分别是什么和实现原理?
- vue-router路由模式
一、vue-router前端路由有两种模式,hash模式和history模式
hash模式
就是指 url 后面的 # 号以及后面的字符。每次刷新页面时是直接更改 # 后的东西。
由于 hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange事件(hashchange只能改变 # 后面的 url片段);虽然 hash路径出现在URL中,但是不会出现在 HTTP请求中,对后端完全没有影响,因此改变 hash值不会重新加载页面,基本都是使用 hash 来实现前端路由的。
history模式
包含 back、forward、go方法;history 模式 URL就要和后端进行一致,所以要改为 history也需要后端的配合,否则会报错;history 每次刷新会重新向后端请求整个网址,也就是重新请求服务器。如果后端没有及时响应,就会报错404!。
二、原理
hash 原理:灵活运用了 html的瞄点功能
,改变 # 后的路径本质上是更换了当前页面的瞄点,所以不会刷新页面。通过监听浏览器的 onhashchange()事件变化,查找对应的路由规则。
history 原理: 利用 H5的 history中新增的两个API
:pushState()和 replaceState()和一个事件onpopstate监听URL变化。
三、优缺点
hash模式
优点:
兼容性强,达到 IE8;
除发送 ajax和资源请求外不会发送其他多余请求;
改变 # 后的路径不会自动刷新页面;
无需服务器进行配合;
缺点:
访问路径上包含 # ,不美观;
对于需要锚点功能的需求会与当前路由机制发生冲突;
重定向操作时,后段无法获取 url完整路径;
history模式
优点:
符合 url 地址规范,没有 # ,使用起来比较美观;
后端可以获取到完整的路由信息;
可以使用 history.state获取完整的路由信息;
可以进行修改历史记录,并且不会立刻向后端发起请求;
缺点:
兼容性只到IE10;
改变 url路径后会重新请求资源;
若访问的路由地址不存在时会报404,需服务端配合支持重定向返回统一的404页面;
如果对于项目没有硬性标准要求,可以直接使用 hash模式开发。
Vue组件渲染和更新过程
- 渲染过程
- 解析模板为render函数(或在开发环境已完成,vue-loader)
- 触发响应式,监听data属性getter setter
- 执行render函数,生成vnode,patch(elem,vnode)
- 更新过程
- 修改data,触发setter(此前在getter中已被监听)
- 重新执行render函数,生成newVnode
- patch(vnode,newVnode)
Vue 在更新 DOM 时是
异步执行的
。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。
怎么定义 vue-router 的动态路由? 怎么获取传过来的值?
一、动态路由的理解
动态路由 就是把匹配某种模式下的路由映射到同一个组件中,其实本质就是通过url进行传参,比如说:有一个商Goods的组件,我们需要让不同的商品id都映射到这个组件中,此时就,需要用到动态路由了。
二、动态路由的配置
可以通过两种方式来传递动态参数
1.params
2.query
以上代码的演示都是在history的路由模式下
使用过 Vue SSR 吗? 说说 SSR?
先说下基本概念:
ssr 的全称是 server side render,服务端渲染
,vue ssr 的意思就是在服务端进行 vue 的渲染
,直接对前端返回带有数据,并且是渲染好的HTML页面;
而不是返回一个空的HTML页面,再由vue 通过异步请求来获取数据,再重新补充到页面中。
这么做的最主要原因,就是搜索引擎优化,也就是SEO
,这更利于网络爬虫去爬取和收集数据
。
你有对 Vue 项目进行哪些优化?
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分使用场景
- v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
- 图片资源懒加载
- 路由懒加载
- 当打包构建项目时,JavaScript包会变的非常打,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,
- 然后当路由被访问的时候才加载对应组件,这样更加高效了。
- 第三方插件的按需引入(element-ui)
- 服务端渲染 SSR or 预渲染
- 固定图标,背景,按钮icon等等,这些图片有一个特点就是固定和用户无关,一般是放在源码包里面,由前端代码直接引入。
- 采用CSS雪碧图:把你的网站用到的一些图片整合到一张单独的图片中:
优点:减少HTTP请求的数量(通过backgroundPosition定位所需图片)。
缺点:整合图片比较大时,加载比较慢(如果这张图片没有加载成功,整个页面会失去图片信息)。
v-for中 key 是否为必须? 以及它的作用?
应许必填
key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
key的主要作用就是用来提高渲染性能的!
Key值的存在保证了唯一性,Vue在执行时,会对节点进行检查,如果没有key值,那么vue检查到这里有 dom节点,就会对内容清空并赋新值如果有key值存在,那么会对新老节点进行对比比较两者key是否相同,进行调换位置或删除操作
Vue中常用的修饰符有哪些?
- 1、表单修饰符
- 用在input标签上的v-model指令后,如
v-model.lazy="value"
- lazy(当光标离开标签时,才会将值赋值给value)
- trim(过滤掉两边的空格)
- number(自动将用户的输入值转为数值类型,但如果这个值无法被parseFloat解析,则会返回原来的值)
- 用在input标签上的v-model指令后,如
- 2、事件修饰符
- 语法 :
<button @click.stop="shout(1)">ok</button>
- stop (阻止事件的冒泡,相当于调用了event.preventPropagation方法)
- prevent (阻止了事件的默认行为,相当于调用了event.preventDefault方法)
- self (只当在 event.target 是当前元素自身时触发处理函数)
- once(绑定了事件以后只能触发一次,第二次就不会触发)
- passive
<div v-on:scroll.passive="onScroll">...</div>
- 用于提升移动端scroll事件的性能。在移动端,当我们在监听元素滚动事件的时候,会一直触发onscroll事件会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给onscroll事件整了一个.lazy修饰符。
- capture(事件捕获,从顶层往下触发)
- 语法 :
<div @click.capture="shout(1)">
obj1
<div @click.capture="shout(2)">
obj2
<div @click="shout(3)">
obj3
<div @click="shout(4)">
obj4
</div>
</div>
</div>
</div>
// 输出结构: 1 2 4 3
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click. prevent.self
会阻止所有的点击
,而 v-on:click.self.prevent 只会阻止对元素自身的点击
- 3、鼠标按钮修饰符
<button @click.left="shout(1)">ok</button> //左键
<button @click.right="shout(1)">ok</button> //右键
<button @click.middle="shout(1)">ok</button> //中键
- 4、键盘修饰符
键盘修饰符是用来修饰键盘事件(
onkeyup
,onkeydown
)的,有如下:
- 普通键(enter、tab、delete、space、esc、up…)
- 系统修饰键(ctrl、alt、meta、shift…)
// 只有按键为enter的时候才触发
<input type="text" @keyup.enter="shout()">
- 5、v-bind修饰符
sync
实现子组件props的双向绑定
//父组件
<comp :myMessage.sync="bar"></comp>
//子组件
this.$emit('update:myMessage',params);
相当于
//父亲组件
<comp :myMessage="bar" @update:myMessage="func"></comp>
func(e){
this.bar = e;
}
//子组件js
func2(){
this.$emit('update:myMessage',params);
}
- 使用sync的时候,子组件传递的事件名格式必须为update:value,其中value必须与子组件中props中声明的名称完全一致
- 注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用
- 将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的
props
设置自定义标签属性,避免暴露数据,防止污染HTML结构
<input id="uid" title="title1" value="1" :index.prop="index">
camel
将命名变为驼峰命名法,如将 view-Box属性名转换为 viewBox
vue.js的两个核心是什么?
数据驱动和组件化
VUEX 是什么?怎么使用?那种场合能用?
vuex 是一个专门为 vue 构建的状态管理工具,主要是为了解决 多组间之间状态共享问题。强调的是集中式管理,(组件与组件之间的关系变成了组件与仓库之间的关系)
vuex 的核心包括:state(存放状态)、mutations(同步的更改状态)、actions(发送异步请求,拿到数据)、getters(根据之前的状态派发新的状态)、modules(模块划分)
state 发布一条新的数据,在 getters 里面根据状态派发新的状态,actions 发送异步请求获取数据,然后在 mutations 里面同步的更改数据
应用场合:购物车的数据共享、登入注册
你怎么理解 Vue中的diff算法?
diff算法并非vue所专有(凡是涉及到虚拟DOM的,多半都要用到diff算法);
- 当data中的属性发生变化时,此时就涉及到了多个key的变化,但是,我们只有一个Watch函数,怎么能确保是哪个key发生了变化呢?
vue组件中data为什么函数返回一个对象
组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data。如果单纯的写成对象形式,就使得所有组件实例共用了一份data,造成了数据污染。
vue-text和vue-html的区别
- v-text是用于操作纯文本,它会替代显示对应的数据对象上的值。当绑定的数据对象上的值发生改变,插值处的内容也会随之更新
- v-html用于输出html,它与v-text区别在于v-text输出的是纯文本,浏览器不会对其再进行html解析,但v-html会将其当html标签解析后输出。
vue3.0特性你有哪些了解?
虚拟DOM重写
优化插槽生成
在当前的 Vue2 版本中,当父组件重新渲染时,其子组件也必须重新渲染。 使用 Vue 3 ,可以单独重新渲染父组件和子组件。
静态树提升
使用静态树提升,这意味着 Vue 3 的编译器将能够检测到什么是静态组件,然后将其提升,从而降低了渲染成本。它将能够跳过未整个树结构打补丁的过程。
静态属性提升
Vue 3 将跳过不会改变节点的打补丁过程。
基于Proxy的响应式系统
目前,Vue 的反应系统是使用 ObectDefineProperty 的 getter 和 setter。 但是,Vue 3 将使用 ES2015 Proxy 作为其观察者机制。 这消除了以前存在的警告,使速度加倍,并节省了一半的内存开销。
1、更容易维护
TypeScript + 模块化 ,在vscode上的提示跟友好,代码提示类型检测。
2、Proxy(代理)响应式
- 在vue2中响应式
- 对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
- 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
-
问题
- 对象直接新添加的属性或删除已有属性, 界面不会自动更新
- 直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}
#Vue3的响应式
-
在vue3中的响应式
- 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等…
- 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
Vue3.0 和2.0 的响应式原理区别
- 1、 Vue2.0 实现MVVM(双向数据绑定)的原理是通过 Object.defineProperty 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
- 2、
3、三个新组件
Fragment(片断)
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
Teleport(瞬移)
- Teleport 提供了一种干净的方法, 让组件的html在父组件界面外的特定标签(很可能是body)下插入显示
Suspense(不确定的)
- 它们允许我们的应用程序在等待异步组件时渲染一些后备内容,可以让我们创建一个平滑的用户体验
vue数据频繁变化,为什么视图就更新一次呢
- Vue在监听到数据有变化的时候分为四步
- 监听到数据变化
- 开启一个变化后数据的队列
- 在同一事件循环中缓冲所有数据改变
- 队列去重重复的id,使其只更新一次
Vue.nextTick()原理
在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。
函数组件
- 函数式组件特点
- 没有管理任何状态
- 没有监听任何传递给它的状态
- 没有生命周期方法
- 它只是接收一些prop的函数
- 函数式组件的优点
- 渲染开销低,因为函数式组件只是函数
- 适用的场景
一些简单的展示组件
,也就所谓的dumb组件,例如button组件,pills,tags,cards,甚至整个页面都是静态文本,比如about页面- v-for 循环中的每项通常都是很好的候选项
- 语法
Vue.component('my-component', {
functional: true,
// Props 是可选的
props: {
// ...
},
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (createElement, context) {
// ...
}
}
)
JavaScript
什么是深持贝和浅拷贝,如何实现深拷贝?
- 深拷贝 :拷贝一个数据后在栈里新开辟一个地址
- 浅拷贝:只是拷贝了数据在栈里的地址 修改时修改的是同一个地址里的数据
- 实现深拷贝
1、通过js的内置对象JSON来进行数组对象的深拷贝 (无法拷贝对象里的方法)
const data = ref({
name:"小明",
age:12
})
let obj = {
name:"小明"
}
let obj2 = JSON.parse(JSON.stringify(obj));
obj2.name = "小红";
console.log(obj.name) // 小明
console.log(obj2.name) // 小红
2、使用递归的方式实现深拷贝
- 事件方法
Element.addEventListener():添加事件的回调函数
Element.removeEventListener():移除事件监听函数
Element.dispatchEvent():触发事件
3、
deepClone(obj){
// 首先如果不是对象并且为空 直接等于返回
debugger
console.log(typeof obj)
var clone;
if (obj && typeof obj !== 'object') clone = obj;
else {
clone = Array.isArray(obj) ? [] : {};
for (let k in obj) {
if (obj.hasOwnProperty(k)) {
if (obj[k] && typeof obj[k] === 'object') {
clone[k] = this.deepClone(obj[k]);
} else {
clone[k] = obj[k];
}
}
}
}
return clone;
}
事件代理
事件代理(Event Delegation),又称之为事件委托。是JavaScript中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown…)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
- 用途:可以减少dom操作,提高了效率,不用for循环为子元素添加事件了
<div id="box">
<input type="button" id="add" value="添加" />
<input type="button" id="remove" value="删除" />
<input type="button" id="move" value="移动" />
<input type="button" id="select" value="选择" />
</div>
window.onload = function(){
var oBox = document.getElementById("box");
oBox.onclick = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLocaleLowerCase() == 'input'){
switch(target.id){
case 'add' :
alert('添加');
break;
case 'remove' :
alert('删除');
break;
case 'move' :
alert('移动');
break;
case 'select' :
alert('选择');
break;
}
}
}
}
1.event对象里记录的有“事件源”,它就是发生事件的子元素。它存在兼容性问题,在老的IE下,事件源是 window.event.srcElement,其他浏览器是 event.target
2.同时增加if判断,通过事件源的nodeName判断是不是li,是的话,才做出反应,不是的话,不理它。为了防止nodeName在不同浏览器获取的字母大小写不同,加个toLowerCase()
JS数据类型,区别
- 基本数据类型:Number、String、Boolead、null、undefined
基本数据类型是直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是存储基本类型值和执行代码的空间。
- 引用类型:Object,function
引用数据类型是存储在堆内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆 中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
两种数据类型的区别:堆比栈空间大,栈比堆运行速度快。堆内存是无序存储,可以根据引用直接获取。基础数据类型比较稳定,而且相对来说占用的内存小。引用数据类型大小是动态的,而且是无限的。
JS 原生的 api 你知道
的有哪? 写出他们的用处
大体分为
1、节点node、Element
2、class
3、对象
- node节点
Node.appendChild(node) //向节点添加最后一个子节点
Node.hasChildNodes() //返回布尔值,表示当前节点是否有子节点
Node.cloneNode(true); // 默认为false(克隆节点), true(克隆节点及其属性,以及后代)
Node.insertBefore(newNode,oldNode) // 在指定子节点之前插入新的子节点
Node.removeChild(node) //删除节点,在要删除节点的父节点上操作
Node.replaceChild(newChild,oldChild) //替换节点
Node.contains(node) //返回一个布尔值,表示参数节点是否为当前节点的后代节点。
Node.compareDocumentPosition(node) //返回一个7个比特位的二进制值,表示参数节点和当前节点的关系
Node.isEqualNode(noe) //返回布尔值,用于检查两个节点是否相等。所谓相等的节点,指的是两个节点的类型相同、属性相同、子节点相同。
Node.normalize() //用于清理当前节点内部的所有Text节点。它会去除空的文本节点,并且将毗邻的文本节点合并成一个。
//ChildNode接口
Node.remove() //用于删除当前节点
Node.before() //
Node.after()
Node.replaceWith()
- element 元素
(1)特性属性
Element.attributes //返回当前元素节点的所有属性节点
Element.id //返回指定元素的id属性,可读写
Element.tagName //返回指定元素的大写标签名
Element.innerHTML //返回该元素包含的HTML代码,可读写
Element.outerHTML //返回指定元素节点的所有HTML代码,包括它自身和包含的的所有子元素,可读写
Element.className //返回当前元素的class属性,可读写
Element.classList //返回当前元素节点的所有class集合
Element.dataset //返回元素节点中所有的data-*属性。
(2)尺寸属性
Element.clientHeight //返回元素节点可见部分的高度
Element.clientWidth //返回元素节点可见部分的宽度
Element.clientLeft //返回元素节点左边框的宽度
Element.clientTop //返回元素节点顶部边框的宽度
Element.scrollHeight //返回元素节点的总高度
Element.scrollWidth //返回元素节点的总宽度
Element.scrollLeft //返回元素节点的水平滚动条向右滚动的像素数值,通过设置这个属性可以改变元素的滚动位置
Element.scrollTop //返回元素节点的垂直滚动向下滚动的像素数值
Element.offsetHeight //返回元素的垂直高度(包含border,padding)
Element.offsetWidth //返回元素的水平宽度(包含border,padding)
Element.offsetLeft //返回当前元素左上角相对于Element.offsetParent节点的垂直偏移
Element.offsetTop //返回水平位移
Element.style //返回元素节点的行内样式
-
属性方法
Element
.getAttribute():读取指定属性
Element.setAttribute():设置指定属性
Element.hasAttribute():返回一个布尔值,表示当前元素节点是否有指定的属性
Element.removeAttribute():移除指定属性 -
获取元素
document.getElementsByClassName ('class'); //通过类名获取元素,以伪数组形式存在。
var Node = document.querySelector('li.className'); //通过CSS选择器获取元素,符合匹配条件的第1个元素。
document.querySelectorAll('ul li'); //查询id需要加#号
- 类名操作
Node.classList.add('class'); //添加class
Node.classList.remove('class'); //移除class
Node.classList.toggle('class'); //切换class,有则移除,无则添加
Node.classList.contains('class'); //检测是否存在class号
- 监听切换历史事件
window.onpopstate
- 类的操作
(1)类名操作
//ie8以下
Element.className //获取元素节点的类名
Element.className += ' ' + newClassName //新增一个类名
//判断是否有某个类名
function hasClass(element,className){
return new RegExp(className,'gi').test(element.className);
}
//移除class
function removeClass(element,className){
element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'),'');
}
//ie10
element.classList.add(className) //新增
element.classList.remove(className) //删除
element.classList.contains(className) //是否包含
element.classList.toggle(className) //toggle class
- 对象
(1)生成实例对象
var a = new Array()
(2)属性
a.length //长度
(3)Array.isArray()
Array.isArray(a) //用来判断一个值是否为数组
(4)Array实例的方法
a.valueof() //返回数组本身
a.toString() //返回数组的字符串形式
a.push(value,vlaue....) //用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。
pop() //用于删除数组的最后一个元素,并返回该元素
join() //以参数作为分隔符,将所有数组成员组成一个字符串返回。如果不提供参数,默认用逗号分隔。
concat() //用于多个数组的合并。它将新数组的成员,添加到原数组的尾部,然后返回一个新数组,原数组不变。
shift() //用于删除数组的第一个元素,并返回该元素。
unshift(value) //用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。
reverse() //用于颠倒数组中元素的顺序,返回改变后的数组
slice(start_index, upto_index); //用于提取原数组的一部分,返回一个新数组,原数组不变。第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。
如果省略第二个参数,则一直返回到原数组的最后一个成员。负数表示倒数第几个。
splice(index, count_to_remove, addElement1, addElement2, ...); //用于删除原数组的一部分成员,并可以在被删除的位置添加入新的数组成员,返回值是被删除的元素。
第一个参数是删除的起始位置,第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
sort() //对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数,表示按照自定义方法进行排序。
该函数本身又接受两个参数,表示进行比较的两个元素。如果返回值大于0,表示第一个元素排在第二个元素后面;其他情况下,都是第一个元素排在第二个元素前面。
map() //对数组的所有成员依次调用一个函数,根据函数结果返回一个新数组。
map(elem,index,arr) //map方法接受一个函数作为参数。该函数调用时,map方法会将其传入三个参数,分别是当前成员、当前位置和数组本身。
forEach() //遍历数组的所有成员,执行某种操作,参数是一个函数。它接受三个参数,分别是当前位置的值、当前位置的编号和整个数组。
filter() //参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。
some() //用来判断数组成员是否符合某种条件。接受一个函数作为参数,所有数组成员依次执行该函数,返回一个布尔值。该函数接受三个参数,依次是当前位置的成员、当前位置的序号和整个数组。
只要有一个数组成员的返回值是true,则整个some方法的返回值就是true,否则false。
every() //用来判断数组成员是否符合某种条件。接受一个函数作为参数,所有数组成员依次执行该函数,返回一个布尔值。该函数接受三个参数,依次是当前位置的成员、当前位置的序号和整个数组。
所有数组成员的返回值都是true,才返回true,否则false。
reduce() //依次处理数组的每个成员,最终累计为一个值。从左到右处理(从第一个成员到最后一个成员)
reduceRight() //依次处理数组的每个成员,最终累计为一个值。从右到左(从最后一个成员到第一个成员)
indexOf(s) //返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1。可以接受第二个参数,表示搜索的开始位置
lastIndexOf() //返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1。
ES6 的新特性你知道哪些?
1、变量声明
let表示声明变量,而const表示声明常量,两者都为块级作用域;const 声明的变量都会被认为是常量,意思就是它的值被设置完成后就不能再修改了:
2、模板字符串
在ES6之前,我们往往这么处理模板字符串:
通过“\”和“+”来构建模板
$("body").html("This demonstrates the output of HTML \
content to the page, including student's\
" + name + ", " + seatNumber + ", " + sex + " and so on.");
而对ES6来说
基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;
ES6反引号(``)直接搞定;
$("body").html(`This demonstrates the output of HTML content to the page,
including student's ${name}, ${seatNumber}, ${sex} and so on.`);
3、箭头函数
let fun = () => {
}
4、函数的参数默认值
// ES6之前,当未传入参数时,text = 'default';
function printText(text) {
text = text || 'default';
console.log(text);
}
// ES6;
function printText(text = 'default') {
console.log(text);
}
printText('hello'); // hello
5、展开运算符(用三个连续的点 (…) 表示)是 ES6 中的新概念,使你能够将字面量对象展开为多个元素
function foo(x,y,z) {
console.log(x,y,z);
}
let arr = [1,2,3];
foo(...arr); // 1 2 3
6、对象和数组解构
// 对象
const student = {
name: 'Sam',
age: 22,
sex: '男'
}
// 数组
// const student = ['Sam', 22, '男'];
// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);
// ES6
const { name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);
7、for…of 和 for…in
- for…of 用于遍历一个迭代器,如数组
let letters = ['a', 'b', 'c'];
letters.size = 3;
for (let letter of letters) {
console.log(letter);
}
// 结果: a, b, c
- for…in 用来遍历对象中的属性:
let stus = ["Sam", "22", "男"];
for (let stu in stus) {
console.log(stus[stu]);
}
// 结果: Sam, 22, 男
es6 数组和对象的新方法
- 数组
find();查找数组某个元素
findIndex();查找某个元素的索引值
some();数组中是否有元素符合条件
every();数组中是否所有的元素都符合条件
- 对象
Object.assign(); 复制一个对象
Object.keys(); 得到一个对象的所有属性;
Object.values(); 得到一个对象的所有可枚举属性值;
Object.entries(); 得到一个对象的键值对数组;
Object.fromEntries(); entries的逆操作;
单例设计模式
单列模式:一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
- 单例的优缺点
- 优点
- 划分独立命名空间,避免全局变量污染
- 代码逻辑集中,业务高度内聚
- 只创建一个实例,减少对象频繁创建、销毁的消耗
- 缺点
- 对 OOP 特性的支持不友好(单例模式内部自己创建实例,外部无法通过new来实例化,无法继承扩展)
- 对代码的可测试性不友好(由于单例模式属硬编码的方式,会导致内部的接口、数据或依赖都无法很好模拟)
- 优点
什么是原型?
在js中所有的引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象。
而在js中的引用类型包括:Object,Array,Date,Function
而所有函数都有一个prototype(原型)属性,属性值是一个普通的对象,也被称为原型对象。
所有引用类型的__proto__属性指向它构造函数的prototype:
什么是原型链
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
注意: 如果最顶层还找不到的话就会返回null
什么是JavaScript 事件循环机制?
事件循环,即 Event Loops。用于协调事件、用户交互、JavaScript 脚本、DOM 渲染、网络请求等等的执行顺序问题。
事件循环是由一个或以上的任务队列组成的。
什么是任务队列?
由于 JavaScript 是 单线程 语言,所以在 JS 中所有的任务都需要排队执行,这些任务共同组成了任务队列,依次排队执行的过程,形成一个执行栈(Execution Context Stack)。
在任务队列中最先执行是同步任务。
事件循环基础
宏任务: script (整体代码) 、setTimeout、setlnterval、setlmmediate、I/0、Ul rendering微任务: promise、Object.observe、MutationObserver任务的优先级: process.nextTick > promise.then > setTimeout > setlmmediate
微任务是跟屁虫,一直跟着当前宏任务后面代码执行到一个微任务就跟上,一个接着一个
微任务 > DOM 渲染 > 宏任务
什么是闭包?闭包会出现内层泄露吗? 为什么?let和闭包的关系?
- 什么是闭包?
闭包就是在一个函数里可以访问另外一个函数内部的变量
- 闭包会出现内存泄漏吗
会 内存泄漏其实就是因为变量的值在内存中,并且这个变量因为闭包一直被引用,垃圾回收机制不能回收。
- let 和闭包的关系
let 可以解决闭包
如果是var 定义的变量,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变。
let声明的变量,仅在块级作用域内有效,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量。而JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
闭包垃圾回收
将是闭包的函数进行重新赋值成 null
let x = 10;
function fn(){
function f(){}
// fn执行时,fn内部的f会被外部的window引用,形成不销毁的EC(fn)
window.f = f;
}
// fn执行产生一个EC(fn)且不被销毁
fn();
// 取消堆中函数的引用,fn指向的堆内存释放
fn = null;
// f仍然能正常执行,虽然fn存储在堆内存中的描述字符串被回收了所以不能fn()了,但EC(fn)还存在
f();
// f堆内存释放,EC(fn)栈内存不存在闭包区域,则栈内存释放
// window.f = null
闭包应用场景
- 1、封装私有变量
- 2、函数防抖
- 3、回调
fetch
fetch是一种HTTP数据请求的方式
fetch不是ajax的进一步封装,而是原生js。Fetch函数就是原生js,没有使用XMLHttpRequest对象
由于Fetch底层是用Promise实现
数组去重的方法?
- 1、对象属性(indexof)
利用对象属性key排除重复项
遍历数组,每次判断新数组中是否存在该属性,不存在就存储在新数组中
并把数组元素作为key,最后返回新数组
这个方法的优点是效率高,缺点是使用了额外空间
var newArr = [];
arr.forEach((key,index)=>{
if(newArr.indexOf(key) === -1){
newArr.push(key)
} })
console.log(newArr);// [1, '1', 17, true, false, 'true', 'a', {}, {}]
- 2、new Set(数组)
Set是一系列无序、没有重复值的数据集合,传入一个需要去重的数组,Set会自动删除重复的元素
再将Set转数组返回。此方法效率高,代码清晰,缺点是存在兼容性问题
const newArr = [...new Set(arr)];
console.log(newArr);// [1, '1', 17, true, false, 'true', 'a', {}, {}]
- 3、filter() + indexof
filter把接收的函数依次作用于每一个数组项,然后根据返回值 true or false 决定是否保留该值
优点在于可在去重时插入对元素的操作,可拓展性强
const newArr= arr.filter(function(item,index,self){
return self.indexOf(item) === index;
})
console.log(newArr);// [1, '1', 17, true, false, 'true', 'a', {}, {}]
防抖
说一下栈和队列的区别? 你可以用两个栈来实现队列的效果吗?
- 栈和队列的区别?
1、队列先进先出
,栈先进后出
。
2、对插入和删除操作的"限定"不同。
栈
是限定只能在表的一端进行插入和删除
操作的线性表。
队列
是限定只能在表的一端进行插入
和在另一端进行删除
操作的线性表。
3、遍历数据速度不同。
栈只能从头部取数据,也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性。
队列则不同,它基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多
TCP的三次握手和四次挥手
三次握手是客户端跟服务器之间建立连接,并且进行通信的过程。相当于客户端和服务器之前你我来往的3个步骤;
- 第一次握手是建立连接,客户端发送请求报文,并传送规定的数据包;
- 第二次握手是服务器端表示接收到连接请求报文,并回传规定的数据包;
- 第三次握手是客户端接收服务器回传的数据包后,再次给服务器端发送数据包。
这样就完成了客户端跟服务端的连接和数据传送
三次握手的目的:
双方确认自己与对方的发送与接收双方正常
四次挥手表示当前这次连接请求已经结束,要断开这次连接
- 第一次挥手是客户端对服务器发送断开请求
- 第二次挥手是服务端表示收到这次断开请求
- 第三次挥手是服务端表示已经断开连接
- 第四次挥手是客户端断开连接
四次挥手的目的:
由于TCP的半关闭的特性;
四次挥手是为了确认TCP连接是否正常完全关闭
箭头函数和普通函数的区别
- 箭头函数
都是匿名函数
而普通函数即可以匿名也可以具名
。 - 箭头函数不能用作构造函数
- 箭头函数的this指向不同
- 普通函数的this指向为调用者 如果用作构造函数,他指向创建的对象实例
- 箭头函数的指向要根据上下文来确定 默认为
父级的this
- 箭头函数不能具有
prototype原型对象
,不具有super,也不具有new.target,不能写Generator函数。
输入url到打开页面 都做了些什么事情
- 1、输入url:查找缓存,/浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,如果有则显示页面的内容。如果没有则进行下一步;
- 2、DNS域名解析:浏览器向DNS服务器发起请求,解析该url中域名对应的IP地址;
- 3、TCP握手:解析出IP地址后,根据IP地址和默认80端口,和服务器经理TCP连接;
- 4、HTTP请求:浏览器非让器读取文件的HTTP请求,该请求报文作为TCP三次握手的滴三次数据发送给服务器;
- 5、HTTP响应返回数据结果
- 6、通过四次挥手关闭/释放TCP连接
- 7、浏览器解析数据并渲染页面
window对象和document对象是什么?有什么区别?
一、指代不同
1、document对象:代表给定浏览器窗口中的HTML文档,document是window的一个对象属bai性。
2、window对象:表示浏览器中打开的窗口。
二、作用不同
1、document对象:使用document对象可以对HTML文档进行检查、修改或添加内容,并处理该文档内部的事件。
2、window对象:浏览器会为HTML文档创建一个window对象,并未每个框架创建一个额外的window对象。
三、使用方式不同:
1、document对象:在Web页面上,document对象可通过window对象的document属性引用,或者直接引用。
2、window对象:没有应用于window对象的公开标准,不过所有浏览器都支持该对象。
简单实现类和继承
let _this = this; // 声明一个 _this 指向当前的this
// 定义一个类名为 myLike 的类
class myLike {
// 定义一个 JS 构造器
constructor(type) {
_this.type = type;
}
// 创建实例方法
sayType() {
console.log('我喜欢' + _this.type);
}
}
// 创建一个类名为 Programmer 的类的继承 myLike 类
class Programmer extends myLike {
constructor(type) {
// 直接调用父类构造器进行初始化操作
super(type);
}
program() {
console.log("我是一个写代码的游戏主播");
}
}
// 测试我刚创建的类
var goPlay = new myLike('打游戏'), // 声明一个打游戏的对象
writeCode = new Programmer('写代码'); // 声明一个写代码的对象
// 开始测试程序结果
goPlay.sayType(); // 输出 我喜欢打游戏
writeCode.sayType(); // 输出 我喜欢写代码
writeCode.program(); // 输出 我是一个写代码的游戏主播
伪数组
- 伪数组具有length属性,但length属性不是动态的,不会随着成员的变化而变化;
- 可以通过索引找到具体的值;
- 伪数组可以通过for循环遍历每一项;
- 伪数组不具备数组的push(), forEach()等方法。
- 伪数组本质是一个 Object,而真实的数组是一个 Array。
伪数组转真实数组
Array.from()方法
var arrLike = {
"0": "1",
"1": "2",
"length": 2
};
var newArr = Array.from(arrLike);
console.log(newArr);
slice.call()方法
var arrLike = {
"0": "1",
"1": "2",
"length": 2
};
var newArr = [].slice.call(arrLike);
console.log(newArr);
for 与 forEach、map的区别
- 在固定长度或者长度不需要计算的时候for循环效率高于foreach和map,for循环中可以通过break终止。
- 在不确定长度或者计算长度有损性能的时候用foreach和map比较方便
forEach、map的区别
相同点:
- 都是循环遍历数组中的每一项
- forEach和map方法里每次执行匿名函数都支持3个参数,参数分别是item(当前每一项),index(索引值),arr(原数组)
- 匿名函数中的this都是指向window
- 只能遍历数组
- 都不会改变原数组
不同点: - map方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值
- forEach方法不会返回新数组
为什么说js是弱类型语言
在弱类型语言中,数据类型可以被忽略,一个变量可以赋不同数据类型的值;而javascript变量在不同的场合可以解释为不同的类型,它允许变量类型的隐式转换和强制转换。在JavaScript中,不必事先声明变量的数据类型就可以使用变量,这时JavaScript解释器会根据情况做出他认为正确的判断。
什么是jsonp 工作原理是什么?他为什么不 是真正的ajax
-
1、jsonp的原理:就是利用浏览器可以动态地插入一段js并执行的特点完成的。
-
2、为什么不是真正的 ajax?
ajax的核心是 : 通过XmlHttpRequest获取非本页内容,
jsonp的核心 : 动态添加
3、ajax和jsonp的调用方式很像,目的一样,都是请求url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;
还是有不同点的:
4、实质不同
ajax的核心是通过xmlHttpRequest获取非本页内容
jsonp的核心是动态添加script标签调用服务器提供的js脚本
-
- ajax通过服务端代理一样跨域
jsonp也不并不排斥同域的数据的获取
- ajax通过服务端代理一样跨域
6 、jsonp是一种方式或者说非强制性的协议
ajax也不一定非要用json格式来传递数据
7、jsonp只支持get请求,ajax支持get和post请求
new过程
new 关键字运算过程会进行如下的操作:
- 1、创建一个空的简单JavaScript对象(即{});
- 2、链接该对象(设置该对象的constructor)到另一个对象 ;
- 3、将步骤1新创建的对象作为this的上下文 ;
- 4、返回新创建的对象,如果该函数没有返回对象,则返回this。
uni-app
uni-app生命周期
onLoad() {
console.log('页面加载')
},
onShow() {
console.log('页面显示')
},
onReady(){
console.log('页面初次显示')
},
onHide() {
console.log('页面隐藏')
},
onUnload() {
console.log('页面卸载')
},
onBackPress(){
console.log('页面返回...')
},
onShareAppMessage() {
console.log('分享!')
},
onReachBottom() {
console.log('下拉加载...')
},
onPageScroll(){
console.log('页面滚动...')
},
onPullDownRefresh() {
console.log('上拉刷新...')
uni.stopPullDownRefresh();
},
- uni-app 组件的生命周期和vue是一样的
手撕代码
推荐文章:https://blog.csdn.net/xgangzai/article/details/111243836