Vue Router,响应式,diff算法

Vue Router模式

**
   hash模式和history模式
       hash模式
       *1.hash是url存在 # 的一种方式,是vueRouter默认模式
       *2.当 # 后面的url地址发生变化会触发hashChange事件,通过监听来更新页面
       *3.只可修改hash部分
       *4.hash模式会创建hashHistory对象,hashHistory有两个方法push()和replace()
       history模式
       *1.history模式url无 # 号,就是普通的url形式
       *2.history模式发生跳转通过history.pushState()和history.replaceState()方法改变浏览器地址
       *3.当浏览器前进后退或调用back(), go(), forward()等方法时,会触发popstate事件,通过监听popstate事件来获取路由地址,从而更新页面
       *4.需要和服务器配合使用,否则会出现404的情况
       history模式在node服务中配置

	```
	const path = require('path')
	// 导入处理history模式的模块
	const history = require('connect-history-api-fallback')
	// 导入express
	const express = require('express');
	
	const app = express();
	// 处理history
	app.use(history());

	app.use(express.static(path.join(__dirname, 'dist')));

	const port = process.env.PORT || 8080;
	app.listen(port);
	```

       history模式在nginx服务中配置

   在nginx.conf文件中找到
   server {
   				listen 80
   				.
   				.
   				.
   				loaction / {
   					root  html
   					index index.html index.htm
   					try_files $uri $uri/ index.html  // 新增($uri当前请求的路径)
				}
   }

   *Vue Router原理

                hash模式

  •          1.url # 后面内容作为地址

  •          2.监听hashChange事件

  •          3.根据当前的地址找到对应的组件重新渲染

             hsitory模式

  •          1.通过history.pushState()方法改变地址栏

  •          2.监听popstate事件

  •          3.根据当前的地址找到对应的组件重新渲染
       

1. Vue Router实现*

  •    1.内部创建一个静态install方法(Vue.use()注入中会默认执行install方法);
  •    2.通过传入options解析配置项routes;
  •    3.监听路由变化,根据路由匹配到对应组件;
  •    4.实现组件router-link(通过render函数渲染a标签 指定a标签的href为跳转的路由)和router-view(查找当前路由对应的组件再通过render函数渲染组件)
	let _vue = null
	export default class VueRouter {
		// vue参数是。vue的构造函数
		static install(vue) {
			// 1.判断插件是否已经安装
			if (VueRouter.install.installed) return
			VueRouter.install.installed = true
			// 2.把vue构造函数设置为全局变量
			_vue = vue
			// 3.把创建的router对象注入到vue所有的实例上
			_vue.prototype.$router = this.$options.router
			
			this.init()
			
		}
		constructor(options) {
			this.options = options // 传入的options
			this.routeMap = {} // 解析出来的route
			// this.data是一个响应式。通过observable创建响应式 
			this.data =  _vue.observable({
				current: '/', // 当前路由地址(根据当前路由加载对应的组件)
			})  
		}
		init() {
			this.createRouteMap()
			this.initComponent(_vue)
			this.initEvent()
		}
		
		// 遍历所有的路有规则 解析成键值对 存放在routeMap中
		createRouteMap() {
			this.$options.routes.forEach(route => {
				this.routeMap[route.path] = route.component
			})
		}

		// 创建 router-link 组件
		initComponent(vue) {
			vue.component('router-link', {
				props: {
					to: String // 类型为字符串
				},
				render(h) {
					return h('a', {
						attrs: {
							href: this.to
						},
						on: { // 添加事件阻止默认事件
							click: this.clickHander
						}
					}, [this.$slots.default])
					methods: {
						// 阻止默认事件 通过pushState 改变当前地址。将当前地址同步
						clickHander(e) {
							// pushState() data title 路径
							history.pushState({}, '', this.to)
							this.$router.data.current = this.to
							e.preventDefault()
						}
					}
				}
				// template: `<a :href='to'><slot></slot></a>` 	// *运行会报错 因为cli创建的vue是运行时版本的(运行效率高)。所以要开始编译器,通过在vue.config.js 设置runtimeCompiler来开启编译器
			})
			
			const self = this
			vue.component('router-view', {
				const component = self.routeMap[self.data.current]
				render(h) {
					return h(component)
				}
			})
		}
		
		// 创建popState 事件来解决当浏览器回退,前进 组件没有更新问题
		initEvent() {
			window.addEventListener('popState', () => {
				this.data.current = window.location.pathname
			})
		}
	}

2. Vue响应式原理*

