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

导航

 

六、脚手架创建项目和组件初始化开发

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>
    - 其他
posted on 2023-06-04 22:44  一路繁花似锦绣前程  阅读(26)  评论(0编辑  收藏  举报