-
Frontend Developer
-
[ React, TypeScript ] Lover.
laclys / front-end_practice Goto Github PK
View Code? Open in Web Editor NEWDaily practice
Daily practice
容器技术的一个实现
快速打包技术
Namespace和Cgroup(负责资源管理控制)两大机制保障
docker != container
read-only
文件”运行中的Docker Image“
read-write
的层 (称之为container layer
,容器层)Image
可以创建多个Container
docker container run nginx
可以去创建一个容器,如果没有nginx会去下载
docker container ls
容器的列出(up)
docker container stop (ID可以只写前面)
容器的停止
docker container ps -a or docker container ls -a
容器的列出(up和exit)(停止的也会展示)
docker container rm
容器的删除
docker container stop $(docker container ps -qa)
批量停止docker container ps -qa
打印出所有container ID传入$
docker container rm $(docker container ps -qa)
同理批量删除docker container ps -qa
打印出所有container ID传入$、
-f
强制)(win不是完整的attached
模式,ctrl+c
不能把容器停掉)
detached模式 > docker container run -d -p 80:80 nginx (后台运行)
docker attach
detached -> attached
(* 创建容器尽量使用detached
模式)
docker container logs
查看log
docker container logs -f
实时查看log
docker container run -it ubuntu sh
创建一个容器并进入交互式模式 (这里创建了ubuntu sh)
docker container exec -it
在一个已经运行的容器里执行一个额外的command
eg:
docker container run -d -p 80:80 nginx
docker exec -it <ID> sh
*exit
退出
容器并不是mini的虚拟机:container其实就是进程。容器中的进程被限制了对CPU内存等资源的访问。当进程停止后,容器就退出
docker container top
显示container运行了那些进程
在本地查找是否有nginx这个image镜像,但是没有发现
去远程的image registry查找nginx镜像(默认的registry是Docker Hub)
下载最新版本的nginx镜像 (nginx:latest 默认)
基于nginx镜像来创建一个新的容器,并且准备运行
docker engine分配给这个容器一个虚拟IP地址
在宿主机上打开80端口并把容器的80端口转发到宿主机上
启动容器,运行指定的命令(这里是一个shell脚本去启动nginx)
Vue3支持Vue2大部分属性
Vue3代码被重新编译成分开的实现不同功能的moudle
性能提升:打包大小减少41%;初次渲染大小快了55%,更新快了133%;内存使用减少了54% (得益于重写虚拟dom和tree shaking实现)
Composition API (Composition:组合)
(复杂组件代码变得越来越难以维护) (解决两个问题:1.把相关的feature组合到一起, 不要分散在多个地方;2.可以非常容易滴重用比mixin有更高的灵活度)
包括:
新特性
Teleport - 瞬移组件位置
Suspense - 异步加载组建的新福音
全局API的修改和优化
更好的TS支持
Mixin的缺点:命名冲突; 不清楚暴露出来的变量的作用;重用到其他Componnent经常会遇到问题
<template>
<img alt="Vue logo" src="./assets/logo.png">
<h1>{{ count }}</h1>
<h1>{{ double }}</h1>
<button @click="increase">👆+1</button>
</template>
<script lang="ts">
import { defineComponent, ref, computed, } from 'vue';
export default defineComponent({
name: 'App',
setup() {
const count = ref(0)
const double = computed(() => {
return count.value * 2
})
const increase = () => {
count.value++
}
return {
count,
increase,
double
}
}
});
</script>
Vue2是通过Object.definedProperty实现的。对于对象新增的属性无能为力,对于数组则需要拦截他的原型方法实现响应式
Vue3使用Proxy实现响应式。Proxy在更高维度上进行属性拦截修改
在 setup 中使用的 hook 名称和原来生命周期的对应关系
beforeCreate -> 不需要
created -> 不需要
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeUnmount -> onBeforeUnmount
unmounted -> onUnmounted
errorCaptured -> onErrorCaptured
// 调试用
renderTracked -> onRenderTracked
renderTriggered -> onRenderTriggered
Vue2对ts支持非常有限,Vue2依赖this上下文对象,往外暴露属性。vue的this比普通的JS object多了很多黑魔法。Vue2在推出中就没有考虑对ts的集成
vue2 都是用class的方式
Vue3有了defineComponent
setup(props,context) // props可以自动推论, context提供了三个this中最常用的属性(setup无法访问vue2最常用的this)
问题: eg: Dialog被包裹在其他组件之中,容易被干扰
(类似react 传送门)
<teleport to="#modal">
....
</teleport>
问题: 异步困境
<Suspense>
<template #default >
<async-show></async-show>
</template>
<template #fallback >
<h1>loadinng....</h1>
</template>
</Suspense>
// AsyncShow
<template>
<h1>{{result}}</h1>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
setup() {
return new Promise((resolve) => {
setTimeout(() => {
return resolve({
result: 42
})
}, 3000)
})
}
})
</script>
1.Vue.config -> app.config
2.全局注册API
3.行为扩展API
构建工具的高阶封装,最核心的是
rollup
和Vue3一起发布的。2.0之后完全独立的框架
vitejs.dev
(官网对比)
Vite做的最好的一个
vite:
看webpack的官网 documentation 非常非常复杂。功能非常非常多,也非常强大。它希望把它能考虑加载的东西全部加载进去。同时它也希望它的核心功能帮助其他功能进行实现。比如我们可以基于webpack 实现一个webpack-dev-server。其实也是围绕webpack核心功能进行构建的。 它有各种各种构建的方式
rollup更加单一专一的工具。rollup诞生的目的就是build ES Module。同时它只专注于build js而不考虑平台。
对于webpack 我们编译出来代码会有很多webpack本身的工具函数。帮助我们加载模块。rollup不会有什么它专有的函数在里面。它会遵循esm的标准。它build出来的代码就符合common.js的标准或者是像umd的标准。没有自己的工具在里面,比较纯粹。只是符合这个规范。rollup初衷不是前端项目,而是工具类库。
vite:目标是工程、项目开发。不像webpack和rollup他们是工具,更加focus或者说执行的是”构建“。vite更加上层。High Level,初衷就是更好的体验,一开始设计就是为了去解决项目开发构建。学习成本比webpack、rollup低很多。
总结:webpack更全面,rollup更专一,vite为项目而生而不是为构建而生。
Vite: 自身插件系统 生态兼容rollup
1.0 vue3
2.0 跨框架
FastRefresh: (官方插件)相比react-hot-loader。解决了很多rhl
无法解决的问题;速度更快;以及支持局部更新
当我们使用CRA创建React App。挂在的APP节点默认被React.StrictMode
严格模式包裹了(react16.3加上的)
React.StrictMode: 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告
state.id值为多少?
大部分人都会说
1
。当然生产环境确实如此。但包裹React.StrictMode
下,开发环境答案是2
React.StrictMode,它在开发环境中将 constructor 函数调用了两次。
为了检测意外的副作用,通过调用两次的方式将一些隐藏地比较深的副作用放大
再比如如下例子
let cache
export default () => {
const [name, setname] = useState('')
const [age, setAge] = useState(18)
// dept name变化,userInfo值变化.
const userInfo = useMemo(() => {
return {
fullname: `my name is ${name}`,
}
}, name)
if (!cache) {
cache = userInfo
}
console.log('!!!', {
cache,
userInfo,
equal: cache === userInfo,
})
return (
<div>
<input type="text" value={age} onChange={e => setAge(e.target.value)} placeholder='age'/>
{JSON.stringify({
name,
age,
userInfo,
})}
</div>
)
}
name没有变化userInfo不需要重新计算, 重新渲染。但开启StrictMode
之后userInfo每次返回的都是全新的对象。不受dept影响
运行代码:这里第一次打印 equal: true,没问题。这里修改input输入框age的值。按道理useMemo的dept只依赖name。但是我们每次修改age,打印出来的都是equal:false,证明userinfo还是render了,渲染出来了新对象
去掉严格模式就正常
使用严格模式开发 要慎重。不光是useMemo、useState、useCallback都不是返回同一个对象
官方说法是:通过在开发环境下故意重复调用,帮助你发现它们,使它们更具确定性。我是觉得挺坑的。
<body>
<div>
普通对象操作:
<span id="common"></span>
</div>
<div>
Reflect函数操作:
<span id="reflect"></span>
</div>
</body>
let user = {
_name: "Guest",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) {
return target[prop];
}
});
let admin = {
__proto__: userProxy,
_name: "Admin"
};
document.getElementById('common').innerHTML = admin.name
let user2 = {
_name: "Guest",
get name() {
return this._name;
}
};
let userProxy2 = new Proxy(user2, {
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
}
});
let admin2 = {
__proto__: userProxy2,
_name: "Admin"
}
document.getElementById('reflect').innerHTML = admin2.name
直接使用普通对象: target[prop]
这里target
实际上还是user
。this 指向的是user
,而不是admin。故html显示Guest
使用Reflect.get(target, prop, receiver)
,第三个参数虽然没有用到,但是它代表的是 Proxy 实例或者继承 proxy 的那个对象。即:代理对象本身,同时也有可能是继承了代理对象的对象,具体区别于调用方
如果使用Reflect.get(target, prop)
相当于 target[prop]
。
**1. Proxy 中接受的 receiver 形参,表示代理对象本身 或者 继承了代理对象的对象。
工作上遇到一个问题,是这样的
假如一个函数test(p)
type p = 't' | 'h'
当test('t')时 返回值类型为 string;
当test('p')时 返回值类型为 number;
type p = 't' | 'h'
function test(p: 't'): number
function test(p: 'h'): string
function test(p: 't' | 'h'): number | string {
if (p == "t") {
return 123
} else {
return "123"
}
}
现阶段的TS,无法做到 p === 't'
或者 p extends 't'
来推断出就是type p = 't' | 'h'
中的't'(ts 的自动推断主要依赖 typeof
、instanceof
、对象类型的属性判断等)
所以我们显式的声明返回值类型_(借助interface)_。 并且通过mapping type
得到返回类型
interface TestReturn {
t: number
h: string
[key: string]: unknown
}
// types util
function isKeys(key: unknown): key is keyof TestReturn {
return key === 'a' || key === 'b'
}
function test<T extends keyof TestReturn>(p: T): TestReturn[Key]
// 重载了一下防止未知字段传入
function test(key: string): unknown {
const store: Values = {
t: 123
h: '123'
}
if (isKeys(key)) {
return store[key]
}
return store[key]
}
个人比较喜欢第二种方式
官方eslint配置之后,副作用函数有props或者state变量未存在于依赖数组中,会有 exhaustive-deps
规则的提醒。
然而一些场景下又与这些规则不符。
比如时间类函数类似componentDidMount
的功能一般会这么写:
useEffect(() => {
// ....
console.log('var1', var1)
console.log('var12, var2)
}, [])
假设var1
& var2
是我们通过 useState或者props传进来的变量。再或者,里面有函数执行,比如一些请求异步函数。然而这里我们没有在依赖数组中加入这些变量(deps)。就触发了 exhaustive-deps
规则的提醒。黄色的小warning,大可不必理会。(自动lint --fix
一股脑把依赖添加到依赖数组里有时会出错)。强迫症出于对eslint的规则遵守。给出的解决方案就是 通过useRef来回避这个问题。对副作用函数中的每个变量都创建对应的useRef值。这样怎么说又麻烦有不合理。比如我们封装一个useMount
function useMount(fn) {
const fnRef = useRef()
fnRef.current = fn;
useEffect(() => {
fnRef.current();
}, [fn]);
}
warning解除
再举个栗子
function Modal({visible, value}) {
useEffect(() => {
visible && console.log('value', value)
}, [visible])
}
这样一个简单的例子。依旧触发了就触发了 exhaustive-deps
规则的提醒,有黄线。
但确确实实有这样的需求和问题。例子中Modal组件需要根据visible变量变化执行 ·”xxxxxx“一系列操作。操作中会引用其他变量比如这里的value
。但是我们确实不希望value
加在deps
中,因为我们并不关心它的变化。(需要引用到其它的 props 或 state 变量)如果把所有变量都加入deps
中带来的副作用可能会造成重复执行,这是非预期的也是没必要的事情(为了解决eslint小黄线而重复执行一次。这里 我们又迫于无奈需要一个辅助变量来记录 visible 变量的前一状态值,用来在副作用函数中判断是否因为 visible 变量变动触发的函数执行。我们又要用useRef
封装一个前值函数。
const usePrevious = (value) => {
const ref = useRef()
useEffect(() => {
ref.current = value
})
return ref.current
}
function Modal({visible, value}) {
const prev = usePrevious(visible)
useEffect(() => {
prev !==visible && visible && console.log('value', value)
}, [visible, prev, value])
}
希望 useEffect 的依赖数组中是与副作用函数更有效的变量,而不是副作用函数中全部引用的变量。
在 类组件中,传递给子组件的props
变量中函数大多数都是this.xxx
形式,即引用都是稳定的。useCallback 函数,只要依赖数组保持稳定,会返回一个引用稳定的函数。useCallback在项目中使用有时候会耗尽心思,一单一单 组件树上层一个不小心出了问题,有是前功尽弃
比如
function Button({ child, disabled, onClick }) {
const handleBtnClick = useCallback(() => {
!disabled && onClick?.()
}, [disabled, onClick]);
return (
<button onClick={handleBtnClick}>{child}</button>
);
}
function App() {
const onBtnClick = () => {}
return (
<Button onClick={onBtnClick} />
)
}
这么一个App
组件包裹着Button
组件。我们希望_disabled 和 onClick 变量的引用变动时才返回一个新的引用的函数_。但是 APP
组件这一层 没有用useCallback
包裹onBtnClick
保持函数稳定,每次传递给Button
的都是新函数(或者说全新的函数引用)子组件接收触发deps
,生成一个全新引用的 handleBtnClick
函数。怎么办,这里我们再在子组件Button
中用useRef
解决问题
function Button({ child, disabled, onClick }) {
const handleBtnClickRef = useRef()
handleBtnClickRef.current = () => {
!disabled && onClick?.()
};
const handleBtnClick = useCallback(() => {
handleBtnClickRef.current()
}, [handleBtnClickRef])
return (
<button onClick={handleBtnClick}>{child}</button>
);
}
我们用useRef
- handleBtnClickRef
保存最新的函数。变量引用是固定的,所以 handleBtnClick 函数的引用也是固定的。触发 onClick 回调函数也能拿到最新的 disabled 和 onClick 值
这里再举一个例子:需要得到一个不变的函数引用,但这个不变的函数执行的时候,执行的是传递的最新函数。这个实在wukong
这各项目里用的。直接复制过来
export function useCallbackRef(fn) {
const fnRef = useRef(fn)
fnRef.current = fn
return useCallback(((...args) => fnRef.current(...args)), [])
}
这里useRef
成为了解决问题的关键。很像class里面的this.xxx
。 mutable
的方式去解决问题。副作用、依赖数组真的增加了很多心智负担
webpack5 一些实践优化
前端框架,核心能力--模板引擎实现
页面正常工作需要实现的功能流程:
如果使用前端框架(数据驱动!!!)
Vue为例页面渲染分4步
function Test1(props: { onChange: (res: string) => void }) {
return (
<input
onChange={(e) => {
props.onChange(e.target.value);
}}
/>
);
}
function Test1Wrapper() {
let a = "";
const [b, setB] = useState(0);
useEffect(() => {
const i = setInterval(() => {
setB((res) => ++res);
}, 1000);
return () => {
clearInterval(i);
};
}, []);
return (
<>
<Test1
onChange={(res: string) => {
a = res;
}}
/>
{b}
<button
onClick={() => {
console.log(a);
}}
>
show a
</button>
</>
);
}
直接写变量 - 时有时无
-> 因为 React 中,类型为 Dispatch 的方法(useReducer 的 dispatch 和 useState 的 setState),都会导致当前函数重新运行
而此处的 a 是个变量,它会在函数运行时重新赋值为 “”,导致之前的状态丢失
因此,如果 log 按钮点击前发生了任何其他依赖的状态变化(包括 context,state,props),a 都会被重置
type Required<T> = {
[P in keyof T]-?: T[P];
};
-?
来标注属性为必填的属性,-
用来清除可选性实现 Required。。
+?
也是有效的,Partial可知 +? 的 +`是可以省略的
eg:
interface Props {
a?: string
b?: string
c?: string
}
// 仅保留b,c属性并转为必填
type NewProps1 = Required<Pick<Props, 'b' | 'c'>>
// 需要保留Props的所有属性,但是b,c需要必填
type NewProps2 = Partial<Props> & Required<Pick<Props, 'b' | 'c'>>
type Record<K extends keyof any, T> = {
[P in K]: T;
};
eg:
// 简单的限定键和值的类型
type Obj1 = Record<string, string>
// 基于其他类型生成新的类型
type FruitTypes = 'apple' | 'banana' | 'pear'
interface FruitInfo {
name: FruitTypes
price: number
}
type Fruits = Record<FruitTypes, FruitInfo>
const fruits: Fruits = {
apple: {
name: 'apple',
price: 10
},
banana: {
name: 'banana',
price: 11
},
pear: {
name: 'pear',
price: 12
}
}
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Exclude<T, U> = T extends U ? never : T;
eg:
// 工具类型 SelectRequired<T, K in keyof T>
interface Props {
a?: string
b?: string
c?: string
d: string
}
type NewProps = SelectRequired<Props, 'b' | 'c'>; // { a?: string, b: string, c: string, d: string }
// 实现
type SelectRequired<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>
type Exclude<T, U> = T extends U ? never : T;
eg:
// 排除一个具体的值
type T0 = Exclude<"a" | "b" | "c", "a"> // "b" | "c"
// 排除一种类型
type T1 = Exclude<string | number | (() => void), Function>
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;
type NonNullable<T> = T extends null | undefined ? never : T
/**
* Obtain the parameters of a function type in a tuple
*/
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
type LabeledTupleType = [id: number, name: string];
在元组类型的语法中,我们可以对泛型使用展开运算符 & 可以在元组中的任何位置使用剩余元素
const TupleA = ['A'] as const;
const TupleB = ['B'] as const;
type TupleType = readonly any[];
function concat<T extends TupleType, U extends TupleType>(arr1: T, arr2: U): [...T, ...U] { // 4.0版本之前会报错:ts(1256)
return [...arr1, ...arr2]; // 4.0版本之前会报错:ts(2741)
}
const TupleC = concat(TupleA, TupleB); // ['A', 'B'] // 合并两个元组为一个新的元组的函数!!! 从而得到了新的元组类型 ['A', 'B']
type ConcatedTuple = [ ...(typeof TupleA), ...(typeof TupleB)];
极大地提升函数式编程的类型体验和可能性
可以在函数组合中使用可变元组约束高阶函数入参和返回值的类型,比如对 JavaScript 内置 bind 方法更好地进行类型检测支持
Break Change🤓 使得字符串类型也具备了可变可运算的能力。
type PrefixType<P extends string, N extends string> = `${P}/${Capitalize<string & N>}`;
type UserLoginAction = PrefixType<'User', 'login'>; // 'User/Login'
可以对字符串类型进行拼接等操作 更加灵活,比如我们在定义路由
的类型以及redux action type
的类型都可以用到
// 比如将 { id: string; name: string} => “user/:id/:name/”
type RoutePath<P extends string, N extends string> = `user/${P}/${N}/`
type a = RoutePath<'123', 'Lac'> // "user/123/Lac/"
实现 lodash get
type PropType<T, Path extends string> =
string extends Path ? unknown :
Path extends keyof T ? T[Path] :
Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
unknown;
declare function get<T, P extends string>(obj: T, path: P): PropType<T, P>;
const obj = { a: { b: {c: 42, d: 'hello' }}};
const value = get(obj, "a.b.c")
在映射类型中,我们可以使用 as 操作符对键名重新映射(可以理解为针对类型的类型断言)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserInfoGetters = Getters<{ id:number; name: string; }> // { getId: () => number; getName: () => string; }
可以在元组的任何地方使用剩余元素表达式,而不再仅仅局限于元组的尾部
const prefixRestTuple: [...rest: string[], number] = ['a', 'b', 1];
const middleRestTuple: [boolean, ...rest: string[], number] = [true, 'a', 'b', 1];
必须显式注解 yield 表达式的返回值类型,否则会提示 noImplicitAny 错误
这个还没有测试,如果使用Redux-saga
管理effect副作用 yield
这块要注意
以保证基础类中的方法不会被覆盖
性能分析:页面白屏过久、操作卡顿
通常前端性能分析两个角度:时间和空间
时间:常见耗时:页面加载耗时、渲染耗时、网络耗时、脚本执行耗时等
空间资源占用,包括CPU占用、内存占用、本地缓存占用等
Chrome DevTools
lighthouse:前身是Chrome DevTools的Audits。优势在于自动化、成本低,会收集网站加载时的性能数据,并根据最佳实践给每一项进行打分,同时针对最低分给出对应的优化方案
- 四个:驱动(Driver)、收集器(Gatherers)、审查器(Audits)和报告(Report)
- 过程:
- 当网站页面开始加载之后,Lighthouse 会使用驱动(Driver)通过 Chrome DevTools Protocol 获取页面的性能数据;
- 驱动(Driver)获取到的数据会被收集器(Gatherers)进行收集,并输出被称为 Artifact 的结果;
- Artifact 会作为审查器(Audits)的输入,审查器会对其运行测试,然后分配通过/失败/得分的结果;
- 审查的结果给到报告(Report),对各个部分进行加权和统计得到面向用户的报告(如最佳实践),并将该报告渲染给用户。
(但是lighthouse不能用于运行时的性能分析,无法给到最佳实践以外的数据/建议)
performance:前身 Timeline面板,常用在页面运行时使用,比如用户点击操作之后的逻辑执行、页面滚动时的页面渲染情况
(对页面进行录制最好打开隐身模式 确保Chrome不被扩展插件影响)
Monitor: 对CPU占用率、内存使用大小、挂在Dom数···
面板可以看到详细的性能数据报考函数调用的情况、各类事件的顺序和耗时、CPU占用情况···我们可以看这些定位性能问题
(用performanc进行分析,分析过程比较繁琐。上手成本不低,除了页面加载耗时、网络耗时。定位具体问题需要看FPS、CPU占用以及火焰图)
ighouse自动化: 通过自动化任务跑脚本的方式,使用lighthouse跑分析报告。通过对比以往的数据来进行功能变更、性能优化等场景的性能回归(成本低)
Chrome-Dev-Protocol: 允许第三方对Chrome网站进行检查、调试、分析等操作。可以自行开发工具 通过Chrome-Dev-Protocol获取Chrome中网站数据
其实就是一个WEB应用。当我们打开 Chrome DevTools 的时候,浏览器内核 Chromium 本身会作为一个服务端,它会通过 WebSocket 与 Chrome DevTools 进行通信
过程如下:
Chrome-Dev-Protocol 不管是win、mac、手机端调试都用的该协议。提供的内容:具有与浏览器不同交互(页面、SW、扩展程序)的API。
该协议把不同操作分为不同的域(就是Devtools中的不同功能模块)
具体几个:
通过使用Chrome-Dev-Protocol 我们可以拿到很多数据包括网络数据、性能数据(JS Runtime像window.performance、window.chrome.loadTimes())
如何使用协议:
Chrome-Dev-Protocol可以模拟浏览器环境 通过一系列工具、规则运行你的页面,最后最后的得到审计报告。那么也可以根据业务的需要对这些数据进行自动化的分析,结合前端工程化在项目上线前进行自动化任务的执行、检测、生成对比报告等,实现自动化性能监控
Next.js
• 直观的、 基于页面 的路由系统(并支持 动态路由)
• 预渲染。支持在页面级的 静态生成 (SSG) 和 服务器端渲染 (SSR)
• 自动代码拆分,提升页面加载速度
• 具有经过优化的预取功能的 客户端路由
• 内置 CSS 和 Sass 的支持,并支持任何 CSS-in-JS 库
• 开发环境支持 快速刷新
• 利用 Serverless Functions 及 API 路由 构建 API 功能
• 完全可扩展
When to Use Static Generation v.s. Server-side Rendering
We recommend using Static Generation (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request.
You can use Static Generation for many types of pages, including:
• Marketing pages
• Blog posts
• E-commerce product listings
• Help and documentation
You should ask yourself: "Can I pre-render this page ahead of a user's request?" If the answer is yes, then you should choose Static Generation.
On the other hand, Static Generation is not a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request.
In that case, you can use Server-side Rendering. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate frequently updated data.
When would you use Server-side rendering?
A: When the data needs to be up-to-date with every request
第一次构建生成文件缓存,第二次构建看文件有没有变化,没有变化就是用缓存
三个问题:
A: thread-loader
A: 最好都加
A:使用缓存有代价,所以必须是很重的任务才有效果,否则适得其反
splitChunks: {
minSize
maxInitialRequests
chunks
minChunks
}
制作平台:IconPark
实现基于 single-spa
HTML Entry 接入方式:接入子应用像iframe一样简单
只配置子应用的首页地址
根据地址来请求整个页面,从页面中提出 script 和 style 资源
script 通过 eval 来执行
资源预加载
配合 HTML Entry ,在空闲时候加载子应用的首页的 style 和 script, style 写入 html,script 放入缓存
样式隔离:子应用之间的样式互不干扰
JS 沙箱:子应用之间的 全局变量/事件 不冲突
mount 生命周期时 proxy 劫持 window
unmount 生命周期时恢复 window 属性和被劫持的方法
prefetch 预加载
requestIdleCallback 时机
请求所有注册的app,匹配 style 和 script 资源,预先下载缓存
接入
添加 package.json 的 name 属性,作为唯一标记,应用之间不重复
打包出来的配置需要更改
需要提供一个不带侧边栏(头)的版本
OO:面向对象
原则一:容器与内容分离
原则二:结构与皮肤分离
原则三:面向设计开发
应用:Grid栅格系统
块(Block)、 元素(Element)、修饰符(Modifier)
作用:命名规范、让页面结构更清晰
进阶版oocss
分类:Base、Layout、 Modules、State、Theme
好处:易维护、易复用、易扩展。。
命名规范:.l-header .is-hidden .theme-nav
一个样式属性一个类
好处:极强的复用性、维护成本低
坏处:破坏了CSS命名语义化
收下我的膝盖
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.