我以为已经会了,之前在公司写项目基本都是用sass写样式,十分顺手。直到有段时间,我准备参考Element Plus来设计自己组件库的工程结构,看到Element Plus那优雅高级的sass用法时,我开始为我的浅薄和无知感到羞愧。这便开始系统学习sass,期间多次想写点学习分享,却都因各种事情而无疾而终。时至今日,终于忙里偷闲有点时间,正好与君共勉。 一、简介作为一款极其成熟的css预处理器,sass具有不少特色功能,帮助我们写出更为优雅、简洁的样式代码。它在css之前引入了样式变量,还支持嵌套、函数、混合、指令控制等功能,极大地拓展了样式玩法。 sass有两种语法格式:SCSS和Sass。 其中SCSS是目前使用较多的一种格式,它仅在CSS3的语法基础上进行拓展,加入了Sass的特有功能,支持绝大多数的CSS hacks写法以及浏览器前缀写法(vendor-specific syntax),它的文件以.scss作为拓展名。 而Sass是最早的语法格式,用缩进代替花括号,用换行代替分号,格式尤为简洁,书写也更加方便。这种格式也支持Sass的所有功能,不过在个别地方与SCSS采用了不同 ...
作为一个强大的多功能JS转译器,Babel有许许多多的模块可用于静态分析。而在项目中,有时候我们也需要创建一个符合项目需求的Babel插件。本文旨在帮助大家深入理解Babel插件工作流程的同时,让大家掌握如何按需求编写自己的Babel插件。 一、基础知识1. Babel对代码处理分为三个步骤:解析(parse)、转换(transform)、生成(generate) 。 2. 解析在 解析 这一步,Babel接收代码,处理后生成 AST 。该步骤分为 词法分析(Lexical Analysis) 和 语法分析(Syntactic Analysis) 两个阶段。 2.1 词法分析Babel接收的源代码为代码字符串,词法分析会把字符串代码转化为 令牌(tokens) 流,令牌是对代码词法的描述。可以看作一个扁平的语法片段数组。例如,对于以下语法片段: 1n * n; 将被转化为如下令牌,可以看到,除了具有type和value属性以外,令牌还和AST一样具有start, end, loc属性。 123456[ { type: { ... }, value: &q ...
我们知道,ES6+语法目前尚未被各大浏览器全面支持,且ES语法新特性也在逐年增加。若想在项目中愉快地使用较新版本的ES语法,那就需要工具来对我们的代码里的语法进行 “降级” 处理。这里附上Babel 官网。依照官网的介绍,Babel便是这么一个我们需要的工具链,能够将使用ES6+语法编写的代码转换为向后兼容的ES语法。除了语法转换,Babel还能以Polyfill的方式向目标环境中添加特性以及完成源码转换等功能。 一、基本使用首先要明确的是,Babel是一个工具链,由许许多多的工具组成。这里简要介绍一下Babel工具的使用,使得我们的ES6+语法的代码被处理成能够在浏览器上运行的代码。 1. Babel 初体验1.1 安装依赖1npm install --save-dev @babel/core @babel/cli @babel/preset-env 1.2 编写配置文件在项目的根目录下创建babel.config.json文件(Babel版本>= v7.8.0),并编写(复制)以下内容: 1234567891011121314151617{ "prese ...
前面几篇走完了createApp的流程,理清了diff算法的思路。现在回归到运行时的核心API上。在第一篇和第二篇中有解读过watch和computed,而本文则主要梳理异步组件的API。 一、defineAsyncComponent用于定义异步组件。 1. defineAsyncComponent入参source,可以是一个异步函数loader,也可以是一个包含有异步函数loader的对象options。当source为options时可以进行更细致的自定义,如推迟时间、异常处理、异常兜底组件、加载组件等。由于import()动态加载得到的是一个Promise,因此,loader常用来结合import()引入单文件组件来构成异步组件。 1234567891011121314export interface AsyncComponentOptions<T = any> { loader: AsyncComponentLoader<T>; loadingComponent?: Component; errorComponent?: Componen ...
上一篇中,我们理清了createApp走的流程,最后通过createAppAPI创建了app。虽然app上的各种属性和方法也都已经有所了解,但其中的mount和unmount方法,都是通过调用render函数来完成的。尽管我们很好奇render函数的故事,可是baseCreateRenderer函数有2000+行,基本都和render相关,因此拆解到本文里叙述,以下方法都定义在baseCreateRenderer函数中。 一、renderrender也不神秘了,毕竟在上一篇文章中露过面,当然这里也顺带提一下 baseCreateRenderer从参数options中解构的一些方法,基本都是些增删改查、复制节点的功能,见名知义了。主要看看render,接收vnode、container、isSvg三个参数。调用unmount卸载或者调用patch进行节点比较从而继续下一步。 判断vnode是否为null。如果对上一篇文章还有印象,那么就会知道,相当于是判断调用的是app.mount还是app.unmount方法,因为app.unmount方法传入的vnode就是null。那么这里对应的 ...
前面几篇介绍的大都是reactivity相关的API。我们在使用Vue3作为前端框架时,往往在我们的main.js/main.ts里来创建vue3的app实例,就会用到createApp这个API。本篇就来简要了解一下createApp里发生的故事。 一、相关 ts 类型可以先瞄一眼与createAppApi相关的ts类型,这样就更能理解它的使用,这里挑几个简要介绍一下。 1. AppApp是createApp返回值的类型,可以看到项目里常用的一些方法都在这里,某些方法返回了this,则可以链式调用。此外,还兼容了vue2的filter,还有一些内部的属性。 12345678910111213141516171819202122232425262728293031323334353637export interface App<HostElement = any> { version: string; // config 上有常用到的 globalProperties config: AppConfig; use(plugin: Plugin, ...op ...
KeepAlive是个抽象组件,自身不会渲染一个 DOM 元素,也不会出现在父组件链中,我们用它来缓存组件的状态。KeepAlive只对插入的单个组件起效果,因此一般只给它安排一个组件。适合与component或router-view搭配使用。 一、ts 类型先来和KeepAlive相关的类型: MatchPattern:匹配模式,是传递的参数include和exclude接收的类型; KeepAliveProps:可传递三个参数,include指定被缓存的组件,exclude指定不缓存的组件,max指定最大缓存组件数量; Cache:变量cache的类型,cache用于缓存组件; Keys:变量keys的类型,keys用于存储被缓存组件对应的key,用于LRU算法; KeepAliveContext:继承自ComponentRenderContext,并拓展了renderer,activate,deactivate三个字段。 1234567891011121314151617181920212223type MatchPattern = string | RegExp | (st ...
前面几篇文章里,介绍几个API的时候,我们发现里面常出现effect、track和trigger,虽然简单说了下track用于依赖收集,trigger来触发更新。但是毕竟没看到具体实现,心里没底。如今便可以一探究竟。 一、ReactiveEffect1. 相关的全局变量之前提到的effect,便是ReactiveEffect的实例。用到了一些重要的全局变量。 targetMap:弱映射,以目标对象target为key,其收集到的依赖集depsMap为值,因此通过目标对象target可以获取到对应的所有依赖; activeEffect:当前活跃的effect,随后会被收集起来; shouldTrack:用作暂停和恢复依赖收集的标志; trackStack:历史shouldTrack的记录栈。 targetMap对比reactive篇章中提到的proxyMap: 两者都是弱映射; 都以目标对象target为key; targetMap全局只有一个;而proxyMap有四种,分别对应reactive、shallowReactive、readonly、shallowReadonly; 一 ...
我们知道,一般用reactive来定义一个响应式对象,ref常用来定义一个响应式的原始值。上篇文章已经聊过了reactive,知晓了如何通过Proxy来对目标对象进行代理从而实现响应式,而非对象的这些原始值的响应式问题就交给ref来解决了。 一、ref 和 shallowRef的函数签名ref和shallowRef各有三种重载,入参各不相同,都返回一个Ref/ShallowRef类型的值。通过createRef函数创建一个响应式的值。和 reactive 相似,reactive也是通过调用createReactiveObject来创建一个响应式的对象。而createRef创建并返回一个 RefImpl 实例。 1234567891011121314151617181920212223242526272829// refexport function ref<T extends object>( value: T): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>;export function ref<T& ...
上次一起阅读了watch和computed的源码,其实应该先看副作用effect,因为各个响应式的API里基本都用到了,等结束了reactive和readonly和ref,就一起看看effect。这次要说的是reactive和readonly,两者在实现上流程大体一致。尤其是对Map和Set的方法的代理拦截,多少有点妙。 一、reactive 和 readonlyVue3使用Proxy来替代Vue2中Object.defineProperty。 12345678910111213const target = { name: "onlyy~",};// 创建一个对target的代理const proxy = new Proxy(target, { // ...各种handler,例如get,set... get(target, property, receiver) { // 其它操作 // ... return Reflect.get(target, property, receiver); ...