目录
一、需求分析和架构设计
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,并注入到脚手架中