导航
HTML
CSS
JavaScript
浏览器 & 网络
版本管理
框架
构建工具
TypeScript
性能优化
软实力
算法
UI、组件库
Node
冷门技能
⭐️ Vue 如何实现双向数据绑定的
Observer(观察者)
- 给 data 对象下所有的属性添加上 get 和 set 方法。
- get: 提供属性值的获取。每个属性如果是第一次调用 get 方法,就给当前属性添加上一个消息订阅器。
- set:当监听到值有改动时,让消息订阅器通知 watcher 更新页面
Compiler(指令解析器)
- 扫描根节点下的所有元素,找到每个节点上的指令并解析。
- 譬如一个 input 标签有个 v-model ,首先通过 vm 实例把属性名相同的值赋给它,再就是添加事件(input),在事件触发的时候把标签上的值赋值给 data 中相应的属性
- 如果是个
{{}}
,就给这个属性添加一个 watcher,在接收到消息订阅器的消息后调用 update 方法更新值
Dep(消息订阅器)
- 负责发布订阅
- 把订阅者放进数组中,每当 notify 被调用后(Observer 的 set 改变时触发),会通知 watcher 订阅者更新视图
Watcher(订阅者)
- 在消息订阅器上订阅每个属性的变化,在属性变化后收到消息执行回调 update 方法,从而更新视图
⭐️ watch,computed,method区别
computed
是有缓存的,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
methods
方法表示一个具体的操作,主要书写业务逻辑。只要调用就会再走一遍代码,不管有没有变化。
watch
一个对象,不存在缓存的。键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed
和methods
的结合体;
框架与库的区别
- 框架是一整套解决方案,对项目侵入性大,开发到后期如果想更换框架,相当于重写
- 库(插件):提供某一个小功能,侵入性小,如果某个库无法完成某些需求,可以较容易的切换其他库完成
- jquery ——> zepto
- EJS ——> art-template
⭐️ Node(后端)中的MVC 与 前端中的 MVVM 之前的区别
- MVC是后端的分层开发概念
- Model (模型层,数据的增删查改crud)
- View (视图层,前端页面)
- Control(控制层,处理业务逻辑,登录、注销)
- MVVM是前端视图层的概念,主要关注视图层分离,也就是说,MVVM把前端的视图层,(每个页面)都分为了三部分:
- Model (数据层,保存着每个页面中单独的数据)
- View (视图层,每个页面中的HTML结构)
- VM (视图模型层,V与M之间的调度者。每当V想要获取或保存数据,都要由VM做中间处理)
组件化和模块化的区别
- 模块化:是从代码逻辑的角度进行划分
- 组件化:是从UI界面的角度进行划分
前端路由和后端路由的区别
- 后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源
- 前端路由:对于单页面应用程序来说,主要通过URL中的**hash(#号)**来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash(锚点)实现
- 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由)
⭐️ 为什么 Vue 中 style 标签添加上 scoped 就能让样式在当前组件有效
因为只要添加上 scoped ,vue 就会自动给当前组件添加一个自定义 data-v-*
的属性。
然后所有该组件的样式,就会以css属性选择器的形式选中标签,让样式只在当前组件生效
总结一下 scoped 三条渲染规则:
- 给 HTML 的 DOM 节点加一个不重复 data 属性(形如:data-v-123)来表示他的唯一性
- 在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-123])来私有化样式
- 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性
// 组件
<div data-v-4412eb03>...</div>
// css
div[data-v-4412eb03] {...}
⭐️ Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?
- Object.defineProperty 无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
- Object.defineProperty 只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
- Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
答案参考:Daily-Interview-Question - 第51题
⭐️ Vue.set 是做什么用的?
为什么不能触发页面更新:
因为vue实例时,深度遍历 data 下所有属性, 把属性全转为 getter/setter。这样才能监听属性变化。所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。
当你在对象上新加了一个属性 newProperty , 当前新加的这个属性并没有加入 vue 检测数据更新的机制(因为是在初始化之后添加的), vue.$set 是能让 vue 知道你添加了属性, 它会给你做处理。
给数组或对象中添加属性,触发视图更新。
有一个obj:{a:1},想要this.obj.b=233,不会触发视图更新
Vue.set(this.obj, 'b', 233)
or this.$set(this.obj, 'b', 233)
Vue内部自己实现了一个对象,其中包含自己的数组操纵方法,使用了能够触发页面更新
⭐️ 双向绑定和 vuex 是否冲突
当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 v-model 会比较棘手:
<input v-model="obj.message">
假设这里的 obj
是在计算属性中返回的一个属于 Vuex store 的对象,在用户输入时,v-model
会试图直接修改 obj.message
。在严格模式中,由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误。
用“Vuex 的思维”去解决这个问题的方法是:给 <input> 中绑定 value,然后侦听 input 或者 change 事件,在事件回调中调用 action:
<input :value="message" @input="updateMessage">
// ...
computed: {
...mapState({
message: state => state.obj.message
})
},
methods: {
updateMessage (e) {
this.$store.commit('updateMessage', e.target.value)
}
}
下面是 mutation 函数:
// ...
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
来源:https://vuex.vuejs.org/zh/guide/forms.html