共计 5780 个字符,预计需要花费 15 分钟才能阅读完成。
文章内容输入起源:拉勾教育大前端高薪训练营
文章内容包含:模块作业、学习笔记
简答题
1、当咱们点击按钮的时候动静给 data 减少的成员是否是响应式数据,如果不是的话,如果把新增成员设置成响应式数据,它的外部原理是什么。
let vm = new Vue({
el: '#el'
data: {
o: 'object',
dog: {}},
method: {clickHandler () {
// 该 name 属性是否是响应式的
this.dog.name = 'Trump'
}
}
})
答:题目中通过 this.dog.name = 'Trump'
给 dog 减少的成员 不是响应式数据。
在 Vue 中能够通过 Vue.set(target, propertyName/index, value)
或者 this.$set(target, propertyName/index, value)
的形式为 target 对象动静增加响应式数据。Vue 2.x 中的原理是:相似于调用 defineReactive(obj, key, val)
办法,利用 Object.defineProperty
的 getter 和 setter 实现响应式数据。
2、请简述 Diff 算法的执行过程
答:
DOM 操作是很耗性能的,因而须要尽量减少 DOM 操作。找出本次 DOM 必须更新的节点来更新,其余的不更新,这个“找出”的过程,就须要 diff 算法。
diff 算法次要执行过程:
- patch(container, vnode),首次渲染,将 container 转为 vnode,并比照新旧 VNode 是否雷同节点而后更新 DOM
- patch(vnode, newVnode),数据扭转二次渲染,比照新旧 VNode 是否雷同节点而后更新 DOM
- createElm(vnode, insertedVnodeQueue),先执行用户的 init 钩子函数,而后把 vnode 转换成实在 DOM(此时没有渲染到页面),最初返回新创建的 DOM
- updateChildren(elm, oldCh, ch, insertedVnodeQueue), 如果 VNode 有子节点,并且与旧 VNode 子节点不雷同则执行 updateChildren(),比拟子节点的差别并更新到 DOM
编程题
我的项目地址 https://github.com/luxiancan/…
1、模仿 VueRouter 的 hash 模式的实现,实现思路和 History 模式相似,把 URL 中的 # 前面的内容作为路由的地址,能够通过 hashchange 事件监听路由地址的变动。
答:参考我的项目 ./vue-router-lxc,文件门路:./vue-router-lxc/src/vuerouter_hash/index.js
2、在模仿 Vue.js 响应式源码的根底上实现 v-html 指令,以及 v-on 指令。
答:参考我的项目 ./mini-vue-lxc,文件门路:./mini-vue-lxc/js/compiler.js
局部代码
// 解决 v-html 指令
htmlUpdater (node, value, key) {
node.innerHTML = value
new Watcher(this.vm, key, newValue => {node.innerHTML = newValue})
}
// 解决 v-on 指令
onUpdater (node, value, key, eventType) {node.addEventListener(eventType, value)
new Watcher(this.vm, key, newValue => {node.removeEventListener(eventType, value)
node.addEventListener(eventType, newValue)
})
}
3、参考 Snabbdom 提供的电影列表的示例,利用 Snabbdom 实现相似的成果
答:参考我的项目 ./snabbdom-demo
我的项目文件阐明
- notes : 笔记
- mini-vue-lxc : 小型 vue 框架,实现了响应式、插值表达式、局部指令等性能
- vue-router-lxc : 基于 vue-cli 模仿实现了 vue-router 的两种模式 history 和 hash
- snabbdom-demo : 应用 snabbdom 开发的小 demo,用于学习和坚固 snabbdom 的根本用法
-
- *
学习笔记
Vue.js 根底回顾
根底构造
- 应用
new Vue({el: '#app', data: {} })
- 应用
new Vue({data: {}, render(h) {}}).$mount('#app')
生命周期
- new Vue() 新建 Vue 实例
- beforeCreate 初始化事件 & 生命周期
- created 初始化注入 & 校验
- 是否指定“el”选项和“template”选项
- beforeMount
- mounted 创立 vm.$el 并用其替换 el
- 当 data 被批改时,触发 beforeUpdate
- 虚构 DOM 从新渲染并利用更新,触发 updated
- 当调用 vm.$destroy() 函数时,触发 beforeDestroy
- destroyed 解除绑定,销毁子组件以及事件监听器
- 销毁结束
Vue 语法和概念
- 插值表达式
- 指令
- 计算属性和侦听器
- Class 和 Style
- 条件渲染 / 列表渲染
- 表单输出绑定
- 组件
- 插槽
- 插件
- 混入 mixin
- 深刻响应式原理
- 不同构建版本的 Vue
Vue-Router 原理实现
Hash 模式和 History 模式的区别
- 不论哪种形式,都是客户端路由的实现形式,当门路发生变化不会向服务器发送申请
-
表现形式的区别
- Hash 模式:https://music.163.com/#/playl…
- History 模式:https://music.163.com/playlis…
-
原理的区别
- Hash 模式是基于锚点,以及 onhashchange 事件
- History 模式是基于 HTML5 中的 History API,history.pushState() IE 10 当前才反对,history.replaceState
History 模式的应用
- History 须要服务器的反对
- 单页利用中,服务端不存在 http://www/testurl.com/login 这样的地址会返回找不到该页面
- 在服务端应该除了动态资源外都返回单页利用的 index.html
History 模式 – Node.js
/* app.js */
const path = require('path')
// 导入解决 history 模式的模块
const history = require('connect-history-api-fallback')
// 导入 express
const express = require('express')
const app = express()
// 注册解决 history 模式的中间件
app.use(history())
// 解决动态资源的中间件,网站根目录 ../web
app.use(express.static(path.join(__dirname, '../web')))
// 开启服务器,端口是 3000
app.listen(3000, () => {console.log('服务器开启,端口:3000')
})
History 模式 – nginx
- 从官网下载 nginx 压缩包 http://nginx.org/en/download….
- 把压缩包解压到 c 盘根目录,c:nginx-1.18.0 文件夹
- 关上命令行,切换到目录 c:nginx-1.18.0
nginx 相干命令
# 启动
start nginx
# 重启
nginx -s reload
# 进行
nginx -s stop
nginx.conf 文件
server {
# ...
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
VueRouter 实现原理
Hash 模式
- URL 中 # 前面的内容作为门路地址
- 监听 hashchange 事件
- 依据以后路由地址找到对应组件从新渲染
History 模式
- 通过 history.pushState() 办法扭转地址栏
- 监听 popstate 事件
- 依据以后路由地址找到对应组件从新渲染
Vue 的构建版本
- 运行时版:不反对 template 模板,须要打包的时候提前编译
- 完整版:蕴含运行时和编译器,体积比运行时版大 10K 左右,程序运行的时候把模板转换成 render 函数
模仿 Vue.js 响应式原理
数据驱动
数据响应式、双向绑定、数据驱动
数据响应式
- 数据模型仅仅是一般的 JS 对象,而当咱们批改数据时,视图会进行更新,防止了繁琐的 DOM 操作,进步开发效率
双向绑定
- 数据扭转,视图扭转;试图扭转,数据也随之扭转
- 咱们能够应用 v-model 在表单元素上创立双向数据绑定
数据驱动
- 数据驱动是 vue 最独特的个性之一
- 开发过程中仅须要关注数据自身,不须要关怀数据是如何渲染到视图的
响应式的外围原理
Vue 2.x
- Object.defineProperty
- 浏览器兼容 IE8 以上(不兼容 IE8)
Vue 3.x
- Proxy
- 间接监听对象,而非属性
- ES6 中新增,IE 不反对,性能由浏览器优化
公布订阅模式和观察者模式
公布 / 订阅模式
- 订阅者
- 发布者
- 信号核心
咱们假设,存在一个“信号核心”,某个人物执行实现,就向信号核心“公布”(publish)一个信号,其余工作能够向信号核心“订阅”(subscribe)这个信号,从而晓得什么时候本人能够开始执行。这就叫做“公布 / 订阅模式”(publish-subscribe pattern)
Vue 的自定义事件
let vm = new Vue()
vm.$on('dataChange', () => {consloe.log('dataChange1')
})
vm.$on('dataChange', () => {consloe.log('dataChange2')
})
vm.$emit('dataChange')
兄弟组件通信过程
// eventBus.js
// 事件核心
let eventHub = new Vue()
// ComponentA.vue
// 发布者
addTodo: function () {// 公布音讯(事件)
eventHub.$emit('add-todo', { text: this.newTodoText})
this.newTodoText = ''
}
// ComponentB.vue
// 订阅者
created: function () {// 订阅音讯(事件)
eventHub.$on('add-todo', this.addTodo)
}
模仿 Vue 自定义事件的实现
class EventEmitter {constructor () {// { 'click': [fn1, fn2], 'change': [fn] }
this.subs = Object.create(null)
}
$on (eventType, handler) {this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
$emit (eventType) {if (this.subs[eventType]) {this.subs[eventType].forEach(handler => {handler()
})
}
}
}
观察者模式
-
观察者(订阅者)— Watcher
- update(): 当事件产生时,具体要做的事件
-
指标(发布者) — Dep
- subs 数组: 存储所有的观察者
- addSub(): 增加观察者
- notify(): 当事件产生时,调用所有观察者的 update() 办法
- 没有事件核心
总结
- 观察者模式 是由具体指标调度,比方当事件触发,Dep 就会去调用观察者的办法,所以观察者模式的订阅者与发布者之间是存在依赖的
- 公布 / 订阅模式 由对立调度核心调用,因而发布者和订阅者不须要晓得对方的存在
模仿 Vue 响应式原理
整体剖析
- Vue 根本构造
- 打印 Vue 实例察看
- 整体构造
- 把 data 中的成员注入到 Vue 实例,并且把 data 成员转成 getter/setter
- Observer: 可能对数据对象的所有属性进行监听,如有变动可拿到最新值并告诉 Dep
Vue
- 负责接管初始化的参数
- 负责把 data 中的属性注入到 Vue 实例,转换成 getter/setter
- 负责调用 observer 监听 data 中所有属性的变动
- 负责调用 compiler 解析指令 / 插值表达式
Observer
- 负责把 data 选项中的属性转换成响应式数据
- data 中的某个属性也是对象,把该属性转换成响应式数据
- 数据变动发送告诉
Compiler
- 负责编译模板,解析指令 / 插值表达式
- 负责页面的首次渲染
- 当数据变动后从新渲染视图
Dep (Dependency)
- 收集依赖,增加观察者(watcher)
- 告诉所有观察者
Watcher
- 当数据变动触发依赖,dep 告诉所有的 Watcher 实例更新视图
- 本身实例化的时候往 dep 对象中增加本人
总结
问题
- 给 data 中某个属性从新赋值成对象,是否是响应式的?— 是
- 给 Vue 实例新增一个成员是否是响应式的?— 否
Virtual DOM 的实现原理
什么是 Virtual DOM
- Virtual DOM(虚构 DOM),是由一般的 JS 对象来形容 DOM 对象,因为不是实在的 DOM 对象,所以叫 Virtual DOM
- 能够应用 Virtual DOM 来形容实在的 DOM
为什么应用 Virtual DOM
- 手动操作 DOM 比拟麻烦,还须要思考浏览器兼容问题,尽管 jQuery 等库简化了 DOM 操作,然而随着我的项目的简单 DOM 操作简单晋升
- 为了简化 DOM 的简单操作于是呈现了各种 MVVM 框架,MVVM 框架解决了视图和状态的同步问题
- 为了简化视图的操作咱们能够应用模板引擎,然而模板引擎没有解决跟踪状态变动的问题,于是 Virtual DOM 呈现了
- Virtual DOM 的益处是当状态扭转时不须要立刻更新 DOM,只须要创立一个虚构树来形容 DOM,Virtual DOM 外部将弄清楚如何无效 (diff) 的更新 DOM
虚构 DOM 的作用
- 保护视图和状态的关系
- 简单视图状况下晋升渲染性能
- 除了渲染 DOM 外,还能够实现 SSR(Nuxt.js/Next.js)、原生利用(Weex/React Native)、小程序(mpvue/uni-app) 等
- 并不是所有状况应用虚构 DOM 都能晋升性能,只有在视图比较复杂的状况下,应用虚构 DOM 才会进步渲染性能