普通视图

发现新文章,点击刷新页面。
昨天 — 2025年12月12日首页
昨天以前首页

Vue3和Vue2的核心区别?很多开发者都没完全搞懂的10个细节

作者 刘大华
2025年12月11日 11:08

大家好,我是大华!这篇我们来讲解Vue2和Vue3的核心区别在哪里?

Vue3是Vue2的升级版,不仅更快,还更好用。它解决了Vue2中一些让人头疼的问题,比如动态添加属性不响应、组件必须包在一个根元素里等等。

下面通过10个常见的对比例子,让你快速看懂Vue3到底新在哪儿、好在哪儿。

1. 响应式系统:Object.defineProperty vs Proxy

Vue 2 无法监听动态添加的属性(除非用 Vue.set);Vue 3 可以直接响应。

// Vue 2 不会触发更新
this.obj.newProp = 'hello'

// Vue 2 正确方式
this.$set(this.obj, 'newProp', 'hello')

// Vue 3 直接赋值即可响应
this.obj.newProp = 'hello'

2. Composition API(组合式 API)



export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}




import { ref } from 'vue'

const count = ref(0)
const increment = () => count.value++


3. TypeScript 支持

// Vue 3 + TypeScript(能更好的支持)

interface Props {
  msg: string
}
const props = defineProps()

Vue 2 虽可通过 vue-class-componentvue-property-decorator 支持 TS,但配置复杂且类型推导弱。


4. Fragment(多根节点)



  <header>Header</header>
  <main>Main</main>




  <header>Header</header>
  <main>Main</main>


5. Teleport(传送门)

将 modal 渲染到 body 下,避免样式嵌套问题


  Open Modal
  
    <div class="modal">
      <p>Hello from Teleport!</p>
      Close
    </div>
  



import { ref } from 'vue'
const open = ref(false)

Vue 2 需手动操作 DOM 或使用第三方库(如 portal-vue)。


6. Suspense(异步组件加载)




const res = await fetch('/api/data')
const data = await res.json()



  <div>{{ data }}</div>



  
    
      
    
    
      <div>Loading...</div>
    
  

Vue 2 无原生 ``,需自行管理 loading 状态。


7. 全局 API 变更

// Vue 2
Vue.component('MyButton', MyButton)
Vue.directive('focus', focusDirective)

// Vue 3
import { createApp } from 'vue'
const app = createApp(App)
app.component('MyButton', MyButton)
app.directive('focus', focusDirective)
app.mount('#app')

Vue 3 的应用实例彼此隔离,适合微前端或多实例场景。


8. 生命周期钩子命名变化

// Vue 2
export default {
  beforeDestroy() { /* cleanup */ },
  destroyed() { /* final */ }
}

// Vue 3(Options API 写法)
export default {
  beforeUnmount() { /* cleanup */ },
  unmounted() { /* final */ }
}

// Vue 3(Composition API)
import { onBeforeUnmount, onUnmounted } from 'vue'
onBeforeUnmount(() => { /* cleanup */ })
onUnmounted(() => { /* final */ })

9. v-model 多绑定












10. 显式声明 emits(推荐)



const emit = defineEmits(['submit', 'cancel'])

const handleSubmit = () => emit('submit')




const emit = defineEmits({
  submit: (payload) => typeof payload === 'string',
  cancel: null
})

Vue 2 中 $emit 无需声明,但不利于工具链和文档生成。


这些示例覆盖了 Vue2 和 Vue3 比较关键的差异点。通过代码对比,可以更清楚地看到 Vue3 在开发体验、性能、灵活性和工程化方面有明细的提升。

结尾

总的来说,Vue3 在保持简单上手的同时,增加了更多实用又强大的功能。不管是写代码更轻松了,还是对 TypeScript、大型项目的支持更好了,都让开发者的工作变得更高效。

本文首发于公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!

📌往期精彩

《async/await 到底要不要加 try-catch?异步错误处理最佳实践》

《如何查看 SpringBoot 当前线程数?3 种方法亲测有效》

《Java 开发必看:什么时候用 for,什么时候用 Stream?》

