wclimb的个人博客--分享


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索

编写一个较为强大的Vue

发表于 2020-05-26 | 分类于 javascript , vue
| 约 1 分钟

编写一个较为强大的Vue,支持虚拟DOM、diff更新以及基本的API,项目地址:https://github.com/wclimb/euv
demo地址:http://www.wclimb.site/euv/

euv

why euv? because:

1
'vue'.split('').sort().join('') // euv

source:

1
'node'.split('').sort().join('') // deno

Quick Start

安装

1
2
3
git clone https://github.com/wclimb/euv.git
cd euv
npm install

运行

1
npm run dev

目前支持功能

  • ✅ 虚拟DOM
  • ✅ Diff更新
  • ✅ {{data}} or {{data + 'test'}} or {{fn(a)}}
  • ✅ v-for // v-for="(item, index) in list" or v-for="(item, index) in 10" or v-for="(item, index) in 'string'"
  • ✅ v-if v-else-if v-else
  • ✅ v-show
  • ✅ v-html
  • ✅ v-model
  • ✅ @click v-on:click 事件(支持绑定其他事件) @click="fn('a',$event)" @click="fn" @click="show = false" @click="function(){console.log(1)}"
  • ✅ methods 方法
  • ✅ computed 计算属性
  • ✅ watch 监听
  • ✅ beforeCreate、created、beforeMount、mounted、beforeUpdate、updated
  • ✅ :class
  • ✅ :style
  • ✅ $nextTick

补充

虚拟dom 不懂的可以看看我之前发的文章(相关代码相比现在有部分改动):http://www.wclimb.site/2020/03/19/simple-virtual-dom/

编写一个webpack

发表于 2020-04-22 | 分类于 javascript , webpack
| 约 12 分钟

实现功能

  1. 支持 esModule
  2. 支持 import() 异步加载文件
  3. 支持 loader

准备工作

我们需要借助 babel 来解析,先 npm init -y

1
npm i @babel/parser @babel/traverse @babel/core @babel/preset-env -D

最终的文件目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|-- dist // 打包目标文件夹
| |-- 0.bundle.js
| |-- 1.bundle.js
| |-- result.js
|-- src // 项目测试代码
| |-- entry.js
| |-- messgae.js
| |-- name.js
| |-- a.js
| |-- b.js
|-- index.html // 加载文件打包出的文件
|-- app.js // 启动文件
|-- init.js // 打包项目需要的初始化代码
|-- babel-plugin.js // babel插件
|-- loader.js // loader
|-- package.json

阅读全文 »

Nodejs之进程

发表于 2020-04-15 | 分类于 Node , 进程
| 约 22 分钟

前言

本文大部分借鉴自深入浅出Node.js

本文开始讲一下 Node 的进程,Node 是建立在 V8 引擎之上的,程序基本上是运行在单进程的单线程上的,那么这样就会导致机器的资源没有被合理的利用,CPU 使用率低,所以我们需要多开进程,充分利用多核 CPU 服务器,但是多开进程又会面临进程管理方面的问题。下面我们来具体说说。

单线程?

我们知道 JavaScript 是单线程的,Node 也是?,Node 其实不是,Node 并非真正的单线程架构,Node 自身还有 一定的 I/O 线程存在,这些 I/O 线程由底层 libuv 处理,这部分线程对于 JavaScript 开发者而言是透明的,只在 C++ 扩展开发时才会关注到。JavaScript 代码永远运行在 V8 上,是单线程的。

创建多进程

Node 提供了创建进程的方法,child_process,这里我们使用 fork 方法,它还有其他几个方法这里就不作介绍了
master.js

1
2
3
4
5
const fork = require("child_process").fork
const cpus = require('os').cpus();
for (var i = 0; i < cpus.length; i++) {
fork('./worker.js');
}

可以看到我们获取当前机器的 cpu 数量,然后批量创建子进程,我们新建 worker.js,内容如下

