当前位置: 首页 > news >正文

面试题vue

vue2

4.1

vue的设计模式

MVVM:Model-View-ViewModel

  • M:数据层,负责业务处理,与服务器交互,可以理解为原生js
  • V:视图层,用户展示层,将数据渲染成UI展示给用户,可理解为html
  • VM:视图数据层,用来链接Model和View的,监听view层/Model层的内容变化,从而渲染视图/修改数据
  • 重心放在:视图层和模型层,双向数据绑定

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

什么是模块化

模块化是将代码按功能拆分成独立、可复用的模块,通过导入/导出整合,提高代码组织性和维护性

举例:一个时间格式化的方法,需要在多个页面应用,就可以将这个方法封装出来,导出,在需要的地方引用即可获取结果

什么是组件化

组件化的核心意义就是解耦合,减少冗余,其优点:

  • 提高可复用性:最大程度提高代码的可复用性,减少开发过程中重复代码的存在,优化整个代码风格
  • 减少耦合度:一个由多个组件拼成的页面,各组件之间各司其职,互不干扰,能快速定位问题
  • 提高可维护性:因为每个组件职责单一,且多方复用,所以只需要维护单个组件,即可全部生效
  • 类似于我做了一个锤子,这个锤子不仅可以砸煤的时候用,砸钉子的时候也可以用,一个工具适用不同场景,这个工具就是组件
  • 原则:按照功能板块划分、本着复用性原则、拆的越细越好(这样才能更好的实现复用)

组件分为:功能性组件、业务性组件

  • 功能性组件:UI组件库已经提供的组件,如:按钮、表单、列表等
  • 业务性组件:根据实际业务自己封装的组件,比如:导航栏等

什么是指令系统

指令系统:vue框架预设好可识别的标签内v-开头的特殊属性:当model层数据发生变化时,将连带影响,响应式的作用于DOM,其核心在于绑定,常用指令:

  • 条件渲染指令v-if
  • 显示渲染指令v-show
  • 列表渲染指令v-for
  • 属性绑定指令v-bing
  • 事件绑定指令v-on,符号:@
  • 双向数据绑定v-model,符号::

vue跟传统开发的区别

vue通过操作数据,间接带动dom同步渲染

其他开发是获取dom,直接操作dom

vue与react对比

相同点:

  • 都有组件化思想
  • 都支持服务端渲染
  • 都有虚拟DOM
  • 都是数据驱动视图
  • 都支持自己的native方案:vue的weex,react的react native
  • 都有自己的构建工具:vue的vue-cli,react的Create react app

不同点:

  • 数据流向不同:react推崇单向数据流,vue推崇双向数据流
  • 数据变化的实现不同:react是不可变数据,vue是可变数据
  • 组件化通信的不同:react是通过回调函数,vue子向父组件有两种方式:事件和回调函数
  • diff算法不同
    • react用diff队列保存更新的dom,生成patch树,再统一更新DOM
    • vue使用双向指针,边对比,边更新DOM

4.2

说说你对SPA的理解

SPA:single Page Application,单页面应用,整个项目只有一个html,多页面的效果实际都是在一个html中加载不同页面片段实现的效果。好处:

  • 避免了页面跳转中的重新加载,无感切换
  • 动态加载适当的资源,保留复用资源
  • spa框架:react、vue、angular

SPA和MPA的区别

1SPAMPA
组成一个html+多个页面片段多个html
刷新方式局部刷新整体刷新
url模式哈希模式历史模式
SEO搜索优化难实现,可以用SSR改善容易实现
数据传递容易通过url、cookie、localStorage传递
页面切换速度快每次都要重新加载
维护成本相对容易相对复杂

SPA的优缺点

优点:

  • 具有桌面应用的即时性,网站的可移植性、可访问性
  • 用户体验好、快,改变内容不需要重新加载页面
  • 良好的前后端分离,分工明确

缺点:

  • 不利于搜索引擎搜索
  • 首次渲染加载速度慢(东西多)

如何给SPA做SEO优化

seo不好的根本原因,是爬虫爬取不到关键字,也就是网页的内容,因为单页面的内容主要由js渲染出来,所以查不到

1.SSR服务端渲染

将组件或页面通过服务器生成html,再返回给浏览器

v-show和v-if的区别

用法相同,都是判断条件是否成立而显示隐藏

不同点

  • 控制手段不同
    • v-show:通过操作display:none,来隐藏DOM,DOM元素不销毁
    • v-if:直接销毁DOM
  • 编译过程不同
    • v-if:有相对于元素的创建和销毁的过程,会重新创建或销毁绑定的事件
    • v-show:只是css层面的隐藏显示,其他不变
  • 渲染条件不同
    • v-if:在渲染阶段,false的元素直接不渲染
    • v-show:会先渲染出来,false的元素display:none
  • 性能消耗不同
    • v-if更高的切换消耗,v-show更高的初始渲染消耗

v-show和v-if的使用场景

频繁切换使用v-show,很少切换使用v-if

因为v-if开销大,但能彻底销毁元素

vue实例挂载的过程

  • beforeCreate:数据初始化未完成,不能访问data、props等
  • created:数据初始化完成,可访问data、props,但没有挂载元素
  • 挂载方法是调用vm.$mount方法
  • vue不允许将根元素挂载到body或html元素上,就是不能用body或html作为根元素
  • render的作用主要生成vnode
  • _update的主要功能是调用patch方法,将vnode转化为真实DOM

