Skip to main content

19 posts tagged with "前端"

View All Tags

· 11 min read
LIU

Vue3 核心特性与实践指南

Vue3 是一个革命性的版本,它不仅带来了性能的提升,更重要的是引入了组合式 API,彻底改变了我们编写 Vue 组件的方式。本文将深入探讨 Vue3 的核心特性,并通过实际案例展示如何更好地使用这些特性。

1. 组合式 API (Composition API)

组合式 API 是 Vue3 最重要的特性之一,它解决了 Vue2 中代码组织的问题,使得逻辑复用和代码组织变得更加灵活。

1.1 基础概念

  • setup() 是组合式 API 的入口点,它在组件实例创建之前执行
  • 由于 setup 在组件实例创建之前执行,所以无法访问 this
  • Vue2 的选项式 API 写法仍然完全兼容,可以渐进式迁移
  • setup 函数接收两个参数:propscontext
import { defineComponent } from 'vue'

export default defineComponent({
props: {
title: String
},
setup(props, context) {
// props 是响应式的,但不要解构它
console.log(props.title)

// context 包含 attrs, slots, emit 等
const { attrs, slots, emit } = context

return {
// 返回的内容会暴露给模板
}
}
})

1.2 响应式系统

Vue3 的响应式系统基于 Proxy 实现,提供了更强大的响应式能力。

ref 的使用

import { ref, watchEffect } from 'vue'

export default {
setup() {
// ref 用于基本类型的响应式
const count = ref(0)

// 访问 ref 的值需要使用 .value
console.log(count.value) // 0

// 在模板中会自动解包,不需要 .value
const increment = () => {
count.value++
}

// watchEffect 会自动追踪依赖
watchEffect(() => {
console.log('count is:', count.value)
})

return {
count,
increment
}
}
}

reactive 的使用

import { reactive, computed, watch } from 'vue'

export default {
setup() {
// reactive 用于对象的响应式
const state = reactive({
count: 0,
double: computed(() => state.count * 2),
history: []
})

// 监听单个属性
watch(
() => state.count,
(newValue, oldValue) => {
state.history.push({
newValue,
oldValue,
timestamp: new Date()
})
}
)

// 深度监听对象
watch(
() => state,
(newValue) => {
console.log('state changed:', newValue)
},
{ deep: true }
)

return {
state
}
}
}

1.3 生命周期钩子

Vue3 的生命周期钩子都带有 on 前缀,并且只能在 setup 中使用:

import { 
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured
} from 'vue'

export default {
setup() {
onBeforeMount(() => {
console.log('组件挂载前')
})

onMounted(() => {
console.log('组件已挂载')
// 可以在这里进行 DOM 操作
})

onBeforeUpdate(() => {
console.log('组件更新前')
})

onUpdated(() => {
console.log('组件已更新')
})

onBeforeUnmount(() => {
console.log('组件卸载前')
})

onUnmounted(() => {
console.log('组件已卸载')
// 可以在这里清理定时器、事件监听等
})

onErrorCaptured((err, instance, info) => {
console.error('捕获到错误:', err)
return false // 阻止错误继续传播
})
}
}

2. 响应式工具函数

2.1 toRefs 和 toRef

import { reactive, toRefs, toRef } from 'vue'

export default {
setup() {
const state = reactive({
count: 0,
name: 'Vue3'
})

// toRefs 将整个对象转换为响应式引用
const { count, name } = toRefs(state)

// toRef 创建单个属性的响应式引用
const countRef = toRef(state, 'count')

// 这些引用都是响应式的
console.log(count.value) // 0
console.log(name.value) // 'Vue3'

return {
count,
name,
countRef
}
}
}

2.2 其他响应式工具

import { 
isRef,
unref,
isReactive,
isReadonly,
markRaw
} from 'vue'

export default {
setup() {
const count = ref(0)

// 检查是否是 ref
console.log(isRef(count)) // true

// 获取 ref 的值,如果不是 ref 则返回原值
console.log(unref(count)) // 0

const state = reactive({})
// 检查是否是响应式对象
console.log(isReactive(state)) // true

// 检查是否是只读对象
console.log(isReadonly(readonly(state))) // true

// 标记对象为非响应式
const raw = markRaw({ count: 0 })
}
}

3. 自定义 Hook

自定义 Hook 是组合式 API 最强大的特性之一,它让我们能够轻松地复用逻辑。

3.1 鼠标位置 Hook

import { toRefs, reactive, onMounted, onUnmounted } from 'vue'

function useMousePosition() {
const state = reactive({
x: 0,
y: 0,
isMoving: false
})

const updateMouse = (e: MouseEvent) => {
state.x = e.pageX
state.y = e.pageY
state.isMoving = true

// 使用 requestAnimationFrame 优化性能
requestAnimationFrame(() => {
state.isMoving = false
})
}

onMounted(() => {
document.addEventListener('mousemove', updateMouse)
})

onUnmounted(() => {
document.removeEventListener('mousemove', updateMouse)
})

return toRefs(state)
}

3.2 网络请求 Hook

import { ref, onMounted } from 'vue'

interface UseFetchOptions {
immediate?: boolean
onSuccess?: (data: any) => void
onError?: (error: Error) => void
}

function useFetch<T>(url: string, options: UseFetchOptions = {}) {
const data = ref<T | null>(null)
const error = ref<Error | null>(null)
const loading = ref(false)

const fetchData = async () => {
loading.value = true
try {
const response = await fetch(url)
data.value = await response.json()
options.onSuccess?.(data.value)
} catch (e) {
error.value = e as Error
options.onError?.(e as Error)
} finally {
loading.value = false
}
}

if (options.immediate) {
onMounted(fetchData)
}

return {
data,
error,
loading,
fetchData
}
}

// 使用示例
export default {
setup() {
const { data, error, loading, fetchData } = useFetch('/api/users', {
immediate: true,
onSuccess: (data) => {
console.log('数据加载成功:', data)
}
})

return {
data,
error,
loading,
fetchData
}
}
}

4. Teleport 组件

Teleport 是 Vue3 新增的组件,用于将组件的内容传送到 DOM 树的其他位置。这在处理模态框、弹出框等场景特别有用。

<template>
<div class="app">
<button @click="showModal = true">打开模态框</button>

