1、脚⼿架的应⽤与回顾
- Vue react 现代框架的脚⼿架回顾
- ⽤户交互与应⽤⽣成
- 实际项⽬中的应⽤
1、狭义: 开发流程
- 分支管理
- 开发环境
- 单元测试\自动化测试
- 迭代部署: ci c持续迭代 cd 持续部署
cicd: devops 自动化的开发运维方式(自己的项目 基础类型 自动化测试脚本 自动化部署脚手架 )
2、广义
- 大范围:开发 发布 运维 维护 后期bug fix整个流程
开发
- 框架选型:
- 不同框架的对比:
- 模块化+组件化:工程化的基石
- 脚手架:项目团队所有的项目产生一个连接 -> 本地服务 mock服务一致的情况下生成一套框架 每一个项目都对齐
- 本地开发服务:dev server
- mock 服务:模拟 api doc
构建
- 打包、依赖、代码处理
- 缓存、增量更新(只更新增量的部分)、资源处理(静态资源、图片资源、插件处理):性能方面的处理
- Es & babel | css & css processor:编译
- 类库打包、工具资源的应用:合并工具 避免重复打包
构建性能优化
部署
- 持续部署
- nginx反向代理、spa路由配置
- 跨域、http证书、流量:如何统计流量 网关:统一的配置
- 环境:如何区分不同的环境 -> 不同 ip -> 不同路由 -> 配置host
性能和规范
- 缓存策略:网络层级(强缓存 协商缓存 304)
网络层面(cdn更新策略: 定时更新 监听某个具体文件 )+文件层面(hash本地文件)(前后端)构建层面缓存- CDN:内容分发网络 -> 在物理上将内容放到更近的不同的节点上 -> 距离服务器更近的获取资源更高效
- 请求:内容压缩、请求合并、数量减少、使用长连接、http2 帧传输、限制6个tcp连接、建立tcp连接优化
- 日志性能:定位问题 发现为题
- 预加载:骨架屏 工程化上给到业务的一个模板
AST的概念: 抽象语法树
- a. ⽣成过程
code ->(简单粗暴的处理方式: 比如字符串替换) ast (工程化的预处理: AST generator)-> code- b. 词法分析 & 语法分析
- c. AST基础处理
- d. babel基础
目标:
// 命令行直接创建基于模版的项目
// vue create ${name}
手写脚手架
npm i path // 路径处理/合并
npm i chalk@4.1.0 // 控制台样式高亮
npm i fs-extra // file system模块的拓展
npm i inquirer@8.2.4 // 命令行输入参数的 轮询 跟用户的交互 补充文件
npm i commander // 在主命令下面生成自定义的指令
npm i axios // 网络请求拿模板什么的
npm i download-git-repo // 下载远程的模板文件 当成一个项目
// ora 控制台的loading
// easy-table 控制台输出表格
// figlet 打logo
// 1. 初始化npm
npm init
// 2. 新建主命令(主入口文件),配置项更新
// bin + package.json
// 3. 关联主命令与配置项
npm link
// package.json 中添加 "bin": "bin/zwCli.js"
#! /usr/bin/env node // 声明全局的可执行的主入口 基于node
const program = require('commander') //自定义指令
// 定义命令和参数
// create命令
program.command('create ' ).description('create a new project').option('-f, --force', 'overwrite target directory if it exist').action((name, options) => {
// 打印执行结果
console.log('program name is', name)
require('../lib/create')(name, options)
})
// 解析用户执行命令的传入参数
program.parse(process.argv)
// 4. 执行主命令即可关联逻辑内容
// zw-cli: 主命令 已经被npm link注入到了全局
交互好帮手 - commander
// 创建命令 - create
program.command('create ' ) // 新建app的名称
.description('create a new project') // 描述
.options('-f, --force', 'overwrite target directory if it exist') // 附加的配置
.action((name, options) => {
// 执行
// 打印执行结果
console.log('program name is', name)
require('../lib/create')(name, options)
})
// 解析用户执行命令的传入参数
program.parse(process.argv)
// 4. 执行主命令即可关联逻辑内容
// zw-cli: 主命令 已经被npm link注入到了全局
lib/create
// 用户的操作 交互
const path = require('path')
const fs = require('fs-extra')
const inquirer = require('inquirer')
const Generator = require('./generator')
// 1. 对外抛出一个方法用来接收用户要创建的文件项目名以及参数
module.exports = async function(name, options) {
// 判断项目是否存在
const cwd = process.cwd()
const targetAir = path.join(cwd, name)
// 目录是否存在
if (fs.existsSync(targetAir)) {
// 是否为强制创建
if (options.force) {
await fs.remove(targetAir)
} else {
// 询问用户是否确定要覆盖
let { action } = await inquirer.prompt([
{
name: 'action',
type: 'list',
message: 'Target directory already exists',
choices: [{
name: 'Overwrite',
value: 'overwrite'
}, {
name: 'Cancel',
value: false
}]
}
])
// 如果用户拒绝覆盖则停止生成操作
if (!action) {
return;
} else if (action === 'overwrite') {
await fs.remove(targetAir)
}
}
}
// 新建模板
const generator = new Generator(name, targetAir)
generator.create()
}
// generator.js 最终生成
const { getRepoList, getTagList } = require('./http')
const ora = require('ora')
const inquirer = require('inquirer')
const util = require('util') // promisfy:promise化
const downloadGitRepo = require('download-git-repo') // 回调函数形式 不支持promise、 需要promise化
const path = require('path')
const chalk = require('chalk')
// 封装loading外壳
async function wrapLoading(fn, message, ...args) {
const spinner = ora(message)
spinner.start()
try {
const result = await fn(...args)
spinner.succeed()
return result
} catch (error) {
spinner.fail('Requiest failed, please refetch...')
}
}
// 生成模板的类 实例化
class Generator {
constructor(name, targetDir) {
this.name = name
this.targetDir = targetDir
this.downloadGitRepo = util.promisify(downloadGitRepo)
}
// 获取用户选择的模版
// 1. 从远端拉模版数据
// 2. 用户去选择自己已有下载的模版名称
// 3. 返回用户选择的模版
async getRepo() {
const repoList = await wrapLoading(getRepoList, 'waiting for fetch template')
// 如果没有拿到模板 退出
if (!repoList) return
// 模板的名字是索引模板的唯一关键字
const repos = repoList.map(item => item.name)
// 让用户去选择自己新下载的模版名称
const { repo } = await inquirer.prompt({
name: 'repo',
type: 'list',
choices: repos,
message: 'Please choose a template to create project'
})
return repo
}
// 获取用户选择的版本
// 1. 基于repo的结果,远程拉版本列表
// 2. 自动选择最新的tag
async getTag(repo) {
const tags = await wrapLoading(getTagList, 'waiting for fetch tag', repo)
if (!tags) return
const tagsList = tags.map(item => item.name)
return tagsList[0]
}
// 下载远程模版
// 1. 拼接下载地址
// 2. 调用下载方法
async download(repo, tag) {
const requestUrl = `FEcourseZone/${repo}${tag ? '#'+tag : ''}`
await wrapLoading(
this.downloadGitRepo,
'waiting download template',
requestUrl,
path.resolve(process.cwd(), this.targetDir)
)
}
// 核心创建逻辑
async create() {
// 1. 获取模版名称
const repo = await this.getRepo()
// 2. 获取tag名称
const tag = await this.getTag(repo)
// 3. 下载模版到目录
await this.download(repo, tag)
console.log(`\r\nSuccessfully created project ${chalk.cyan(this.name)}`)
}
}
module.exports = Generator
// 网络请求:拉取模板
const axios = require('axios')
// 拦截器
axios.interceptor.response.use(res=>{
return res.data
})
// 获取模板列表
async function getPepoList(){
return axios.get('https://api.github.com/orgs/FEcourseZone/repos')
}
// 获取版本信息
async function getTagList(repo) {
return axios.get(`https://api.github.com/repos/FEcourseZone/${repo}/tags`)}
module.exports = {
getRepoList
}
执行
zw-cli
zw-cli create app
npm publish 封装到npm包里
1. 从零配置webpack
- a. ⼯具的三⼤件
- b. Webpack⼯作模式
- C. 模块化打包的实现
2. webpack异步玩法
- a. webpack异步加载的原理
- b. 路由懒加载的重要意义
- c. webpack的分包策略
- d. 重点⾯试题以及⾯试官到底想问什么
3. webpack的HMR
- a. 什么是HMR
- b. webpack集成HMR
- c. 实现原理与流程
- d. 为什么⾯试官总是喜欢问HMR
4. webpack的loader与插件
- a. 编写webpack的loader
- b. 编写webpack的插件