**
       vue2通过Object.defineProperty来实现响应式

	// 模拟 vue 中的data
	let data = {
    	msg: 'hello',
    	count: 10
	}
	
	// 模拟 vue 实例
	let vm = {}
	
	Object.keys(data).forEach(key => {
		// 数据劫持。当访问和设置vm中的成员时,做一些干预操作
		Object.defineProperty(vm, key, {
	    	// 可枚举(可遍历)
	    	enumerable: true,
	    	
	    	// 可配置(可以使用 delete 删除,可以通过 defineProperty 重新定义当前属性)
	    	configurable: true,
    	
	    	// 当获取值的时候执行
	    	get() {
	        	return data.msg
	    	},
	    	
	    	// 当设置值的时候执行
	    	set(newVal) {
	        	if (newVal === data.msg) return
	        	data.msg = newVal
	        	document.querySelector('#app').textContent = data.msg
	    	}
		})
	})
	
	vm.msg = 'hell workd'
	console.log(vm.msg) // ====> hell workd
	

       vue3通过Proxy来实现响应式

	// 模拟 vue 中的data
	let data = {
    	msg: 'hello',
    	count: 10
	}
	
	// 模拟 vue 实例
	// new Proxy 监听的是对象而非属性
	let vm = new Proxy(data, { // 执行代理行为的函数
	
		// 当访问vm成员的时候执行
		get(target, key) {
	        console.log(`获取 ${key}`)
	        return target[key]
	    },
	    set(target, key, value) {
	        if (target[key] === value) {
            	return
	        }
	        target[key] = value
	        console.log(`设置 ${key}=${value}`)
	    }
	})
	

3. Virtual Dom 的实现原理

虚拟DOM是一个普通的javaScript对象用来描述真实DOM,创建虚拟DOM的开销比普通DOM开销小很多。

为什么要是使用虚拟Dom
1.MVVM框架 解决视图和状态同步问题
2.普通模版(jquery等)可以简化视图操作,没办法跟踪状态
3.虚拟DOM跟踪状态变化

虚拟DOM的作用
1.维护视图和状态的关系(可以保存视图的状态)
2.复杂情况下提高渲染性能(首屏加载或第一次加载DOM时并没有提高渲染性能)
3.跨平台(因为虚拟DOM是普通的js对象可以做任何处理)

vue2是基于snabbdom库来实现的

	import { init } from '/node_modules/snabbdom/build/package/init';
	import { h } from '/node_modules/snabbdom/build/package/h';
	
	// 1.导入模块
	import { styleModule } from '/node_modules/snabbdom/build/package/modules/style'
	import { eventListenersModule } from '/node_modules/snabbdom/build/package/modules/eventlisteners';
	// 2.注册模块
	// init 初始化所引用的一些模块,返回patch函数,并且可以定义以哪种方式来转换虚拟DOM(默认是浏览器下的DOM对象)
	const patch = init([
	 styleModule,
	 eventListenersModule
	])
	// 3.使用h() 函数创建vnode, 第二个参数传入模块中使用数据(是一个对象)
	// h() 用到了函数重载(函数名字相同,参数不同)
		// 1. 参数个数或参数类型不同的函数
		// 2. js中没有重载概念
		// 1. ts中有重载,不过重载的实现还是通过代码来调整参数
	
	// vnode = {
	//		sel: '', // 选择器和类名
	//		data: '', // 模块中的数据
	//		childern: '', // 子节点
	//		elm: '', // vnode转化的真实DOM元素
	//		text: '', // 文本内容。和chindern互斥只有一个有值
	//		key: '', // 唯一标识,有利于对比重用DOM, 并且避免渲染错误(如果不设置key会判断sel属性进行重用DOM,假如子节点有checked选中,会重用dom导致显示错误)
	// }
	let vnode = h('div', [
	    h('h1', {
	        style: {
	            backgroundColor: 'red',
	        }
	    }, 'Hello world'),
	    h('p', {
	        on: {
	            click: eventHandler
	        }
	    }, 'hell p')
	])
	function eventHandler () {
	    console.log('click p')
	}
	// 4.使用patch()函数的第一个参数传入要操作的元素,第二个参数传入要操作的元素 
	// patch() 
	//	 1.把新新节点变化内容渲染到真实DOM,返回新节点作为下次的旧节点
	//	 2.对比新旧vnode 是否是相同节点(判断节点的key和sel相同)
	//   3.如果不是相同节点,删除之前的内容,重新渲染
	//   4.如果相同节点对比节点来更新(diff算法)
	//   diff
	//		1. 首先判断新旧节点是否相同,如果相同直接返回,如果新节点的data属性有值,遍历data属性更新节点
	//      2. 判断新节点是否有childern属性,如果存在,再去对比新旧节点的子节点
					// 四种情况
	//				2-1. 从旧开始节点和新开始节点开始做比较
	//				2-2. 从旧结束节点和新结束节点开始做比较
	//				2-3. 从旧开始节点和新结束节点开始做比较
	// 				2-4. 从旧结束节点和新开始节点开始做比较
	//				先对比旧开始节点和新开始节点,如果是相同节点(会重用旧节点的DOM从而优化性能),会将下一个节点作为开始节点,如果不是相同节点会通过旧的结束节点和新的结束节点做比较,如果不相同然后从旧开始节点和新结束节点做比较,如果是相同节点,会把旧元素移动到最后,然后对比下一组节点,如果不是相同节点,从旧节点的结束节点和新节点的开始节点做比较,如果是相同节点,把当前元素移动到最前面,如果以上情况都不满足,从旧节点中依次查找新节点的开始节点,如果找到把当前元素移动到最前面,如果没找到,创建新的DOM元素,插入到最前面位置,如果旧节点的开始节点大于旧节点的结束节点,创建新的节点插入到旧节点,然后从开始对比,对比结束后发现新节点中有剩余的节点,创建新的DOM放在旧节点DOM的后面,如果新节点的开始节点大于旧结束节点,对比新旧节点,对比完成后发现旧节点有剩余的节点,删除旧节点上的节点和DOM
	
	
    //    内部实现
	//	  (1) 判断vnode中sel属性 => 解析出标签和选择器 => 创建元素
	//	  (2) 判断vnode中是否有childern属性 => 如果存在遍历子节点然后递归调用createElm()
	//	  (3) 判断vnode中text属性 => 如果有值,创建文本节点 => 挂载到vnode的elm属性上
	//	  (4) 判断是否有hook(钩子函数) => 执行hook
	//	  (5) 把创建的DOM插入到DOM元素上
	//	  (6) 删除旧的节点
	let app = document.getElementById('app')
	let oldVnode = patch(app, vnode)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/556917.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis key(BigKey、MoreKey)的存储策略