<Teleport to="body">
<div v-if="showModal" class="modal-overlay" @click="closeModal">
<div class="modal-content" @click.stop>
<h2>{{ title }}</h2>
<p>{{ content }}</p>
<div class="modal-footer">
<button @click="confirm">确认</button>
<button @click="closeModal">取消</button>
</div>
</div>
</div>
</Teleport>
</div>
</template>

<script>
import { ref } from 'vue'

export default {
props: {
title: {
type: String,
default: '提示'
},
content: {
type: String,
required: true
}
},

setup(props, { emit }) {
const showModal = ref(false)

const closeModal = () => {
showModal.value = false
emit('close')
}

const confirm = () => {
emit('confirm')
closeModal()
}

return {
showModal,
closeModal,
confirm
}
}
}
</script>

<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}

.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
min-width: 300px;
}

.modal-footer {
margin-top: 20px;
text-align: right;
}

.modal-footer button {
margin-left: 10px;
}
</style>

5. 性能优化

Vue3 在性能方面有显著提升

5.1 虚拟 DOM 优化

  • 静态树提升:将静态内容提升到渲染函数之外
  • 静态属性提升:将静态属性提升到渲染函数之外
  • 补丁标记:为动态节点添加标记,优化 diff 算法

5.2 响应式系统优化

import { shallowRef, shallowReactive, markRaw } from 'vue'

export default {
setup() {
// 浅层响应式,只跟踪顶层属性
const shallowState = shallowReactive({
user: {
name: 'Vue3',
address: {
city: 'Beijing'
}
}
})

// 浅层 ref,只跟踪 .value 的变化
const shallowRef = shallowRef({
count: 0
})

// 标记为非响应式
const staticData = markRaw({
config: {
theme: 'dark'
}
})
}
}

5.3 组件优化

import { defineAsyncComponent } from 'vue'

export default {
components: {
// 异步组件
AsyncComponent: defineAsyncComponent(() =>
import('./AsyncComponent.vue')
),

// 带加载状态的异步组件
AsyncComponentWithLoading: defineAsyncComponent({
loader: () => import('./AsyncComponent.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})
}
}

6. TypeScript 支持

Vue3 使用 TypeScript 重写,提供了更好的类型支持。

6.1 组件类型定义

import { defineComponent, PropType } from 'vue'

interface User {
id: number
name: string
age: number
}

export default defineComponent({
props: {
// 基础类型
title: {
type: String,
required: true
},

// 对象类型
user: {
type: Object as PropType<User>,
required: true
},

// 数组类型
items: {
type: Array as PropType<User[]>,
default: () => []
}
},

setup(props) {
// props 类型会被正确推导
console.log(props.user.name)

return {}
}
})

6.2 组合式 API 类型

import { ref, computed } from 'vue'

interface State {
count: number
name: string
}

export default defineComponent({
setup() {
// ref 类型
const count = ref<number>(0)

// reactive 类型
const state = reactive<State>({
count: 0,
name: 'Vue3'
})

// computed 类型
const double = computed<number>(() => count.value * 2)

return {
count,
state,
double
}
}
})

7. 调试技巧

7.1 渲染追踪

import { onRenderTracked, onRenderTriggered } from 'vue'

export default {
setup() {
onRenderTracked((event) => {
console.log('组件渲染追踪:', {
target: event.target,
type: event.type,
key: event.key
})
})

onRenderTriggered((event) => {
console.log('组件渲染触发:', {
target: event.target,
type: event.type,
key: event.key,
newValue: event.newValue,
oldValue: event.oldValue
})
})
}
}

7.2 性能分析

import { onMounted, onUnmounted } from 'vue'

export default {
setup() {
let startTime

onMounted(() => {
startTime = performance.now()
})

onUnmounted(() => {
const endTime = performance.now()
console.log(`组件生命周期: ${endTime - startTime}ms`)
})
}
}

8. 最佳实践

8.1 代码组织

// composables/useCounter.js
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const double = computed(() => count.value * 2)

function increment() {
count.value++
}

function decrement() {
count.value--
}

return {
count,
double,
increment,
decrement
}
}

// components/Counter.vue
import { useCounter } from '../composables/useCounter'

export default {
setup() {
const { count, double, increment, decrement } = useCounter()

return {
count,
double,
increment,
decrement
}
}
}

8.2 状态管理

// store/counter.js
import { reactive } from 'vue'

export const store = reactive({
count: 0,
increment() {
this.count++
}
})

// components/Counter.vue
import { store } from '../store/counter'

export default {
setup() {
return {
store
}
}
}

9. 迁移建议

从 Vue2 迁移到 Vue3 时,建议采用渐进式迁移策略:

  1. 使用 @vue/composition-api 插件在 Vue2 中提前使用组合式 API
  2. 优先迁移新功能到组合式 API
  3. 使用 defineComponent 包装组件获得更好的类型推导
  4. 注意生命周期钩子的变化
  5. 使用新的响应式 API 替代 Vue.observable

10. 总结

Vue3 带来了许多激动人心的新特性,特别是组合式 API 的引入,使得代码组织更加灵活,逻辑复用更加方便。通过合理使用这些特性,我们可以构建出更易维护、性能更好的应用。

· 4 min read
LIU

Typescript

  • 静态类型风格的类型系统
  • javascript的超集
  • 从es6到es10甚至是esnext的语法支持
  • 类型检查、自动补全类型方法
  • ts的思想就是获得类型

· 5 min read
LIU

Electron 简介

Electron 是一个使用 JavaScript、HTML 和 CSS 构建跨平台桌面应用程序的框架。它将 Chromium 和 Node.js 合并到同一个运行时环境中,使开发者能够使用 Web 技术构建桌面应用。

主进程 - Main Process

  • 可以使用和系统对接的 Electron API - 创建菜单,上次文件等等
  • 可以创建多个 渲染进程 Renderer Process
  • 全面支持 Node.js
  • 只有一个主进程,作为整个程序的入口点
  • 负责创建和管理应用窗口
  • 处理系统级事件(如应用启动、退出等)
  • 管理应用生命周期
  • 可以访问所有 Node.js API

渲染进程 - Renderer Process

  • 可以有多个,每个对应一个窗口
  • 每个都是一个单独的进程
  • 全面支持 Node.js 和 DOM API
  • 可以使用一部分 Electron 提供的 API
  • 负责渲染用户界面
  • 处理用户交互
  • 可以访问部分 Node.js API(需要配置)
  • 默认情况下运行在沙箱环境中

进程之间的通讯方式

Electron 使用 IPC (进程间通信) 在进程之间进行通讯,和 Chromium 完全一致。

IPC 通信方式

  1. ipcMain (主进程)

    • 监听来自渲染进程的消息
    • 发送消息到渲染进程
    • 处理渲染进程的请求
  2. ipcRenderer (渲染进程)

    • 发送消息到主进程
    • 监听来自主进程的消息
    • 请求主进程执行操作

示例代码

主进程 (main.js):

const { ipcMain } = require('electron')

// 监听来自渲染进程的消息
ipcMain.on('message-to-main', (event, arg) => {
console.log(arg) // 打印 "Hello from renderer"
// 回复消息给渲染进程
event.reply('message-from-main', 'Hello from main process')
})

渲染进程 (renderer.js):

const { ipcRenderer } = require('electron')

// 发送消息到主进程
ipcRenderer.send('message-to-main', 'Hello from renderer')

// 监听来自主进程的回复
ipcRenderer.on('message-from-main', (event, arg) => {
console.log(arg) // 打印 "Hello from main process"
})

实践

项目设置

  1. 初始化项目
npm init
npm install electron --save-dev
  1. 配置开发环境
  • 安装nodemon
    • npm install nodemon --save-dev
    • 修改package.json "start": "nodemon --watch main.js --exec 'electron .'"

主进程配置

main.js : 创建窗口 - BrowserWindow

const {app, BrowserWindow} = require('electron')

app.on('ready', () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true
}
})

