目录

1 组件化

注册组件的基本步骤

全局组件和局部组件

为什么组件data必须是函数

2 模板编译

3 渲染过程

4 前端路由

vue-router实现原理:


1 组件化

可以从以下几点进行阐述:

组件化是 Vue.js 中的重要思想,它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一颗组件树:

定义
组件是可复用的 Vue 实例,准确讲它们是VueComponent的实例,继承自Vue。

优点
组件化可以增加代码的复用性、可维护性和可测试性。

使用场景
什么时候使用组件?以下分类可作为参考:

通用组件:实现最基本的功能,具有通用性、复用性,例如按钮组件、输入框组件、布局组件等。
业务组件:它们完成具体业务,具有一定的复用性,例如登录组件、轮播图组件。
页面组件:组织应用各部分独立内容,需要时在不同页面组件间切换,例如列表页、详情页组件

如何使用组件
定义:Vue.component(),components选项,sfc
分类:有状态组件,functional,abstract
通信:props,e m i t ( ) / emit()/emit()/on(),provide/inject,c h i l d r e n / children/children/parent/r o o t / root/root/attrs/$listeners
内容分发:<slot>,<template>,v-slot
使用及优化:is,keep-alive,异步组件

注册组件的基本步骤

Vue.extend()这种写法在Vue2.x以后基本就很少见了,会直接使用语法糖,但内部还是这个原理。

全局组件和局部组件

当我们通过调用 Vue.component() 注册组件时,组件的注册是全局的,这意味着该组件可以在任意 Vue 实例下使用;如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

组件的本质
vue中的组件经历如下过程
组件配置 => VueComponent实例 => render() => Virtual DOM=> DOM
所以组件的本质是产生虚拟DOM

为什么组件data必须是函数

组件是一个单独功能模块的封装,这个模块有属于自己的HTML模板。组件对象也有一个 data 属性,只是这个 data属性必须是一个函数,而且这个函数返回一个对象,对象内部保存着数据。

首先,如果不是一个函数,Vue直接就会报错;其次,Vue 让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响

2 模板编译

vue模板=》render函数=》vnode=》进行patch和diff

对于Vue来说,我们所认为的“HTML”其实都是字符串。

Vue会根据其规定的模板语法规则,将其解析成AST语法树;

然后会对这个大对象进行一些初步处理,比如标记没有动态绑定值的节点;

最后,会把这个大对象编译成render函数,并将它绑定在组件的实例上。

这样,我们所认为的“HTML”就变成了JavaScript代码,可以基于JavaScript模块规则进行导入导出,在需要渲染组件的地方,就调用render函数,根据组件当前的状态生成虚拟dom,然后就可以根据这个虚拟dom去更新视图了。

Vue的模板编译就是将“HTML”模板编译成render函数的过程。这个过程大致可以分成三个阶段:

  • 解析阶段:将“HTML”模板解析成AST语法树;
  • 优化阶段:从AST语法树中找出静态子树并进行标记(被标记的静态子树在虚拟dom比对时会被忽略,从而提高虚拟dom比对的性能);
  • 代码生成阶段:通过AST生成代码字符串,并最终生成render函数。

(注:当前节点及其所有子节点都是静态节点,当前节点才会被打上静态节点的标记)

(注:静态根节点是指本身及所有子节点都是静态节点,但是父节点为动态节点的节点,找到了静态根节点也就找到了“静态子树”)

template = '<div :id="myid">hello,xiang</div>';var compiler = require('vue-template-compiler')
compiler.compile(template)返回结果是一个render函数:with(this){return _c('div',{attrs:{"id":myid}},[_v("hello,xiang")])}

_c是createElement 函数,也就是snbbdom中的h函数, _v是createTextVNode

函数会通过“with”语法将this上的属性和数据解析成变量,比如:代码字符串中的_c相当于this._c,name相当于this.name。_c、_v这些变量其实就是vue创建不同类型虚拟dom节点的方法,比如_c就是我们在写render函数时非常熟悉的创建元素类型节点的createElement方法,_v是创建文本类型节点的方法。

前置知识:js的with语法

vue template complier 将模板编译为render函数

执行render函数生成vnode

with语法

改变{}内自由变量的查找规则,当做obj属性来查找

如果找不到匹配的obj属性,就会报错

with要慎用,它打破了作用域规则,易读性变差

模板编译

模板编译为render函数,执行人的人函数返回vnode

基于vnode再执行patch和diff

使用webpack vue-loader,会在开发环境下编译模板(重要)