1. MoreKey 案例 1.1 大批量往 redis 里面 插入2000w 测试数据key (1) Linux Bash 下面执行&#xff0c;插入 100w rootspray:~# for((i1;i<100*10000;i)); do echo "set k$i v$i" >> /tmp/redisTest.txt; done; 查看 rootspray:~# more /tmp/redisTest.…

SpringBoot多数据源(五)

SpringBoot多数据源-集成多个mybatis框架 1.基本框架2.使用2.1项目结构2.2 依赖导入2.3 application.yml配置2.4 创建读与写的SqlSessionFactoryBean 3.总结 1.基本框架 通过启动多个SqlSessionFactoryBean&#xff0c;每个SqlSessionFactoryBean对应一个datasource和指定位置的…

【计算机毕业设计】点餐平台网站——后附源码

&#x1f389;**欢迎来到琛哥的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 琛哥&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 琛哥在深度学习任务中展现出卓越的能力&a…

吴恩达2022机器学习专项课程(一) 6.1 动机第三周课后实验:Lab1使用逻辑回归进行分类

问题预览/关键词 回归和分类的区别&#xff1f;逻辑回归的作用是&#xff1f;什么是二分类问题&#xff1f;二分类问题案例如何表达二分类的结果&#xff1f;逻辑回归通常用哪种表达形式&#xff1f;什么是正样本和负样本&#xff1f;什么是阈值&#xff1f;可视化线性回归解决…

什么台灯对眼睛好?揭秘央视315推荐的护眼灯

目前很多家长都纠结这个问题&#xff0c;那就是孩子上学以后要怎么保护眼睛&#xff0c;晚上写作业用什么台灯对比较好一点&#xff1f;我建议最好选择一款合格、专业的护眼台灯&#xff0c;因为市面上大多数台灯都是没有专业光源技术&#xff0c;甚至部分廉价台灯还会使用低成…

Linux 网络基本命令