mainWindow.loadFile('./index.html')

// 打开开发者工具
mainWindow.webContents.openDevTools()
})

// 处理窗口关闭事件
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

安全最佳实践

  1. 上下文隔离

    • 启用 contextIsolation: true
    • 使用 preload 脚本安全地暴露 API
  2. 沙箱模式

    • 默认启用沙箱模式
    • 限制渲染进程的权限
  3. 内容安全策略 (CSP)

    • 设置适当的 CSP 头
    • 限制资源加载来源
  4. 进程间通信安全

    • 验证所有 IPC 消息
    • 限制 IPC 通道的使用

调试技巧

  1. 主进程调试

    • 使用 --inspect 参数启动应用
    • 在 Chrome DevTools 中调试
  2. 渲染进程调试

    • 使用 webContents.openDevTools()
    • 使用 Chrome DevTools 进行调试

打包和分发

  1. 使用 electron-builder
npm install electron-builder --save-dev
  1. 配置 package.json
{
"build": {
"appId": "com.example.app",
"mac": {
"category": "public.app-category.utilities"
},
"win": {
"target": "nsis"
},
"linux": {
"target": "AppImage"
}
}
}
  1. 构建命令
electron-builder build

性能优化

  1. 内存管理

    • 及时释放不需要的资源
    • 使用 webContents.clearCache()
  2. 启动优化

    • 使用 app.commandLine.appendSwitch('disable-gpu')
    • 延迟加载非关键模块
  3. 渲染进程优化

    • 使用 requestIdleCallback
    • 避免阻塞主线程

常见问题解决

  1. 白屏问题

    • 检查文件路径
    • 确认 webPreferences 配置
  2. 进程通信失败

    • 检查 IPC 通道名称
    • 确认消息格式
  3. 打包问题

    • 检查依赖项
    • 确认资源路径

· 13 min read
LIU

JS的垃圾回收机制

垃圾的产生和回收

写代码时,创建的任何基本类型、对象、函数等等都是需要占用内存的,这些都是引擎去分配的,不需要我们显示手动的去分配内存

javascript 的引用数据类型是保存在堆内存中的,然后在栈内存中保存的一个对堆内存中实际对象的引用,所以javascript 中对引用数据类型的操作,都是操作对象的引用而不是实际的对象,可以理解为,栈内存中保存了一个地址,这个地址和堆内存中的实际值是相关联的

let test = {
a:"one"
};
test = ['two']

· 11 min read
LIU

uni-app

  • uni-app 中 css 关于 calc()函数计算无效问题

    原本以为uni-app不支持calc计算,后来发现:

    计算符:+ - * /

    计算符前后都需要空格,否则计算无效

.tets{
height: calc(var(--status-bar-height) + 10px);
}

· 11 min read
LIU

ssh

  • SSH连接报错:Permission denied, please try again

    • 当使用 SSH 登录云服务器 ECS (Elastic Compute Server) Linux 服务器时,如果是 root 用户,即便正确输入了密码,也会出现类似如下错误信息。

      Permission denied, please try again. SSH 服务器拒绝了密码,请再试一次。 但非root用户可以正常登录,而且root用户通过 管理终端 登录也正常。

    • 通过 管理终端 (VNC连接)进入系统。 通过 cat 等指令查看 /etc/ssh/sshd_config 中是否包含类似如下配置: PermitRootLogin no

    • PermitRootLogin yes 使用如下指令重启 SSH 服务: service sshd restart

· 12 min read
LIU

Vue2

组件化基础

  • 数据驱动视图 (MVVM,setState)
  • MVVM
    • M => data
    • V => view
    • VM => 一个抽象的层级,Vue提供的能力,提供 V 和 M 连接的一部分

响应式

  • 组件 data 的数据一旦变化,会触发视图的更新
  • Object.defineProperty (Vue3.0启用 proxy)
    • 监听对象,监听数组
    • 复杂对象,深度监听
    • image-20220525161254722
  • Object.defineProperty缺点:
    • 深度监听,需要递归到底,一次性计算量较大
    • 无法监听新增属性、删除属性 (Vue.set / Vue.delete)
    • 无法原生监听数组,需要特殊处理
  • Proxy 兼容性不好,且无法 polyfill

组件渲染和更新的过程

虚拟 DOM (Virtual DOM) 和 Diff

  • vdom 是实现 vue 和 React 的重要基石
  • diff 算法 是 vdom 中最核心、最关键的部分
  • DOM 操作非常耗费性能
  • vdom - 用 JS 模拟 DOM 结构,新旧 vnode对比,计算出最小的变更,最后更新 DOM
  • vdom 核心概念:h、vnode、patch、diff、key 等
  • vdom 存在的价值:数据驱动视图,控制 DOM 操作等

image-20220524232902156