1
2
3
4
5
6
7
8
9
const http = require("http");
process.title = "worker";
console.log(process.pid)
http
.createServer(function(req, res) {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello World\n");
})
.listen(Math.round((1 + Math.random()) * 1000);

启动

1
node master.js

控制台打印的pid

1
2
3
4
5
6
7
35115
35116
35117
35118
35119
35120
...

1
2
3
4
5
6
7
8
9
10
11
+--------+
| Master | <------ master.js
+--------+
/ | \
/ | \
/ | \ <----复制
/ | \
v v v
+--------+ +--------+ +---------+
| 工作进程 | | 工作进程 | | 工作进程 | <------ worker.js
+--------+ +--------+ +---------+
阅读全文 »

Nodejs之实现路由和中间件

发表于 2020-04-14 | 分类于 Node , 模版引擎
| 约 9 分钟

前言

今天来实现一个 node 的路由和中间件

铺垫

我们首先起一个服务

1
2
3
4
5
6
7
8
const http = require("http");
const url = require("url");
http
.createServer(function(req, res) {
const pathname = url.parse(req.url).pathname;
res.end(req.method.toLowerCase() + ": " + pathname);
})
.listen(3000);

访问 http://localhost:3000/ 显示 get: /
访问 http://localhost:3000/test 显示 get: /test

最简单的方法就是下面代码判断逻辑,这样做的话太麻烦了,杂糅在一起了,还得进一步判断不同的请求方法,到底是 get 还是 post 还是其他的,重复代码也会增加

1
2
3
4
5
6
7
8
9
10
11
12
switch (pathname) {
case "/":
res.end("root");
break;
case "/test":
res.end("test");
break;
default:
res.writeHead(404);
res.end("404");
break;
}

阅读全文 »

Nodejs之实现一个模版引擎

发表于 2020-04-14 | 分类于 Node , 模版引擎
| 约 6 分钟

前言

最近看完了朴大的 《深入浅出Node.js》 这本书,在里面学到了许多,也推荐大家可以去看一下,看完感觉可以写几篇文章记录总结一下,提升一下印象,毕竟学到的东西感觉不记下来过不久就容易忘记,大家也要养成学习记录的习惯,方便以后重温,今天来实现一个 node 的模版引擎,当然这个模版引擎在前端也可以用。

借鉴ejs标签

标签借鉴 ejs,用过的同学肯定知道,例如使用 <%= name %> 这种标签来展示数据,使用 <% if(name){ %> 这种标签来做 js 操作,如 if 判断/ for循环,比如下面

1
2
3
4
5
<% if (name) { %>
<h1><%=name%></h1>
<% } else { %>
<h1>请登陆</h1>
<% } %>

渲染方法

我们先来看个简单的渲染

阅读全文 »

Nodejs之实现代理

发表于 2020-04-04 | 分类于 Node , 代理
| 约 6 分钟

前言

我们上线一个网站,往往需要代理来完成需求,不然的话就只能使用 IP 加端口的方式来访问应用,目前基本上都会使用 Nginx 来完成反向代理,那么我们开发 Node 应用一定需要 Nginx 吗?肯定不是,我们完全可以通过 Node 来做,但是大多是不建议你用 Node 去处理,因为 Nginx 功能更强大,比如负载均衡,本文主要是帮你了解如何通过 Node 实现代理

使用http-proxy

最简单的做法是拿已经写好的包直接使用

1
$ cnpm i http-proxy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const proxy = require("http-proxy").createProxyServer({});
server = require("http").createServer(function(req, res) {
const host = req.headers.host;
switch (host) {
case "your domain":
proxy.web(req, res, { target: "http://localhost:8000" });
break;
default:
res.writeHead(200, {
"Content-Type": "text/plain"
});
res.end("Welcome to my server!");
}
});
console.log("listening on port 80");
server.listen(80);

上面👆代码监听 80 端口(如果你的服务器目前使用来 Nginx,暂用80 端口的话,需要先暂停 Nginx 服务),然后我们通过访问域名(前提是域名做好了解析),然后使用 proxy.web方法反向代理到当前服务下的 8000 端口,到此一个简单的服务完成了

阅读全文 »

Vue源码之nextTick

发表于 2020-03-25 | 分类于 javascript , vue
| 约 4 分钟

前言

今天我们开始讲一下 Vue 的 nextTick 方法的实现,无论是源码还是开发的过程中,经常需要使用到 nextTick,Vue 在更新 DOM 时是异步执行的,只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环 “tick” 中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

阅读全文 »

撸一个简易Virtual DOM

发表于 2020-03-19 | 分类于 javascript , vue
| 约 8 分钟

前言

上一篇我们讲了一下 Vue 的 虚拟DOM,从创建到更新整个流程。今天带大家撸一个简易的虚拟DOM,本文的大部分借鉴 Vue 源码

浏览器渲染流程

摘自浏览器工作原理与实践

  1. 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
  3. 创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树。
  5. 为每个图层生成绘制列表,并将其提交到合成线程。
  6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  7. 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
  8. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

准备

首先我们要明白什么是虚拟DOM,为什么要用到它,虚拟DOM 是使用 javascript 对象来描述 DOM 树,一切的更新修改都是在更改这个对象,然后反应到真实的 DOM 下。要说为什么要用到它,肯定是因为性能好,因为直接操作 DOM 的代价是很大的,比如,一次操作中有 10 次更新 DOM 的动作,虚拟DOM 不会立即操作 DOM,而是将这 10 次更新的 diff 内容保存到本地一个 js 对象中,最终将这个 js 对象一次性 反应到 DOM 树上,再进行后续操作,避免大量无谓的计算。也有人反驳说,使用 虚拟DOM 比起操作真实 DOM 要慢,的确如此,使用 虚拟DOM 确实没有原生操作快,但是既然使用了框架,优化框架都会帮你做,你不用自己去手动做DOM的优化,不用处处去考虑操作 DOM 带来的性能问题,使用 虚拟DOM 可以让性能得到有力的保证。

可以参考知乎问题 网上都说操作真实 DOM 慢,但测试结果却比 React 更快,为什么?

实现

使用js对象模拟DOM树

这里我们不使用 Vue 的那种根据 template 生成 虚拟DOM 的方法,那样太复杂了,这里只讲简单的方法,我们直接使用 js 对象来描述 DOM

比如如下 的html 代码

1
2
3
4
5
6
7
8
<div id="app">
<h1 data-title="header">Virtual Dom</h1>
<ul class="ul1">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>

阅读全文 »

Vue源码之虚拟DOM

发表于 2020-03-17 | 分类于 javascript , vue
| 约 20 分钟

前言

上一篇我们讲了一下 Vue 的双向数据绑定原理,今天我们开始讲一下 Vue 的 虚拟DOM。
本文会先分析一下 Vue 的 虚拟DOM,然后下一篇文章会带大家撸一个简易的 虚拟DOM

Vue虚拟DOM

创建虚拟DOM

https://github.com/wclimb/vue/blob/dev/src/core/vdom/vnode.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
functionalContext: Component | void; // only for functional component root nodes
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions
) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.functionalContext = undefined
this.key = data && data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}