《这 10 个 MySQL 高级用法,让你的代码又快又好看》

前端怎么防止用户复制?这10种方法让你的内容更安全

作者 刘大华
2025年12月10日 10:54

大家好,我是大华!在我们写前端的时候,有时候会遇到这种禁止用户复杂网页内容的需求,这里来分享几种常见的方法,但是这些方法也不能完全阻止内容被复制。

因为用户还是可以通过开发者工具,截图提取文字等等这些方式来进行复制。不过我们也可以在一定程度上提升复制的门槛,防止普通用户随意复制。


以下是几种常用方案:

1. 禁用文本选择

使用 CSS 禁止用户选中文本:

body {
  -webkit-user-select: none; /* Safari */
  -moz-user-select: none;    /* Firefox */
  -ms-user-select: none;     /* IE/Edge */
  user-select: none;         /* 标准语法 */
}

也可以针对特定元素设置:

<div style="user-select: none;">这段文字无法被选中</div>

用户仍可查看源代码或使用其他手段复制。


2. 监听并阻止复制、剪切、粘贴事件

通过 JavaScript 拦截相关键盘和剪贴板事件:

document.addEventListener('copy', (e) => {
  e.preventDefault();
  alert('复制已被禁止');
});

document.addEventListener('cut', (e) => {
  e.preventDefault();
  alert('剪切已被禁止');
});

document.addEventListener('paste', (e) => {
  e.preventDefault();
  alert('粘贴已被禁止');
});

用户还是可以按下F12禁用 JavaScript 或绕过事件监听。


3. 将内容嵌入图片或 Canvas

将关键内容渲染为图片或使用 <canvas> 绘制,使文本不可直接选中:

<img src="text-as-image.png" alt="不可复制的文字">

或使用 canvas 动态绘制:

<canvas id="myCanvas"></canvas>
<script>
  const ctx = document.getElementById('myCanvas').getContext('2d');
  ctx.font = '20px Arial';
  ctx.fillText('这段文字无法复制', 10, 50);
</script>

不利于 SEO、无障碍访问(屏幕阅读器无法读取),且加载慢。


4.用户按F12就出发debug功能

当用户按下F12时,触发debug调试,让用户无法选中页面内容。

<script>
(function antiDebug() {
  let devOpen = false;
  const threshold = 100;

  function check() {
    const start = performance.now();
    debugger;
    const end = performance.now();

    if (end - start > threshold) {
      if (!devOpen) {
        devOpen = true;
        document.body.innerHTML = '<h2>检测到开发者工具,请关闭后重试。</h2>';
        // 可选:上报用户行为
        // fetch('/log-devtools', { method: 'POST' });
      }
    } else {
      devOpen = false;
    }

    setTimeout(check, 1000);
  }

  check();
})();
</script>

5. 动态文本拆分与重组

将文本内容拆分成多个DOM节点,增加复制难度:

<div id="protected-text">
  <!-- 文本将被JavaScript拆分并插入 -->
</div>

<script>
  const text = "这是一段需要保护的机密内容,不能被轻易复制";
  const container = document.getElementById('protected-text');
  
  // 将每个字符用span包裹
  text.split('').forEach(char => {
    const span = document.createElement('span');
    span.textContent = char;
    span.style.display = 'inline-block'; // 增加选择难度
    container.appendChild(span);
  });
</script>

进阶版:随机插入不可见字符或零宽字符:

function obfuscateText(text) {
  const zeroWidthSpace = '\u200B'; // 零宽空格
  return text.split('').map(char => 
    char + zeroWidthSpace.repeat(Math.floor(Math.random() * 3))
  ).join('');
}

const originalText = "保护内容";
document.getElementById('text').textContent = obfuscateText(originalText);

6. 使用CSS伪元素显示内容

通过CSS的::before::after伪元素显示文本:

<style>
.protected-content::before {
  content: "这段文字通过CSS生成,无法直接选中和复制";
}
</style>

<div class="protected-content"></div>

7. 字体反爬虫技术

使用自定义字体文件,将字符映射关系打乱:

@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
}

