Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.vue3介绍及源码开发环境的搭建 #32

Open
Zijue opened this issue Jul 20, 2021 · 0 comments
Open

1.vue3介绍及源码开发环境的搭建 #32

Zijue opened this issue Jul 20, 2021 · 0 comments

Comments

@Zijue
Copy link
Owner

Zijue commented Jul 20, 2021

vue3与vue2的对比

  • 源码采用 monorepo 方式进行管理,将模块拆分到package目录中
  • Vue3 采用ts开发,增强类型检测。Vue2 则采用flow
  • Vue3的性能优化,支持tree-shaking,不使用就不会被打包
  • Vue2 后期引入RFC,使每个版本改动可控 rfcs
  • Vue3 劫持数据采用proxy,Vue2 劫持数据采用defineProperty。 defineProperty有性能问题和缺陷
  • Vue3中对模板编译进行了优化,编译时 生成了Block tree,可以对子节点的动态节点进行收集,可以减少比较,并且采用了 patchFlag 标记动态节点
  • Vue3 采用compositionApi 进行组织功能,解决反复横跳,优化复用逻辑 (mixin带来的数据来源不清晰、命名冲突等),相比optionsApi 类型推断更加方便
  • 增加了 Fragment,Teleport,Suspense组件

vue3整体架构

  • monorepo介绍
    Monorepo 是管理项目代码的一个方式,指在一个项目仓库(repo)中管理多个模块/包(package)

    • 一个仓库可维护多个模块,不用到处找仓库
    • 方便版本管理和依赖管理,模块之间的引用,调用都非常方便
    • 缺点:仓库体积会变大
  • vue3项目结构

    • reactivity:响应式系统
    • runtime-core:与平台无关的运行时核心 (可以创建针对特定平台的运行时 - 自定义渲染器)
    • runtime-dom:针对浏览器的运行时。包括DOM API,属性,事件处理等
    • runtime-test:用于测试
    • server-renderer:用于服务器端渲染
    • compiler-core:与平台无关的编译器核心
    • compiler-dom:针对浏览器的编译模块
    • compiler-ssr:针对服务端渲染的编译模块
    • compiler-sfc:针对单文件解析
    • size-check:用来测试代码体积
    • template-explorer:用于调试编译器输出的开发工具
    • shared:多个包之间共享的内容
    • vue:完整版本,包括运行时和编译器
                            +---------------------+
                            |                     |
                            |  @vue/compiler-sfc  |
                            |                     |
                            +-----+--------+------+
                                  |        |
                                  v        v
               +---------------------+    +----------------------+
               |                     |    |                      |
     +-------->|  @vue/compiler-dom  +--->|  @vue/compiler-core  |
     |         |                     |    |                      |
+----+----+    +---------------------+    +----------------------+
|         |
|   vue   |
|         |
+----+----+   +---------------------+    +----------------------+    +-------------------+
    |         |                     |    |                      |    |                   |
    +-------->|  @vue/runtime-dom   +--->|  @vue/runtime-core   +--->|  @vue/reactivity  |
              |                     |    |                      |    |                   |
              +---------------------+    +----------------------+    +-------------------+

vue3模块管理环境搭建

  • yarn初始化项目
    因为我们希望通过monorepo的方式管理项目下的多个模块,npm安装管理模块无法实现此功能,故使用yarn来管理模块。
yarn init -y

修改生成的package.json文件,添加以下内容:

{
  "private": true, // 表示该项目不会发布到npm上,只是用来管理的
  "workspaces": [ // 指定管理的包的路径
    "packages/*"
  ]
}
  • 安装 TypeScript
  1. 在根目录下安装:yarn add typescript -W
  2. 初始化typescriptnpx tsc --init(不带npx表示执行全局下的tsc,加npx执行的是当前目录下node_modules/.bin/tsc命令去初始化)
  3. 修改生成的tsconfig.json文件:
// "module": "commonjs", // rollup不支持打包commonjs
"module": "ESNext", // 一般默认都修改为ESNext
  • 安装依赖
yarn add rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve @rollup/plugin-json execa -D -W

# rollup        打包工具
# rollup-plugin-typescript2     rollup和ts的桥梁
# @rollup/plugin-node-resolve   解析node第三方模块
# @rollup/plugin-json   支持引入json
# execa         开启子进程方便执行命令
  • 项目结构