一、查看网络信息 ifconfig 二、关闭网络 ifdown ens33 (有的电脑不一定是ens33&#xff0c;具体看上图画线的地方) 三、开启网络 ifup ens33

【Canvas与艺术】绘制绿圈三红五星Premium Quality标志

【说明】 构图简约但美观。 【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>使用HTML5/Canvas绘制绿圈三红五星Prem…

JavaWeb--前端工程化

目录 1. 前端工程化 1.1. 概述 1.2. 前端工程化实现技术栈 2. ECMA6Script 2.1. es6的介绍 2.2. es6 变量 / 模版字符串 2.2.1. let 与 var 的差别 2.2.2. const 与 var 的差异 2.2.3. 模板字符串 2.3. 解构表达式 / 赋值 2.3.1. 数组解构赋值 2.3.2. 对象解构赋值 …

2024年分享酷我音乐如何下载mp3的方法

这里教大家用酷我音乐小程序的下载方法,小程序下载资源的方法有3种 1.利用专业的抓包工具(Fiddler/Charles)进行获取,然后分析数据包,最后直接用下载器下载分析出来的链接。强烈不推荐,因为大部分人并非程序员出身 2.录屏,录屏效率太慢,所以也不推荐 3. 利用专门的下载资源的…

微纤维眼镜清洁布的革命性进化

在日常生活中&#xff0c;眼镜是许多人不可或缺的日常用品&#xff0c;无论是视力矫正还是防护眼睛免受阳光的伤害。然而&#xff0c;眼镜的清洁常常是一个令人头疼的问题&#xff0c;特别是在面对指纹、灰尘和其他污垢时。传统的清洁方法往往需要化学清洁剂&#xff0c;不仅繁…

【css】select实现placeholder效果

场景&#xff1a;使用select下拉选择框的时候&#xff0c;需要像其他控件一样提示默认信息。 问题&#xff1a;表单控件select没有placeholder属性。 解决方案&#xff1a;通过css实现&#xff0c;不需要js <style>select > option[disabled]{ color:#999;cursor: n…

如何用Python构建一个生产级别的电影推荐系统 - 机器学习手册

构建项目是彻底学习概念并发展必要技能的最有效方式之一。 项目使您沉浸在现实世界的问题解决中&#xff0c;巩固您的知识&#xff0c;并培养批判性思维、适应能力和项目管理专业知识。 本指南将带您逐步构建一个根据用户喜好量身定制的电影推荐系统。我们将利用一个庞大的包…

Homebrew安装与卸载

卸载 /bin/bash -c "$(curl -fsSL https://gitee.com/ineo6/homebrew-install/raw/master/uninstall.sh)"安装 /bin/bash -c "$(curl -fsSL https://gitee.com/ineo6/homebrew-install/raw/master/install.sh)"1、复制命令到命令行执行&#xff0c;输入1…

多模态中的视觉编码器clip以及输入分辨率

在多模态的视觉编码主干中&#xff0c;若采用分类的backbone效果很差&#xff0c;经过语义对齐的backbone&#xff0c;比如clip的vit&#xff0c;效果则好很多。 1.Cogvlm中的EVA2-CLIP-E&#xff0c;VIT中最后一层被移除&#xff0c;4.4B&#xff0c;支持分辨率为334/490. 2.…

[源码分享]基于Unity的Live2D虚拟人物——结合了GPT、Azure、情绪识别和口型同步,也可以集合苹果Vision Pro做成3D的形象

# 技术文档 ## 1 项目简介 ### 项目目录 ``` Assets ├─ Animator // 动画 ├─ Code // 代码 │ ├─ AI // AI 模块 │ │ ├─ LM // 语言模型模块 │…

Python爬虫数据可视化分析

Python爬虫用于从网络上获取数据&#xff0c;数据可视化分析则是将获取的数据进行可视化展示和分析&#xff0c;帮助我们更好地理解数据、发现规律、做出决策。下面是一个基本的Python爬虫数据可视化分析的流程&#xff1a; 步骤一&#xff1a;数据爬取 1.选择合适的爬虫工具&a…

使用PL\SQL将Excel表格导入到oracle数据库中

因为要测试生产问题&#xff0c;需要把生产上oracle导出数据导入到测试环境oracle数据库中&#xff0c;尝试了N种方法&#xff0c;发现使用PL\SQL 的ODBC 方法比较好用 1、开始 首先使用plsqldev里面的&#xff0c;工具--》下面的odbc导入器 2、配置 点击之后&#xff0c;会…

LUA脚本判断是否为空

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Lua是一个小巧的脚…

MOS产品在储能上的应用分析与推荐

电化学储能可与光伏、风电等新能源发电相结合&#xff0c;缓解可再生能源稳定性差的问题。同时&#xff0c;电化学储能可提供调峰、调频、AGC、黑启动等辅助服务&#xff0c;保障电网安全。此外&#xff0c;电化学储能可以起到削峰填谷的作用&#xff0c;为住宅、工业和商业用户…

阻塞队列(模拟+生产者消费者)

阻塞队列 字面意思&#xff0c;带有阻塞功能的队列&#xff0c;满足队列先进先出的性质 作用&#xff1a; 1.如果队列为空&#xff0c;此时执行出队列操作&#xff0c;就会阻塞&#xff0c;直到往此队列里添加元素为止&#xff08;队列不为空&#xff09; 2.如果队列为满&#…
最新文章