vue的生命周期

beforeCreate组件实例创建前
created组件实例创建完成
beforeMount组件挂载之前
mounted组件挂载完成
beforeUpdate组件更新之前
updated组件更新完成
beforeDestroy组件销毁之前
Destroyed组件销毁之后
activatedkeep-alive缓存的组件被激活时
deactivatedkepp-alive缓存的组件被停用时
errorCaptured捕获到子孙组件的错误时

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

应用场景:

beforeCreate一般初始化new Vue()实例时触发,做一些初始化任务
created组件初始化完成,各种数据都可调用,常用于异步数据获取,如调用API
beforeMount未执行渲染,dom未创建
mounteddom渲染成功,用于获取访问数据和dom元素
beforeUpdate更新前,可用于获取更新前各种状态
updated更新后,所有状态已是最新
beforeDestroy销毁前,组件的方法,属性还能被调用
destroyed销毁后,所有方法属性都被释放

created和mounted的区别?

最大的区别,两者都能拿到数据,但created中dom对象还没挂载,初始化数据的修改建议放在created中,如果放在mounted可能导致dom的再次刷新,出现闪屏的情况

为什么v-if和v-for不能一起用

重点:v-for优先级比v-if优先级高

这个观点是在element渲染的过程中得出的,会先判断for属性,再判断if属性

这就导致,for循环会先执行,会先渲染所有循环标签,渲染完之后,再判断if,如果是false,再去删除

这将造成无用的性能浪费

**如何避免:**在v-if的子元素中嵌套v-for

SPA首屏加载速度慢怎么解决

速度慢原因

  • 网络延迟
  • 资源过大
  • 接口重复请求加载
  • 加载脚本时,渲染内容堵塞

优化方案:

1.减少入口资源加载

  • router采用动态加载路由引用加载,只在应用到路由是才加载对应资源

  • routes:[ path: 'Blogs',name: 'ShowBlogs',component: () => import('./components/ShowBlogs.vue')
    ]
    

2.图片资源的压缩

如在线icon,雪碧图等,如图片webp替代png等

3.静态资源本地缓存

  • http缓存,设置Cache-ControlEtagLast-Modified标头

4.UI框架按需加载

  • //全局引用
    import ElementUI from 'element-ui'
    Vue.use(ElementUI)//按需引用
    import { Button, Input, Pagination } from 'element-ui';
    Vue.use(Button)
    Vue.use(Input)
    Vue.use(Pagination)
    

5.组件重复打包

  • //在webpack的config.js中配置
    minChunks: 3 //将引用超过3次的包抽离出来,放到公共依赖文件,避免重复加载组件

