一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

一、需求分析和架构设计

1、产品研发流程
* 公司起步:公司成立、产品雏形(域名、网站、公众号等)、招聘、技术基建
* 1.0项目启动:kick-off项目启动会、项目计划项目角色
* 需求:编写需求、评审需求(可能多轮)、UI视觉稿评审
* 技术方案设计:技术难点调研、技术方案设计、技术方案评审(可能多轮)
* 开发:写代码包括单元测试、代码走查、合并代码(持续集成)、自测(准入测试)
* 联调:前后端联调、UI视觉稿确认、产品需求确认
* 测试:提测、测试、修改bug、测试准出
* 上线:预览环境上线、预览环境回归测试、线上环境上线、线上环境回归测试
* 项目总结:项目总结会(总结分析项目的进度、质量、风险、问题等,争取下次项目规避)
* 年度总结:每个人的年度总结、绩效沟通
2、以架构师的思维分析需求
* 引导分享:需求分析过程的一个重要步骤,引导用户分享产品,是让业务增长的一种很重要的方式
* 统计(一周汇总):
    - pv(page访问量)/uv(user访问量)
    - 自定义事件(比如广告飘窗点击事件)
    - 成果转化率 = pv或uv / 自定义事件
* 分渠道统计:
    - 原理:url?channel=
    - 通过channel来统计哪家广告位所产生的page访问量
3、知识库(语雀)
* 存放项目团队文档(需求文档、技术方案和技术调研、研发规范细则、测试记录、运维、UI设计)
* 支持协同编辑
4、架构师的职责
* 在深入理解业务需求之后,能用软件把业务给模拟出来。并且保证稳定执行,和后续增长。
* 不一定非得用上看似高大上的技术和框架。技术永远都是为业务服务的。
4、ssr(服务端渲染)
* toB(面向企业):不适合用
* toC(面向消费者):适合用,对前端渲染性能有要求
5、单一数据源
* 一致的、最新的、完整的、无冗余的、可靠的
* 通过建立产品数据的逻辑联系,将物理上分散的产品数据形成逻辑上的统一整体,为产品数据的
  访问与操作提供唯一的数据源
6、技术方案设计文档
整体架构设计 V1.0
* 需求
    - 需求文档链接
* 范围
    - 整体设计,架构设计,没有细节
* 模块设计
    - 模块的拆分和关系图,结果
    - 模块的关键功能,职责等
    - 特殊的模块重点说明
        组件库,独立第三方,同时用于编辑器和H5
        自研统计服务,为何自研
* 作品的数据结构
    - vuex store的结构,解释
    - 数据流转关系图
* 扩展性保证
    - 扩展组件,数据结构层面
    - 扩展编辑器的功能,例如:组件隐藏、锁定
    - 扩展页面的配置
      (讨论集思广益)
* 开发提效
    - 脚手架
    - 组件平台
* 运维保障
    - 线上服务和运维服务
    - 安全
    - 监控和报警
    - 服务扩展性:流量大

二、脚手架架构设计和框架搭建

1、脚手架简介
vue create vue-test-app --force -r https://registry.npm.taobao.org
* 命令的组成
    - 主命令:vue
    - command:create
    - command的param:vue-test-app
    - option:--force可以缩写成-f,-r是--registry的缩写。
      【vue create --help】命令可以查看所有支持的option
    - option的param:为true则可以省略,比如:--force true
2、脚手架的执行原理
* 在终端输入vue create vue-test-app
* 终端解析出vue命令
* 终端在环境变量中找到vue命令
* 终端根据vue命令链接到实际文件vue.js
* 终端利用node执行vue.js
* vue.js解析command/options
* vue.js执行command
* 执行完毕,退出执行
3、全局安装@vue/cli的执行原理
* where和type命令:
    - where vue(where.exe vue):查看vue命令所在路径,可以看到两个文件vue(shell脚本文件)
      和vue.cmd(windows内核脚本文件)
    - type vue.cmd:查看vue.cmd脚本文件内容,可以看到实际执行文件
      (环境变量npm目录/node_modules/@vue/cli/bin/vue.js)