snabbdom

  • github.com/snabbdom/snabbd om

  • 简洁强大的 vdom 库

  • Vue 参考它实现的 vdom 和 diff

  • Vue3 重写了 vdom 的代码,优化了性能,但是 vdom 的基本理念不变

  • h函数、vnode数据结构、patch函数

  • patchVnode函数:新旧都有 children,执行update。新有旧无,执行add。新无旧有,执行remove

  • updateChildren函数

    • image-20220525101858219
    • image-20220525101730643

h 函数

第一个参数:标签

第二个参数:属性

第三个参数:子元素

patch 函数 渲染 vnode

const vnode = h('ul#list',{},[
h('li.item',{},'Item1'),
h('li.item',{},'Item2')
])
const = document.getElementById('container')

patch(container,vnode) //初次渲染

const newVnode = h('ul#list',{},[
h('li.item',{},'Item')
])

patch(vnode,newVnode) //dom更新

diff 算法

  • diff 算法是 vdom 中最核心、最关键的部分
  • diff 即对比,是一个广泛的概念,如 linux diff 命令,git diff 等
  • 两个 JS 对象也可以做 diff :jiff 库
  • 树的 diff 的时间复杂度是 O(n^3)
    • 遍历 tree1
    • 遍历 tree2
    • 排序
    • 1000个节点,要计算一亿次,算法不可用
  • 优化时间复杂度到 O(n)
    • 只比较同一层级,不跨级比较
    • tag 不同,则直接删掉重建,不再深度比较
    • tag 和 key ,两者都相同,则认为是相同节点,不再深度比较

模板编译

  • JS 的 with 语法

    • 它改变了 {} 内自由变量的查找规则,当做 obj 属性来查找
    • 如果找不到匹配 obj 下的属性,就会报错
    • with 要慎用,它打破了作用域规则,易读性变差
    • image-20220525105152755
  • vue template complier 将模板编译为 render 函数

  • 执行 render 函数生成 vnode

  • vue的模板不是 html,有指令,插值,js表达式,判断,循环

  • 因此,模板一定是转换为某种 js 代码,即编译模板

  • vue组件可以用 render 代替 template

  • vue-template-compiler

    • 模板 通过 compiler 编译成 render 函数,执行函数生成 vnode,再到渲染和更新

    • 基于 vnode 再执行 patch 和 diff

    • 这里面的 _c 等同于 createElement 等同于 h函数

    • const compiler = require('vue-template-compiler')

      // 插值
      const template = `<p>{{message}}</p>`
      with(this){return createElement('p',[createTextVNode(toString(message))])}
      // h -> vnode
      // createElement -> vnode

      // 表达式
      const template = `<p>{{flag ? message : 'no message found'}}</p>`
      with(this){return _c('p',[_v(_s(flag ? message : 'no message found'))])}

      // 属性和动态属性
      const template = `
      <div id="div1" class="container">
      <img :src="imgUrl"/>
      </div>
      `
      with(this){return _c('div',
      {staticClass:"container",attrs:{"id":"div1"}},
      [
      _c('img',{attrs:{"src":imgUrl}})])}

      // 条件
      const template = `
      <div>
      <p v-if="flag === 'a'">A</p>
      <p v-else>B</p>
      </div>
      `
      with(this){return _c('div',[(flag === 'a')?_c('p',[_v("A")]):_c('p',[_v("B")])])}

      // 循环
      const template = `
      <ul>
      <li v-for="item in list" :key="item.id">{{item.title}}</li>
      </ul>
      `
      with(this){return _c('ul',_l((list),function(item){return _c('li',{key:item.id},[_v(_s(item.title))])}),0)}

      // 事件
      const template = `
      <button @click="clickHandler">submit</button>
      `
      with(this){return _c('button',{on:{"click":clickHandler}},[_v("submit")])}

      // v-model
      const template = `<input type="text" v-model="name">`
      // 主要看 input 事件
      with(this){return _c('input',{directives:[{name:"model",rawName:"v-model",value:(name),expression:"name"}],attrs:{"type":"text"},domProps:{"value":(name)},on:{"input":function($event){if($event.target.composing)return;name=$event.target.value}}})}

      // render 函数
      // 返回 vnode
      // patch

      // 编译
      const res = compiler.compile(template)
      console.log(res.render)

      // 从 vue 源码中找到缩写函数的含义
      function installRenderHelpers (target) {
      target._o = markOnce;
      target._n = toNumber;
      target._s = toString;
      target._l = renderList;
      target._t = renderSlot;
      target._q = looseEqual;
      target._i = looseIndexOf;
      target._m = renderStatic;
      target._f = resolveFilter;
      target._k = checkKeyCodes;
      target._b = bindObjectProps;
      target._v = createTextVNode;
      target._e = createEmptyVNode;
      target._u = resolveScopedSlots;
      target._g = bindObjectListeners;
      target._d = bindDynamicKeys;
      target._p = prependModifier;
      }

组件 渲染/更新/过程

  • 初次渲染过程
    • 解析模板为 render 函数 (或在开发环境已完成,vue-loader)
    • 触发响应式,监听 data 属性 getter setter
    • 执行 render 函数,生成 vnode,patch(elem,vnode)
    • 执行 render 函数会触发 getter
  • 更新过程
    • 修改 data,触发 setter ( 此前在getter 中已被监听 )
    • 重新执行 render 函数
    • patch (vnode, newVnode)
  • 异步渲染
    • $nextTick 待 DOM 渲染完再回调
    • 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
    • 汇总 data 的修改,一次性更新视图
    • 减少 DOM 操作次数,提高性能

image-20220525123729108

Vue3

Vue3对比Vue2的优势

  • 性能更好
  • 体积更小
  • 更好的ts支持
  • 更好的代码组织
  • 更好的逻辑抽离
  • 更多新功能

Options API 生命周期

  • destroyed 改为 unmouted
  • beforeDestroy 改为 beforeUnmount
  • 其他沿用 Vue2 的声明周期
//使用setup() 替代
beforecreate -> use setup()
created -> use setup()

//前面加on
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onbeforeUpdate
updated -> onUpdated
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured

//更改名称
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted

//新加入,主要用于开发阶段调试使用
onRenderTracked
onRenderTriggered

Composition API 对比 Options API

  • Composition API
    • 更好的代码组织
    • 更好的逻辑复用
    • 更好的类型推导