3 渲染过程


初次渲染过程 

  1. 解析模板为render函数,开发环境中,vue-loader会做这件事

  2. 触发响应式,利用getter和setter监听data属性

  3. 首次执行render函数(会触发getter),生成vnode,patch(elem, vnode)

更新过程

  1. 修改data,触发setter

  2. 重新执行render函数,生成newVnode

  3. patch(vnode,newVnode)

流程图:

  1. 组件被 complier 编译成 Render 函数

  2. 执行 Render函数 生成 vDom

  3. 在 Render 函数中,触发 Data 属性的 getter,并被收集起来(Collect as Dependency)

  4. 观察(Watcher) 这些被收集的依赖

  5. 修改 Data 属性,setter 去通知(Notify)Watcher

  6. 若 Watcher 中存在修改的 Data 属性,则触发(Trigger)re-render

  7. 重新执行 Render 函数


异步渲染

  1. 汇总data修改,一个事件循环中多次修改会汇总一次性更新试图

  2. 减少渲染次数,提高性能

  3. nexttick在渲染完成后执行

4 前端路由

后端路由:
早期传统MVC网站路由都是服务端主导,前端通过不同URL请求后端,后端框架有专门的路由模块用来匹配URL地址,然后根据不同地址和参数调用相关处理程序并返回html页面给前端。

前端路由:
后来前后端分离,react/vue等框架流行,路由由前端主导。还是由前端改变url,但要做到不发生真实的网页跳转,即不向服务器请求网页。然后改由前端监听路由变化,并截获路由进行匹配以显示不同的前端组件,组件再通过ajax获取视图所需json数据。

前端路由分两种:hash模式 和 history模式。
Vue Router sh

hash模式 实现原理

(1) 通过a标签、window.location改变hash。

hash是URL中#及后面的那部分,改变hash会记入历史栈,不会发起页面请求。

(2) 通过hashchange事件监听hash变化,触发页面改变。

a标签跳转、window.location跳转,浏览器前进后退引起的hash变化都可以触发chashchange 事件。

location.hash 获取浏览器的hash值

hashchange 监听浏览器的hash值变化

hash特点:

  1. hash变化会触发网页跳转,就是浏览器的前进、后退

  2. hash变化不会刷新页面,SPA的必备条件

  3. hash不会提交到server端

//这里需要你在html中创建一个id为app的dom用于放置内容
var appNode = document.querySelector("#app");
window.addEventListener("hashchange", () => {Router.hash.handler();
});
var Router = {//list = 路由列表, 模仿vue-router中的routerlist写法, component做了简化只是一段文字;list: [{ path: "/", name: "index", component: "This is index page" },{ path: "/hash", name: "hash", component: "This is hash page" },{ path: "/history", name: "history", component: "This is history page" },{ path: "*", name: "notFound", component: "404 NOT FOUND" }],//输入path:String对应路由列表中的path, 实现渲染功能render: function(path) {var ele = null;//用于存储没有匹配路径时的404内容, 在这里默认路由列表中最后一个元素为404内容var naEle = this.list[this.list.length - 1];//通过path找出路由列表中对应的路由信息this.list.forEach(item => {if (item.path === path) ele = item;});//如果找到了path对应的路由信息, 则返回; 没找到的话, 返回404信息ele = ele ? ele : naEle;//将路由信息中的component加载进根节点appNode.innerHTML = ele.component;},hash: {//渲染handler: function() {Router.render(this.getState());},//获取当前hashgetState: function() {var hash = window.location.hash;hash = hash ? hash.slice(1) : "/";return hash;},getUrl: function(path) {var href = window.location.href;var i = href.indexOf("#");var base = i >= 0 ? href.slice(0, i) : href;return base + "#" + path;},push: function(path) {window.location.hash = path;},replace: function(path) {window.location.replace(this.getUrl(path));},go: function(n) {window.history.go(n);}}
};
//加载初始页面
Router.render(window.location.hash ? window.location.hash.slice(1) : "/");

history的原理是H5的几个新API
history.pushState(data,title,url):在浏览器中新增一条历史记录;
data会在onpopstate事件触发时作为参数传递过去,title为页面标题,url为页面地址;

history.replaceState(data,title,url):在浏览器中替换当前历史记录;
data会在onpopstate事件触发时作为参数传递过去,title为页面标题,url为页面地址;

history.length():当前历史列表中的历史记录条数;

window.onpopstate:实际上popstate是一个浏览器内置的点击事件,响应pushState和replaceState的触发调用;

