2025-04-12 23:21:56
摘要:一、实现DOM操作API
1. 构建环境
创建runtime-dom目录,存放dom操作api
创建runtime-core目录,存放允许时虚拟dom操作核心代码
在两个目录中分别构建环境
/packages/runtime-dom/package.json
{
name: @vue/runtime-dom,
module: dist/runtime-dom.esm-bundler.js,
unpkg: dist/runtime-dom.global.js,
buildOptions: {
name: VueRuntimeDOM,
formats: [
esm-bundler,
cjs,
global
]
}
}
/packages/runtime-core/package.json
{
name: @vue/runtime-core,
module: dist/runtime-core.esm-bundler.js,
unpkg: dist/runtime-core.global.js,
buildOptions: {
name: VueRuntimeCore,
formats: [
esm-bundler,
cjs,
global
]
}
}
2. 定义操作API
/packages/runtime-dom/src/nodeOps.ts
// dom操作 vue虚拟dom, 通过数据对象在内存中对比差异, 找到最小的改动点,使用dom操作完成更新。
// 原生dom操作api
export const nodeOps = {
// 插入, 追加
insert: (child, parent, anchor = null) = {
parent.insertBefore(child, anchor); // parent.appendChild(child)
},
// 删除子节点
remove: child = {
const parent = child.parentNode;
if (paren……
阅读全文
2025-04-05 18:21:43
摘要:Vue3响应式的实现跟Vue2最大的不同点是Vue3中的响应式是个独立模块,可以单独拿出来使用。
不仅仅响应式是模块化的,Vue3的设计理念也是基于模块化来设计的。而要使用Vue2的响应式,就必须加载整个Vue2的库。
一、rollup环境搭建
我们使用Vue3搭建上面的时候,经常会用到Vite,而Vite实际上是基于rollup实现的。
Rollup 是一个 JavaScript 模块打包工具,它可以将多个 JavaScript 文件(模块)打包成一个或多个优化后的文件。与 Webpack 类似,但 Rollup 更专注于 ES6 模块的打包,通常用于库/框架的开发。
除了rollup外,esbuild也是一个很不错的前端构建工具。
rollup
类库打包工具, 专注于模块的tree-shaking, 尽量减少模块打包的大小。
拥有庞大的插件生态系统,如代码拆分,语法转换等
常用于构建library, 特别是专注于es6模块
前端工程打包,一般使用webpack
esbuild
主要特点速度很快,用go编写,可以并行处理。
对ast语法树操作能力不太强, 老项目升级用esbuild
开发使用esbuild构建编译,生产打包使用成熟的rollup
接下来我们使用先使用rollup搭建一个本地环境
环境搭建
新建一个vue3的文件夹,然后安装rollup
yarn add rollup --dev
目录搭建
--packages # 因为Vue3是基于模块化来设计的,我们设计包文件的时候也分开构建,所以packages目录下都有会自己的项目文件
--reactivity # 响应式实现包
--dist
--node_modules
--src
index.ts
package.json
pnpm-lock.yaml
--shared # 公共库
--dist
--src
index.ts
package.json
--scripts
build.js
dev.js
.npmrc
package.json
pnpm-lock.yaml
rollup.config.js
tsconfig.json
基本配置
rollup.config.js
i……
阅读全文
2025-03-29 16:26:46
摘要:阅读vue(2.6)的源码,data响应式大概有如下4个过程:
数据代理:core/instance/state.js---vue函数---initMixin---initState---数据遍历---observer/index.js---Observer--- defineReactive---defineProperty
数据装载:$mount---装载template---compileToFunctions---mountComponet---创建【渲染Watcher】---Watcher.get()---Watcher.get()依赖收集---Dep.target就是当前的Watcher(data中的属性—dep-----watcher 形成双向引用)
数据相应式:需要修改data属性---触发setter回调执行---通知dep上的watcher更新dep.notify()---遍历所有的watcher并调用watcher的update方法---调用run方法---调用get()方法实现页面渲染
数据渲染:vm.__patch__---createPatchFunction---patchVnode---双方都有孩子节点updateChildrendiff代理
以下我们参考vue的源码,记录实现data响应式的全过程
一、数据劫持
收先我们混入一个方法,给Vue对象添加一个内部_init方法,我们在这个内部方法中去实现需要的功能
//index.js
import { initMixin } from ./init;
function Vue(options){ // vue2 配置项
console.log(my vue 6666)
this._init(options); // vue 内部约定 私有的方法或属性 一般是以 $ _ 开头
}
initMixin(Vue); // 一执行,Vue就有_init方法
export default Vue
//init.js 初始化
import { initState } from ./state;
// 混入 类似于 对象语言的继承
// 在不改变这个函数的前提下,给这个函数增加方法 增强功能
// 原理是在prototype上添加方……
阅读全文
2025-03-22 14:44:23
摘要:一、Vue3项目搭建
Node.js:是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O 模型,让JavaScript 运行在服务端的开发平台; 版本要求=12.0.0;
npm:Nodejs下的包管理器;版本要求 6.x yarn:包管理器;
yarn 包管理器是 npm 的一个替代方案,由Facebook于2016年10月发布。
Vite:是一个 web 开发构建工具,使用 Vite 可以快速构建 VUE 项目比webpack打包更加快速
快速的冷启动
即时的模块热更新
真正的按需编译
创建vite3项目:npm init vite,输入项目名称
安装依赖
cd vite-project 进入项目
依赖安装
npm install 或 cnmp i
运行vite项目:npm run dev
访问:http://localhost:5173
包管理工具Yarn的使用
yarn create vite:输入项目名称
cd vite-d2
安装依赖:yarn
项目启动:yarn dev
二、Vue3.2新特性
1. createApp
在 Vue 3 中,改变全局 Vue 行为的 API 现在被移动到了由新的 createApp 方法所创建的应用实例上。
import { createApp } from 'vue'
const app = createApp({})
该实例提供了一个应用上下文,由于 createApp 方法返回应用实例本身,因此可以在其后链式调用其它方法
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
const app = createApp(App);
app.use(store)
app.use(router)
app.mount('#app');
//合并之后的代码:
createApp(App).use(store).use(router).mount('#app')
2. setup函数
setup函数是vue3中专门为组件提供的新属性。
创建组件实例,然后初……
阅读全文
2025-03-15 10:19:14
摘要:TypeScript 是一种由微软开发的自由开源的编程语言,他是JavaScript的一个超集,扩展了JavaScript的 语法,主要提供了类型系统和对 ES6 的支持。
一、类型定义
window.onload=function(){
//类型系统
let flag:boolean = false; //布尔类型
let num:number = 15; //数值类型
let str:string = 'abc'; //字符串类型
let str2:string=`hello,${str}`;
let msg:string = `hello,${str},${num}`;
let u: undefined = undefined;
let n: null = null;
//为什么要TS
// function sum(n1:number,n2:number){
// return n1+n2
// };
// sum(10,20);
// sum('10',20);
let count:number = 10;
count = 100;
//声明的变量有多种类型——联合类型
let id:number | string = 10;
id = '111';
//任意类型 :any 万能类型
let x:any = true;
x = 111;
x = 'abc';
//引用类型
//数组
let arr:number[] = [1,2,3,4,5];
//最简单的方法是使用「类型 + 方括号」来表示数组: number[]
let arr2:string[] = ['a','b','hello'];
let arr3:(number|string)[] = ['a',100,'hello'];
let arr4:any[] = ['a',100,'hello'];
//数组泛型 Array类型
let arr5:Arraynumber= [1,2,3,4,5];
let ……
阅读全文
2025-03-08 15:14:23
摘要:一、let与const
let用来声明变量,它的用法类似于 var
不存在变量提升
同一个作用域内不能重复定义同一个名称
有着严格的作用域
const 声明一个只读的常量。一旦声明,常量的值就不能改变。
保持let的不存在变量提升,同一个作用域内不能重复定义同一个名称,有着严格的作用域
const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动
window.onload = function () {
// let 和var的不同
// 1、不存在变量提升
// console.log(a);
// var a = 100;
// //解析过程
// //预解析
// var a;
// //逐行解析
// console.log(a);
// a = 100;
// //
// console.log(a); //Cannot access 'a' before initialization
// let a = 100;
//2、同一个作用域内不能重复定义同一个名称
// var a = 10;
// var a = 100;
// console.log(a); //100
// var a = 10;
// let a = 100;
// console.log(a); //Identifier 'a' has already been declared
// let a = 10;
// let a = 100;
// let a = 10;
// a = 100;
// console.log(a); //100
//3、有着严格的作用域
// function fn(){
// var a = 'a';
// if(true){ //块级作用域
// let a = 'b'
// };
// console.log(a) //'a'
// };
// fn();
……
阅读全文
2025-03-01 16:56:47
摘要:一、JS数据结构
js的数据定义与检查
var str = 'abc';
var num = 123;
var bool = true;
var und = undefined;
var n = null;
var arr=['x','y','z'];
var obj = {};
var fun = function() {};
console.log(typeof str); //string
console.log(typeof num); //number
console.log(typeof bool); //boolean
console.log(typeof und); //undefined
console.log(typeof n); //object
console.log(typeof arr); //object
console.log(typeof obj); //object
console.log(typeof fun); //function
1. 字符串
window.onload = function () {
// 字符串常用的属性和方法
// charAt() 方法可返回指定位置的字符。
// concat() 方法用于连接两个或多个字符串。
// indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
// lastIndexOf() 方法可返回一个指定的字符串值最后出现的位置
// includes() 方法用于判断字符串是否包含指定的子字符串
// replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串
// split() 方法用于把一个字符串分割成字符串数组
// substr() 方法可在字符串中抽取从开始下标开始的指定数目的字符
// substring() 方法用于提取字符串中介于两个指定下标之间的字符
// slice(start, end) 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分
// toLowerCase() 方法用于把字符串转换为小写
// toUpperCase() 方法用于把字……
阅读全文
2008-08-30 14:31:19
摘要:在上一篇的示例中,我们定义了两个语法甘露,一个是Class()函数,一个是New()函数。使用Class()甘露,我们已经可以用非常优雅的格式定义一个类。例如前例中的:
var Employee = Class(Person, //派生至Person类
{
Create: function(name, age, salary)
{
Person.Create.call(this, name, age); //调用基类的构造函数
this.salary = salary;
},
ShowMeTheMoney: function()
{
alert(this.name + $ + this.salary);
}
});
这种类的写法已经和C#或Java的格式非常相似了。不过,其中调用基类的构造函数还需要用“Person.Create.call(this, name, age)”这样的方式来表达。这需要用到基类的类名,并要用call这种特殊的方式来传递this指针。这和C#的base()以及Java的super()那样的简介调用方式比起来,还需要进一步美化。
而New()函数的使用也不是很爽。前例中需要用“New(Employee, [Steve Jobs, 53, 1234])”这样的方式来创建对象,其中第一个参数是类,其他构造参数需要用数组包起来。这和JavaScript本来那种自然的“new Employee(Steve Jobs, 53, 1234)”比起来,丑陋多了。这也需要美化。
为了实现这些美化工作,我们需要回顾一下new一个对象的实质。前面我们说过:
var anObj = new aClass();
相当于先创建一个空白对象anObj,然后将其作为this指针调用aClass()函数。其实,这个过程中还有一个关键步骤就是将aClass的prototype属性,赋值给anObj内置的prototype属性。尽管我们无法访问到anObj内置的prototype属性,但它却为对象提供了可以调用的方法。
由于前例中的Class()语法甘露实际上是构造了一个原型,并将这个原型挂在了相应的原型链上。由于它返回的是一个对象而不是函数,因此由它定义出来的Per……
阅读全文
2008-08-28 11:10:18
摘要:正当我们感概万分时,天空中一道红光闪过,祥云中出现了观音菩萨。只见她手持玉净瓶,轻拂翠柳枝,洒下几滴甘露,顿时让JavaScript又添新的灵气。
观音洒下的甘露在JavaScript的世界里凝结成块,成为了一种称为“语法甘露”的东西。这种语法甘露可以让我们编写的代码看起来更象对象语言。
要想知道这“语法甘露”为何物,就请君侧耳细听。
在理解这些语法甘露之前,我们需要重新再回顾一下JavaScript构造对象的过程。
我们已经知道,用 var anObject = new aFunction() 形式创建对象的过程实际上可以分为三步:第一步是建立一个新对象;第二步将该对象内置的原型对象设置为构造函数prototype引用的那个原型对象;第三步就是将该对象作为this参数调用构造函数,完成成员设置等初始化工作。对象建立之后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关,与构造函数再扯不上关系了。换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用。
那么,我们能否自己定义一个对象来当作原型,并在这个原型上描述类,然后将这个原型设置给新创建的对象,将其当作对象的类呢?我们又能否将这个原型中的一个方法当作构造函数,去初始化新建的对象呢?例如,我们定义这样一个原型对象:
var Person = //定义一个对象来作为原型类
{
Create: function(name, age) //这个当构造函数
{
this.name = name;
this.age = age;
},
SayHello: function() //定义方法
{
alert(Hello, I'm + this.name);
},
HowOld: function() //定义方法
{
alert(this.name + is + this.age + years old.);
}
};
这个JSON形式的写法多么象一个C#的类啊!既有构造函数,又有各种方法。如果可以用某种形式来创建对象,并将对象的内置的原型设置为上面这个“类”对象,不就相当于创建该类的对象了吗?
但遗憾的是,我们几乎不能访问到对象内置的原型……
阅读全文
2008-08-26 23:29:04
摘要:想必君的悟性极高,可能你会这样想:如果在JavaScript内置的那些如Object和Function等函数的prototype上添加些新的方法和属性,是不是就能扩展JavaScript的功能呢?
那么,恭喜你,你得到了!
在AJAX技术迅猛发展的今天,许多成功的AJAX项目的JavaScript运行库都大量扩展了内置函数的prototype功能。比如微软的ASP.NET AJAX,就给这些内置函数及其prototype添加了大量的新特性,从而增强了JavaScript的功能。
我们来看一段摘自MicrosoftAjax.debug.js中的代码:
String.prototype.trim = function String$trim() {
if (arguments.length !== 0) throw Error.parameterCount();
return this.replace(/^\s+|\s+$/g, '');
}
这段代码就是给内置String函数的prototype扩展了一个trim方法,于是所有的String类对象都有了trim方法了。有了这个扩展,今后要去除字符串两段的空白,就不用再分别处理了,因为任何字符串都有了这个扩展功能,只要调用即可,真的很方便。
当然,几乎很少有人去给Object的prototype添加方法,因为那会影响到所有的对象,除非在你的架构中这种方法的确是所有对象都需要的。
前两年,微软在设计AJAX类库的初期,用了一种被称为“闭包”(closure)的技术来模拟“类”。其大致模型如下:
function Person(firstName, lastName, age)
{
//私有变量:
var _firstName = firstName;
var _lastName = lastName;
//公共变量:
this.age = age;
//方法:
this.getName = function()
{
return(firstName + + lastName);
};
this.SayHello = function()
{
alert(Hello, I'm ……
阅读全文