image-20220524193550744

理解 ref / toRef / toRefs

  • ref

    • 生成值类型的响应式数据
    • 可用于模板和 reactive
    • 通过 .value 修改值
  • toRef

    • 针对一个响应式对象(reactive 封装)的 prop

    • 创建一个 ref,具有响应式

    • 两者保持引用关系

    • toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式

    • const state = reactive({
      age:10,
      name:'xx'
      })
      const ageRef = toRef(state,'age') //单个
      //修改其一都会联动
      state.age = 20
      ageRef.value = 30
  • toRefs

    • 将响应式对象 (reactive 封装)转换为普通对象

    • 对象的每个 prop 都是对应的 ref

    • 两者保持引用关系

    • //不能直接结构使用,需要 toRefs
      const state = reactie({
      age:10,
      name:'xx'
      })
      const stateAsRefs = toRefs(state) //整个

      const {age:ageRef,name:nameRef} = stateAsRefs //每个属性 都是ref响应式对象
      return {
      ageRef,
      nameRef
      }
      return stateAsRefs
      return {
      ...stateAsRefs
      }
      return toRefs(state)
    • image-20220524225231538

  • 最佳使用方式

    • 用 reactive 做对象的响应式,用 ref 做值类型响应式
    • setup 中返回 toRefs(state),或者 toRef(state,'xx')
    • ref 的变量命名都用 xxxRef
    • 合成函数返回响应式对象时,使用 toRefs,有助于使用方对数据进行结构

6-9

路由

image-20220525135916734

路由模式

  • hash

    • hash 变化会触发网页跳转,即浏览器的前进和后退

    • hash 变化不会刷新页面,SPA 必需的特点

    • hash 永远不会提交到 server 端

    • window.onhashchange

    • window.onhashchange = (event) =>{
      console.log('old url:',event.oldURL)
      console.log('new url:',event.newURL)
      console.log('hash:',event.hash)
      }
  • H5 history

    • 用 url 规范的路由,但跳转时不刷新页面

    • history.pushState 向浏览器历史添加了⼀个状态(增加⼀个记录)

    • history.replaceState 把当前的页⾯的历史记录替换掉

    • window.onpopstate

    • // 页面初次加载,获取 path
      document.addEventListener('DOMContentLoaded', () => {
      console.log('load', location.pathname)
      })

      // 打开一个新的路由
      // 用 pushState 方式,浏览器不会刷新页面
      document.getElementById('btn1').addEventListener('click', () => {
      const state = { name: 'page1' }
      console.log('切换路由到', 'page1')
      history.pushState(state, '', 'page1') // 重要
      })

      // 监听浏览器前进、后退
      window.onpopstate = (event) => { // 重要
      console.log('onpopstate', event.state, location.pathname)
      }

总结

v-show 和 v-if

  • v-show 通过 CSS display 控制显示和隐藏
  • v-if 组件真正的渲染和销毁
  • 频繁的切换显示状态用 v-show,否则用 v-if

v-for 中的 key

  • 必须用 key,且不能是 index 和 random
  • diff 算法中 通过 tag 和 key 来判断 ,是否是sameNode
  • 减少渲染次数,提升渲染性能

Vue组件中生命周期(父子组件)

  • 单组件生命周期图
  • 父子组件生命周期关系

Vue组件通信

  • 父子组件 props 和 $emit
  • 自定义事件 event.$on 、event.$emit、event.$off
  • Event.Bus
  • vuex

v-model实现原理

  • input 元素的 value = this.name
  • 绑定 input 事件 this.name = $event.target.value
  • data 更新 触发 re-render
  • image-20220525183151761

computed 特点

  • 缓存,data 不变不会重新计算
  • 提高性能

ajax请求放在哪个生命周期

  • mounted
  • 放在 mounted 之前没用,只会让逻辑更加混乱

将组件所有props传递给子组件

  • $props
  • <User v-bind="$props" />

beforeDestory

  • 解除自定义事件 event.$off
  • 清除定时器
  • 解绑自定义的 DOM 事件,如 window,scroll 等

作用域插槽

  • 将子组件的 data 传递给父组件,子组件:<slot :user="user">
  • 父组件通过 <template v-slot='slotProps'> 接收

Vuex中 action 和 mutation

  • action 支持异步,mutation 不支持
  • Mutation 做原子操作(每次只做一个操作)
  • action 可以整合多个 mutation

Vue-router 异步加载

  • component:()=>import('./xxxxx')

Vue监听数组变化

  • Object.definedProperty 不能监听数组变化
  • 重新定义原型,重写 push pop 等方法,实现监听
  • Proxy 可以原生支持监听数组变化

diff算法过程

· 23 min read
LIU

1.介绍

基础知识学习概览

2.开始

3.CSS

块级元素 & 内联元素

  • display:block / table ; 有 div h1 h2 table ul ol p 等
  • display:inline / inline-block ; 有 span img input button 等

布局

  • BFC 块级格式化上下文,一块独立渲染区域,内部元素的渲染不会影响边界以外的元素

    • float 不是 none
    • position 是 absolute 或 fixed
    • overflow 不是 visible
    • display 是 flex inline-block 等
  • 圣杯布局和双飞翼布局

    • 使用float布局
    • 两侧使用 margin 负值,以便和中间内容横向重叠
    • 防止中间内容被两侧覆盖,一个用padding 一个用 margin
  • clearfix

    • .clearfix{
      clear: both;
      content: '';
      display: block;
      width: 0;
      height: 0;
      visibility: hidden;
      }
  • flex 布局

    • flex-direction
    • justify-content
    • align-items
    • flex-wrap
    • align-self

定位

  • relative 依据自身定位
  • absolute 依据最近一层的定位元素定位
    • absolute relative fixed
    • body
  • 水平居中
    • inline 元素:text-align:center
    • block 元素:margin: auto
    • absolute 元素:left:50% + margin-left 负值
  • 垂直居中
    • inline 元素:line-height 的值等于height
    • absolute元素:top:50% + margin-top 负值
    • absolute元素:transfrom(-50%,-50%)
    • absolute元素:top,left,bottom,right = 0 + margin:auto (终极解决方案)

图文样式

  • line-height 会继承父元素,设置百分比时,会取 %* 计算后的结果

响应式

  • rem 基于根元素的相对单位
  • media-query 根据不同的屏幕宽度设置跟元素 font-size
  • vw / v

4.变量类型和计算

  • 值类型 和 引用类型

    • null 特殊引用类型,指针指向为空地址

    • null == undefined // true
      // 除了 == null 之外,其他一律都用 ===
      const obj = {x:100}
      if(obj.a == null){}
      // 这里相当于:
      // if (obj.a === null || obj.a === undefined){}
  • Typeof 能准确判断值类型(除了null),引用类型只能判断 function 和 object

对于 null来说,虽然它是基本类型,但是会显示 object,这是一个存在很久了的 Bug

typeof null // 'object'
typeof NaN // 'number'
typeof dd // 'undefined' dd is not defined


10 && 0 // 10
'' || 'aaa' // 'aaa'
!window.aa // true

JS的最初版本中,使用的是 32 位系统,为了性能考虑使用低位存储了变量的类型信息,000 开头代表是对象,然而 null表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个Bug却是一直流传下来。

  • 深拷贝

    • function deepClone(obj){
      if(typeof(obj)!=='object' || obj==null) return obj;
      let copy = obj instanceof Array ? [] : {}
      for(let key in obj){
      if(obj.hasOwnProperty(key)){
      copy[key] = deepClone(obj[key])
      }
      }
      return copy
      }
  • 类型判断 instanceof

    xiaoming instanceof Student // true
    xiaoli instanceof People // true

    [] instanceof Array // true
    [] instanceof Object // true
    {} instanceof Object // true

5.原型和原型链

JS它本身是基于原型继承的语言

hasOwnProperty

  • 实例化对象的隐式原型 === 类(class)的显示原型

    • 每个 class 都有显示原型 prototype
    • 每个实例都有隐式原型 __proto__
    • 实例的 __proto__ 指向对应 class 的 prototype
    • xiaoming.__proto__ === Student.prototype
    • 现在自身属性和方法查找
    • 如果找不到则自动去 __proto__ 中查找

    image-20220523223620922

  • 继承
    • Student 继承于 People
    • Student.prototype.__proto__ === People.prototype
  • 简易 Jquery

    • class jQuery {
      constructor(selector) {
      const result = document.querySelectorAll(selector)
      const length = result.length
      for (let i = 0; i < length; i++) {
      this[i] = result[i]
      }
      this.length = length
      this.selector = selector
      }
      get(index) {
      return this[index]
      }
      each(fn) {
      for (let i = 0; i < this.length; i++) {
      const elem = this[i]
      fn(elem)
      }
      }
      on(type, fn) {
      return this.each(elem => {
      elem.addEventListener(type, fn, false)
      })
      }
      // 扩展很多 DOM API
      }

      // 插件
      jQuery.prototype.dialog = function (info) {
      alert(info)
      }

      // “造轮子”
      class myJQuery extends jQuery {
      constructor(selector) {
      super(selector)
      }
      // 扩展自己的方法
      addClass(className) {

      }
      style(data) {

      }
      }

      // const $p = new jQuery('p')
      // $p.get(1)
      // $p.each((elem) => console.log(elem.nodeName))
      // $p.on('click', () => alert('clicked'))

6.作用域和闭包

  • 作用域和自由变量

  • 自由变量

    • 一个变量在当前作用域没有定义,但被使用了
    • 向上级作用域,一层一层向上寻找,直到找到为止
    • 如果到全局作用域都没有找到,则报错 xx is not defined
  • 闭包

    • 所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!!!

    • function print(fn){
      const a = 200
      fn()
      }
      const a = 100
      function fn(){
      console.log(a)
      }
      print(fn) // 100

this

常规函数中的 this 取什么样的值是在函数执行时确认,不是在函数定义的时候确认的

对于箭头函数,this 关键字指向的是它当前周围作用域(简单来说是包含箭头函数的常规函数,如果没有常规函数的话就是全局对象),这个行为和常规函数不同。

  • 作为普通函数去调用
  • 使用 call apply bind(call,apply 直接调用就执行,bind是返回一个新的函数)
  • 作为对象方法被调用
  • 在class方法中调用
  • 箭头函数,箭头函数中的this,是指向它上级作用域的this

bind

// 模拟 bind
Function.prototype.bind1 = function () {
// 将参数拆解为数组
const args = Array.prototype.slice.call(arguments)
// 获取 this(数组第一项)
const t = args.shift()
// fn1.bind(...) 中的 fn1
const self = this
// 返回一个函数
return function () {
return self.apply(t, args)
}
}

function fn1(a, b, c) {
console.log('this', this)
console.log(a, b, c)
return 'this is fn1'
}

const fn2 = fn1.bind1({x: 100}, 10, 20, 30)
const res = fn2()
console.log(res)

7.异步

  • 同步和异步的区别是什么
    • 同步会阻塞代码执行
    • 异步不会阻塞代码执行
  • 手写用Promise加载一张图片
  • 前端使用异步的场景?
    • 网络请求,如ajax 图片加载
    • 定时任务,如setTimeout
  • 单线程和异步
    • 异步 是由单线程这个背景而来的
    • JS是单线程语言,只能同时做一件事
    • 浏览器和nodejs已支持JS启动进程,如Web Worker
    • JS和DOM渲染共用一个线程,因为JS可修改DOM结构
    • 遇到等待(如网络请求,定时任务)不能卡住
    • 回调callback函数形式
  • Callback hell 和 Promise

8.异步进阶

  • 网络请求

  • 定时任务

  • Event Loop

  • Promise

    • 三种状态:pending、resolved、rejceted
    • pending --> resolved 或者 pending --> rejected
    • 变化不可逆
  • then 和 catch 改变状态

    • then 正常返回 resolved ,里面有报错则返回 rejected
    • catch 正常返回 resolved ,里面有报错则返回 rejected
    • resolved 触发后续 then 的回调
    • rejected 触发后续 catch 的回调
  • async / await

    • 语法糖
    • 异步回调 callback hell
    • Promise then catch 链式调用,但也是基于回调函数
    • 同步语法,彻底消灭回调函数
    • await 必须有 async 包裹
  • async / await 和 Promise 的关系

    • 执行 async 函数,返回的是 Promise 对象

    • await 相当于 Promise 的 then

    • try...catch 可捕获异常,代替了 Promise 的 catch

    • async function fn(){
      return 100;
      }
      (async function(){
      const a = fn();
      const b = await fn();
      })()
      // a 是返回的 promise 100
      // b 是 then 返回的值 100
  • for - of

    • 支持异步
    • image-20220601092931985
  • 宏任务和微任务

    • 宏任务:setTimeout、setInterval、Ajax、DOM事件
    • 微任务:Promise、async / await
    • 微任务执行时机比宏任务要早
  • event loop 和 DOM 渲染

    • call Stack 空闲
    • 尝试 DOM 渲染
    • 触发 Event Loop
    • image-20220601125606276

