vue3源码学习:打包流程实现

前言

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

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

  • 一个仓库可维护多个模块,不用到处找仓库
  • 方便版本管理和依赖管理,模块之间相互引用,调用都非常方便

缺点:仓体积会变大

4.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完整版本,抱愧运行时和编译器

项目打包流程构建

1.安装依赖
依赖
typescript 支持typescript
rollup 打包工具
rollup-plugin-typescript2 rollup和ts的桥梁
@rollup/plugin-node-resolve 解析node第三方模块
@rollup/plugin-josn 支持引入json
execa 开启子进程方便执行命名
  • 初始化项目,安装对应的依赖
yarn init -y
yarn add @rollup/plugin-json @rollup/plugin-node-resolve execa rollup rollup-plugin-typescript2 typescript
2.初始化typescript
npm i typescript -g
tsc --init    // 生成tsconfig.json
  1. tsconfig.json文件内容(对于其中每一项的含义,后面补充)
    vue3源码学习:打包流程实现_第1张图片

  2. 在tsconfig.json文件中添加对packages包中项目文件的检验

.......
"moduleResolution":"node",
    "baseUrl":".",
    "paths": {
      "@vue/*":[
        "packages/*/src"
      ]
    }
.........
3.初始化项目目录
  1. 新建shared和reactivity包和启动脚本目录,项目结构如下

vue3源码学习:打包流程实现_第2张图片

  1. 修改总目录中package包中配置
  "private": true, //表述属性是私有的
  "workspaces": [ //工作空间,表示找到packages下面的所有包
    "packages/*"
  ],
  "scripts": {
    "dev" : "node scripts/dev.js",
    'build': "node scripts/build.js"
  },
  • 配置好workspaces的目的是为了,在node_modules下面创建下软链接,以便每个包之间都可以相互引用

vue3源码学习:打包流程实现_第3张图片

  1. 修改reactivity和shared包中package.js中的配置
  • reactivity中packjson中配置
{
  "name": "@vue/reactivity",
  "version": "1.0.0",
  "main": "index.js", //主要给commonjs使用
  "license": "MIT",
  "module": "dist/reactivity.esm-bundler.js", //保证在webpack工程化 import时候使用
  "buildOptions": {
     "name": "VueReactivity",
     "formats": [ //这里表示需要打包的种类
       "cjs",
       "esm-bundler",
       "global"
     ]
   }
}
  • shared中packge.json配置
{
  "name": "@vue/shared",
  "version": "1.0.0",
  "main": "index.js",
  "module": "dist/shared.esm-bundler.js",
  "license": "MIT",
  "buildOptions":{
    "name":"VueShared",
    "formats":[
      "cjs",
      "esm-bundler"
    ]
  }
}
4.编写打包脚本中build.js中的打包方法
  • 读取packages下面所有的文件夹,并且异步调用打包方法,进行打包
  • build就做了一件事情,就是传值到rollup环境变量中
// 把package目录下面的所有都打包
    const fs = require('fs') 
    const execa = require('execa')
    // [ 'reactivity', 'shared' ] 可以筛出这两个模块
    const targets = fs.readdirSync('packages').filter(f => { //找到packages下面的所有包,筛选出文件夹
        if(fs.statSync(`packages/${f}`).isDirectory()){
            return false
        }
        return  true
    })


    //对我们的目标依次进行打包
    async function build(target){
        //启用rollup打包
        await execa('rollup',['-c','--environment',`TARGET:${target}`],{stdio:'inherit'}) //当子进程打包的信息共享给父进程
    }

    function runParallel(targets,iteratorFn){
        const res = []
        for(item of targets){
            const p = iteratorFn(item)
            res.push(p)
        }
        return Promise.all(res)
    }
  	//异步执行所有打包
    runParallel(targets,build).then(res => {
      //打包完毕后执行的操作
    })
5.新增rollup.config.js编写打包脚本
  • 打印rollup中的环境变量,可以看到是传过来的变量
console.log('rollup',process.env.TARGET) 
//process.env.TARGET reactivity process.env.TARGET reactivity
  • 写rollup.config.js中的配置
  • 首先到对应包中package.json的配置
  • 然后根据buildOptions中要生成选项生成对应的rollup打包配置
// rollup的配置

import path from 'path';
import json from '@rollup/plugin-json';
import resolvePlugin from '@rollup/plugin-node-resolve'
import ts from 'rollup-plugin-typescript2'

// 根据环境变量中的target属性 获取对应模块中的 pakcage.json

const packagesDir = path.resolve(__dirname,'packages'); // 找到packages 
console.log('process.env.TARGET',process.env.TARGET); 

// packageDir 打包的基准目录
const packageDir = path.resolve(packagesDir,process.env.TARGET)  // 找到要打包的某个包

// 永远针对的是某个模块
const resolve = (p)=>path.resolve(packageDir,p)



const pkg = require(resolve('package.json'));
const name = path.basename(packageDir); // 取文件名

// 对打包类型 先做一个映射表,根据你提供的formats 来格式化需要打包的内容
const outputConfig = { // 自定义的
    'esm-bundler':{
        file: resolve(`dist/${name}.esm-bundler.js`),
        format:'es'
    },
    'cjs':{
        file:resolve(`dist/${name}.cjs.js`),
        format:'cjs'
    },
    'global':{
        file:resolve(`dist/${name}.global.js`),
        format:'iife' // 立即执行函数
    }
}
const options = pkg.buildOptions; // 自己在package.json中定义的选项

function createConfig(format,output) {
    output.name = options.name;
    output.sourcemap = true; // 生成sourcemap

    // 生成rollup配置

    return {
        input: resolve(`src/index.ts`),
        output,
        plugins:[
            json(),
            ts({ // ts 插件 
                tsconfig:path.resolve(__dirname,'tsconfig.json')
            }),
            resolvePlugin() // 解析第三方模块插件
        ]
    }

}
// rollup 最终需要到出配置
export default options.formats.map(format=>createConfig(format,outputConfig[format]))
6.运行yarn run build打包
  • 运行打包命令,可以看到在每个包下面都生成对应的打包文件(根据package.json中配置项生成)
    vue3源码学习:打包流程实现_第4张图片

总结

  • vue3打包流程就是找到每个包然后循环package.json中配置,然后生成不同的配置文件

你可能感兴趣的:(vue3,学习,前端,javascript)