前言
Vue3正式版发布没多久,当初赶上公司在拆分项目,很多模块都被拆分成了单独的子项目,刚好由我来负责项目的技术选型和搭建,之前的项目团队都是Vue2
来开发的,Vue2
的一些不足就不多说了,本来新项目想直接上react
,边开发边学习的,但是考虑到了后边很可能有其他同事来接手,如果都是react
代码,学习的成本就会较大,刚好Vue3
也发正式版了,索性就直接上Vue3
,如果只是写写业务的话,Vue2
转Vue3
,基本上看看文档几天就能够上手直接一把梭。
Vue3
用了也有一段时间了,但是对于其原理和一些新玩法新花样,还是没有完全吃透,就像问:Vue2
和Vue3
响应式原理有啥不同?答:Vue2
用的是Object.defineProperty
,Vue3
用的是Proxy
,然后就没有然后了,完全经不住深挖。之前也看过Vue2
的原理,经过这小两年时间的洗礼,基本上已经忘了个七七八八,所以趁着现在正在写重学前端这一系列文章,那么Vue3
原理这块,也打算尽早提上日程,和之前的那些文章一样,我依旧会用最适合小白的文字,来慢慢讲解,同时对我来说,每次写一篇文章都是一次新的学习,那我们就开始吧!
学完本篇文章你能收获到
- 什么是
Monorepo
; pnpm
的基本概念以及简单用法,使用pnpm
搭建Monorepo
环境;Vue3
组成的简介,实现自己的Vue3
的搭建;
Vue3设计思想的变化
我们先来了解一下Vue3
和Vue2
在哪些设计方面有了很大的变化:
Vue3
将很多功能都设计成了单独的模块,比如可以直接import { ref, reactive } from 'vue'
使用响应式的方法,模块间耦合度低,可以独立使用;而Vue2
没办法单独使用部分模块,就算只用到了响应式的部分,也只能引入完整的Vuejs
。Vue2
很多的方法,都是直接挂载到vm
也就是实例上了,导致没使用的这些方法,也会被打包进最终的打包文件中;Vue3
中的功能,因为进行了模块拆分,都是函数式API
,所以打包的时候利用Tree-shaking
机制,做到了按需引入,有效的减少打包的体积。Vue3
可以自定义渲染器,增强了扩展能力,暴露了很多的方法,可以进行自定义逻辑;而在一些跨平台的框架中比如小程序,如果想使用Vue2
作为技术栈,则需要在Vue2
的源码基础上,改动源码的逻辑,才能进行打包,这相当于破坏了源码,随着更新也会出现一些问题。
搭建开发环境
一上来如果就带着大家去看源码,相信没有多少人能够接收,直接就把文章pass掉了,所以,我们可以先自己简单实现Vue3
的原理,实现一个简版的Vue3
,之后我们再去调试源码,再去看,就会变得没那么困难和难以接受了。我们先搭建一个开发环境。我们要使用Monorepo
的方式来搭建整个项目,那什么是Monorepo
呢?
Monorepo
是目前很多大型开源项目,管理代码的一个方式,就是在一个git
项目仓库中管理多个模块或者工具包。Vue3
的源码就是采用这种方式,将模块拆分到package
目录中,那么好处就是:
- 一个仓库可维护多个模块或工具包,不用到处找各自的仓库。
- 方便每个模块的版本管理和依赖管理,模块之间的引用和调用变的十分方便。
我们使用pnpm
这个工具,来搭建Monorepo
环境。简单说下pnpm
是个啥,其实就是个包管理工具,特点就是非常快,并且能节省磁盘空间。大体的使用方法和npm
没啥区别,具体细节可以查看官方文档,我们这里直接用,不来细说。
上文说到Vue3
采用的是Monorepo
这种方式,所以,我们只需要大致写一下Vue3
中包含的各种包,就能实现一个简单版本的Vue3
了。常见的包有:
reactivity
:响应式系统compiler-core
:编译核心compiler-dom
:针对浏览器的编译模块……
生成项目的基本架构
首先,如果没有安装过pnpm
,需要全局安装,这里使用npm install pnpm -g
这个方式来安装;之后mkdir vue3-source-code
来创建文件夹,使用pnpm init -y
命令,初始化package.json
文件。之后我们创建如下所示的目录结构:
1 | vue3-source-code |
接下来,我们开始完善配置项和一些调试代码,让项目能够跑的通: 在项目根目录,使用tsc --init
命令,来初始化tsconfig.json
文件,如果没有安装过tsc
,需要先全局执行npm install typescript -g
命令。我们直接把以下配置填写在tsconfig.json
文件中。
1 | // ts.config.json文件 |
心细的朋友可能发现,最后两个配置项没有注释,我们一会再来解释这两个配置项的作用。
1 | // .npmrc文件 |
这个配置项非常有意思,我们来解释一下这个配置项是啥意思呢?那么这里不得不提及一下npm
在安装依赖时候的特征了:那就是会将依赖拍平在node_modules文件夹中,而pnpm
在安装依赖之后,则不会将依赖拍平在node_modules
文件夹中。举个栗子🌰,在一个空白项目中,如果我们使用了npm install webpack
命令,那么当你打开node_modules
文件夹的时候,会发现安装了一大堆依赖,此时我们在项目中使用require('express')
,发现依旧不会报错,因为在安装webpack
的时候,也用到了express
这个依赖,而且都拍平在node_modules
文件夹下了,所以在项目中require('express')
是能够找到,而且不会报错的;但果我们使用pnpm install webpack
的话,此时再打开node_modules
文件,会发现少了很多东西,观察目录结构会发现,其实依赖都被放在了.pnpm
这个文件夹下,此时如果我们直接require('express')
,则就会报错,因为node_modules
目录下,根本不存在express
模块。那么,在.npmrc
文件中加入了shamefully-hoist = true
这个配置项,就能够将.pnpm
中的依赖,拍平在node_modules
文件夹中,达到的效果就和npm
很类似了。
接下来cd
进入shared
目录,使用pnpm init -y
命令初始化,并将package.json
文件中配置项改为"name": "@vue/shared" ......
。
1 | // shared/src/index.ts 文件中,我们先写一个判断是否为数组的方法,并将其导出 |
同样的方法,cd
进入reactivity
目录下,使用pnpm init -y
命令初始化,并将package.json
文件中配置项改为"name": "@vue/reactivity" ......
。接下来,如果我们想在reactivity/src/index.ts
文件中,使用shared
包中暴露出来的那个isArray
方法,那么应该如何引入呢?首先想到的就是我直接import { isArray } from '../../shared/src/index.ts'
不就完了么,相对路径一把梭,乍看一眼没啥问题,但是稍微一想,像shared,reactivity
这种包,最后发布可是要打包完后,单独发布到npm
上边的,这时候使用相对路径,那肯定就不太合适了吧。有朋友又会说了,那直接用import { isArray } from '@vue/shared'
来导入不就好了么?没错,但是如果不进行任何配置,这种写法是去哪里找@vue/shared
的这个包呢?node_modules
目录中,那node_modules
目录中没有这个shared
包啊,该怎么办呢?聪明的朋友已经还记得,上文我们在配置tsconfig.json
文件的时候,埋下了一个伏笔。没错,就是最后两个配置项。
1 | "baseUrl": "./", |
首先,baseUrl
可以将根路径定位在当项目的根目录。其次paths
可以自定义寻找包的路径,比如上边配置的意思就是,只要import
了以@vue/*
开头的包,那么就会去packages文件夹下的*/src
目录下寻找。所以加上了这个配置项,我们在reactiviey/src/index.ts
文件中,就可以正常的导入shared
模块了。
1 | // reactiviey/src/index.ts |
pnpm-workspace.yaml
文件中,我们先填写如下内容,代表packages
文件夹下所有的目录,都当做包来管理。
1 | packages: |
至此,一个简单的Monorepo
环境就已经搭建好啦~,那么有朋友可能会问了,这咋跑起来?我们写一个库肯定得边写边调试的吧,都看不到效果,怎么知道写的有没有问题呢?别急,我们继续往下走。
编写脚本进行开发环境打包
对于开发环境,我们使用esbuild
包进行打包,对于生产环境,我们使用rollup
进行打包。 首先在项目根目录先安装包pnpm intall esbuild -D -w
,之所以加上-w
是为了能够让依赖成功安装在项目根目录,不然就会报错。我们先把入口写成固定为reactivity/src/index.ts
。具体的配置,可以查看esbuild
的官方文档,下边就直接写上需要的配置项,并简单做下注释解释。
1 | // scripts/dev.js 文件 |
在package.json
文件中添加一个命令,进行打包。
1 | // package.json |
之后,执行npm run dev
命令,可以看到,在reactivity
文件夹下,生成了reactivity.js
和reactivity.js.map
两个文件,我们打开reactivity.js
文件,可以看到,打包结果为:
1 | // packages/shared/src/index.ts |
那么此时js
文件就已经被成功的打包了,在dist
目录下,我们新建个index.html
文件,看看刚才打包的结果,能不能在页面上用:
1 | <!-- reactivity/dist/index.html --> |
特别要注意的是,<script>
标签要加上type="module"
,因为我们是通过esModule
的方式进行导入导出的。此时我们要启动一个本地服务器,来查看index.html
文件,这里方式很多,我推荐使用一条命令,能够直接启动一个本地服务器,并且不需要安装任何东西。我们cd
进入到reactivity
目录下,执行npx serve dist
命令(要保证npm版本≥5.2才能够使用npx),这套命令就是将dist文件夹作为服务器根目录,然后将index.html
文件默认作为主文件入口进行展示,执行完毕后,可以看到默认的端口是3000,我们直接在浏览器中打开localhost:3000
,打开控制台,可以发现输出了2行代码,一个是reactivity.js
文件中输出的,一个是index.html
文件中,导入进来输出的。
到这里,我们开发环境的基本架子,就搭建好了,至于生产环境的配置,我们之后的文章会提到,接下来的文章,我们首先来学习一下Vue3
的响应式原理吧!
作者:柠檬soda水
链接:https://juejin.cn/post/7195065768231305274
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。