.
├── package.json            # 项目信息
├── packages                # monorepo方式管理包根路径
│   ├── reactivity          # Vue响应式模块
│   │   ├── package.json
│   │   └── src
│   │       └── index.ts
│   └── shared              # Vue共享模块
│       ├── package.json
│       └── src
│           └── index.ts
├── rollup.config.js        # rollup配置文件
├── scripts                 # 打包脚本
│   └── build.js
├── tsconfig.json           # typescript配置文件
└── yarn.lock
  • 配置模块名称及自定义打包配置
// packages/reactivity/package.json
{
  "name": "@vue/reactivity", //@vue表示命名空间
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "buildOptions": { //自定义打包配置项
    "name": "VueReactivity",
    "formats": [ // 打包的格式
      "esm-bundler",
      "cjs",
      "global"
    ]
  }
}

编译打包环境搭建

  • packages下模块进行打包

scripts/build.js

const fs = require('fs');
const execa = require('execa');

// 1.解析packages目录,过滤出所有的目录(即需要打包的模块)
const dirs = fs.readdirSync('packages').filter(item => {
    if (!fs.statSync(`packages/${item}`).isDirectory()) {
        return false;
    }
    return true;
});

// 2.并行打包所有文件夹
async function build(target) {
    /* 
     * -c 表示使用配置文件rollup.config.js
     * --environment 表示rollup执行时传递环境变量,此处传递的环境变量为`TARGET:${target}`
     * { stdio: 'inherit' } 表示子进程输出打印到父进程标准输出中
     */
    await execa('rollup', ['-c', '--environment', `TARGET:${target}`], { stdio: 'inherit' });
}
async function runParallel(dirs, exec) {
    let result = [];
    for (let item of dirs) {
        result.push(exec(item));
    }
    return Promise.all(result); // 待所有打包操作完毕后,调用成功
}
runParallel(dirs, build).then(() => {
    console.log('打包成功');
})
  • rollup配置

rollup.config.js

import path from 'path';
import ts from 'rollup-plugin-typescript2'; // 解析ts插件
import resolvePlugin from '@rollup/plugin-node-resolve'; // 解析第三方模块

// 获取packages目录
let packagesDir = path.resolve(__dirname, 'packages');
// 获取需要打包模块的路径
let pkgDir = path.resolve(packagesDir, process.env.TARGET);

const resolvePath = item => path.resolve(pkgDir, item); // 将用户传入的路径与打包模块目录合并

// 获取打包模块的package.json文件内容
let pkg = require(resolvePath('package.json'));
// 获取模块自定义参数
let pkgOpts = pkg.buildOptions;
// 获取模块的文件夹名称
let pathName = path.basename(pkgDir);
// 一个包需要打包成多个格式 esModule commonjs iife
const outputConfig = {
    'esm-bundler': {
        file: resolvePath(`dist/${pathName}.esm-bundler.js`),
        format: 'es'
    },
    'cjs': {
        file: resolvePath(`dist/${pathName}.cjs.js`),
        format: 'cjs'
    },
    'global': {
        file: resolvePath(`dist/${pathName}.global.js`),
        format: 'iife'
    }
}
function createConfig(output){
    output.name = pkgOpts.name; // 用于 iife 在 window 上挂载的属性
    output.sourcemap = true; // 生成sourcemap,便于调试。tsconfig.json中也需要开启
    return {
        input: resolvePath('src/index.ts'), // 打包入口
        output,
        plugins: [
            ts({ // ts 编译时配置文件
                tsconfig: path.resolve(__dirname, 'tsconfig.json')
            }),
            resolvePlugin()
        ]
    }
}
// 根据用户打包模块中提供的formats选项,去outputConfig配置里取值生成配置文件
export default pkgOpts.formats.map(format=>createConfig(outputConfig[format]));
  • 配置脚本执行命令

package.json 添加scripts

  "scripts": {
    "build": "node scripts/build.js"
  }

保存后,在项目根目录下命令行执行:npm run build

编译开发打包环境搭建

虽然目前可以成功打包整个项目模块,但是在开发过程中每次调用npm run build很浪费性能,我们更希望可以控制某个模块单独进行打包。于是我们添加一个执行脚本"dev": "node scripts/dev.js"

scripts/dev.js

const execa = require('execa');

async function build(target) {
    // -cw 表示打包并监视文件文件变化,需要打包的模块变化时自动打包
    await execa('rollup', ['-cw', '--environment', `TARGET:${target}`], { stdio: 'inherit' });
}
build('reactivity'); // 仅打包响应式模块

扩展:组件库也可以采用monorepo的方式,好处就是每个组件都可以独立发布。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant