Vue(2),实战讲述Flutter跨平台框架应用

24 阅读3分钟

文末

从转行到现在,差不多两年的时间,虽不能和大佬相比,但也是学了很多东西。我个人在学习的过程中,习惯简单做做笔记,方便自己复习的时候能够快速理解,现在将自己的笔记分享出来,和大家共同学习。

个人将这段时间所学的知识,分为三个阶段:

第一阶段:HTML&CSS&JavaScript基础

第二阶段:移动端开发技术

第三阶段:前端常用框架

  • 推荐学习方式:针对某个知识点,可以先简单过一下我的笔记,如果理解,那是最好,可以帮助快速解决问题;

  • 大厂的面试难在,针对一个基础知识点,比如JS的事件循环机制,不会上来就问概念,而是换个角度,从题目入手,看你是否真正掌握。所以对于概念的理解真的很重要。 开源分享:docs.qq.com/doc/DSmRnRG…

  • *监听 data 中所有属性的变化,设置成响应式数据

  • *调用解析功能(解析模板内的插值表达式、指令等)

class Vue {

constructor (options) {

// 1. 存储属性

this.$options = options || {}

this.$data = options.data || {}

// 判断 el 值的类型,并进行相应处理

const {el} = options

this.$el = typeof el === 'string' ? document.querySelector(el) : el

// 2. 将 dta 属性注入到 Vue 实例中

_proxyData(this, this.$data)

// *3. 创建 Observer 实例监视 data 的属性变化

new Observer(this.$data)

// *4. 调用 Compiler

new Compiler(this)

}

}

// 将 data 的属性注入到 Vue 实例

function _proxyData (target, data) {

Object.keys(data).forEach(key => {

Object.defineProperty(target, key, {

enumerable: true,

configurable: true,

get () {

return data[key]

},

set (newValue) {

data[key] = newValue

}

})

})

}

Observer 类


  • 功能

  • 通过数据劫持方式监视 data 中的属性变化,变化时通知消息中心 Dep

  • 需要考虑 data 的属性也可能为对象,也要转换成响应式数据

class Observer {

// 接受传入的对象,将这个对象的属性转换为 Getter / Setter

constructor(data) {

this.data = data

// 遍历数据

this.walk(data)

}

// 封装用于数据便利的方法

walk(data) {

// 将遍历后的属性,都转换为 Getter/Setter

Object.keys(data).forEach(key => this.convert(key, data[key]))

}

// 封装用于将对象转换为响应式数据的方法

convert(key, value) {

defineReactive(this.data, key, value)

}

}

// 用于为对象定义一个响应式的属性

function defineReactive(data, key, value) {

// 创建消息中心

const dep = new Dep()

// 检测是否为对象,如果是,创建一个新的 observer 实例进行管理

observer(value)

// 进行数据劫持

Object.defineProperty(data, key, {

enumerable: true,

configurable: true,

get() {

console.log('获取了属性')

// * 在触发 Getter 时添加订阅者

Dep.target && dep.addSub(Dep.target)

return value

},

set(newValue) {

console.log('设置了属性')

if (value === newValue) return

value = newValue

observer(value)

// * 数据变化时,通知消息中心

dep.notify(0)

}

})

}

function observer(value) {

if (typeof value === 'object' && value !== null) {

return new Observer(value)

}

}

Dep 类


  • Dep 是 Dependency 的简写,含义 “依赖”,指的是 Dep 用于收集与管理订阅者与发布者之间的依赖关系

  • 功能:

  • *为每个数据收集顶硬的依赖,存储依赖

  • 添加并存储订阅者

  • 数据变化时,通知所有观察者

class Dep {

constructor () {

// 存储订阅者

this.subs = []

}

// 添加订阅者

addSub (sub) {

if (sub && sub.update) {

this.subs.push(sub)

}

}

// 通知订阅者的方法

notify () {

// 遍历订阅者,并执行更新功能

this.subs.forEach(sub => {

sub.update()

});

}

}

Watcher 类


  • 功能:

  • 实例化 Watch 时,往 dep 对象中添加自己

  • 当数据变化触发 dep,dep 通知所有对应的 Watcher 实例更新视图

class Watcher {

constructor (vm, key, cb) {

// 当前 Vue 实例

this.vm = vm

// 订阅的属性名

this.key = key

// 数据变化后,要执行的回调

this.cb= cb

// 触发 Getter 前,将当前订阅者实例存储给 Dep 类

Dep.target = this

// 记录属性更改之前的值,用于进行更新状态检测(导致了属性 Getter 的触发)

this.oldValue = vm[key]

// 操作完毕后,清除 target ,用于存储下一个 Watch 实例

Dep.target = null

}

// 封装数据变化时,更新视图的功能

update () {

const newValue = this.vm[this.key]

if (newValue === this.oldValue) return

// 数据改变,调用更新后的回调

this.cb(newValue)

}

}

Compiler 类


  • 功能:

  • 进行编译模板,并解析内部指令与插值表达式

  • 进行页面的首次渲染

  • 数据变化后,重新渲染视图

class Compiler {

constructor(vm) {

this.vm = vm

this.el = vm.$el

// 初始化模板编译方法

this.compile(this.el)

}

// 基础模板方法

compile(el) {

const childNodes = el.childNodes

Array.from(childNodes).forEach(node => {

// 检测节点类型(文本节点、元素节点)

if (isTextNode(node)) {

// 编译文本节点内容

this.compileText(node)

} else if (isElementNode(node)) {

// 编译元素节点内容

this.compileElement(node)

// 检测当前节点是否存在子节点

if (node.childNodes && node.childNodes.length) {

this.compile(node)

}

}

});

}

// 封装文本节点编译方法

compileText(node) {

const reg = /{{(.+?)}}/g

// 去除内容中不必要的空格与换行

const value = node.textContent.replace(/\s/g, '')

// 声明数据存储多段文本

const tokens = []

// 记录已经操作过的位置的索引

let lastIndex = 0

// 记录当前提取内容的初始索引

let index

let result

while (result = reg.exec(value)) {

// 本次提取内容的初始索引

index = result.index

// 处理普通文本

if (index > lastIndex) {

// 将中间部分的内容存储到 tokens 中

tokens.push(value.slice(lastIndex, index))

}

// 处理插值表达式内容(去除空格的操作可省略)

const key = result[1].trim()

// 根据 key 获取对应属性值,存储到 tokens

tokens.push(this.vm[key])

// 更新 lastIndex

lastIndex = index + result[0].length

// 创建订阅者,Watcher 实时订阅数据变化

const pos = tokens.length - 1

new Watcher(this.vm, key, newValue => {

// 数据变化,修改 tokens 中的对应数据

tokens[pos] = newValue

node.textContent = tokens.join('')

})

}

if (tokens.length) {

// 初始页面初始渲染

node.textContent = tokens.join('')

}

}

// 封装元素节点编译方法

compileElement(node) {

// 获取属性节点

Array.from(node.attributes).forEach(attr => {

最后

正值招聘旺季,很多小伙伴都询问我有没有前端方面的面试题!

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

前端资料图.PNG