9.DOM

  • dom是哪种数据结构
    • 树(DOM树)
  • dom操作常用API
    • 获取dom节点
    • attribute ( 在dom标签内的属性 ) 修改html属性,会改变html结构
    • property( .style / .className / .nodeName / .nodeType )JS属性操作形式,修改对象属性,不会体现到html结构中
    • 两者都有可能引起dom重新渲染
  • dom结构操作
    • document.creatElement
    • document.appendChild ( 如果是现有节点,会移动 )
    • p1.parentNode 获取父元素
    • p1.childNodes 获取子元素(含text)
    • div1.removeChild 删除节点
    • 一次性插入多个节点,考虑性能:*createDocumentFragment()、再appendChild

10.BOM

Browser Object Model 浏览器操作相关

  • navigator 浏览器信息 (UA)
    • navigator.userAgent
  • screen 屏幕信息
    • screen.width
    • screen.height
  • location 地址url信息
    • location.href : 完整的url
    • location.protocol : https:
    • location.host : ui.lllllll.top
    • location.search : ?a=100&b=200
    • location.hash : #anchor
    • loaction.pathname : /class/chapt/100.html
  • history 前进后退信息
    • history.back()
    • history.forward()

11.事件

  • 事件绑定

    • addEventListener

    • 通用的事件绑定函数

    • function bindEvent(elem,type,fn){
      elem.addEventListener(type,fn)
      }
    • image-20220523120643186

    • event.preventDefault() 阻止默认行为

  • 事件冒泡

    • event.stopPropagation() 阻止冒泡
    • 应用场景:代理 ⬇️
  • 事件代理

    • 用e.target获取触发元素
    • 用matches来判断是否是期望触发的元素

12.ajax

  • new XMLHttpRequest()
var url = "server.php";  //设置请求的地址
var xhr = XMLHttpRequest(); //实例化XMLHttpRequest对象
xhr.open("POST", url, true); //建立间接,要求异步响应
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); //设置为表单方式提交
xhr.onreadystatechange = function () { //绑定响应状态事件监听函数
if (xhr.readyState == 4) { //监听readyState状态
if (xhr.status == 200 || xhr.status == 0) { //监听HTTP状态码
console.log(xhr.responseText); //接收数据
}
}
}
xhr.send(data数据);

image-20220523102525361

  • Fetch

  • 跨域

    • 同源策略:协议,域名,端口
    • 图片、css、js 可无视同源策略,可跨域
    • <img/> 可用于统计打点,可使用第三方统计服务
    • <link/><script>可使用CDN,CDN一般都是外域
    • <script>可使用JSONP
    • CORS - 服务器设置http header
      • Access-Control-Allow-Origin
      • Access-Control-Allow-Headers
      • Access-Control-Allow-Methods
  • axios

    • 支持promise
    • XMLHttpRequest 封装
    • 支持浏览器和node

13.存储

前端存储

  • 本身用于浏览器和server通讯
  • 被『借用』到本地存储来
  • 可用document.cookie = 'xxx' 来修改,用 ; 号分割,同key覆盖,不同key追加,这种方式太过简陋
  • 最大存 4KB
  • 每次请求都会发送到服务端,增加请求数据量

localStorage、sessionStorage

  • HTML5专门为存储而设计,最大可存5M
  • API简单易用 setItem / getItem
  • 不会随着http请求发送出去
  • localStorage 数据会永久存储,除非代码或者手动删除
  • sessionStorage 数据只存在于当前会话,浏览器关闭就清空

14.http

状态码分类

  • 1xx 服务器收到请求

  • 2xx 请求成功,如200

  • 3xx 重定向,如302

  • 4xx 客户端错误,如404

  • 5xx 服务端错误,如500

常见状态码

  • 200 成功

  • 301 永久重定向(配合location,浏览器自动处理)

  • 302 临时重定向(配合location,浏览器自动处理)

  • 304 资源未被修改

  • 404 资源未找到

  • 403 没有权限

  • 500 服务器错误

  • 504 网关超时

methods

  • 传统的methods:GET/POST
  • 现在的methods:
    1. get 获取数据
    2. post 新建数据
    3. patch/put 更新数据
    4. delete 删除数据
    5. option

Restful API

  • 传统API设计:把每个url当做一个功能
  • Restful API 设计:把每个url当做一个唯一的资源
    • 尽量不用url参数
      • 传统API设计:/api/list?page=2
      • Restful API 设计:/api/list/2
    • 用methods表示操作类型
      • 相同url,使用不同methods区分

headers

  • Request Headers
    • Accept 浏览器可接受的数据格式
    • Accept-Encoding 浏览器可接收的压缩算法,如gzip
    • Accept-Languange 浏览器可接收的语言,如zh-CN
    • Connection: keep-alive 一次TCP连接重复使用
    • cookie
    • Host
    • User-Agent(简称UA)浏览器信息
    • Content-type 发送数据的格式,如 application/json
  • Response Headers
    • Content-type 返回数据的格式,如application/json
    • Content-length 返回数据的大小,多少字节
    • Content-Encoding 返回数据的压缩算法,如gzip
    • Set-Cookie

http缓存

强缓存:Cache-Control --> max-age、no-cathe

协商缓存:Last-Modified -->

  • Etag / If-None-Match
  • 上面两个可以共存,优先使用Etag,Last-Modified只能精确到秒级

命中缓存,返回 304 资源未被修改

