1.vue单向数据流
1.1单向数据流核心
确保数据在组件间传递是单向的,从父组件流向子组件。vue中的单向数据流主要通过props实现,Vue2 的单向数据流核心是 "props 只读,修改需通过事件通知父组件"。
1.2单向数据流存在的意义
数据流向清晰:所有数据变更都能追溯到源头(通常是父组件或更上层的状态管理),便于调试和维护。
避免数据混乱:若允许子组件直接修改父组件数据,当多个子组件同时修改时,会导致数据状态不可预测。
1.3具体实现展示
1.3.1父传子单向数据流
父组件属性绑定传入数据
javascript
<!-- 父组件 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接收数据并使用
javascript
<!-- 子组件 Child.vue -->
<template>
<div>{{ message }}</div> <!-- 显示父组件传递的数据 -->
</template>
<script>
export default {
props: ['message'] // 声明接收的 props
}
</script>
1.3.2子组件修改数据:通过事件通知父组件
子组件通过 $emit 触发一个自定义事件,并传递需要修改的值
javascript
<!-- 子组件 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 自动同步到子组件。
javascript
<!-- 父组件 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
javascript
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 的文件名.
javascript
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 中引用的旧资源路径
javascript
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.0
add_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的堆内存的存储机制有关。
- 基本类型的值存储在栈内存中,变量指向的是具体的值。
- 引用类型的值是存储在堆内存 中,变量在栈内存中存储的是指向堆内存中该对象的引用地址(类似指针)。
基本类型(值直接存储在栈内存中,变量与值直接绑定)
javascript
const num = 10;
num = 20; // 报错:Assignment to constant variable(不能给常量重新赋值)
const str = 'hello';
str = 'world'; // 同样报错
const bool = true;
bool = false; // 报错
引用类型(堆内存指向引用地址,类似指针)
javascript
const obj = { name: 'foo' };
obj = { age: 18 }; // 报错:堆内存中创建新对象引用地址发生改变
obj.name = 'bar'; // 允许,修改属性值
obj.age = 18; // 允许,新增属性
delete obj.name; // 允许,删除属性
const 与堆内存的联系在于 ------ 它保护的是栈内存中指向堆内存的引用地址不变,但不限制堆内存中对象本身的修改。
5.EventBus(全局事件总线)
适合解决非父子组件间的通信问题,核心思路通过全局的Vue实例作为"事件总线"实现事件的发布于订阅。
5.1.创建EventBus实例
创建独立的事件总线文件,作为全局的 "中介",管理所有组件的事件订阅与发布。
javascript
// src/utils/eventBus.js
import Vue from 'vue'
// 实例化一个Vue对象作为事件总线
export default new Vue()
5.2$on订阅事件--接收数据
接收数据的组件中,通过 $on 订阅事件,并在组件销毁前用 $off 取消订阅(避免内存泄漏)。
javascript
<!-- 组件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发布事件--传递数据
javascript
<!-- 组件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,大小写不敏感)
javascript
{
"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分包预加载与页面跳转
页面跳转:分包内页面的跳转路径需使用完整路径(包含分包根目录)。
javascript
// 从主包跳转到分包页面
uni.navigateTo({
url: '/pages/user/profile/profile'
});
// 分包内页面互跳(也需完整路径)
uni.navigateTo({
url: '/pages/goods/list/list'
});
分包预加载 :配置分包在特定时机预加载(进入主包某页面),在 pages.json 中添加 preloadRule。
javascript
{
"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"]
}
}
}