首页
直播
壁纸
友链
搜索
1
微信小程序支付全链路实战:JSAPI 下单、调起支付、回调验签与退款
266 阅读
2
微信小程序云开发实战:云函数、云数据库与云存储的正确使用姿势
256 阅读
3
微信小程序自定义 tabBar 实战:custom-tab-bar 从适配到深色模式
255 阅读
4
微信小程序 Skyline 渲染引擎实战:worklet 动画从原理到落地
253 阅读
5
微信小程序分包进阶:独立分包、预下载与分包异步化实战
246 阅读
服务器运维
后端技术
前端技术
梯子
数据库
小程序
登录
搜索
标签搜索
fastadmin
Redis
微信小程序
前端开发
RabbitMQ
Go
服务器
codex
buildadmin
小程序
mysql
Nginx
Docker
Vue3
Node.js
MySQL优化
Linux
TypeScript
JWT
PHP
沿途的风景
累计撰写
74
篇文章
累计收到
0
条评论
首页
栏目
服务器运维
后端技术
前端技术
梯子
数据库
小程序
页面
直播
壁纸
友链
搜索到
15
篇与
» 前端技术
的结果
2026-06-26
Vue 3 响应式原理剖析:从 Proxy 到副作用调度
Vue 3 用 Proxy 重写了响应式系统,性能与能力都大幅提升。本文从源码设计角度剖析依赖收集、触发更新、副作用调度和计算属性的实现原理,并手写一个迷你响应式系统。一、Vue 2 vs Vue 3 响应式对比特性Vue 2 (defineProperty)Vue 3 (Proxy)属性新增/删除需要 Vue.set/Vue.delete直接支持数组索引赋值不支持支持Map/Set不支持支持初始化递归遍历所有属性惰性代理,访问时才转换性能大对象初始化慢按需代理,更快// Vue 2 的局限 this.obj.newProp = 'value' // ❌ 不触发更新 this.arr[0] = newItem // ❌ 不触发更新 this.arr.length = 0 // ❌ 不触发更新 // Vue 3 全部支持 state.newProp = 'value' // ✅ state.arr[0] = newItem // ✅ state.arr.length = 0 // ✅ state.map.set('key', val) // ✅二、Proxy 基础const raw = { price: 100, quantity: 2 } const proxy = new Proxy(raw, { get(target, key, receiver) { console.log(`读取 ${key}`) track(target, key) // 依赖收集 return Reflect.get(target, key, receiver) }, set(target, key, value, receiver) { console.log(`设置 ${key} = ${value}`) const result = Reflect.set(target, key, value, receiver) trigger(target, key) // 触发更新 return result }, deleteProperty(target, key) { const result = Reflect.deleteProperty(target, key) trigger(target, key) return result }, has(target, key) { track(target, key) return Reflect.has(target, key) } }) proxy.price // 读取 price proxy.quantity = 3 // 设置 quantity = 3为什么用 Reflect? 保证 this 指向代理对象,让内嵌对象也能被正确追踪:const obj = reactive({ nested: { count: 0 } // nested 会被惰性转换为响应式 }) // obj.nested 访问时返回嵌套对象的代理 // 这是 Vue 3 实现深层响应式的关键三、手写迷你响应式系统1. 副作用函数与依赖收集let activeEffect = null const targetMap = new WeakMap() // 追踪依赖 function track(target, key) { if (!activeEffect) return // targetMap: target → key → Set<effect> let depsMap = targetMap.get(target) if (!depsMap) { depsMap = new Map() targetMap.set(target, depsMap) } let deps = depsMap.get(key) if (!deps) { deps = new Set() depsMap.set(key, deps) } deps.add(activeEffect) } // 触发更新 function trigger(target, key) { const depsMap = targetMap.get(target) if (!depsMap) return const deps = depsMap.get(key) if (!deps) return // 复制一份,避免无限循环 const effectsToRun = new Set(deps) effectsToRun.forEach(effect => { // 避免递归调用自身 if (effect !== activeEffect) { effect() } }) }2. effect 函数function effect(fn) { const effectFn = () => { // 清除旧依赖(解决分支切换问题) cleanup(effectFn) activeEffect = effectFn fn() activeEffect = null } effectFn.deps = [] effectFn() return effectFn } function cleanup(effectFn) { effectFn.deps.forEach(depSet => { depSet.delete(effectFn) }) effectFn.deps.length = 0 }3. reactive 实现const reactiveMap = new Map() function reactive(target) { // 同一对象只代理一次 if (reactiveMap.has(target)) { return reactiveMap.get(target) } const proxy = new Proxy(target, { get(target, key, receiver) { if (key === '__isReactive') return true track(target, key) const result = Reflect.get(target, key, receiver) // 惰性深层代理:嵌套对象访问时才转换 if (typeof result === 'object' && result !== null) { return reactive(result) } return result }, set(target, key, value, receiver) { const oldValue = target[key] const type = Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD' const result = Reflect.set(target, key, value, receiver) // 值变化才触发 if (oldValue !== value || (typeof oldValue === 'NaN' && typeof value === 'NaN')) { trigger(target, key, type) } return result }, deleteProperty(target, key) { const hadKey = Object.prototype.hasOwnProperty.call(target, key) const result = Reflect.deleteProperty(target, key) if (hadKey && result) { trigger(target, key, 'DELETE') } return result } }) reactiveMap.set(target, proxy) return proxy }4. ref 实现function ref(value) { return { get value() { track(this, 'value') return value }, set value(newValue) { if (newValue !== value) { value = newValue trigger(this, 'value') } } } }5. computed 实现function computed(getter) { let value let dirty = true // 脏标记:是否需要重新计算 // 作为副作用运行,依赖变化时标记为脏 const effectFn = effect(() => { // 每次依赖变化重新执行,但不会立即触发渲染 if (dirty) { value = getter() } }) // 依赖变化时置脏 // (简化实现,真实实现用 scheduler) return { get value() { if (dirty) { value = getter() dirty = false } return value } } }更接近真实的 computed:function computed(getter) { let value let dirty = true const runner = effect(getter, { lazy: true, // scheduler:依赖变化时不立即执行,只标记脏 scheduler() { if (!dirty) { dirty = true // 通知依赖该 computed 的渲染副作用更新 triggerRefValue(this) } } }) return { get value() { if (dirty) { value = runner() dirty = false } // 让当前 activeEffect 追踪此 computed trackRefValue(this) return value } } }四、副作用调度与批更新Vue 3 的组件更新是异步批处理的:const queue = new Set() let isFlushing = false function queueJob(job) { queue.add(job) // Set 自动去重,同一组件多次修改只更新一次 if (!isFlushing) { isFlushing = true Promise.resolve().then(flushJobs) } } function flushJobs() { // 按组件 id 排序,父组件先更新 const jobs = [...queue].sort((a, b) => a.id - b.id) queue.clear() isFlushing = false jobs.forEach(job => job()) }这就是为什么修改多次数据只触发一次渲染:import { ref, nextTick } from 'vue' const count = ref(0) // 触发 3 次修改,但只渲染 1 次 count.value++ count.value++ count.value++ // DOM 更新是异步的 console.log(document.querySelector('#count').textContent) // 还是旧值 nextTick(() => { console.log(document.querySelector('#count').textContent) // "3" })五、Vue 3 编译优化静态提升(hoistStatic)// 模板中的静态节点会被提升到 render 函数外部 // 只创建一次,重复渲染时直接复用 // 编译前 // <div> // <span class="static">静态内容</span> // <span>{{ dynamic }}</span> // </div> // 编译后 const _hoisted_1 = createVNode("span", { class: "static" }, "静态内容") function render() { return createVNode("div", null, [ _hoisted_1, // 直接复用 createVNode("span", null, toDisplayString(dynamic)) ]) }Patch Flags(补丁标记)// 动态节点带上标记,diff 时只对比标记的部分 createVNode("span", null, text, 1 /* TEXT */) createVNode("span", { class: dynamicClass }, null, 2 /* CLASS */) createVNode("span", { id: dynamicId }, null, 4 /* PROPS */, ["id"]) // PatchFlag 枚举: // TEXT = 1 只有文本变化 // CLASS = 2 只有 class 变化 // STYLE = 4 只有 style 变化 // PROPS = 8 只有 props 变化 // FULL_PROPS = 16 props 有动态 key // NEED_HYDRATION = 32 // STABLE_FRAGMENT = 64 // KEYED_FRAGMENT = 128 带 key 的 fragment // ...Block Tree(块树)// 动态节点被收集到 block 中,diff 时跳过静态内容 function render() { return (openBlock(), createElementBlock("div", null, [ // 大量静态节点... createVNode("span", null, dynamic, 1 /* TEXT */) // 这个动态节点会被"block"追踪 // 更新时直接定位,不需要遍历整棵树 ])) }六、调试响应式onTrack 与 onTrigger// 仅开发模式可用 const state = reactive({ count: 0 }, { onTrack(event) { // 依赖被追踪时 console.log('track:', event.key, event.target) }, onTrigger(event) { // 依赖变化触发时 console.log('trigger:', event.key, event.type, event.newValue) } }) watch(source, callback, { onTrack(e) { debugger }, onTrigger(e) { debugger } })常见响应式陷阱// 1. 解构 reactive 丢失响应式 const state = reactive({ count: 0 }) let { count } = state // ❌ count 是普通值 const { count } = toRefs(state) // ✅ 保持响应式 // 2. ref 在模板中自动解包,在 JS 中需要 .value const count = ref(0) console.log(count) // Ref 对象 console.log(count.value) // 0 // 3. reactive 被整体替换 let state = reactive({ list: [] }) state = reactive({ list: [1] }) // ❌ 旧引用的依赖全部失效 // ✅ 用 ref 包裹,或只修改属性 // 4. markRaw 跳过转换 const state = reactive({ chart: markRaw(new ECharts()) // ✅ 第三方实例不代理 })七、小结Vue 3 响应式系统的核心是 Proxy 拦截 + WeakMap 存储依赖 + 副作用调度。理解 track/trigger 机制、惰性深层代理和异步批更新,不仅能帮你避开响应式陷阱,也是进阶 Vue 源码的第一步。手写迷你版响应式系统是最好的学习方式。本文由 inspirecl.asia 原创,转载请注明出处。
2026年06月26日
5 阅读
0 评论
0 点赞
2026-06-25
Vue 3 组合式 API 深度解析:Composition API 实战
组合式 API(Composition API)是 Vue 3 最重要的特性,解决了 Options API 在复杂组件中逻辑分散的问题。本文通过实战案例深入讲解 setup、响应式 API、生命周期钩子和自定义组合函数。一、为什么需要组合式 APIOptions API 的痛点// Options API:同一功能的代码被拆散到不同选项中 export default { data() { return { // 搜索功能的数据 searchQuery: '', searchResults: [], // 用户功能的数据 user: null, userPermissions: [], } }, computed: { // 搜索功能的计算属性 filteredResults() { /* ... */ }, // 用户功能的计算属性 isAdmin() { /* ... */ }, }, methods: { // 搜索功能的方法 handleSearch() { /* ... */ }, // 用户功能的方法 loadUser() { /* ... */ }, }, mounted() { // 搜索和用户的初始化逻辑混在一起 this.handleSearch() this.loadUser() } }组合式 API 的优势<script setup> // 搜索功能的所有代码聚合在一起 const { searchQuery, searchResults, handleSearch } = useSearch() // 用户功能的所有代码聚合在一起 const { user, isAdmin, loadUser } = useUser() </script>优势说明逻辑复用自定义组合函数替代 mixin,无命名冲突代码组织相关逻辑聚合,而非按选项类型分散类型推导更好的 TypeScript 支持代码压缩setup 语法糖减少样板代码生产性能编译优化(静态提升、patch flag)二、setup 与 script setup基本用法<script setup> import { ref, computed, onMounted } from 'vue' // 顶层变量自动暴露给模板 const count = ref(0) const message = 'Hello' const double = computed(() => count.value * 2) function increment() { count.value++ } onMounted(() => { console.log('组件已挂载') }) </script> <template> <button @click="increment">{{ count }} × 2 = {{ double }}</button> </template>defineProps 与 defineEmits<script setup> // 编译宏,无需导入 const props = defineProps({ title: String, items: { type: Array, default: () => [] }, disabled: Boolean }) // TypeScript 方式 // const props = defineProps<{ // title: string // items: Item[] // disabled?: boolean // }>() const emit = defineEmits(['update', 'delete']) // 带验证的 emit // const emit = defineEmits<{ // (e: 'update', value: number): void // (e: 'delete', id: string): void // }>() function handleUpdate() { emit('update', props.title) } </script>defineExpose 与 defineModel<script setup> import { ref } from 'vue' const count = ref(0) // 暴露给父组件的方法 defineExpose({ reset: () => { count.value = 0 }, getValue: () => count.value }) // Vue 3.4+ defineModel 双向绑定 const modelValue = defineModel({ default: '' }) const visible = defineModel('visible', { type: Boolean, default: false }) </script>三、响应式 APIref 与 reactiveimport { ref, reactive, shallowRef, shallowReactive } from 'vue' // ref — 适合基本类型和需要整体替换的对象 const count = ref(0) const user = ref({ name: '张三', age: 25 }) console.log(count.value) // 访问需 .value user.value.name = '李四' // 修改属性 user.value = { name: '王五' } // 整体替换 // reactive — 适合不替换整体的复合对象 const state = reactive({ items: [], loading: false, filters: { category: null, price: null } }) state.loading = true // 直接访问,无需 .value state.items.push({ id: 1 }) // ⚠️ reactive 的陷阱 let { items } = state // ❌ 解构丢失响应式 items = toRef(state, 'items') // ✅ 用 toRef 保持响应式ref vs reactive 选择原则场景推荐基本类型ref需要整体替换ref表单对象reactive组合函数返回值ref(保持统一)大型只读配置shallowRef / markRawreadonly 与 shallowRefimport { readonly, shallowRef, triggerRef, markRaw } from 'vue' // readonly — 防止修改(如传递给子组件的 props) const original = reactive({ count: 0 }) const copy = readonly(original) // copy.count++ // ⚠️ 警告:无法修改 // shallowRef — 只追踪 .value 本身,适合大列表 const list = shallowRef([]) // 替换整个数组触发更新 list.value = [...list.value, newItem] // 修改内部属性后手动触发 list.value.push(anotherItem) triggerRef(list) // markRaw — 排除响应式转换(如第三方类实例) const chart = markRaw(new ECharts())computed 与 watchimport { ref, computed, watch, watchEffect } from 'vue' const firstName = ref('三') const lastName = ref('张') // computed — 有缓存 const fullName = computed(() => `${lastName.value}${firstName.value}`) // 可写 computed const fullNameWritable = computed({ get: () => `${lastName.value}${firstName.value}`, set: (val) => { lastName.value = val[0] firstName.value = val.slice(1) } }) // watch — 明确监听源,可获取旧值 watch(fullName, (newVal, oldVal) => { console.log(`${oldVal} → ${newVal}`) }) // watch 对象属性 watch( () => state.filters, (newFilters) => { fetchList(newFilters) }, { deep: true, immediate: true } ) // watchEffect — 立即执行,自动追踪依赖 watchEffect(() => { console.log(`${firstName.value} ${lastName.value}`) }) // watchEffect 清理副作用 watchEffect((onCleanup) => { const timer = setTimeout(() => { console.log(firstName.value) }, 500) onCleanup(() => clearTimeout(timer)) // 依赖变化时清理 })toRefs 与 toValueimport { toRefs, toRef, toValue, reactive } from 'vue' const state = reactive({ x: 1, y: 2 }) // toRefs — 用于解构 reactive 对象时保持响应式 const { x, y } = toRefs(state) // toRef — 单个属性 const yRef = toRef(state, 'y') // toValue — 统一处理 ref / getter / 普通值(Vue 3.3+) function resolveValue(maybeRefOrGetter) { return toValue(maybeRefOrGetter) } resolveValue(refValue) // ref → .value resolveValue(() => 123) // getter → 调用结果 resolveValue(123) // 普通值 → 原样返回四、生命周期钩子<script setup> import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured, onActivated, onDeactivated } from 'vue' 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 // 阻止继续传播 }) // KeepAlive 相关 onActivated(() => console.log('组件激活')) onDeactivated(() => console.log('组件失活')) </script>五、自定义组合函数useFetch — 数据请求// composables/useFetch.js import { ref, shallowRef, triggerRef, toValue, watchEffect } from 'vue' export function useFetch(url) { const data = shallowRef(null) const error = ref(null) const loading = ref(false) async function doFetch() { data.value = null error.value = null loading.value = true try { const res = await fetch(toValue(url)) if (!res.ok) throw new Error(res.statusText) data.value = await res.json() } catch (e) { error.value = e } finally { loading.value = false } } // url 是响应式时自动重新请求 watchEffect(doFetch) return { data, error, loading, retry: doFetch } }<script setup> import { ref } from 'vue' import { useFetch } from '@/composables/useFetch' const userId = ref(1) const { data: user, error, loading } = useFetch( () => `/api/users/${userId.value}` // getter 形式 ) </script> <template> <div v-if="loading">加载中...</div> <div v-else-if="error">错误: {{ error.message }}</div> <div v-else-if="user">{{ user.name }}</div> </template>useLocalStorage — 本地存储// composables/useLocalStorage.js import { ref, watch } from 'vue' export function useLocalStorage(key, defaultValue) { const value = ref( JSON.parse(localStorage.getItem(key)) ?? defaultValue ) watch(value, (val) => { localStorage.setItem(key, JSON.stringify(val)) }, { deep: true }) function remove() { localStorage.removeItem(key) value.value = defaultValue } return { value, remove } }useMouse — 鼠标追踪// composables/useMouse.js import { ref, onMounted, onUnmounted } from 'vue' export function useMouse() { const x = ref(0) const y = ref(0) function update(e) { x.value = e.pageX y.value = e.pageY } onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) return { x, y } }useDebounceFn — 防抖// composables/useDebounceFn.js import { customRef } from 'vue' export function debouncedRef(value, delay = 200) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) } } }) }六、provide 与 inject<!-- 祖先组件 --> <script setup> import { provide, ref, readonly } from 'vue' const theme = ref('dark') const user = ref({ name: '张三' }) // 提供只读数据 provide('theme', readonly(theme)) provide('user', readonly(user)) // 提供修改方法 provide('updateTheme', (val) => { theme.value = val }) </script><!-- 后代组件(任意深度) --> <script setup> import { inject } from 'vue' const theme = inject('theme', 'light') // 带默认值 const user = inject('user') const updateTheme = inject('updateTheme') updateTheme('light') </script>七、小结组合式 API 是 Vue 3 的核心范式:ref/reactive 管理状态,computed/watch 处理派生与副作用,自定义组合函数实现逻辑复用。掌握这些 API 后,你会发现复杂组件的开发变得清晰而高效。本文由 inspirecl.asia 原创,转载请注明出处。
2026年06月25日
5 阅读
0 评论
0 点赞
2026-03-05
Vue3 Composition API 深度解析:从 ref 到自定义 Hook
前言Vue3 的 Composition API 是对 Options API 的范式升级。它解决了大型组件中逻辑复用和组织的问题,让代码更灵活、更可维护。一、响应式基础1.1 ref vs reactiveimport { ref, reactive, toRefs } from "vue"; // ref:适用于基本类型 const count = ref(0); count.value++; // reactive:适用于对象 const state = reactive({ name: "张三", age: 25, }); // reactive 的陷阱:解构会失去响应性 const { name } = state; // name 不再是响应式 // 解决方案:toRefs const { name: reactiveName } = toRefs(state);1.2 computedimport { ref, computed } from "vue"; const firstName = ref("张"); const lastName = ref("三"); // 只读计算属性 const fullName = computed(() => `${firstName.value}${lastName.value}`); // 可写计算属性 const fullNameWritable = computed({ get() { return `${firstName.value}${lastName.value}`; }, set(newValue: string) { firstName.value = newValue[0]; lastName.value = newValue.slice(1); }, });二、watch 和 watchEffect2.1 watchimport { ref, watch } from "vue"; const keyword = ref(""); // 监听单个 ref watch(keyword, (newVal, oldVal) => { console.log(`关键词从 ${oldVal} 变为 ${newVal}`); }); // 监听多个源 watch([keyword, category], ([newKeyword, newCategory]) => { search(newKeyword, newCategory); }); // 深度监听对象 watch( () => state, (newState) => { saveToStorage(newState); }, { deep: true } );2.2 watchEffectimport { ref, watchEffect } from "vue"; const userId = ref(1); // 自动追踪依赖,立即执行 watchEffect(() => { fetchUser(userId.value); // userId 变化时自动重新执行 });三、自定义 Hook(组合式函数)3.1 useMousePosition// composables/useMousePosition.ts import { ref, onMounted, onUnmounted } from "vue"; export function useMousePosition() { const x = ref(0); const y = ref(0); function update(e: MouseEvent) { x.value = e.pageX; y.value = e.pageY; } onMounted(() => { window.addEventListener("mousemove", update); }); onUnmounted(() => { window.removeEventListener("mousemove", update); }); return { x, y }; }使用:<script setup lang="ts"> import { useMousePosition } from "@/composables/useMousePosition"; const { x, y } = useMousePosition(); </script> <template> <p>鼠标位置: {{ x }}, {{ y }}</p> </template>3.2 useFetch// composables/useFetch.ts import { ref, watchEffect } from "vue"; export function useFetch<T>(url: () => string) { const data = ref<T | null>(null); const error = ref<string | null>(null); const loading = ref(false); watchEffect(async () => { loading.value = true; error.value = null; try { const res = await fetch(url()); if (!res.ok) throw new Error(`HTTP ${res.status}`); data.value = await res.json() as T; } catch (e) { error.value = (e as Error).message; } finally { loading.value = false; } }); return { data, error, loading }; }四、生命周期钩子import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from "vue"; onBeforeMount(() => { /* 组件挂载前 */ }); onMounted(() => { /* DOM 已挂载 */ }); onBeforeUpdate(() => { /* 响应式数据变更,DOM 更新前 */ }); onUpdated(() => { /* DOM 更新后 */ }); onBeforeUnmount(() => { /* 组件卸载前 */ }); onUnmounted(() => { /* 组件已卸载 */ });五、总结Composition API 的核心优势在于逻辑复用和代码组织。通过自定义 Hook,可以将组件逻辑提取为可复用的函数,告别 Options API 时代 mixin 的命名冲突和数据来源不透明问题。建议在项目中优先使用 <script setup> 语法,配合 TypeScript 获得最佳开发体验。
2026年03月05日
16 阅读
0 评论
0 点赞
2026-02-26
buildadmin百度编辑器放两个只能显示一个
刚刚试了下,是因为两个编辑器要去后台获取两次配置,而相同的请求会被拦截,导致获取不到,直接把从后台获取到的配置复制过来,不使用接口去获取就可以了还有一种方式是在请求体中增加一个随机字符串即可将两个请求识别为不同的请求
2026年02月26日
21 阅读
0 评论
0 点赞
2026-02-06
hkcms使用ueditor编辑器,七牛添加了防盗链后,后台图片不显示问题
为了防止自己网站资源被人盗用,减少不必要的流量损失,开启网站防盗链是非常有必要的。但是,为了让网站内容更丰富,后台都会选择使用文本编辑器然添加文章。而有些文本编辑器是通过 iframe 内嵌实现的,这样一来就可能会有一个问题,当我们通过编辑器上传图片给文章插入图片,结果图片不显示为什么会这样呢?这不是自己网站内的图片吗?今天博主就一百度ueditor编辑器为例来说说为什么会出现这样的情况,顺便告诉大家如何处理这样个问题。我们先来打开带有百度ueditor编辑器的页面解析渲染后的代码,定位到编辑器文本输入区域,我们会发现百度编辑器的文本编辑区其实是通过 iframe 嵌入的,而 iframe 的 src 是一串 js 代码。<!DOCTYPE html> <html xmlns='http://www.w3.org/1999/xhtml' class='view' > <head> <style type='text/css'> .view{padding:0;word-wrap:break-word;cursor:text;height:90%;} body{margin:8px;font-family:sans-serif;font-size:16px;} p{margin:5px 0;} </style> <!-- 如果你是复制我的这段代码,在此之前一定要先看下自己编辑器下 iframe.css 的路径是不是跟我的一样--> <~-- 不同框架引入百度 ueditor 编辑器,解析后 iframe.css 的路径可能不一样--> <link rel='stylesheet' type='text/css' href='/statics/js/ueditor/themes/iframe.css'/> </head> <body class='view' ></body> <script type='text/javascript' id='_initialScript'> setTimeout(function(){editor = window.parent.UE.instants['ueditorInstant0'];editor._setup(document);},0);var _tmpScript = document.getElementById('_initialScript');_tmpScript.parentNode.removeChild(_tmpScript); </script> </html>编辑器内的图片之所以不显示,是因为图片的 referer 不是我们期望的域名链接,而 iframe 资源的 referer 是以 src 的主域名为主的,iframe 的 src 没有主域名,它里面的资源当然也就不会有 referer ,所以里面的图片才不会显示。既然知道了问题的根源,解决起来就好办了。iframe 的主要功能是内嵌页面,既然如此为什么不把 src 的内容放到自己网站内的一个页面内,然后在页面加载完编辑器的第一时间把 iframe 的 src 换成这个页面在自己网站内的访问链接,这样 src 不就有主域名了吗,iframe 内的资源自然也就有 referer了,图片也就可以显示了。开启防盗链后编辑器图片不显示具体解决方法1、把 iframe src 里的代码放到 uedit.html 页面,并上传到网站根目录(可以是任意目录位置,只要能通过域名访问到就可以)。【这个页面以后几乎都不会修改,所以可以把权限设置成444】2、找到有使用编辑器的页面,在页面内加入下面这段 js 脚本代码,这是个定时器,可以放在任意位置,定时器的目的是监测编辑器的文本编辑区是否加载,如果加载了就把 iframe 的 src 换成可以访问的 uedit.html 页面的链接。之所以用定时器,是因为,并是不页面加载完后编辑器的文本编辑器也自动加载了,从 src 的代码中可以发现,百度编辑器在加载时也用了定时器,所以这里用定时器来监测文本编辑器是否已加载,定时器的时间间隔设置的很短,所以在页面加载时几乎察觉不到变化。// ueditor_0 文本编辑器 iframe 的 id var timer = setInterval(function(){ if($('#ueditor_0').length>0){ $('#ueditor_0').attr('src', '/uedit.html') clearTimeout(timer) } }, 10)1、iframe 本质就是内嵌页面,在 src 通过 js 脚本来实现和直接加载页面的效果是一样的,所以不用担心修改后添加不了文章。2、uedit.html 这个文件名可以随意取,只要里面放的是 文本编辑器 iframe src 里的 js 代码就可以,文件位置也可以随意放,只要能通过域名访问到就可以。3、如果之后在后台有使用编辑器,可以把 uedit.html 的文件权限设置成 444 。4、如果你用的不是百度编辑器也可以参照这种方法来做,图片不显示无外乎 没有 referer 或者 referer 不对,只要编辑器内的资源的 referer 是自己期望的,自然可以正常使用。
2026年02月06日
12 阅读
0 评论
0 点赞
1
2
3
0:00