Starting again company 03
1.vue单向数据流
1.1单向数据流核心
确保数据在组件间传递是单向的,从父组件流向子组件。vue中的单向数据流主要通过props实现,Vue2 的单向数据流核心是 “props 只读,修改需通过事件通知父组件”。
1.2单向数据流存在的意义
数据流向清晰:所有数据变更都能追溯到源头(通常是父组件或更上层的状态管理),便于调试和维护。
避免数据混乱:若允许子组件直接修改父组件数据,当多个子组件同时修改时,会导致数据状态不可预测。
1.3具体实现展示
1.3.1父传子单向数据流
父组件属性绑定传入数据
<!-- 父组件 Parent.vue -->
<template><Child :message="parentMsg" />
</template>
<script>
import Child from './Child.vue'
export default {components: { Child },data() {return { parentMsg: 'Hello from parent' }}
}
</script>子组件props接收数据并使用
<!-- 子组件 Child.vue -->
<template><div>{{ message }}</div> <!-- 显示父组件传递的数据 -->
</template>
<script>
export default {props: ['message'] // 声明接收的 props
}
</script>1.3.2子组件修改数据:通过事件通知父组件
子组件通过 $emit 触发一个自定义事件,并传递需要修改的值
<!-- 子组件 Child.vue -->
<template><button @click="changeMsg">修改数据</button>
</template>
<script>
export default {props: ['message'],methods: {changeMsg() {// 触发自定义事件,传递新值this.$emit('update-message', 'New message from child')}}
}
</script> 父组件监听该事件,在事件处理函数中修改自身数据,通过 props 自动同步到子组件。
<!-- 父组件 Parent.vue -->
<template><!-- 监听子组件的自定义事件,执行修改逻辑 --><Child :message="parentMsg" @update-message="handleUpdate" />
</template>
<script>
export default {data() { return { parentMsg: 'Hello' } },methods: {handleUpdate(newVal) {this.parentMsg = newVal // 父组件自行修改数据}}
}
</script>2.vue2项目打包发布后项目刷新无变化
主要原因是打包后静态资源文件名未变,服务器返回304缓存,需通过「文件名加哈希」让更新后的资源文件名不同,确保 JS、CSS、图片等资源通过「哈希值」区分版本。
强制刷新浏览器,排除本地缓存--->检查 dist 目录是否更新,服务器文件是否为最新--->配置 vue.config.js 确保 JS/CSS 带哈希--->服务器配置 index.html 不缓存,静态资源长缓存;
2.1JS配置 webpack.config.js 的 output
const path = require('path');module.exports = {output: {// 入口 JS 文件(如 main.js)的输出路径和命名filename: 'js/[name].[contenthash:8].js', // 异步加载的 chunk(如路由懒加载的组件)的命名chunkFilename: 'js/[name].[contenthash:8].chunk.js', // 输出目录(通常为 dist)path: path.resolve(__dirname, 'dist'),// 每次打包前清空 dist 目录(避免旧文件残留)clean: true }
};2.2CSS 提取带内容哈希
如果用 mini-css-extract-plugin 提取 CSS 为单独文件,需同步配置 CSS 的文件名.
const MiniCssExtractPlugin = require('mini-css-extract-plugin');module.exports = {module: {rules: [{test: /\.css$/,use: [MiniCssExtractPlugin.loader, 'css-loader'] // 提取 CSS 到文件}]},plugins: [new MiniCssExtractPlugin({// CSS 文件命名(同样使用 contenthash)filename: 'css/[name].[contenthash:8].css',chunkFilename: 'css/[name].[contenthash:8].chunk.css'})]
};2.3确保 HTML 不缓存
即使 JS/CSS 带哈希,若 index.html 被缓存,浏览器仍会加载旧 HTML 中引用的旧资源路径
server {location / {root /path/to/your/dist; # 指向打包后的 dist 目录try_files $uri $uri/ /index.html; # 支持 Vue 路由 history 模式# 对 HTML 禁用缓存add_header Cache-Control "no-cache, no-store, must-revalidate";add_header Pragma "no-cache"; # 兼容 HTTP/1.0add_header Expires "0"; # 过期时间设为 0}# 带哈希的静态资源可长期缓存(1年)location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|eot|ttf)$ {add_header Cache-Control "max-age=31536000, immutable";}
}3.CDN(内容分发网络)
核心目标通过分布式节点网络,将静态资源缓存到离用户最近的节点,从而降低延迟,提升访问速度,并减轻服务器的压力。
3.1核心架构
源站(Origin Server)
存放原始资源的服务器(即你的业务服务器,如部署前端代码的 Nginx 服务器)。
CDN 节点会定期从源站同步资源(首次请求未命中时也会主动拉取)。
CDN 节点(Edge Node)
分布在全球 / 全国各地区的缓存服务器(如一线城市、二三线城市、海外地区),直接向用户提供资源。
节点数量越多、覆盖越广,用户访问延迟越低(例如北京用户从北京节点获取资源,无需访问千里之外的源站)。
中心控制系统(DNS + 调度系统)
DNS 解析:通过自定义 DNS 服务器,将用户的域名解析到「最优 CDN 节点」(而非源站 IP)。
调度策略:根据用户 IP 地理位置、节点负载、网络质量等,选择延迟最低的节点(例如上海用户优先分配到上海节点)。
3.2 CDN的完整工作流程
用户发起请求:用户在浏览器输入
https://cdn.example.com/index.html,请求访问前端页面。DNS 解析导向 CDN 节点:
本地 DNS 向 CDN 的 DNS 服务器查询
cdn.example.com的 IP。CDN DNS 根据用户 IP 定位到最近的节点(如杭州节点),返回该节点 IP。
CDN 节点处理请求:
用户向杭州节点请求
index.html,节点检查本地缓存:若已缓存且有效,直接返回缓存的
index.html。若未缓存或已过期,节点向源站(如
origin.example.com)请求index.html,缓存后返回给用户。
加载关联资源:
index.html中引用的app.456.jsstyle.789.css等资源,同样通过上述流程从 CDN 节点获取(若命中缓存,加载速度极快)。资源更新与同步:当前端代码更新并重新打包后(如
app.678.js),由于文件名哈希变化,CDN 节点会将其视为新资源,自动从源站拉取并缓存,用户下次访问时即可获取最新版本。
4.const声明对象是否能被修改
const声明的对象能被修改,与js的堆内存的存储机制有关。
- 基本类型的值存储在栈内存中,变量指向的是具体的值。
- 引用类型的值是存储在堆内存中,变量在栈内存中存储的是指向堆内存中该对象的引用地址(类似指针)。
基本类型(值直接存储在栈内存中,变量与值直接绑定)
const num = 10;
num = 20; // 报错:Assignment to constant variable(不能给常量重新赋值)const str = 'hello';
str = 'world'; // 同样报错const bool = true;
bool = false; // 报错引用类型(堆内存指向引用地址,类似指针)
const obj = { name: 'foo' };
obj = { age: 18 }; // 报错:堆内存中创建新对象引用地址发生改变
obj.name = 'bar'; // 允许,修改属性值
obj.age = 18; // 允许,新增属性
delete obj.name; // 允许,删除属性 const 与堆内存的联系在于 —— 它保护的是栈内存中指向堆内存的引用地址不变,但不限制堆内存中对象本身的修改。
5.EventBus(全局事件总线)
适合解决非父子组件间的通信问题,核心思路通过全局的Vue实例作为“事件总线”实现事件的发布于订阅。
5.1.创建EventBus实例
创建独立的事件总线文件,作为全局的 “中介”,管理所有组件的事件订阅与发布。
// src/utils/eventBus.js
import Vue from 'vue'
// 实例化一个Vue对象作为事件总线
export default new Vue()5.2$on订阅事件--接收数据
接收数据的组件中,通过 $on 订阅事件,并在组件销毁前用 $off 取消订阅(避免内存泄漏)。
<!-- 组件A:src/components/ComponentA.vue -->
<template><div><h3>组件A(接收方)</h3><p>收到的消息:{{ receivedMsg }}</p></div>
</template><script>
import bus from '@/utils/eventBus' // 导入事件总线export default {data() {return {receivedMsg: ''}},mounted() {// 订阅名为 "sendToA" 的事件,回调函数接收参数bus.$on('sendToA', (msg) => {this.receivedMsg = msg // 处理收到的数据})},beforeDestroy() {// 组件销毁前取消订阅,防止事件重复触发bus.$off('sendToA')}
}
</script>5.3$emit发布事件--传递数据
<!-- 组件B:src/components/ComponentB.vue -->
<template><div><h3>组件B(发送方)</h3><button @click="sendMessage">向组件A发送消息</button></div>
</template><script>
import bus from '@/utils/eventBus' // 导入事件总线export default {methods: {sendMessage() {// 发布名为 "sendToA" 的事件,并传递数据bus.$emit('sendToA', '你好,组件A!我是组件B~')}}
}
</script>5.UniApp中分包
通过将代码拆分为主包和多个分包,实现按需加载,提升应用性能。
5.1分包概念
主包:包含启动页、TabBar 页面及所有分包都需用到的公共资源(如全局组件、工具类),是应用首次启动时必须加载的部分。
分包:按功能模块拆分的代码包,只有当用户访问分包内的页面时,才会触发下载(H5 端除外,H5 会打包为单独 chunk,按需加载)。
限制:
微信小程序:主包 + 所有分包大小不超过 20MB,单个分包不超过 2MB(最新限制可能放宽,以官方文档为准)。
其他平台(如 App、支付宝小程序等)也有各自的大小限制,需参考对应平台规范。
5.2分包配置方法
项目根目录的 pages.json 中配置 subPackages(或 subpackages,大小写不敏感)
{"pages": [// 主包页面(必须放在 pages 数组中){"path": "pages/index/index","style": { ... }},{"path": "pages/login/login","style": { ... }}],"subPackages": [// 分包 1:用户模块{"root": "pages/user", // 分包根目录(需手动创建文件夹)"pages": [{"path": "profile/profile", // 页面路径,完整路径为 pages/user/profile/profile"style": { ... }},{"path": "settings/settings", // 完整路径:pages/user/settings/settings"style": { ... }}]},// 分包 2:商品模块{"root": "pages/goods","pages": [{"path": "list/list","style": { ... }}]}]
}5.3分包预加载与页面跳转
页面跳转:分包内页面的跳转路径需使用完整路径(包含分包根目录)。
// 从主包跳转到分包页面
uni.navigateTo({url: '/pages/user/profile/profile'
});// 分包内页面互跳(也需完整路径)
uni.navigateTo({url: '/pages/goods/list/list'
});分包预加载:配置分包在特定时机预加载(进入主包某页面),在 pages.json 中添加 preloadRule。
{"preloadRule": {// 当进入主包的 index 页面时,预加载 user 分包"pages/index/index": {"network": "all", // 网络环境:all(不限)/ wifi(仅wifi)"packages": ["pages/user"] // 需预加载的分包 root 列表},// 当进入 goods/list 页面时,预加载其他分包"pages/goods/list/list": {"network": "wifi","packages": ["pages/order"]}}
}