7 SY Vue 原理 副本 页面编辑错误

目录

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());
    },
    //获取当前hash
    getState: 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());
    },
    //获取当前path
    getState: 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面向个体消费者)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值