虚拟dom面事题&snabbdom

Posted 暑假过期le

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虚拟dom面事题&snabbdom相关的知识,希望对你有一定的参考价值。

虚拟DOM

为什么使用虚拟dom

提高性能:

虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能

操作:

手动操作dom比较麻烦,还要考虑兼容问题 虽然有jq库简化dom操作但是项目复杂度越来越高,dom操作复杂提升,既要考虑dom操作还有考虑数据操作

mvvm框架:

为了简化dom复杂操作,mvvm框架解决了视图和数据同步问题

简化视图:

可以使用模板引擎,但是模板引擎没有解决跟踪数据变化问题于是虚拟dom出现了(数据改变后,无法获取上次状态,只有件页面上的元素删除,在重新创建,同时频繁操作dom性能非常低)

虚拟dom的作用

维护视图和状态的关系(虚拟dom会记录状态的变化,只需要更新状态变化的内容就可以了)

虚拟dom除了渲染dom以外,还可以实现渲染到其他的平台,可以实现服务段的渲染(ssr)

原生应用(react Native) 小程序等

Snabbdom基本使用

什么是Snabbdom?

在学习Vue或React中,我们了解最多的就是虚拟DOM,虚拟DOM可以看作是一颗模拟了DOM的javascript树,主要是通过vnode实现一个无状态的组件,当组件状态发生变更时,就会触发 virtual-dom 数据的变化,然后使用虚拟节点树进行渲染,但是在渲染之前,会使用新生成的虚拟节点树和上一次生成的虚拟节点树进行对比,只渲染两者之间不同的部分。

创建项目

创建项目,并安装parcel

//创建项目目录
md snabbdom-demo
// 进入项目目录
 cd snabbdom-demo
// 创建package.json
npm init -y
//本地安装parcel
npm install parcel-bundler

配置package.json中的scripts

 "srcipts":
     "dev":"parcel index.html --open" , //open打开浏览器
     "build":"parcel build index.html"
 

创建目录结构

导入snabbdom

官方文档

https://github.com/snabbdom/snabbdom

下面先安装Snabbdom.(这里最新版本有问题,可以先安装0.7.4)

npm install snabbdom@0.7.4

在项目的js文件夹下的01- basicusage.js文件中,添加如下代码:

import  snabbdom  from "snabbdom";
console.log(snabbdom);

以上代码的意思就是导入snabbdom这个包,然后打印其内容。

项目的启动

npm run dev

这时,开启的端口号为1234

http://localhost:1234

在打开的浏览器中,查看控制台的输出,发现输出的内容为undefined

为什么输出的是undefined呢?

这里我们需要查看对应的源码,

node_modules/snabbdom/snabbdom.js文件中,

我们可以看到在整个文件中,并没有使用export default的方式进行导出,所以就不能使用import snabbdom这种方式进行导入。同时在,源码中,我们可以看到导出了三项内容分别为h,thunk,init.

所以,导入的代码修改成如下的形式

import  h, thunk, init  from "snabbdom";
console.log(h, thunk, init);

Snabbdom的核心仅提供最基本的功能,只导出了三个函数init(),h( ),thunk( )

init函数是高阶函数,返回patch( )

h函数返回虚拟节点VNode,这个函数我们在使用Vue.js的时候见过。

h()函数用于创建虚拟DOM,在Snabbdom中用VNode描述虚拟节点,也就是虚拟DOM

new Vue(
router,
render:h=>h(App)
).$mount('#app')

thunk函数是一种优化策略,可以在处理不可变数据时使用(用于优化复杂的视图)

Snabbdom的基本使用

下面我们来看一下Snabbdom的基本使用,snabbdom.js中编写代码

import  h, thunk, init  from 'snabbdom'
/**
 * // init方法返回值为patch函数,patch函数作用是对比两个vndoe的差异并更新到真实的DOM中。
 * init函数的参数是一个数组,数组中的内容是模块。
 */