6.开启Gzip压缩

  • 拆包后,再用g-zip做一下压缩:

  • cnmp i compression-webpack-plugin -D
    
  • 再在webpack上配置:

    • const CompressionPlugin = require('compression-webpack-plugin')configureWebpack: (config) => {if (process.env.NODE_ENV === 'production') {// 为生产环境修改配置...config.mode = 'production'return {plugins: [new CompressionPlugin({test: /\.js$|\.html$|\.css/, //匹配文件名threshold: 10240, //对超过10k的数据进行压缩deleteOriginalAssets: false //是否删除原文件})]}}
      

为什么data是一个方法,而不是对象

避免数据污染

因为vue组件可能会创建很多个实例:

  • 如果data是对象,那所有实例的data都指向这个data对象,导致数据污染
  • 如果data是function,则每个实例都有自己的data,互不干扰,数据隔离

根实例对象可以是对象也可以是函数,根实例是单例

组件实例对象必须是函数,组件是n例

为什么vue中给对象加新属性,没有刷新页面?

vue2是用==Object.defineProperty==实现数据响应式

const obj = {}
Object.defineProperty(obj, 'foo', {get() {console.log(`get foo:${val}`);return val},set(newVal) {if (newVal !== val) {console.log(`set foo:${newVal}`);val = newVal}}})
}
  • 根本原因:vue给所有data中的属性都添加了get、set方法,实现响应式,新添加的属性没有get、set方法,所以不是响应式数据

  • 解决办法

    • Vue.set(obj,propertyName,value)的方式新增属性

    • Object.assign:

      • this.someObject = Object.assign({},this.someObject,{newProperty1:1,newProperty2:2 ...})
        //先合并再整体赋值给对象,因为someObject本身是响应式,所有属性会被重新赋值get、set方法
        
    • this.$forceUpdate():慎用,迫使Vue 实例重新渲染

  • 总结:

    • 添加少量属性:用Vue.set()
    • 添加大量属性:用Object.assign()
    • 实在不行:用forceUpdate
    • Vue3能直接添加新属性

vue中组件和插件的区别?

组件:一个.vue文件都可以视为一个组件

优势:

1.降低耦合度:即拿即用

2.调试方便:可快速定位问题,一劳永益

3.提高可维护性:后期维护成本低,随时修改

插件:类似于UI组件库中的buttons,这种就是插件

注册方式不同

  • 组件:Vue.component()
  • 插件:Vue.use(插件名字,{ /* ... */} ),插件需要在实例new之前注册

应用场景:

  • 组件 (Component) 是针对 App 的业务模块,它的目标是 App.vue

  • 插件 (Plugin) 是针对vue功能模块,它的目标是 Vue 本身

vue组件的通信方式有哪些

1.父子组件通信

2.兄弟组件通信

3.祖孙组件通信

4.非关系组件通信

常见的通信方案

  1. 通过props传输
  2. 通过$emit触发自定义事件
  3. 使用 ref
  4. EventBus
  5. $parent$root
  6. attrslisteners
  7. ProvideInject
  8. Vuex
  • props传输:父传子

    • children中的props属性可拿到父组件应用子组件时身上带的标签属性

    • //Children
      props:{  // 字符串形式  name:String // 接收的类型参数  // 对象形式  age:{    type:Number, // 接收的类型为数值  defaule:18,  // 默认值为18  require:true // age属性必须传递  }  
      }  
      //parent
      <Children name="jack" age=18 />  
  • $emit:子传父

    • //Children
      this.$emit('add', good)  ,//第一个参数是方法名,第二个参数是参数
      //Parent
      <Children @add="cartAdd($event)" />
      
  • $refs:父拿子数据,获取子组件实例

    • this.$refs.children.name
      
  • EventBus:兄弟互传

    • 创建一个公共方法bus,挂载到VUE的原型上,这样所有的实例都能调用到

    • bus内有两个方法:$emit$on

      • $emit理解为触发
      • $on理解为监听
    • class Bus {  constructor() {  this.callbacks = {};   // 存放事件的名字  }  $on(name, fn) {  this.callbacks[name] = this.callbacks[name] || [];  this.callbacks[name].push(fn);  }  $emit(name, args) {  if (this.callbacks[name]) {  this.callbacks[name].forEach((cb) => cb(args));  }  }  
      }  // main.js  
      Vue.prototype.$bus = new Bus() // 将$bus挂载到vue实例的原型上  
      // 另一种方式  
      Vue.prototype.$bus = new Vue() // Vue已经实现了Bus的功能
      
  • $parent:兄弟互传,父辈或祖辈做桥接

    • //组件A
      this.$parent.on('add',fn)
      //组件B
      this.$parent.emit('add',123)
      
  • provide和inject

    • 在祖先组件定义provide属性,返回传递的值

    • 在后代组件通过inject接收组件传递过来的值

    • //祖先组件
      provide(){return{foo:'foo'}
      }
      //后代组件
      inject:['foo']
      
  • vuex:存放共享变量

    • 一个属性三个方法:state、getter、mutations、actions
      • state:类似data
      • getter:类似于计算属性
      • mutations:同步函数,操作state和getter
      • actions:异步函数的调用,比如调用API获取数据
    • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

双向数据绑定

重点ViewModel他的职责是:

  • 数据变化后更新视图
  • 视图变化后更新数据

它的主要组成为:

  • 监听器(Observer):监听所有数据的属性
  • 解析器(Compiler):解析每个元素节点的属性,绑定对应的更新函数

对nextTick的理解

问题:因为vue中的更新是一个异步更新队列,我们在没有更新数据之前就去操作,拿到的是旧数据

nextTick的作用就是等待数据更新后能在nextTick中拿到最新的准确的结果

对mixin的理解

mixin的意思是混入,是一个包含data、components、computed、methods、props等全部属性的js文件

这个文件可以局部混入或全局混入到.vue文件中,在mixin文件中写的所有的方法都会生效

Vue.component('componentA',{mixins: [myMixin]
})

使用场景:当我们在开发过程中发现多处使用同一样功能的代码,这块代码可以提炼出来,放到js文件中,在需要的地方混入

我们在开发后台的时候,应用的上传功能,因为这个上传在拿到文件资源后上传到oss,但是这个上传之前还有很多操作去做,比如判断体积,判断格式等等,这些代码大部分类似,又不能写到组件里,我们就提出来,用mixin去做的混入,再用到的组件中去嵌入他,一劳永逸,比组件更灵活

对slot的理解

slot:插槽,开发者留个使用者自由定义的位置

v-slot的修饰符是:#

插槽三种使用方式

  • 默认插槽:直接在组件标签内写入

    • //子组件
      <template><slot><p>插槽后备的内容</p></slot>
      </template>
      //父组件
      <Child><div>默认插槽</div>  
      </Child>
      
  • 具名插槽:指定插槽名,分别插入

    • //子组件
      <template><slot>插槽defalut的内容</slot><slot name="content">插槽content的内容</slot>
      </template>
      //父组件
      <child><template v-slot:default>具名插槽</template><!-- 具名插槽⽤插槽名做参数 --><template v-slot:content>内容...</template>
      </child>
      
  • 作用域插槽:子组件传给父组件自己的属性,给父组件使用

    • //子组件
      <template> <slot name="footer" testProps="子组件的值"><h3>没传footer插槽</h3></slot>
      </template>
      //父组件
      <child> <!-- 把v-slot的值指定为作⽤域上下⽂对象 --><template v-slot:default="slotProps">来⾃⼦组件数据:{{slotProps.testProps}}</template><template #default="slotProps">来⾃⼦组件数据:{{slotProps.testProps}}</template>
      </child>
      

4.3

你能说一下key的作用吗

key的应用场景:

  • v-for

  • +new Date()生成的时间戳做key

    • <Comp :key="+new Date()" />
      

    key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点

对keep-alive的理解

Keep-alive是vue内置的组件,主要的作用是缓存不活跃的组件,避免重复渲染

keep-alive中的属性:

  • include:包括,正则表达式,名称匹配的缓存
  • exclude:排除,正则表达式,名称匹配的不缓存
  • max:数字,最大缓存数量

基础用法:


//路由中设置是否keepAlive
{path: 'list',name: 'itemList', // 列表页component (resolve) {require(['@/pages/item/list'], resolve)},meta: {keepAlive: true,title: '列表页'}
}
//渲染处判断该路由是否需要缓存
<keep-alive><!-- 需要缓存的视图组件 --> <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive><!-- 不需要缓存的视图组件 --><router-view v-if="!$route.meta.keepAlive"></router-view>

修饰符

修饰符是用于限定某些功能的快捷提示词:

表单修饰符

事件修饰符

鼠标修饰符等

  • .stop:阻止冒泡行为
  • .native:绑定原生方法
  • .once:只执行一次
  • .self:事件绑定在自己身上
  • .prevent:阻止默认行为
  • .caption:事件捕获
  • .keyCode:监听特定键盘按下
  • .right:右键按下

你有自定义过指令吗

有的,比如表单禁止重复提交(节流)、图片懒加载这种

核心是使用Vue.directive,给自定义的指令绑定一个函数去做的,比如重复提交这个:

// 1.设置v-throttle自定义指令
Vue.directive('throttle', {bind: (el, binding) => {let throttleTime = binding.value; // 节流时间if (!throttleTime) { // 用户若不设置节流时间,则默认2sthrottleTime = 2000;}let cbFun;el.addEventListener('click', event => {if (!cbFun) { // 第一次执行cbFun = setTimeout(() => {cbFun = null;}, throttleTime);} else {event && event.stopImmediatePropagation();}}, true);},
});
// 2.为button标签设置v-throttle自定义指令
<button @click="sayHello" v-throttle>提交</button>

你用过过滤器吗

过滤器(filter)是输送介质管道上不可缺少的一种装置

主要作用就是获得一些规范化的内容

比如用户名所有英文必须大写,这个让用户去规范有点麻烦我就可以用过滤器

//用法,在DOM里
{{msg|msgFormat}}
//方法,再script里
filters: {msgFormat: function (value) {if (!value) return ''value = value.toString()return value.charAt(0).toUpperCase() + value.slice(1)}
}

什么是虚拟DOM?

  • 实际上它只是一层对真实DOM的抽象,以JavaScript 对象 (VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上

  • 通过VNodevue可以对这颗抽象树进行创建节点,删除节点以及修改节点的操作, 经过diff算法得出一些需要修改的最小单位,再更新视图,减少了dom操作,提高了性能

  • createElement 创建 VNode 的过程,每个 VNodechildrenchildren 每个元素也是一个VNode,这样就形成了一个虚拟树结构,用于描述真实的DOM树结构

你有做过axios的封装吗?

当然,首先,axios封装我理解就是模块化的一个展现
所有的请求都是基于XMLHttpRequest服务发送http请求,axios也是基于这个开发的

封装的目的:一行代码就能拿到后端返回的结果

封装的方向

  • 拦截请求和响应:做一些统一操作,比如设置超时时间,设置请求头,状态码的返回、

    • axios.interceptors.request.use(config=>{},err=>{})//请求拦截
      axios.interceptors.respose.use(response=>{},err=>{})//响应拦截
      
  • 统一维护不同环境下的接口地址

  • 封装请求方法

简单的axios

class Axios {constructor() {}request(config) {return new Promise(resolve => {const {url = '', method = 'get', data = {}} = config;// 发送ajax请求const xhr = new XMLHttpRequest();xhr.open(method, url, true);xhr.onload = function() {console.log(xhr.responseText)resolve(xhr.responseText);}xhr.send(data);})}
}

xhr的了解

xhr:xmlHttpRequest,是客户端向服务端发送http请求获取数据的服务

5个状态值:

  • 0:未初始化,尚未调用open()
  • 1:启动,已经调用open(),但尚未调用send()
  • 2:发送,已经调用send(),但尚未接收响应
  • 3:接收,已经接受大部分响应数据
  • 4:完成,已经接收到全部响应

SSR是什么?

SSR:server-side-rendering:服务端渲染

vue项目的结构?

vue项目结构的规范化,能够使开发者达到一个群体共识

目录的设计应该遵循一些原则:

  • 文件夹跟内部文件的语义一致性
  • 单一入口/出口
  • 就近原则,耦合度高的文件应放在一起
  • 公共的文件引用应用绝对路径等
project|  .env.production //环境变量|  .gitignore      //git配置|  babel.config.js //|  package.json    //项目启动命令及项目配置|  vue.config.js   //vue层面的项目配置|--public					 //公共资源|  index.html    //出口|  favicon.ico|--src|--components    //公共组件|--button			 //按钮组件|index.js|index.less|index.vue|--pages|--login       //登录页|index.js|index.vue|index.less|--router        //路由配置|index.js|--services      //接口api|--utils         //工具文件夹|--store         //vuex仓库

vue中权限怎么做?

  • 接口权限:后端返回指定的错误码,做后续操作
  • 按钮权限:每次登录都会返回一个权限功能列表,页面初始化时根据权限检索功能,没有权限的按钮统一做限制
  • 路由权限:每次跳转都需要路由守卫router.beforeEach()去校验权限并做相应处理
  • 菜单权限:服务器直接返回需要渲染的菜单,再进行对应权限的渲染

权限功能需要前后端协商一致前端尽可能的去控制,更多的需要后台判断

如何解决跨域?

跨域:跨域的本质是浏览器同源策略的一种安全手段

同源的定义:

  • 协议相同
  • 主机相同
  • 端口相同
  • 反之则被浏览器认为非同源,举措就是不要返回的内容

解决办法

  • cors
  • proxy
  • nginx反向代理

cors:跨域资源共享,通过服务端响应头的设置,使浏览器接受数据

set('Access-Control-Allow-Origin', '*');
set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');

proxy:代理的方式,vue本地开发的方式,先代理到本地服务器,再传给前端,制造同源的假象

amodule.exports = {devServer: {host: '127.0.0.1',port: 8084,open: true,// vue项目启动时自动打开浏览器proxy: {'/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址changeOrigin: true, //是否跨域pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替'^/api': "" }}}}
}

你怎样处理项目中的错误

先定位错误类型:

  • 后端接口错误
  • 代码中本身逻辑错误

最直观的是去观察控制台

如果是后端的去跟后端伙伴协调,前端定位错误产生的位置,再分析错误逻辑,找到解决办法

vue3的了解,跟vue2的区别

vue3创建的初衷

  • 利用新的语法特性(es6)
  • 解决架构问题

vue3的优化

  • 速度更快
  • 体积更小
  • 更易维护
  • 更接近原生
  • 更易使用

vue3新特性

  • 组件支持多个根节点
  • 使用composition api代替options api
    • 响应式系统API变了:
    • ref、reactive、readonly、computed、watch、watchEffect
  • 生命周期用法变了
    • setup()作为入口,beforeCreatecreated都在setup方法中实现
    • 其他钩子函数都在基础上加了on开头
    • beforeDestroy变成onBeforeUnmount
    • destroyed变成onUnmounted

Vue3

vue3性能优化都有哪些?

  1. 虚拟dom的优化

    1. vue2所有的dom节点都会生成虚拟DOM,静态节点+动态节点

    2. //所有节点都会生成虚拟dom
      <template><div id="content"><p class="text">静态文本</p><p class="text">{{ message }}</p><p class="text">静态文本</p></div>
      </template>
    3. 静态资源的diff和循环是无用的,它无需修改

vue3根据以上问题做的优化:

  • diff算法优化
  • 静态提升
  • 事件监听缓存

4.4

vue3为什用ProxyAPI代替defineProperty API

vue2中存在的问题

  • 响应式用的Object.defineProperty()

    • 所有属性添加get、set方法
    • 当key为对象时,在set方法中,递归执行get、set方法
    • 在删除/添加时没有监听到
    • 如果深层的嵌套对象关系,需要深层的进行监听
  • 总结

    • 检测不到对象属性的删除和添加
    • 数组API方法无法监听到,如:push、pop
    • 嵌套对象,需要深层监听,造成性能问题

proxy的优点

  • proxy的监听针对是对象解决了增删检测不到的问题
  • get方法中加一层判断对象的嵌套:解决深层监听的问题

vue3的composition API和vue2的options API

optionsAPI:(选项式api),就是.vue文件,通过共同定义methodscomputedwatchdata等属性,处理页面逻辑

Composition Api:组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(高内聚,低耦合

  • 解释:所谓的碎片化,是指一个功能在vue2分布在data,method等多个模块中,而vue3推崇在一个模块中完成这些

比较

  • 逻辑组织
    • optionsAPI:一个功能可能需要data、methods等多模块共同实现
    • compositionAPI:一个功能在一个方法中都能定义,实现高内聚
  • 逻辑复用
    • optionsAPI:用mixins,命名冲突,数据来源不明
    • compositionAPI:hook 函数,直接引用,数据来源清晰
  • 总结:
    • 逻辑组织和逻辑复用方面,compositionAPI优于optionsAPI
    • 因为Composition API几乎是函数,会有更好的类型推断
    • Composition API中见不到this的使用,减少了this指向不明的情况

什么是tree sharking?

tree sharking:是通过清除多余代码,再去打包,压缩打包体积的技术

主要做两件事:

  • 判断哪些模块已经加载
  • 判断哪些模块和变量未被使用或者引用,进而删除对应代码

通过TreeSahrking给我们带来的好处:

  • 减少程序体积(更小)
  • 减少程序执行时间(更快)
  • 便于将来对程序进行优化(更友好)

ES6

4.4

Var、let、const的区别

es5中,在顶层声明的变量可以认做全局变量,js中是window,node中是global对象

var:es5

  • var声明的变量会被提升
  • var能对一个变量声明多次,比如var a=1,var a=1

let:es6

  • let类似于var,但声明只在当前代码块生效

  • 不存在变量提升

  • 只要作用域中存在let,则不收外界影响

    • var a=1function test(){a=10;//errorlet a
      }
      
  • 相同作用域不能重复声明

const:es6

  • 声明且赋值常量,声明后不能再修改
  • 如果a已经被var和let声明过,则不能被const声明了

区别:

1.变量提升

  • var会提升,let、const不会提升

2.暂时性死区

  • var因为提升没有暂时性死区,let、const只有在声明后才能被获取

3.块级作用域

  • var不存在块级作用域,let、const存在块级作用域

4.重复声明

  • var可以重复声明,let、const不能重复声明

5.修改值

  • var、let可以修改,const不可以修改

Es6数组新增了哪些扩展?

  1. ...展开运算符:将数组展开成列表

    1. console.log(1, ...[2, 3, 4], 5)//1 2 3 4 5
      //在解构赋值和形参中,将作为剩余运算符
      let [a,...b]=[1,2,3,4,5]// a=1 b=[2,3,4,5]
      const obj = {a: 1, b: 2};
      let arr = [...obj];//TypeError: Cannot spread non-iterable object

​ 2.只可以在有遍历器的对象中使用

  1. 新增构造函数

    1. Array.from():将类数组转为真正的数组:类似数组的对象和可遍历的对象,set、map

      1. Array.from([1,2,3],(x)=>x*x) // [1, 4, 9] ,第二个值为操作函数
        
    2. Array.of(1,2,3):将一组值转为数组

      1. 没参数:返回空数组
      2. 1个参数,生成长度为n的数组
      3. 2+个参数,生成参数数组
  2. 实例新增的方法

    • copyWithin()
    • find()、findIndex()
    • fill()
    • entries(),keys(),values()
    • includes()
    • flat(),flatMap()
  • copyWithin(target,find,findIndex):复制指定成员到指定位置
  • find():找出第一个符合条件的数组成员
  • findIndex():找出第一个符合条件的数组成员的位置,如果没有符合的返回-1
  • fill(content,start,end):填充值,可以规定从哪里开始填充,到哪里结束
  • entires、keys、values:获取数组的键或值
    • keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历
  • includes():判断数组中是否存在某个值
  • flat(2):将数组扁平化,参数为扁平嵌套的层数

4.5

ES6对象新增了哪些扩展?

属性名简写

//属性名简写
let x=1let y={x}//等同于y={x:x}
//方法名简写
let 0 ={test(){return 1}
}
  • 简写的方法不能作为构造函数

super关键字

  • this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象

解构赋值应用

  • //在解构赋值中,未被读取的可遍历的属性,分配到指定的对象上面
    let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
    x // 1
    y // 2
    z // { a: 3, b: 4 }
    

ES6中属性的遍历

5种方法:

  1. for...in:循环遍历对象自身的和继承的可遍历对象属性
  2. Object.keys(obj):返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名
  3. Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名
  4. Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有 Symbol 属性的键名
  5. Reflect.ownKeys(obj):返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举

对象新增的方法

  1. Object.is(param1,param2):判断两个值是否相等,类似于全等,但区别是,NAN=NAN,+0!=-0

  2. Object.assign(target,source1,source2):将2,3参数合并到target对象中

  3. Object.setPrototypeOf():设置一个对象的原型对象

    1. const o = Object.setPrototypeOf({}, null);
      
  4. Object.getPrototypeOf():获取一个对象的原型对象

  5. Object.keys(obj)、values()、entries():都是获取对象键或值

  6. Object.fromEntries():将一个键值对数组转为对象

4.7

Es6新增的set、map方法

一句话Set是一种叫做集合的数据结构,Map是一种叫做字典的数据结构

共同点:集合字典都可以存储不重复的值

不同点:集合是以**[值,值]的形式存在,字典是以[键,值]**的形式存在

  • set的方法

    • add()、delete()、has()、clear()

      • let s=new set()
        //add
        s.add(1).add(2).add(2)//最后一个2不会被添加,因为已经存在了
        //delete
        s.delete(2)
        
    • 用法:

      • 扩展运算和Set相结合,实现数组去重

      • let arr=[1,2,2,3,4,4,4]
        arr1=[...new Set(arr)]//arr1=[1,2,3,4]
  • Map的方法Map类型是键值对的有序列表,而键和值都可以是任意类型

    • size属性、set、get、delete、clear、has

ES6中的Promise

promise:翻译为承诺,是解决传统异步编程回调炼狱的方案

优点

  • 链式操作减轻了编码难度
  • 可读性更高

三种状态

  • pending:等待中
  • fullfilled:已成功
  • rejected:已失败

实例方法:

  • then(resolvtion,rejection)

    • 第一个参数是成功后执行的方法,第二个参数是失败后执行的方法

    • getJSON('/posts.json').then(function(posts) {// ...
      }).catch(function(error) {// 处理 getJSON 和 前一个回调函数运行时发生的错误console.log('发生错误!', error);
      });
      
  • catch(rejection)

    • 等同于then(null,rejection)
  • finally(fn):不管失败还是成功,都会执行的方法

构造函数的方法

  • let p =Promise.all([p1,p2,p3]):所有实例都返回成功或失败,才会进行下一步

    • 所有实例都为fullfiled,p为fullfiled
    • 只要有一个是rejected,p为rejected
  • Promise.race([p1,p2,p3]):率先改变状态的实例值返回

  • Promise.resolve('foo'):将现有对象转为Promise,状态为resloved

    • Promise.resolve('foo')
      // 等价于
      new Promise(resolve => resolve('foo'))
      
    • 当参数为Promise对象时,不改动,直接返回,状态为resloved

    • 当参数为then对象时,转为Promise对象,立即执行

    • 当参数为其他时,转为Promise对象,状态为resloved

    • 当没有参数时,返回Promise对象,状态为resloved

  • Promise.reject():将现有对象转为Promise,状态为rejected

    • Promise.reject('出错了')
      //等价于
      const p = new Promise((resolve, reject) => reject('出错了'))
      //等价于
      p.then(null, function (s) {console.log(s)
      });
      

async/await/generator

Generator 函数是 ES6 提供的一种异步编程解决方案

Async/await则是generator的语法糖,相当于会自动执行generator函数

区别

  • async/await、promise。都是用来处理一步操作的
  • generator不是为处理异步创建的,他的作用可以对象迭代、控制输出等
  • promise相对于Generator、async更复杂
  • async、await用法相对简洁,是处理异步的最终方案

es6中的module

module:模块,是能够单独命名并独立地完成一定功能的程序语句的集合

模块的作用

  • 代码抽象
  • 代码封装
  • 代码复用
  • 依赖管理

常见的模块模式

  • commonJS(node)
  • AMD(require)
  • CMD(import/export)ES6

CMD的用法

  • export:用于规定模块的对外接口

  • import:其他模块导入需要的功能

  • //导出方式export
    export {
    a,b,c
    }
    import {a} from '...'
    import * as abc from '...'//导出方式export defalut
    export default {a,b,c
    }
    import abc from '...'
    

javascript

4.7

js的数据类型

  • 基本数据类型6种
    • number:不同进制表示55
      • 55:十进制067:8进制0x37:十六进制
    • string:
    • boolean
    • undefined
    • null
    • Symbol
  • 引用数据类型7种+其他
    • Object
    • function
    • Array
    • Date
    • Regexp
    • Set
    • Map等

存储的区别

  • 基础数据类型内容存放在
  • 引用数据类型内容存放在中,地址存放在

数组常用方法

  1. 操作方法
    1. push():尾部推入==(原)==
    2. pop():尾部弹出==(原)==
    3. unshift():头部推入==(原)==
    4. shift():头部弹出==(原)==
    5. splice(start,end):删除数组中多项,返回删除的项==(原)==
    6. slice(start,end):复制数组中的多想,返回复制的项==(新)==
    7. indexOf(param):返回参数所在位置,没有返回-1
    8. includes():返回数组中是否存在参数,布尔值
    9. find():返回第一个匹配的元素
    10. filter():返回所有匹配的元素
  2. 排序方法
    1. reverse():反转所有元素
    2. sort(fn):升序或降序所有元素
      1. sort((a,b)=>a>b):升序
      2. sort((a,b)=>b>a):降序
  3. 转换方法
    1. arr.join(' '):数组转换为字符串,以空字符串连接
  4. 迭代方法
    1. arr.forEach((item,index)=>{}):循环遍历
    2. arr.some():返回布尔值,只要有一个符合条件返回true
    3. arr.every():返回布尔值,所有符合条件才返回true
    4. arr.filter():返回符合条件的项
    5. arr.map():对数组每一项进行,返回最终生成的新数组

字符串常用方法

所有字符串的方法都不是改变原字符串,而是创建新副本

  1. :不改变原字符
    1. str.concat('world'):返回拼接好的新字符串,不改变原字符串
  2. :不改变原字符串
    1. str.slice(start,end):删除字符串某区域内的字符
    2. str.substr(start,length):从start开始的length个字符串
    3. str.substring(start,end):从start开始,到end结束
  3. :不改变原字符串
    1. str.trim()、str.trimLeft()、str.trimRight():删除前后空字符串
    2. str.repeat(n):将字符串重复n次,返回结果
    3. str.toLowerCase()、str.toUpCase():将字符串全部大写或小写
  4. :
    1. str.indexOf(param)、str.lastIndexOf(param):返回字符首次/最后一次出现的位置
    2. str.charAt(n):获取n位的值
    3. str.startWith('foo'):检查开头是否匹配foo,不匹配则返回false
    4. str.includes('foo'):检查是否包含foo
  5. 转换
    1. str.split('+'):以+号分割字符串,返回最终数组
  6. 模版匹配:参数可以是正则,也可以是字符串
    1. str.match(/.at/):返回所有匹配成功项的数组
    2. str.search(/at/),返回匹配索引
    3. str.replace(/at/,'oao'):替换匹配到的值为oao

js中的类型转换机制

显示转换:强制转换,执行转换的方法实现的转换

隐式转换:自动转换,比如执行加法运算时

  • 显示转换:

    • Number():将任意字符转为数字

      • Number('324') // 324
        Number('324abc') // NaN
        Number('') // 0 空字符串转为0
        Number(true) // 1
        Number(false) // 0
        Number(null) // 0
        Number(undefined) // NaN
        Number({a: 1}) // NaN
        Number([5]) // 5 对象:通常转换成NaN(除了只包含单个数值的数组)
        
    • parseInt():逐个解析字符,遇到不能转换的字符就停

      • parseInt('123b2')//123
        
    • String():任意类型的值转为字符串

      • String(1)//'1'
        String(false)//'false'
        String(null)//'null'
        String(undefined)//'undefined'
        String({a:1})//'[Object object]'
        String([1,2,3])//'1,2,3'
        
    • Boolean():将任意值转为布尔值

      • Boolean(0)//false
        Boolean(null)//false
        Boolean(undefined)//false
        Boolean('')//false
        Boolean({})//true
        Boolean([])//true
        Boolean(new Boolean(false))//true
        

隐示转换:运算过程中的转换

  • 比较运算符:==、!=、>、<、if、while,等需要布尔值的地方

  • 算数运算符:+、-、*、/、%

  • 转为false的情况:

    • 0、‘’、undefined、null、false、NaN
    • 其他都为true
  • 自动转为字符串

    • 加法如果一方为字符串,则先转为字符串

      '5' + 1 // '51'
      '5' + true // "5true"
      '5' + false // "5false"
      '5' + {} // "5[object Object]"
      '5' + [] // "5"
      '5' + function (){} // "5function (){}"
      '5' + undefined // "5undefined"
      '5' + null // "5null"
      
    • 除了加法,其他运算符都为转为数值计算

      • '5'-'2':3
        '5'*2:10
        null-1:-1
        true-1:0
        false-1:-1
        undefined-1:NAN
        'abc'-1"NAN
        

=====的区别

==:模糊比较

===:精准比较

==

  • 简单类型的比较先转为数值类型,再进行比较
  • 简单类型与对象比较:对象转化成其原始类型的值,再比较
  • 两个都为对象类型:则比较他们的引用地址
  • null 和 undefined 相等
  • 存在NAN则返回false

===:只有两个操作数在不转换的前提下相等才返回 true

  • null、undefined与自身严格相等:null===nullundefined===undefined

深拷贝与浅拷贝

浅拷贝:只关注拷贝对象自身的属性,如果属性是对象,拷贝的是引用地址

  • 浅拷贝现象的方法:
    • Object.assign()
    • arr.slice(),arr.concat()
    • 拓展运算符

深拷贝:如果对象属性为基础类型,直接拷贝,如果对象属性为引用类型,递归拷贝

  • 深拷贝现象的方法:

    • _.cloneDeep():lodash
    • JSON.stringfy():会忽略值为:undefined/symbol/和函数
  • 深拷贝手写:

    • function deepClone(obj,hash=new WeakMap()){if(typeof obj!='object') return obj;//非对象情况下返回if(hash.has(obj))return hash.get(obj); //处理循环引用if(obj instanceof Date){//处理日期对象let newObj=new Date(obj);hash.set(obj,newObj);return newObj;}if(obj instanceof RegExp){//处理正则let newObj=new RegExp(obj);hash.set(obj,newObj);return newObj;}let newObj=Array.isArray(obj)?[]:{};//处理数组和对象hash.set(obj,newObj);for(let key in obj){ //递归遍历对象if(obj.hasOwnProperty(key)){newObj[key]=deepClone(obj[key],hash);}}return newObj;
      }
      

闭包是什么

闭包:闭包让你可以在一个内层函数中访问到其外层函数的作用域

闭包的特点:因为一个函数的存在,导致其父作用域的变量不能被回收

  • 创建私有变量

  • 延长变量的声明周期

  • function makeSizer(size) {return function() {document.body.style.fontSize = size + 'px';};
    }
    let p1=makeSizer(12)
    document.getElementById('size-12').onclick = p1;
    

柯里化函数:避免频繁调用具有相同参数函数的同时,又能够轻松的重用

// 我们可以使用闭包柯里化这个计算面积的函数
function getArea(width) {return height => {return width * height}
}
let getAreaFromWidth=getArea(10);
let area1=getAreaFromWidth(20);
let area2=getAreaFromWidth(30);

你对作用域链的理解

作用域:即变量和函数生效(能访问到)的区域

作用域分为:

  • 全局作用域:不在函数和块级作用域内声明的变量,都是全局作用域

  • 函数作用域:function函数中声明的变量

  • 块级作用域:花括号中声明的变量,var声明的变量因为会提升,所以会作为全局变量

  • let a=10//全局变量
    //函数作用域
    function(){let num=20//私有变量}
    //块级作用域
    {const num=111//局部let num2=222;//局部var num3=333;//全局
    }
    

作用域链:当使用一个变量时,当前作用域找不到的话,会像父级作用域寻找,如果没有继续像上寻找,直到全局作用域,都没有就报错,逐级向上,就近原则

原型和原型链

原型:所有对象都有prototype属性,函数即是对象,也是函数

原型链:所有实例都有__proto__,指向其构造对象的prototype属性

  • 构造函数即存在__proto__,也存在prototype
    • Person.proto==Function.prototype:函数是由Function构造器构造的
    • Person.prototype.proto==Object.protptype:prototype本身是一个对象,所以原型链指向Object的prototype
  • 一切对象都是继承自Object对象,Object 对象直接继承根源对象null
  • 一切的函数对象(包括 Object 对象),都是继承自 Function 对象
  • Object 对象直接继承自 Function 对象
  • Function对象的__proto__会指向自己的原型对象,最终还是继承自Object对象

http://www.mrgr.cn/news/97447.html

相关文章:

  • Ubuntu 22 Linux上部署DeepSeek R1保姆式操作详解(Xinference方式)
  • AI Agent设计模式一:Chain
  • vue2拖拉拽做个模拟公式工具
  • 【零基础入门unity游戏开发——动画篇】Animation动画窗口,创建编辑动画
  • [C++面试] explicit关键字面试点总结
  • modprobe: can‘t open ‘modules.dep‘: No such file or directory
  • 使用NVM下载Node.js管理多版本
  • 下载安装Node.js及其他环境
  • Opencv计算机视觉编程攻略-第十节 估算图像之间的投影关系
  • Linux服务宕机,java服务导致的内存溢出
  • Uni-app入门到精通:uni-app的基础组件
  • 量子纠错码实战:从Shor码到表面码
  • k8s的StorageClass存储类和pv、pvc、provisioner、物理存储的链路
  • WebRTC技术简介及应用场景
  • 蓝桥杯 web 展开你的扇子(css3)
  • 通过ansible+docker-compose快速安装一主两从redis+三sentinel
  • Gateway 网关 快速开始
  • verilog学习--1、语言要素
  • 【大模型深度学习】如何估算大模型需要的显存
  • C# 与 相机连接