image-20220522214450117

  • 关于缓存

    • 什么是缓存?
    • 为什么需要缓存?
      • 加快访问速度
    • 哪些资源可以被缓存? 静态资源(js/css/img)

    image-20211228234639645

  • 缓存策略(强制缓存+协商缓存)

    • 强制缓存

    • image-20211229221536801

      • headers加Cache-Control : max-age=5184000 (秒)
      • 在Response Headers中,控制强制缓存的逻辑,本质上还是服务端控制
      • Cache-Control 的值 :
        • max-age 最大过期时间
        • no-cathe 不用强制缓存,交给服务端,服务端怎么处理,客户端不管
        • no-store 不用强制缓存,也不让服务端做缓存,比较彻底
        • private 只允许最终用户做缓存
        • public 允许中间的路由或代理做缓存
      • Expires 同在Response Headers中,同为控制缓存过期,已被 Cache-Control 代替
    • 协商缓存(对比缓存)

    • image-20211229221502775

      • 服务端缓存策略(服务端来判断一个资源是不是可以被缓存,或者是不是可以用缓存,不是缓存在服务端)

      • 服务端判断客户端资源,是否和服务端资源一样

        • 如果一致返回304,否则返回200和最新资源和资源标识
      • 304 资源未被修改

    • 资源标识

      1. Last-Modified 资源的最后修改时间
      • Last-Modified (服务端返回)/ If-Modified-Since (客户端请求带上)
      1. Etag 资源的唯一标识(一个字符串,类似指纹)
        • Etag / If-None-Match
      2. 会优先使用Etag
      3. Last-Modified 只能精确到秒级
      4. 如果资源被重复生成,而内容不变,则Etag更准确
  • image-20211229230539315

  • 刷新操作方式,对缓存的影响

    1. 正常操作,输入url跳转,前进后退 (强缓有效,协缓有效)
    2. 手动刷新,F5 (强缓无效,协缓有效)
    3. 强制刷新,ctrl+F5 (强缓无效,协缓有效)

15.开发环境

git

git status #查看文件修改状态
git checkout -b dev #新建并切换到该分支
git checkout master #切换分支
git branch #查看分支列表

#分支合并
git fetch #拉取所有分支



抓包

移动端h5

windows:fiddler

mac:Charles

  • 手机和电脑连同一个局域网
  • 将手机代理到电脑上
  • 手机浏览网页,即可抓包

Webpack Babel

模块化

模块化就是一个导入和导出的过程

ES6:

// a.js
export const num = 123;
export function fn(){};

// 导入
import {const} from './a.js'

// b.js default
export default {
name : 'xxx'
}
import n from './b.js'

linux命令

ssh root@192.168.xx.xx  #登录
ls #查看文件夹
ls -a #查看所有包含隐藏 -a = all linux下 .开头的都是隐藏文件
ll #看列表形式的文件夹
clear #清屏
mv a.html b.html #修改文件名、移动文件
cp a.js a1.js #拷贝
rm #删文件
rm -rf #删文件夹
touch d.js #新建文件
vi d.js #新建文件并打开 i 编辑插入、 Esc + :wq 保存并退出、:q!不保存直接退出
cat d.js #查看文件
head d.js #打印出文件前面几行
tail d.js #打印末尾几行
grep "babel" package.json #查找文件内容


16.运行环境

从输入url到渲染出页面的整个过程

加载过程

  • DNS解析:域名 - > IP地址
  • 浏览器根据IP地址向服务器发起 http 请求
  • 服务器处理 http 请求,并返回给浏览器

渲染过程

  • 根据 HTML 代码生成 DOM Tree
  • 根据 CSS 代码生成 CSSOM ( css object model )
  • 将 DOM Tree 和 CSSOM 整合成 Render Tree
  • 根据 Render Tree 渲染页面
  • 遇到 <script> 则暂停渲染

image-20220524104918600

安全

XSS 跨站请求攻击

  • 嵌入<script>脚本,获取cookie,发送到我的服务器
  • 替换特殊字符,如 < 变为 &lt; / > 变为 &gt;

XSRF 跨站请求伪造

  • 一封电子邮件,邮件正文隐藏着 <img src="xxx.com/pay?id=100"> ,你查看邮件,就帮我购买了这个商品

  • iframe

  • 使用 post 接口

  • 增加验证,例如密码,短信验证码,指纹等

18.总结

强制类型转换和隐式类型转换

  • 强制:parseInt 、 parseFloat 、 toString 等
  • 隐式:if 、 逻辑运算、==、+ 拼接字符串

全等函数:isEqual

[10,20,30].map(parseInt) => [10,NaN,NaN]

image-20220524131258487

阻止事件冒泡和默认行为

  • event.stopPropagation()
  • event.preventDefault()

减少DOM操作

  • 缓存dom查询结果
  • createDocumentFragment() 创建文档片段

JSONP、AJAX

jsonp是通过script标签发送请求,并通过回调函数的形式执行

ajax是通过XMLHttpRequest

函数声明和函数表达式区别

函数声明会提升 function fn(){}

函数表达式没有提升 const fn = function(){}

函数声明会在代码执行前预加载,而函数表达式不会

new Object() 和 Object.create()

  • {} 等同于 new Object(),原型 Object.prototype
  • Object.create(null) 没有原型
  • Object.create({...}) 指定原型

JS实现继承

  • class 继承
  • prototype 继承

JS捕获异常

  • try catch
  • window.onerror

将URL参数解析为JS对象

function queryToObj(){
let res = {}
let query = location.search.substr(1)
query.split('&').forEach(item=>{
let s = item.split('=')
res[s[0]] = s[1]
})
return res
}
function queryToObj(){
let res = {}
const list = new URLSearchParams(location.search)
list.forEach((val,key)=>{
res[key] = val
})
return res
}

Object.assign 不是深拷贝,是浅拷贝

RAF - requestAnimationFrame

  • 想要动画流程,更新频率要60帧/s,即16.67ms更新一次视图
  • setTimeout 需要手动控制,而RAF浏览器会自动控制
  • 后台标签或隐藏 iframe 中,RAF会暂停,而setTImeout依然执行
function animate(){
curWidth = curWidth + 1
if(curWidth < maxWidth){
window.requestAnimationFrame(animate)
}
}
animate()

性能优化的点

原则:多实用内存,缓存,减少计算,减少网络请求

方向:加载页面,页面渲染,页面操作流畅度

前端模块化

全局函数:绑在全局对象上,可能会引发命名冲突,或者数据不安全

NameSpace:封装成对象,减少了全局变量,解决了命名冲突,但是外部可以修改模块内部的数据

匿名函数自调用:通过闭包的方式向外暴露接口,不太好处理模块之间的依赖关系

文件模块化:

- CommonJS
- AMD
- UMD (前两种的兼容)
-