.protected-text {
  font-family: 'CustomFont';
}

在字体文件中,将实际字符与显示字符的映射关系打乱,比如:

  • 字符a在字体中实际显示为b
  • 字符b显示为c,以此类推

后端配合:服务器动态生成字体文件,定期更换映射关系。


8. Canvas + WebGL 渲染文本

使用更复杂的图形渲染技术:

<canvas id="textCanvas" width="800" height="100"></canvas>
<script>
  const canvas = document.getElementById('textCanvas');
  const ctx = canvas.getContext('2d');
  
  // 设置文字样式
  ctx.font = '24px Arial';
  ctx.fillStyle = '#333';
  
  // 绘制干扰元素
  ctx.fillText('保护内容', 50, 50);
  
  // 添加噪声干扰
  for(let i = 0; i < 100; i++) {
    ctx.fillRect(
      Math.random() * 800, 
      Math.random() * 100, 
      1, 1
    );
  }
</script>

9. SVG 文本渲染

使用SVG渲染文本,增加选择难度:

<svg width="400" height="100">
  <text x="10" y="30" font-family="Arial" font-size="20" 
        fill="black" style="user-select: none;">
    这段SVG文本难以复制
  </text>
  <!-- 添加干扰路径 -->
  <path d="M10,40 L390,40" stroke="#eee" stroke-width="1"/>
</svg>

10. 实时DOM监控与修复

监控DOM变化,防止用户通过开发者工具修改内容:

const contentElement = document.getElementById('protected-content');
const originalContent = contentElement.innerHTML;

// 定时检查内容完整性
setInterval(() => {
  if (contentElement.innerHTML !== originalContent) {
    contentElement.innerHTML = originalContent;
    console.log('检测到内容篡改,已恢复');
  }
}, 500);

// 使用MutationObserver更精确的监控
const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    if (mutation.type === 'childList' || mutation.type === 'characterData') {
      contentElement.innerHTML = originalContent;
    }
  });
});

observer.observe(contentElement, {
  childList: true,
  characterData: true,
  subtree: true
});

综合防御策略建议

对于高安全要求的场景,建议采用分层防御

class ContentProtector {
  constructor() {
    this.init();
  }
  
  init() {
    this.disableSelection();
    this.bindEvents();
    this.startMonitoring();
    this.obfuscateContent();
  }
  
  disableSelection() {
    document.head.insertAdjacentHTML('beforeend', `
      <style>
        body { -webkit-user-select: none; user-select: none; }
        .protected { cursor: default; }
      </style>
    `);
  }
  
  bindEvents() {
    // 绑定所有阻止事件
    ['copy', 'cut', 'paste', 'contextmenu', 'keydown'].forEach(event => {
      document.addEventListener(event, this.preventDefault);
    });
  }
  
  preventDefault(e) {
    e.preventDefault();
    return false;
  }
  
  startMonitoring() {
    // 启动各种监控
    this.monitorDevTools();
    this.monitorDOMChanges();
  }
  
  obfuscateContent() {
    // 内容混淆处理
    // ...
  }
  
  monitorDevTools() {
    // 开发者工具检测
    // ...
  }
  
  monitorDOMChanges() {
    // DOM变化监控
    // ...
  }
}

// 初始化保护
new ContentProtector();

总结

这些方法只能防普通用户,防不住真正想复制的人。

因为别人还是可以通过看源码、截图、关掉 JS 等方式绕过限制。

所以建议:

  • 别过度防护,以免影响正常用户和 SEO;
  • 重要内容靠后端控制(比如登录才能看全文);
  • 组合使用几种方法,提高门槛就好,别追求绝对安全。

毕竟前端展示的内容,就默认是能被看到的,也就能被复制的。

本文首发于公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!

📌往期精彩

《async/await 到底要不要加 try-catch?异步错误处理最佳实践》

《都在用 Java8 和 Java17,那 Java9 到 16 呢?他们真的没用吗?》

《Java 开发必看:什么时候用 for,什么时候用 Stream?》

《这 10 个 MySQL 高级用法,让你的代码又快又好看》

❌
❌