目录
六、脚手架创建项目和组件初始化开发
1、ejs模板的三种用法
- index.js
const ejs = require('ejs')
const path = require('path')
const fs = require('fs')
const html = '<div><%= user.name %></div>'
const options = {}
const data = {
user: {
name: "sam"
}
}
const data2 = {
user: {
name: "shuangyue"
}
}
// 一、 模板引擎的三种基本用法
// 第一种用法
// 返回compiled function,用于解析html中的ejs模板
const template = ejs.compile(html, options)
const compiledTemplate = template(data)
const compiledTemplate2 = template(data2)
// console.log(compiledTemplate)
// console.log(compiledTemplate2)
// 第二种用法
const renderedTemplate = ejs.render(html, data, options)
// console.log(renderedTemplate)
// 第三种用法
// 3.1 Promise
const renderedFile = ejs.renderFile(path.resolve(__dirname, 'template.html'), data, options)
// renderedFile.then(file => console.log(file))
// 3.2 callback
const callbackData = {
user: {
name: "sam",
nickname: "<div>sam</div>",
copyright: "imooc"
}
}
const callbackOptions = {
// 2、自定义分隔符
// - delimiter: "?"
// - <?- include('./footer.html', { user }) -?>
delimiter: "%"
}
// 3、自定义文件加载器
ejs.fileLoader = function (filePath) {
const content = fs.readFileSync(filePath).toString()
return '<div style="color: red;">from <%= user.copyright %></div>' + content
}
ejs.renderFile(path.resolve(__dirname, 'template.html'), callbackData, callbackOptions, (err, file) => {
console.log(file)
})
/**
* 二、标签含义
* - <% '脚本' 标签,用于流程控制,无输出。
* - <%_ 删除其前面的空格符
* - <%= 输出数据到模板(输出是转义 HTML 标签)
* - <%- 输出非转义的数据到模板
* - <%# 注释标签,不执行、不输出内容
* - <%% 输出字符串 '<%'
* - %> 一般结束标签
* - -%> 删除紧随其后的换行符
* - _%> 将结束标签后面的空格符删除
*/
- template.html
<% if(user) { %>
<% for(let i = 0; i < 10; i++) { %>
<%# 输出用户的名称 %>
<%_ %><div><%= user.name -%></div>
<%_ %><%- user.nickname -%>
<% } -%>
<% } %>
<%# 三、ejs模板几种特殊用法 %>
<%# 1、包含 %>
<%- include('./footer.html', { user }) -%>
- footer.html
<div>footer copyright@<%= user.copyright %></div><% -%>
2、glob用法小结
// 安装:npm i glob@8
const glob = require('glob')
glob('**/*.js', {
ignore: ['node_modules/**', 'webpack.config.js']
}, function (err, file) {
console.log(err, file)
})
3、ejs源码详解--彻底搞懂模板动态渲染原理
* ejs核心执行流程如下
- new Template:初始化Template对象
- compile:编译Template,并返回一个新的Function,需要传入data参数完成渲染模板
compile
new Template
template → createRegex
compile
generateSource → generateFunctionSource → generateFunction
4、require源码解析--彻底搞懂npm模块加载原理
- require的使用场景
* 加载模板类型:
- 加载内置模块:require('fs')
- 加载node_modules模块:require('ejs')
- 加载本地模块:require('./utils')
* 支持文件类型
- 加载.js文件
- 加载.json文件
- 加载.node文件
- 加载.mjs文件
- 加载其他类型文件
- require源码阅读过程中的一些思考
* CommonJS加载主模块的流程
* require如何加载内置模块?
* require如何加载node_modules模块?
* require为什么会将非js/json/node文件视为js文件加载
* require连续加载同一个模块时,是如何进行缓存的?
- Module对象
* id:源码文件路径,如:/Users/sam/Desktop/vue-test/imooc-test/bin/ejs/index.js
* path:源码文件对应的文件夹,通过path.dirname(id)生成
* exports:模块输出的内容,默认为{}
* parent:父模块信息
* filename:源码文件路径
* loaded:是否已经加载完毕
* children:子模块对象集合
* paths:模块查询范围
- require执行流程
Module._load
loadNativeModule 加载内置模块
module = new Module 实例化模块
Module._cache[filename]=module 缓存模块
module.load
findLongestRegisteredExtension 查询文件后缀
Module._extensions[extension] 执行源码文件
module._compile
compileFunction 生成编译函数
compiledWrapper.call 执行require源码
- require执行流程总结
* relativeResolveCache[relResolveCacheIdentifier]查询缓存路径
* Module._cache[filename]查询缓存模块
* Module._resolveFilename查询模块的真实路径
* loadNativeModule加载内置模块
* new Module实例化Module对象
* module.load(filename)加载模块
* findLongestRegisteredExtension获取文件后缀
* Module._extensions[extension](this,filename)解析模块并执行模块
* module._compile编译模块代码
* compileFunction将模块代码生成可执行函数
* exports,require,module,filename,dirname生成入参
* compiledWrapper.call执行模块函数
* return module.exports输出模块返回结果
- 参考资料
* 阮一峰require源码解读:http://www.ruanyifeng.com/blog/2015/05/require.html
* UTF8 DOM:https://www.imooc.com/article/26166
* Shebang:https://blog.csdn.net/u012294618/article/details/78427864
- 扩展知识
* require.main的妙用
- 用于测试:https://blog.csdn.net/qq_29438877/article/details/103828845
七、B端项目需求分析和架构设计
1、简介
* 将收获什么
- 做怎样的项目完成瓶颈期的突破
- 怎样从需求中寻找关键难点
- 怎样写技术解决方案
- 怎样进行基础的技术选型
* 关键词
- 挖掘难点-找到项目中的痛点
- 技术解决方案-以文档的形式创造可追溯的思考模型
- 业务组件库-多项目复用的业务组件库
- 编辑器-界面到数据的映射
- Typescript,Vue3,React
* 学习方法
- 改变思维模式-磨刀不误砍柴工
- 抽象思维,不要关注具体框架和细节,整体把控
- 发散性思维-一个问题对应多种解决方案
2、复杂项目
* 业务的复杂度
- 交互的复杂性
- 数据结构和状态的复杂性
- 多项目互相依赖的复杂性
- 打包
- 性能优化
- 第三方使用和调研以及二次开发
* 流程的复杂度
- git flow
- lint工具
- 单元测试
- commit信息
- PR review
- CI/CD
3、需求分析
* 项目难点分析
- 怎样实现组件
- 跨项目使用
- 组件的可扩展性
- 编辑器整体状态
- 增加和删除
- 属性渲染成表单
- 实时的反馈
- 插件化
* 组件库难点解决方案
- 两个项目怎样重用组件
- 组件的属性应该怎样设计
- 组件的扩展性怎样保持
4、编辑器难点解析(伪代码)
- store
interface EditorStore {
components: ComponentData[];
//
currentElement: string;
}
interface ComponentData {
props: { [key: string] : any };
id: string;
name: string;
}
const components = [
{ id: '1', type: 'l-text',
props: { text: 'hello', color: 'green' }},
{ id: '2', type: 'l-text',
props: { text: 'hello2', color: 'purple' }},
]
components.map(component => <component.name { ...props } />)
const templateComponents = [
{ type: 'l-text',
props: { text: '模板一', color: 'green' }},
{ type: 'l-text',
props: { text: '模板二', color: 'purple' }},
]
templateComponents.map(component => <Wrapper><component.name { ...props } /></Wrapper>)
components = components.filter((component) => component.id !== id))
- 选中
const textComponentProps = {
text: 'hello',
fontFamily: 'HeiTi',
color: '#fff'
}
const propsMap = {
text: {
component: 'input'
},
fontFamily: {
component: 'dropdown'
},
color: {
component: 'color-picker'
}
}
map(textComponentProps, (key, value) => {
const handleChange = (propKey, newValue, id) => {
const updatedComponent = store.components.find(component.id === id)
updatedComponent.props[propKey] = newValue
}
<propsMap[key].component value={value} @change={handleChange}/>
})
5、技术选型
* typescript
- 程序更容易理解
- 效率更高
- 更少的错误
- 良好的包容性
* vue和react
- 代码实现风格
- 数据更新机制
- 代码重用性
6、语言(typescript)和基础框架(vue3)
* 脚手架(linding-cli-dev)
* 测试工具(jest + vue-test-utils)
* 构建工具(webpack+rollup)
* 持续集成(travis)
* UI组件库(ant-design-vue)
* 状态管理和路由(vuex vue-router)
* 第三方库插件等
* 样式解决方案等
八、前端基础技术回顾和巡礼
1、typescript
* 基本类型
* interface
* class
* 泛型
* 声明文件
* 类型推论
* 联合类型
* 交叉类型
* 类型断言
* 内置类型
* 类型别名
* keyof in
interface FunctionWithProps {
(x: number): number
name: string
}
const a: FunctionWithProps = (x: number) => {
return x
}
a.name = 'abc'
// 约束实例
interface ClockInterface {
currentTime: number
alert(t: number): void
}
// 约束构造和静态
interface ClockStatic {
new (h: number, m: number): void
time: number
}
const Clock: ClockStatic = class implements ClockInterface {
constructor(h: number, m: number) {
console.log(h, m)
}
static time = 12
currentTime = 123
alert(t: number) {
console.log(t)
}
}
interface CountryResp {
name: string
area: number
population: number
}
function withAPI<T>(url: string): Promise<T> {
return fetch(url).then((resp) => resp.json())
}
withAPI<CountryResp>('country.resp').then((resp) => {
console.log(resp.name)
console.log(resp.area)
console.log(resp.population)
})
interface IPerson {
name: string
age: number
}
// 一、Partial(内置类型)将传入的类型变为可选类型
// const person: Partial<IPerson> = {}
// 二、Partial(内置类型)实现过程
// keyof
type keys = keyof IPerson
// lookup types
type NameType = IPerson['name']
// mapped types
type Test = {
[key in keys]: any
}
type PersonOpt = {
[p in keys]?: IPerson[p]
}
type TPerson<T> = T extends { name: string }
? { name: string; age: number }
: { age: number }
type Person1 = TPerson<{ married: boolean }>
type Person2 = TPerson<{ name: string; married: boolean }>
type HTTPMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE'
declare function myFetch<T = any>(
url: string,
method: HTTPMethod,
dara?: any
): Promise<T>
declare namespace myFetch {
const get: <T = any>(url: string) => Promise<T>
const post: <T = any>(url: string, data: any) => Promise<T>
}
export = myFetch
2、vue3
* 新特性总结
- https://v3.vuejs.org/guide/migration/introduction.html
* 为什么要有新版本?
- vue2的困境-抽象逻辑代码的缺失
- Typescript支持很差
* Composition API
- setup
- ref
- reactive**注意丧失响应性**
- toRefs
- 生命周期
* 深入响应式对象原理
- 保存effect,未来想重新执行的代码-压入特定的数据结构
- 探测对象值的改变-**Proxy对象**
- 执行(trigger)之前的effect-触发已经保存在数据结构中的effect函数
* 副作用side-effect
- 纯函数
~ 相同的输入。永远会得到相同的输出
~ 没有副作用
- 副作用-函数外部环境发生的交互
- React和Vue的函数式写法
- watchEffect-响应式对象改变的时候自动触发
~ 自动收集依赖
~ 手动销毁副作用
~ 使副作用失效
~ 副作用执行顺序
- watch-精确控制effect
* 自定义Hooks
- 将相关的feature组合在一起
- 非常易于重用
* 自定义函数的优点
- 以函数的形式调用,清楚的了解参数和返回的类型
- 避免命名冲突
- 代码逻辑脱离组件存在
- 泛型在函数中的使用
- 和React的实现对比
* 没有讲到可以自学的知识
- Teleport
- Fragment
- Emits Component Options
- Global API修改
- 语法糖
~ <script setup>
~ <style vars>
- 其他