nodejs:vue 3 + vite 作为前端,将 html 填入<iframe>,在线查询英汉词典

向 doubao.com/chat/ 提问:
node.js + js-mdict 作为后端,vue 3 + vite 作为前端,编写在线查询英汉词典

后端部分(express + js-mdict

详见上一篇:nodejs:express + js-mdict 作为后端,vue 3 + vite 作为前端,在线查询英汉词典


前端部分(Vue 3 + Vite)

1. 创建前端项目

node -v
v18.20.6
npm -v
10.8.2

cd \js
cnpm create vite@latest mydict-web --template vue
 选 Vue 
 选 Javascript

项目结构‌:Vite 会自动创建一个基本的项目结构,包括 src目录下的组件、路由和状态管理等文件。主要文件和目录如下:

  • App.vue:根组件
  • main.js:应用程序入口
  • router:Vue Router配置
  • store:状态管理

在 public 中添加一些英汉字典的样式:oalecd8e.css , oalecd8e.js , uk_pron.png, us_pron.png,
copy jquery-3.2.1.min.js pulibc\jquery.js 

修改 index.html 中的标题如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite 在线英汉词典查询</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

修改 src/main.js 如下

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

vite 代理服务器(Proxy)的配置通常用于开发环境,以解决跨域请求等问题。
修改 vite.config.js 如下

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vite.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8006', // 后端服务地址
        changeOrigin: true, // 是否改变源地址
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }    
})

2. 安装依赖

cd mydict-web
cnpm install axios

cnpm install vue-router -S

 package.json 如下

{
  "name": "mydict-web",
  "private": true,
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.7.9",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.2.1",
    "vite": "^6.1.0"
  }
}

3. 编写前端代码

修改 src/App.vue 文件:

<template>
  <div id="app">
    <input v-model="sWord" placeholder="请输入英文单词" @input="prefix" @keyup.enter="search" target="iframe">
    &nbsp; <button @click="search">查询</button>
    &nbsp; <button @click="prefix">前缀查询</button>
    &nbsp; <button @click="fuzzy">模糊查询</button>
    <h3>查询结果</h3>
    <div style="float:left; width:100%;">
      <div id="result" style="float:left; width:75%; height:500; border:2px;">
        <iframe ref="iframe" name="iframe" width="100%" height="500"> </iframe>
      </div>
      <div v-if="alist">
        <div v-html="alist" style="float:right; width:25%; height:500; border:2px;"></div>
      </div>
    </div>
    <div v-if="error">{{ error }}</div>
  </div>
</template>

<script setup>
import { ref,onMounted,onUnmounted } from 'vue';
import axios from 'axios';

const sWord = ref('');
const result = ref('');
const error = ref('');
const iframe = ref(null);
const alist = ref(null);

// 查询 query(word),返回:html
const query = async (word) => {
  try {
    const response = await axios.get('/api/query', {
      params: {
        word: word
      }
    });
    let htm1 = response.data;
    const frame1 = iframe.value;
    if (frame1 && htm1) {
      let doc1 = frame1.contentWindow.document;
      doc1.open();
      doc1.write(htm1);
      doc1.close();
    }
    error.value = '';
  } catch (err) {
    result.value = '';
    error.value = err.response?.data?.error || '请求出错,请稍后重试';
  }
};

// 查询<input >,返回:json.result
const search = async () => {
  try {
    const response = await axios.get('/api/search', {
      params: {
        word: sWord.value
      }
    });
    let htm1 = response.data.result;
    const frame1 = iframe.value;
    if (frame1 && htm1) {
      let doc1 = frame1.contentWindow.document;
      doc1.open();
      doc1.write(htm1);
      doc1.close();
    }
    error.value = '';
  } catch (err) {
    result.value = '';
    error.value = err.response?.data?.error || '请求出错,请稍后重试';
  }
};

// 前缀查询
const prefix = async () => {
  try {
    const response = await axios.get('/api/prefix', {
      params: {
        word: sWord.value
      }
    });
    let items = [];
    let wordls = response.data.wordls;
    wordls.forEach((item, i) => {
      if (i<=20){
        items[i] = `<a href="http://localhost:8006/query?word=${item}" target="iframe">${item}</a>`;
      }
    });
    if (items.length >0){ alist.value = items.join('<br>\n');}
    else { alist.value = '';}
    error.value = '';
  } catch (err) {
    alist.value = '';
    error.value = err.response?.data?.error || '请求出错,请稍后重试';
  }
};

// 模糊查询
const fuzzy = async () => {
  try {
    const response = await axios.get('/api/fuzzy', {
      params: {
        word: sWord.value
      }
    });
    let items = [];
    let wordls = response.data.wordls;
    wordls.forEach((item, i) => {
      if (i<=20){
        items[i] = `<a href="http://localhost:8006/query?word=${item}" target="iframe">${item}</a>`;
      }
    });
    if (items.length >0){ alist.value = items.join('<br>\n');}
    else {alist.value = '';}
    error.value = '';
  } catch (err) {
    alist.value = '';
    error.value = err.response?.data?.error || '请求出错,请稍后重试';
  }
};

// 网页内<a>链接的点击事件
const aClick = (event) => {
  // 检查点击的是否是 <a> 标签
  if (event.target.tagName === 'A') {
    // 阻止默认跳转(即导航到新页面)
    event.preventDefault();
    // 获取链接的 href 属性
    let href = event.target.getAttribute('href');
    console.log('a href:', href);
    // 添加处理 link 逻辑
    if (href.startsWith('entry://')) {
        query(href.substring(8));
    }
  }
};

// 在组件挂载时添加事件监听器
onMounted(() => {
  //document.addEventListener('click', aClick);
    const frame1 = iframe.value;
    frame1.addEventListener('load', () => {
      const iframeDoc = frame1.contentDocument;
      if (iframeDoc) {
        iframeDoc.addEventListener('click', aClick);
      }
    });
});

// 在组件卸载时移除事件监听器,以避免内存泄漏
onUnmounted(() => {
  //document.removeEventListener('click', aClick);
    const frame1 = iframe.value;
    const iframeDoc = frame1.contentDocument;
    if (iframeDoc) {
      iframeDoc.removeEventListener('click', aClick);
    }
});
      
</script>

<style scoped>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: left;
  color: #2c3e50;
  margin-top: 10px;
}
</style>

4. 运行前端项目

cd mydict-web
npm run dev

> mydict-web@0.1.0 dev
> vite
 
  VITE v6.1.1  ready in 1083 ms
 
  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
h
 
  Shortcuts
  press r + enter to restart the server
  press u + enter to show server url
  press o + enter to open in browser
  press c + enter to clear console
  press q + enter to quit
o

 注意事项

  • 跨域问题:在开发环境中使用 cors 中间件解决跨域问题,在生产环境中可以通过配置反向代理等方式处理。
  • MDX 文件路径:确保 app.js 中的 mdict Path 指向正确的 .mdx 词典文件。
  • 安全性:在生产环境中,需要考虑对后端接口进行安全防护,如限制请求频率、验证请求来源等。

通过以上步骤,你就可以实现一个简单的在线英汉词典查询系统。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值