以上代码是Vue创建 虚拟DOM 的类,一眼看过去是不是感觉东西太多了?其实我们主要关注主要的几个参数就可以了,tag 、data、children、text、elm、key

  • tag 表示当前 vnode 的标签类型,比如 div ul
  • data 表示当前 vnode 标签上的 attribute,可能是class、id、key
  • children 表示当前 vnode 的子节点
  • text 表示文本内容
  • elm 表示当前 vnode 的真实 DOM 节点
  • key diff算法 需要用到,就是我们开发中写的 :key
阅读全文 »

Vue源码之双向数据绑定

发表于 2020-03-15 | 分类于 javascript , vue
| 约 17 分钟

前言

近一年多都在做小程序开发,Vue 感觉都有写些生疏了,从今天开始阅读一下 Vue 的源码,了解其内部的工作机制,本文涉及的 Vue 版本为 2.6.11,我已经提前 fork 了一份到 github 上

双向数据绑定

提到 Vue,自然会想到双向数据绑定,要说他的原理,你也能脱口而出,使用 Object.defineProperty 的 get、set来实现,但要把功能做更强大健壮,往往并不是这么简单。Vue的双向数据绑定由以下几个部分组成

  1. Obserber监听器:负责数据的劫持调用Object.defineProperty来实现监听效果,get负责收集依赖,set负责派发更新
  2. Dep订阅器:负责订阅者的收集,收集依赖
  3. Watcher订阅者:负责更新视图

从入口开始

https://github.com/wclimb/vue/blob/dev/src/core/instance/init.js#L15

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
......
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
......
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}

从上面可以看出初始化 Vue 会调用多个函数来做不同的事情,本文主要讲解双向数据绑定,所以其他的在这里都不重要,这里我们着重关注 initState 内部方法

阅读全文 »
12…4
wclimb

wclimb

40 日志
18 分类
21 标签
GitHub QQ群 小程序 公众号
Links
  • Lsnsh
  • curtain Tan
  • eraylee
  • flutter体验
  • 技术鹏
  • 苏博客
  • Blog Luo
© 2017 - 2021 wclimb
赣ICP备 17009321号
访问人数 人次