let patch = init([])
/**
 * 创建虚拟dom
 * 第一个参数:标签+选择器(id选择器或类选择器)
 * 第二个参数:如果是字符串的话就是标签中的内容
 */
let vnode = h('div#container.cls', 'hello chejia')
//我们这里需要将创建的虚拟dom,最终要渲染到`index.html`中`container`这个div中,所以这里需要获取一下该div
let app = document.querySelector('#container')
/**
 * 要想将虚拟DOM渲染到app中,需要用到patch函数。
 * 我们知道patch函数的作用是对比两个vnode的差异来更新到真实的`DOM`中。
 * 但是我们目前没有两个虚拟DOM.那么patch方法的第一个参数也可以是真实的DOM.patch方法会将真实的DOM转换成VNode.
 * 第二个参数:为VNode
 * 返回值为vnode
 */
let oldNode = patch(app, vnode)
// vnode = h('div', "hello xx")
// patch(oldNode, vnode)

在index.html中

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="container" class="cls"></div>
</body>

</html>
<script src="./src/basicusage.js">
</script>

在上面的代码中,我们又创建了一个虚拟DOM ,vnode。然后把这个vnodeoldNode进行对比,最后渲染到页面中。

scr目录下面在创建一个文件:02-basicusage.js

// 本案例实现的要求是:在div中设置子元素h1,p
import  h, init  from "snabbdom";
let patch = init([]);
// h函数的第二个参数可以是一个数组,在该数组中添加所要创建的子元素。
let vnode = h("div#container", [
  h("h1", "hello world"),
  h("p", "这是一个p标签"),
]);
let app = document.querySelector("#app");
patch(app, vnode);

同时还需要修改index.html文件中的引入

<body>
    <div id="app"></div>
    <script src="./js/02-basicusage.js"></script>
  </body3>

模块

常用模块

attributes:设置DOM元素的属性,内部使用setAttribute()来设置属性,处理布尔类型的属性(可以对布尔类型的属性作相应的判断处理,布尔类型的属性,我们比较熟悉的有selected,checked`等)。

props: 和attributes模块类似,设置DOM元素的属性element[attr]=value,不处理布尔类型的属性。

class: 切换样式类,注意:给元素设置类样式是通过sel选择器。··

dataset:设置HTML5 中的 data-* 的自定义属性

eventlisteners: 注册和移除事件

style:设置行内样式,支持动画(内部创建transitionend事件),会增加额外的属性:delayed / remove / destroy

使用模块的步骤:

第一步:导入需要的模块

第二步:在init()中注册模块

第三步:使用h函数创建VNode的时候,可以把第二个参数设置为对象(对象中是模块需要的数据,可以设置行内样式、事件等),其它参数往后移。

下面我们要实现的案例,就是给div添加一个背景,同时为其添加一个单击事件,当然在div中还要创建两个元素分别是h1p.

import  init, h  from "snabbdom";
//导入模块
import style from "snabbdom/modules/style";
import eventlisteners from "snabbdom/modules/eventlisteners";
//注册模块
let patch = init([style, eventlisteners]);
// 使用h函数的第二个参数传入模块所需要的数据(对象)
let vnode = h(
  "div",
  
    style: 
      backgroundColor: "red",
    ,
    on: 
      click: eventHandler,
    ,
  ,
  [h("h1", "Hello Vue"), h("p", "这是p标签")]
);
function eventHandler() 
  console.log("点击了我");

let app = document.querySelector("#app");
patch(app, vnode);

以上是关于虚拟dom面事题&snabbdom的主要内容,如果未能解决你的问题,请参考以下文章

虚拟dom面事题&snabbdom

虚拟dom面事题&snabbdom

深拷贝和原型原型链和web api 和 this指向等(中初级前端面事题)

深拷贝和原型原型链和web api 和 this指向等(中初级前端面事题)持续更新中,建议收藏

深拷贝和原型原型链和web api 和 this指向等(中初级前端面事题)持续更新中,建议收藏

深拷贝和原型原型链和web api 和 this指向等(中初级前端面事题)持续更新中,建议收藏