* 【npm i -g @vue/cli】时发生了什么?
    - 下载@vue/cli包
    - 解析package.json({"bin":{"vue":"bin/vue.js"}})
    - 环境变量npm目录下生成vue和vue.cmd文件
* vue.js第一行【#!/usr/bin/env node】linux环境下作用。
  终端输入./vue.js,相当于执行/usr/bin/env node ./vue.js。
  /usr/bin/env是环境变量,node是命令
4、脚手架开发流程详解
* 开发流程
    - 创建npm项目
    - 创建脚手架入口文件,最上方添加:#!/usr/bin/env node
    - 配置package.json,添加bin属性
    - 编写脚手架代码
    - 将脚手架发布到npm
* 使用流程
    - 安装脚手架:npm i -g your-own-cli
    - 使用脚手架:your-own-cli
* 脚手架开发难点解析
    - 分包:将复杂的系统拆分成若干个模块
    - 命令注册:vue create、vue add、vue invoke
    - 参数解析:vue command [options] <params>
    - options全称:--version、--help
    - options简写:-V、-h
    - 带params的options:--path /desktop/vue-test
    - 帮助文档:
        global help
            usage
            options
            commands
        command help
            usage
            options
    - 其他:
        命令行交互
        日志打印
        命令行文字变色
        网络通信:http/websocket
        文件处理
5、实操入门第一个脚手架
* 新建linding-cli目录,进入linding-cli目录
* npm init -y
* 新建入口文件:/bin/index.js
    - 第一行:#!/usr/bin/env node
