主观能动性是一个哲学概念,亦称“自觉能动性”,它指人的主观意识和实践活动对于客观世界的能动作用。主观能动性有两方面的含义: 一是人们能动地认识客观世界;二是在认识的指导下能动地改造客观世界。在实践的基础上使二者统一起来,即表现出人区别于物的主观能动性。能动性是指对外界或内部的刺激或影响作出积极的、有选择的反应或回答。人的能动性与无机物、有机生命体、高等动物的能动性有别,称为主观能动性,其特点是通过思维与实践的结合,自觉地、有目的地、有计划地反作用于客观世界。
一、基础
简介
Vue(读音 /vjuː/,类似于view)是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。特性
- 轻量级20kb min + gzip
- 渐进式框架
- 响应式的更新机制
- 学习成本低
-
- beforeCreate->created->beforeMount->mounted->beforeUpdate->updated->beforeDestroy->destroyed。
环境搭建
- 谷歌浏览器
- vs code编辑器
- Nodejs + npm
开干
- 引入vue
- new Vue
- Vue.component及其缺点
- v-on:click简写@click
- v-model
- v-bind
- v-for
- scope作用域
vue cli
- 安装
npm install -g @vue/cli - 单文件组件
- 插槽v-slot
- 安装
二、脚手架
- 安装node
- node -v 查看node版本
- npm是集成在node中的,npm -v 查看npm版本
- Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
- Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。
- 安装cnpm
- npm install -g cnpm –registry=https://registry.npm.taobao.org
- cnpm -v
- 安装webpack
- npm install webpack -g
- webpack -v
- webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
- 安装vue cnpm install vue
- 安装vue-cli脚手架构建工具 cnpm install –global vue-cli
- 创建一个基于webpack模板的新项目 vue init webpack project-name
- 安装依赖 cnpm install
- 运行项目 cnpm run dev
三、双向绑定
- 单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新。有单向绑定,就有双向绑定。如果用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定。
四、自定义指令
五、异步编程
六、组件间传值
七、solt用法
- Slot的通俗理解就是是“占坑”,在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中
<slot>位置),当插槽<slot name=”mySlot”>有命名时,组件标签中使用属性slot=”mySlot”的元素就会替换该对应位置内容。
八、Vuex
- vuex是怎么存储变量的
- vuex中muation和action分别是怎么实现同步和异步更改store的值的
- Eventbus和vuex的区别
由于props和$emit一般用于父子组件间传递数据,当子组件过多时使用起来会很复杂,因此可以引入一个eventBus来订阅和发送全部组件间的数据。
九、Vue 常见知识点
9.1 生命周期
- 0.x ~ 1.x 的旧生命周期:
init→created→beforeCompile→compiled→ready→beforeDestroy→destroyed - 2.x 的生命周期:
beforeCreate→created→beforeMount→mounted→beforeUpdate→updated→activated/deactivated→beforeDestroy→destroyed - 加了
keep-alive之后:只多两个钩子——activated(被缓存组件重新激活时触发)与deactivated(组件被停用时触发)。activated:常用于进入页面时重新拉取列表、开启定时器、恢复滚动位置deactivated:常用于离开页面时清除定时器、保存草稿- 注意:
keep-alive的组件不会走destroyed,所以清理逻辑不要只写在destroyed里,否则内存和定时器都不会释放。
9.2 父子组件之间传值(含非父子)
| 方向 | 方式 | 说明 |
|---|---|---|
| 父 → 子 | props |
单向数据流,子组件不能直接改 prop(要改需 emit 让父改,或改本地副本) |
| 子 → 父 | $emit 自定义事件 |
父用 @event="handler" 监听 |
| 父调子 | ref + $refs |
直接拿到子组件实例,调其方法或读其 data |
| 双向 | v-model |
本质是 :value + @input 的语法糖(组件上为 :modelValue + @update:modelValue) |
| 任意组件 | EventBus / $bus |
订阅-发布模式,$emit 发、$on 收;组件过多时用起来会失控,建议只在少量场景使用 |
| 任意组件 | Vuex / Pinia |
集中式状态管理,跨层级、跨模块共享数据的正解 |
| 跨层级 | provide / inject |
祖先直接向后代注入,跳过中间层级 |
| 跨层级 | $attrs / $listeners |
属性与事件透传 |
由于 props 和 $emit 一般用于父子组件间传递数据,当子组件过多时使用起来会很复杂,因此可以引入一个 EventBus 来订阅和发送全部组件间的数据。
9.3 获取 DOM 元素
ref:给元素加ref="myRef",用this.$refs.myRef直接拿到真实 DOM 节点。el:mounted里this.$el拿组件自身根节点;this.$el.querySelector('.cls')可查内部节点。document.getElementById等原生 API:能用,但丧失响应式语义,容易与虚拟 DOM 的更新时机打架,不推荐。- 时机要点:
ref只有在mounted及之后才拿得到,created阶段 DOM 还没生成。
- 时机要点:
9.4 data 为什么是一个函数?
- 因为组件是可以被复用的,如果
data直接是一个对象,那么所有组件实例就会共享同一个对象引用,一个实例改了数据,其他实例也跟着变。 - 写成函数,每次创建组件实例时都会调用它并
return一个全新的对象,各实例持有独立的数据副本。 - 根实例
new Vue({ data: {...} })只有一个实例、不会被复用,所以可以写成对象。
9.5 双向绑定原理
- Vue 2.x:
Object.defineProperty- 在
Observer中递归遍历data的每个属性,用Object.defineProperty改写为getter/setter getter负责依赖收集:把当前正在求值的Watcher记录进Dep(一个依赖收集容器)setter负责派发更新:数据变化时通知Dep,Dep再通知所有Watcher执行updateWatcher分三种:渲染Watcher(更新视图)、用户Watcher($watch)、计算属性Watcher- 缺点:无法监听数组下标赋值和对象属性的新增/删除(所以需要
Vue.set/Vue.delete);初始化时递归遍历所有属性,数据量大时性能开销明显。
- 在
- Vue 3.x:
Proxy- 用
Proxy代理整个对象,配合Reflect操作原对象 - 直接解决了 2.x 的数组下标和属性增删问题,并且是惰性的(访问到才递归代理),初始化更快
- 通过
track(收集)和trigger(触发)两个函数完成依赖管理
- 用
9.6 双向数据绑定在什么时候收集订阅者,怎么收集的?
- 时机:在
new Watcher()阶段,构造函数最后会主动调用一次this.get();get()内部先执行pushTarget(this)把当前Watcher挂到Dep.target,然后求值被监听的那个表达式(比如渲染Watcher就是执行render)。 - 怎么收集:求值过程中必然会读到响应式属性,从而触发该属性的
getter;getter里判断Dep.target存在,就调用dep.depend(),把当前Watcher加进自己的subs队列,同时反向在Watcher里记录该Dep。 - 求值结束后执行
popTarget()清空Dep.target,避免收集到无关依赖。 - 一句话总结:订阅者在”读数据”时被动地被收进
Dep.subs,而”读数据”这个动作由Watcher.get()主动触发。
9.7 router 跳转方式与传参
| 方式 | 写法 | 说明 |
|---|---|---|
| 声明式 | <router-link :to="..."> |
渲染成 <a>,最常规的跳转 |
| 编程式 | this.$router.push(...) |
压入历史栈,可回退 |
| 编程式 | this.$router.replace(...) |
替换当前记录,不可回退 |
| 编程式 | this.$router.go(n) |
前进/后退 n 步,等价于 history.go(n) |
传参方式对比:
| 方式 | 写法 | 取值 | 特点 |
|---|---|---|---|
| 查询参数 query | push({ path:'/a', query:{ id:1 } }) |
this.$route.query.id |
参数暴露在 URL,刷新不丢 |
| 命名路由 params | 路由需定义 name,push({ name:'a', params:{ id:1 } }) |
this.$route.params.id |
参数不在 URL(除非路由 path 中声明了占位符),刷新会丢 |
| 动态路由参数 | 路由 path 写 /user/:id |
this.$route.params.id |
参数是 URL 的一部分,刷新不丢 |
| props 解耦 | 路由配置 props: true |
组件里直接 props: ['id'] |
组件不依赖 $route,复用性最好 |
导航守卫(按执行顺序):
| 类型 | 钩子 | 触发时机 |
|---|---|---|
| 全局前置 | router.beforeEach |
每次路由跳转前,最常用(登录校验、权限) |
| 全局解析 | router.beforeResolve |
所有组件内守卫和异步路由组件解析完之后 |
| 全局后置 | router.afterEach |
跳转完成后,无 next,常用于改标题、埋点 |
| 路由独享 | beforeEnter |
写在某条路由配置里 |
| 组件内 | beforeRouteEnter |
进入该组件前,此时 this 还不可用,需在 next(vm => {}) 里拿实例 |
| 组件内 | beforeRouteUpdate |
当前路由改变但该组件被复用时(如 /user/1 → /user/2) |
| 组件内 | beforeRouteLeave |
离开该组件前,常用于”有未保存内容,确认离开?” |
9.8 路由实现:hash 模式与 history 模式
| 对比项 | hash 模式 | history 模式 |
|---|---|---|
| URL 形态 | example.com/#/user/1 |
example.com/user/1 |
| 原理 | 监听 hashchange 事件,# 后的内容变化不触发页面请求 |
依赖 HTML5 history.pushState / replaceState,通过 popstate 监听 |
| 是否请求服务端 | 否,# 之后的部分不会发给服务器 |
是,刷新时会带完整路径请求服务端 |
| 是否需要服务端配合 | 不需要 | 需要,服务器必须把未匹配的路径统一回退到 index.html,否则刷新 404 |
| 兼容性 | 好(低版本 IE 也支持) | 需 HTML5 支持 |
| 美观度 | 带 #,略丑 |
干净,对 SEO 相对友好 |
- nginx 上 history 模式的经典配置:
try_files $uri $uri/ /index.html;
9.9 单页面应用(SPA)的优缺点
| 维度 | 优点 | 缺点 |
|---|---|---|
| 体验 | 页面切换无需整页刷新,流畅;可做转场动画 | 首屏需要等 JS 加载并执行完才能渲染,白屏时间较长 |
| 前后端 | 前后端职责分离清晰,前端专注交互,后端专注数据接口 | 需要在客户端处理大量原本由服务端承担的逻辑 |
| 开发 | 组件化、路由化,复用性与可维护性高 | 工程化配置复杂(构建、打包、路由、状态管理) |
| SEO | 可以做预渲染/SSR | 纯 CSR 对搜索引擎不友好 |
| 浏览器 | — | 前进后退需自行管理(路由拦截),且依赖 JS,禁用 JS 后不可用 |
| 部署 | 一套静态资源即可 | 需处理缓存、路由回退、首屏体积优化 |
9.10 会话存储与本地存储
| 对比项 | cookie |
sessionStorage |
localStorage |
|---|---|---|---|
| 存储容量 | 约 4KB | 约 5MB | 约 5MB |
| 生命周期 | 可设 expires,默认关浏览器即失效 |
关闭标签页即清除 | 永久,除非手动清除 |
| 作用域 | 同源,可设 path / domain |
同源 + 同一标签页 | 同源,多标签页共享 |
| 随请求发送 | 会,每次 HTTP 请求都带在 header 里 | 不会 | 不会 |
| 服务端可读 | 可以 | 不可以 | 不可以 |
| 典型用途 | 会话标识、登录态 | 表单临时数据、一次性流程状态 | 长期偏好设置、Token |
9.11 什么是 WebSQL,是 H5 的一部分吗?
- WebSQL 是一套在浏览器中通过 SQL 语句操作客户端数据库的 API(
openDatabase/transaction/executeSql)。 - 它曾经是 HTML5 规范草案的一部分,但由于各家浏览器底层实现不统一(SQLite 版本差异)、标准难以收敛,W3C 已于 2010 年停止维护该规范,所以严格来说它现在不属于 HTML5 标准。
- 现状:Chrome / Safari 仍保留,Firefox 从未支持。新项目应改用 IndexedDB(标准化的 NoSQL 客户端数据库)。
9.12 Vue / React / Angular 的区别
| 对比项 | Vue | React | Angular |
|---|---|---|---|
| 定位 | 渐进式框架,可只引核心库 | 专注视图层的 UI 库 | 大而全的完整框架 |
| 数据绑定 | 双向绑定(v-model) |
单向数据流,双向需自己写 | 双向绑定([(ngModel)]) |
| 模板 | HTML 模板 + 指令 | JSX(JS 里写标签) | HTML 模板 + 指令 |
| 数据更新 | 响应式(2.x defineProperty / 3.x Proxy) |
setState + 虚拟 DOM diff |
脏值检查(Zone.js) |
| 语言 | JS / TS | JS / TS | 强绑定 TypeScript |
| 学习曲线 | 低,上手最快 | 中,需理解 JSX 与状态管理 | 陡,概念多(模块、依赖注入、RxJS) |
| 生态 | 官方维护 router / vuex,风格统一 | 生态最繁荣,选型自由但需自己拼装 | 官方全家桶,开箱即用 |
| 适合 | 中小型项目、快速迭代 | 大型项目、跨端(React Native) | 大型企业级应用 |
补充:
vuex:Vue 的集中式状态管理,解决多组件共享状态、跨层级传参的问题。axios:基于 Promise 的 HTTP 客户端,用于发起请求(区别于 jQuery 的 ajax,更现代、支持拦截器与取消)。- UI 框架:Vue 系常用 Element UI / Element Plus / Vant(移动端)/ Ant Design Vue;React 系常用 Ant Design / MUI。
常见面试题
一、语言基础
- 用到了 ES6 的哪些特性,有什么感受,跟 ES5 对比有什么不同?
- 闭包的作用与举例
- JS 的原型链和继承
this的指向规则let/const/var的区别,什么是暂时性死区
二、框架与工程化
- webpack 常用的哪些功能,自己的理解,和 gulp 对比有什么不同?
vue-cli怎么使用自定义组件- Vue 生命周期
- 父组件和子组件之间的通信
- 3 个 Vue 常用的生命周期钩子函数
vuex有哪几种特性- React 与 Vue 在数据更新机制上的差异
三、浏览器与网络
- 从输入 URL 到页面展示发生了什么
- 跨域是怎么产生的,有哪些解决方案
- 强缓存与协商缓存的区别
- 事件循环中宏任务与微任务的执行顺序
前端三大框架
| 框架 | 核心特性 | 实现原理 |
|---|---|---|
| React | 组件化、JSX、单向数据流、虚拟 DOM、Hooks、函数式编程 | 虚拟 DOM + Diff 算法;setState 触发重渲染,通过 Fiber 架构把渲染拆成可中断的小任务,实现时间切片与优先级调度 |
| Vue | 模板 + 指令、双向绑定、响应式、单文件组件、渐进式 | 2.x:Object.defineProperty 劫持 + Dep/Watcher 依赖收集 + 虚拟 DOM Diff;3.x:Proxy + effect 副作用收集 + 编译期优化(静态提升、Patch Flag) |
| Angular | 完整框架、TypeScript、依赖注入、模块化、RxJS、指令 | 变更检测(Change Detection):Zone.js 拦截异步任务,触发自上而下的脏值检查;也可切换为 OnPush 策略手动控制检测范围 |
选型一句话:要生态和灵活选 React,要上手快和模板直观选 Vue,要企业级全家桶和强类型约束选 Angular。