history.back(-1):返回到当前页的上一页(原页面表单中的内容会保留)

history.back(0):页面刷新

history.back(1):当前页前进一页

history.go(-1): 返回到当前页的上一页(原页面表单中的内容会丢失,效果基本和history.back(-1)一样

history.forward():当前页面前进一页(和history.back(1)效果一样

此外,history方法可以直接调用,例:history.pushState(),也可以用window.history.pushState()来调用。因为history是属于浏览器中的子对象,两种调用方法都是生效的;

var appNode = document.querySelector("#app");
window.addEventListener("popstate", () => {Router.history.handler();
});
var Router = {list: [{ path: "/", name: "index", component: "This is index page" },{ path: "/hash", name: "hash", component: "This is hash page" },{ path: "/history", name: "history", component: "This is history page" },{ path: "*", name: "notFound", component: "404 NOT FOUND" }],render: function(path) {var ele = null;var naEle = this.list[this.list.length - 1];this.list.forEach(item => {if (item.path === path) ele = item;});ele = ele ? ele : naEle;appNode.innerHTML = ele.component;},history: {//渲染handler: function() {Router.render(this.getState());},//获取当前pathgetState: function() {const path = window.location.pathname;return path ? path : '/';},//pushState相关参数说明//状态对象(state object):一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,会触发popstate事件,并能在事件中使用该对象。//标题(title):一般浏览器会忽略,最好传入null。//地址(URL):就是需要新增的历史记录的地址,浏览器不会去直接加载改地址,但后面也可能会去尝试加载该地址。此外需要注意的是,传入的URL与当前URL应该是同源的。push: function(path) {window.history.pushState(null, null, path)this.handler()},replace: function(path) {window.history.replaceState(null, null, path)this.handler()},go: function(n) {window.history.go(n);}}
};
//加载初始页面
Router.render(window.location.pathname);

前端路由:原理篇_Palate的博客-CSDN博客_前端路由原理通过这篇文章,你可以了解到:为什么需要前端路由?解决了什么问题?前端路由的基本原理是什么?hash路由的hash值会发送到服务端吗?history路由为什么需要服务端支持?https://blog.csdn.net/weixin_51670675/article/details/124239269?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%89%8D%E7%AB%AF%E8%B7%AF%E7%94%B1%E7%9A%84%E5%8E%9F%E7%90%86&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-124239269.142%5Ev47%5Enew_blog_pos_by_title,201%5Ev3%5Econtrol_2&spm=1018.2226.3001.4187前端路由原理解析(含代码实现)_jim点点点点点的博客-CSDN博客关于前端路由你可能需要知道的内容什么是路由? 前端路由出现之前又是怎么实现路由的?前端路由hash 路由history 路由前端路由的缺点总结写在正文前: 作为一位已经工作了两年的前端 CRUD boy, 整日潜水在论坛看见各位大佬们谈天说地, 表示万分仰慕, 也想加入各位的吹水大军. 为此, 下定决心正式开始写文章锻炼自己的吹水能力, 也希望自己能坚持写下去. 还望各位大佬多多指正, 给小弟一...https://blog.csdn.net/jind325/article/details/105325221?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166291643516800182127497%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166291643516800182127497&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-105325221-null-null.142%5Ev47%5Enew_blog_pos_by_title,201%5Ev3%5Econtrol_2&utm_term=%E5%89%8D%E7%AB%AF%E8%B7%AF%E7%94%B1%E4%BB%A3%E7%A0%81&spm=1018.2226.3001.4187

vue-router实现原理:

SPA(single page application):单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。单页面应用(SPA)的核心之一是: 更新视图而不重新请求页面;vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式;根据mode参数来决定采用哪一种方式。

方式1:直接修改地址栏

方式2:this.$router.push(‘路由地址’)

方式3: <router-linkto="路由地址"></router-link>

hash 通过window.onhashchange监听

H5 history 通过history.pushState 和 window.onpopstate监听 实现的

H5 history  需要后台支持

两者选择

to B 的系统推荐用hash,简单易用,对url规范不敏感

eg.管理系统(ToB就是在企业业务中,以企业作为服务主体为企业客户提供平台、产品或服务并赚取利润的业务模式,我们也可以把它称之为企业服务。)

to C的系统,可以考虑选择 H5 history,但需要服务端支持

eg.系统需要做SEO 搜索引擎优化

(tTo B 英文为To Business面向企业 , To C为To Customer面向个体消费者)

能选择简单的,就别用复杂的,要考虑成本和收益

7 SY Vue 原理 副本 页面编辑错误相关推荐

  1. 【Vue原理】Diff - 源码版 之 Diff 流程

    写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧 研究基于 Vue版本 [2.5.17] 如果你觉得排版难 ...

  2. vue 加载页面时触发时间_详解Vue.js在页面加载时执行某个方法

    详解Vue.js在页面加载时执行某个方法 jQuery中可以这样写 vue中,如果要达到相同效果,可以使用vue的生命周期函数,如create或者mounted 附上vue.js的生命周期函数执行流程 ...

  3. Java快速开发平台,JEECG 3.7.5 Vue SPA单页面应用版本发布

    JEECG 3.7.5 Vue SPA单页面应用版本发布 导读            ⊙ Vue+ElementUI SPA单页面应用 ⊙Datagrid标签快速切换BootstrapTable列表风 ...

  4. vue如何创建vnode_【Vue原理】Component - 源码版 之 创建组件VNode

    专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧 研究基于 Vue版本[2.5.17] 今天就要开启我们 Component 探索之旅 ...

  5. element 往node里面增加属性值_【Vue原理】Compile - 源码版 之 Parse 属性解析

    写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧 研究基于 Vue版本 [2.5.17] 如果你觉得排版难 ...

  6. ASP.NET Core 中文文档 第三章 原理(5)错误处理

    原文:Error Handling 作者:Steve Smith 翻译:谢炀(Kiler) 校对:高嵩(jack2gs).何镇汐 当你的ASP.NET应用发生错误的时候, 你可以采用本文所述的各种方法 ...

  7. vue 启动时卡死_十分钟浅入Vue 原理

    vue原理 引用 众所周知vue是一个MVVM 渐进式框架,MVVM是vue的设计模式,在vue框架中数据会自动驱动视图. 1.MVVM设计模式 ​ 解释 View是视图,就是DOM:对应视图也就是H ...

  8. vue 数值 拼接字符串_【Vue原理】Compile - 白话版

    写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧 研究基于 Vue版本 [2.5.17] 如果你觉得排版难 ...

  9. v-model双向绑定原理_【Vue原理】VModel 白话版

    ↑点击上方 "神仙朱" 一起研究Vue源码吧 专注 Vue 源码分享,为了方便大家理解,分为了白话版和 源码版,白话版让大家可以轻松理解工作原理,源码版让大家更清楚内部操作和 Vu ...

最新文章

  1. 经典模式流水灯实验的个人总结和思考
  2. mfc程序转化为qt_工控编程,Qt 学习之路
  3. 更新CentOS Mysql到官方较新版本[以6.5为例]
  4. Database之SQLSever:SQL命令实现的高级案例集合之单表/多表(筛选、统计个数)之详细攻略
  5. c语言 遍历.jpg图像,求指导,如何用c语言实现读取*.raw格式图像
  6. Java多线程相关的几十个问题
  7. HDU 5763 Another Meaning KMP+DP
  8. 纳德拉:微软正计划“终极移动设备”
  9. C#生成图形验证码 (转)
  10. POJ 2377 Bad Cowtractors
  11. 谷歌账号 (亲测有效)
  12. excel换行快捷键_超实用的16个Excel快捷键,一定要收藏!
  13. Java开发OCR文字识别系统
  14. mysql数据库安全开关_对MySQL数据库的安全进行的详述
  15. BAT机器学习面试1000题系列(第1~305题
  16. 一对一直播源码开发,一对一视频直播解决方案新思路
  17. Java 链表知识总结
  18. Zephyr 3.2 弃用devicetree 中node 里的label property
  19. python数据分析库pandas-三、: python数据分析处理库-Pandas
  20. 电信云的原理及相关架构

热门文章

  1. Don Box博客中文版入驻CSDN,志愿者翻译招募中....
  2. qsort的基本用法
  3. java Servlet 笔记
  4. 小感悟:多对多关系,一定要创建关系表吗?
  5. 自然语言处理NLP 2022年最新综述:An introduction to Deep Learning in Natural Language Processing
  6. python机器语言直接用二进制代码表达指令_尔雅慕课搜题app,超星尔雅考试搜题,超星慕课查题app,学堂云搜题...
  7. 回车符,换行符与'\0'
  8. STM32三菱FX1N,FX2N,FX3U,PLC方案 可以直接上传下载梯形图,在线监控,具有称重功能,数码管功能,可以做到10路模拟量
  9. JavaScript计算两个日期之间相差的天数
  10. 《本地计算机DNS缓存文件》