* package.json配置:{"bin":{"linding":"bin/index.js"}}
* npm login(注意镜像源问题:https://registry.npmjs.org)
* npm publish(注意包名重复问题)
* 调试:
    - 远程调试:npm i -g linding-cli
    - 本地调试(linding-cli目录下):npm link
      (linding-cli同级目录npm i -g linding-cli,测试失败)
* 分包:
    - 本地调试:
        ~ linding-cli目录下:npm link
        ~ linding-cli-lib目录下:npm link(取消:npm unlink)
          (注意package.json入口文件:{"main":"lib/index.js"})
        ~ linding-cli目录下:npm link linding-cli-lib
          (注意不支持es6模块化。取消:npm unlink linding-cli-lib)
        ~ linding-cli的package.json(为了npm publish,需手动添加依赖):
          {"dependencies":{"linding-cli-lib":"^1.0.0"}}
    - 远程调试:
        ~ linding-cli-lib目录下:npm publish
        ~ linding-cli目录下:npm i -S linding-cli-lib
* 命令注册和参数解析:
    - const argv = require("process").argv:获取命令以空格分隔的字符串数组
const packageJson = {
  name: "cli-name",
  /**
   * 当cli-name作为库被其他项目引用时,
   * require("cli-name")相当于require("cli-name/src/index.js")
   */
  main: "src/index.js",
  /**
   * 当cli-name本地npm link或者远程npm -g install cli-name,
   * 会生成cli-cmd命令,执行bin/index.js文件
   */
  bin: {
    "cli-cmd": "bin/index.js"
  }
}
# 发布测试
# 修改版本号:npm version 1.0.0-beta.0(也可手动修改package.json的version)
npm publish --tag beta
# 删除版本
npm unpublish [<pkg>][@<version>] --force
# 发布正式
# 修改版本号:npm version major/minor/patch
# 简写:npm publish
npm publish --tag next
6、lerna简介
* 原生脚手架开发痛点分析
    - 痛点一:重复操作:多peckage本地link、多peckage依赖安装、多peckage单元测试、
      多peckage代码提交、多peckage代码发布
    - 痛点二:版本一致性:发布时版本一致性、发布后相互依赖版本升级
    - package越多,管理复杂度越高
* lerna简介
    - lerna是一个优化基于git+npm的多peckage项目的管理工具
    - 优势:大幅减少重复操作、提升操作的标准化
    - lerna是架构优化的产物,它揭示了一个架构真理:项目复杂度提升后,就需要对项目进行架构优化,
      架构优化的主要目标往往都是以效能为核心
* lerna开发脚手架流程
    - 脚手架项目初始化:初始化npm项目、安装lerna、lerna init初始化项目
    - 创建package:lerna create创建package、lerna add安装依赖、lerna link链接依赖
    - 脚手架开发和测试:lerna exec执行shell脚本、lerna run执行npm命令、
      lerna clean清空依赖、lerna bootstrap重装依赖
    - 脚手架发布上线:lerna version bump version、
      lerna changed查看上版本以来的所有变更、lerna diff查看diff、
      lerna publish项目发布
- - lerna开发脚手架流程(划重点) - -
- - 脚手架项目初始化 - -
初始化npm项目 - 安装lerna - lerna init初始化项目
- - 创建package - -
lerna create创建package - lerna add安装依赖 - lerna link链接依赖
- - 脚手架开发和测试 - -
lerna exec执行shell脚本 lerna run执行npm命令 - lerna clean清空依赖 lerna bootstrap重装依赖
- - 脚手架发布上线 - -
lerna version bump version lerna changed查看上版本以来的所有变更 - lerna diff查看diff lerna publish项目发布
7、实操基于lerna搭建脚手架框架
* 创建linding-cli目录,进入linding-cli目录
* npm init -y
* npm i -D lerna(推荐全局也安装一下)
* lerna init
    - lerna.json修改{"version":"1.0.0"}
* lerna create core packages
    - packages可省略
    - npm创建组织机构:add organizations、输入组织机构名linding-cli、create、skip
    - package name:(core) @linding-cli/core
    - 其他任意
* lerna create utils
    - package name:(core) @linding-cli/utils
    - 其他任意
* lerna add @linding-cli/utils packages/core
    - packages/core包安装@linding-cli/utils依赖
    - 不指定包则给所有包安装依赖:lerna add axios(会给所有包安装依赖)
* lerna clean清空依赖(包下package.json中不必要的依赖记得手动删除)
* lerna bootstrap重装依赖
* lerna link软链接依赖(--force-local强制本地依赖)
    - 前提手动配置好packages/core/package.json
      {"dependencies":{"@linding-cli/utils":"^1.0.0"}}
* lerna exec --scope @linding-cli/utils -- rm -rf node_modules/
    - --scope指定包,不指定则所有包
    - rm -rf是linux命令,windows删除文件我不做测试
* lerna run --scope @linding-cli/utils test
    - --scope指定包,不指定则所有包
    - test为包中package.json配置的命令(exit 1则会报错)
* lerna version:修改版本号,需要有git的commit记录,需要关联远程git仓库
* lerna changed:查看自上个版本以来的所有变更(package.json中的version)
* lerna diff:查看自上个git提交以来的所有变更,需要有git的commit记录
* lerna publish
    - 记得需要npm login
    - git远程仓库多一个tag,当远程仓库有相同tag会发布失败
      (本地仓库也会多一个tag,本地删除tag:git tag -d 标签名)
    - 包下的package-lock.json不提交npm会发布失败
    - 因为使用organizations形式发布的包默认是private的,需配置所有包的package.json
      {"publishConfig":{"access":"public"}}
8、lerna源码阅读
* core/lerna/package.json找到入口文件core/lerna/cli.js
* debug configurations添加node.js,
  node parameters输入core/lerna/cli.js ls
* 调试注意点
    - 安装依赖
    - settings -> node.js and npm -> 
      选中coding assistance for node.js(内置库代码高亮)
    - settings -> stepping -> 取消选中do not step into library scripts
      和do not step into scripts(允许跳转到库文件中去)
* 断点调试:
    - step over:逐行执行
    - step into:进入方法
    - step out:跳出方法
    - resume program:执行至下一个断点,没有断点则执行结束
* 项目本地依赖引用方法:
    - core包的package.json:{"dependencies":{"@linding-cli/utils":"file:../utils"}}
    - 需要npm i,node_modules/@linding-cli下的是core包和utils包的软链接
      (通过resolveLocalDependencyLinks()方法将本地链接解析成线上链接)
    - linding-cli测试:
        ~ 新建入口文件:packages/core/bin/index.js,第一行:#!/usr/bin/env node
        ~ 引用utils包:const utils = require("@linding-cli-dev/utils")
        ~ packages/core/package.json配置:{"bin":{"linding":"bin/index.js"}}
        ~ core包需npm link,然后执行linding命令
* 脚手架command执行过程(lerna源码)
    - core/lerna/cli.js
    - core/lerna/index.js
    - commands/list/command.js(以list为例)
    - commands/list/index.js
    - core/command/index.js
* 事件循环复习
    - Promise(Fn):js主线程
    - Promise.prototype.then(Fn):微任务队列
* node在加载模块时向上下文环境注入的5个变量:
    - require、exports、module、__filename、__dirname
* import-local:
    - 可以优先调用本地lerna命令
* path.resolve和path.join区别
    - join是路径拼接,resolve相当于cd
* pkg-dir:
    - 从给定目录开始向上查找,直到找到package.json所在目录并返回
* Module:
    - Module._nodeModulePaths:生成node_modules可能的路径
    - Module._resolveFilename:解析模块的真实路径
* Object.create(null):创建的对象没有原型链,节约内存空间
* "/xxx/yyy".indexOf("/",1):查找第二个"/"的索引
9、yargs快速入门
#!/usr/bin/env node

// 1、安装:npm i -S yargs
const yargs = require("yargs/yargs")
// 2、安装:npm i -S dedent
const dedent = require("dedent")

/*
const {hideBin} = require("yargs/helpers")
const arg = hideBin(process.argv)
const cli = yargs(arg)*/
const pkg = require("../package.json")
const cli = yargs()
const argv = process.argv.slice(2)
const context = {
    lindingVersion: pkg.version
}

cli
    .scriptName("linding")// 设置执行文件的名字:$0
    .usage("用法: $0 [command] <options>")// 用法提示
    .demandCommand(1, "一个command是必须的. 通过--help查看所有可用的命令和选项")
    .strict()// 未知参数提示
    .recommendCommands()// 最相似命令提示
    .fail((err, msg) => {
        console.log(err, msg)
    })// 信息定制化处理
    .alias("h", "help")// --help别名
    .alias("v", "version")// --version别名
    .alias("r", "registry")// --registry别名
    .wrap(cli.terminalWidth())// 文本显示宽度(命令行宽度)
    .epilogue(dedent`
    当命令执行失败,所有的日志都写在当前工作目录的lerna-debug.log文件中
    更多信息,请查询我们的手册:
    `)// 结尾提示
    .options({
        debug: {
            type: "boolean",
            describe: "启动debug模式",
            alias: "d"
        }
    })// 多个选项
    .option("ci", {
        type: "boolean",
        hidden: true// 定义隐藏选项,一般是内部开发人员使用
    })// 单个选项
    .option("registry", {
        type: "string",
        describe: "定义全局仓库"
    })
    .group(["debug"], "开发选项组")// 选项分组
    .group(["registry"], "其他选项组")
    .command("init [name]", "完成项目初始化", yargs => {
        yargs
            .option("name", {
                type: "string",
                describe: "项目的名称",
                alias: "n"
            })// 【linding init -h】时会显示此选项信息
    }, argv => {
        console.log(argv)// 【linding init】时会打印此参数信息
    })// 命令
    .command({
        command: "list",
        aliases: ["ll", "ls"],// list别名
        describe: "包列表",
        builder(yargs) {
        },
        handler(argv) {
            console.log(argv)
        }
    })
    // .argv
    .parse(argv, context)// argv会拼接context,并注入到脚手架中
posted on 2022-05-26 00:13  一路繁花似锦绣前程  阅读(174)  评论(0编辑  收藏  举报