基于Vue+nodejs+Element-ui的聊天框项目(二)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 基于Vue+nodejs+Element-ui的聊天框项目

聊天框内容代码

<template>
  <div class="box">
    <div class="top">
      <i class="el-icon-caret-left" @click="go"></i>
      <span>{{ list.username }}</span>
      <img :src="list.image" alt="" />
    </div>
    <ul class="center" ref="center">
      <li
        v-for="item in messages"
        :key="item.id"
        :class="{ userId: item.user_id === userinfo.user_id }"
      >
        <img :src="list.image" alt="" v-if="item.user_id === list.user_id" />
        <p>
          <span v-if="item.user_id === list.user_id" class="title"
            >{{ list.username }} <br
          /></span>
          <span class="content">{{ item.content }}</span>
        </p>
        <img
          :src="userinfo.image"
          alt=""
          v-if="item.user_id !== list.user_id"
        />
      </li>
    </ul>
    <div class="bottom">
      <input
        type="text"
        placeholder="请输入内容"
        v-model="input"
        @keydown.enter="send"
        class="inp"
      />
      <button class="btn" @click="send">发送(S)</button>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [],
      input: "",
      messages: [],
      userinfo: [],
      timeout: "",
    };
  },
  mounted() {
    this.userinfo = this.$store.state.userInfo;
    // console.log(this.userinfo);
    //   console.log(this.$route.query.userid);
    let { userid } = this.$route.query;
    // console.log(userid);
    this.$axios({
      url: `/api/my/userid?id=${userid}`,
      method: "get",
      headers: {
        Authorization: localStorage.getItem("token"),
      },
    }).then(({ data }) => {
      // console.log(data);
      this.list = data.list;
    });
    var i = 0;
    this.timeout = setInterval(() => {
      i++;
      this.$nextTick(() => {
        // console.log(this.$refs.center.scrollHeight);
        this.$refs.center.scrollTop = this.$refs.center.scrollHeight;
      });
      if (i > 3) {
        clearInterval(this.timeout);
      }
    }, 500);
  },
  destroyed() {
    clearInterval(this.timeout);
  },
  watch: {
    messages: {
      handler() {
        this.gets();
      },
      immediate: true,
    },
  },
  methods: {
    go() {
      this.$router.go(-1);
    },
    send() {
      let { userid } = this.$route.query;
      if (this.input !== "") {
        this.$axios({
          url: `/api/my/publish`,
          method: "post",
          headers: {
            Authorization: localStorage.getItem("token"),
          },
          data: {
            content: this.input,
            receive_id: userid,
          },
        }).then(({ data }) => {
          this.messages = data.list;
          // console.log(data);
          this.input = "";
          this.$nextTick(() => {
            this.$refs.center.scrollTop = this.$refs.center.scrollHeight;
          });
        });
      } else {
        this.$message({
          message: "发布消息不能为空!",
          type: "warning",
        });
      }
    },
    gets() {
      let { userid } = this.$route.query;
      this.$axios({
        url: `/api/my/messages?display=${userid}`,
        method: "get",
        headers: {
          Authorization: localStorage.getItem("token"),
        },
      }).then(({ data }) => {
        // console.log(data);
        this.messages = data.list;
      });
    },
  },
};
</script>
<style lang="scss" scoped>
.box {
  height: 100vh;
  .top {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 0.5rem;
    padding: 0.05rem 0.1rem;
    background-color: gainsboro;
    display: flex;
    align-items: center;
    justify-content: space-between;
    i {
      font-size: 0.2rem;
    }
    img {
      width: 0.4rem;
      height: 0.4rem;
      border-radius: 50%;
    }
  }
  .center {
    padding: 0.5rem 0 0.4rem 0;
    height: 100vh;
    overflow: auto;
    background-color: #f5f5f5;
    li {
      list-style: none;
      width: 100%;
      display: flex;
      align-items: center;
      padding: 0 0.05rem;
      p {
        margin: 0.1rem;
        .title {
          font-size: 0.1rem;
          color: gray;
        }
        .content {
          padding: 0.02rem 0.05rem;
          background-color: #fff;
          border-radius: 0.05rem;
        }
      }
      img {
        width: 0.3rem;
        height: 0.3rem;
        border-radius: 50%;
      }
    }
    .userId {
      justify-content: flex-end;
    }
  }
  .bottom {
    width: 100vw;
    position: fixed;
    left: 0;
    bottom: 0;
    display: flex;
    .inp {
      width: 2.55rem;
      padding: 0.1rem;
      border: 1px solid gainsboro;
    }
    .inp:focus {
      outline: none;
    }
    .btn {
      width: 1rem;
      padding: 0 0.1rem;
      box-sizing: content-box;
      border: none;
      background-color: rgb(182, 151, 151);
      color: rgb(0, 155, 39);
      margin: 0;
    }
  }
}
</style>
  • 好友页代码
<template>
  <div class="box">
    <ul>
      <li v-for="item in list" :key="item.id" @click="add(item)">
        <img :src="item.image" alt="" />
        <p>{{ item.username }}</p>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [],
    };
  },
  mounted() {
    this.$axios({
      url: "/api/my/userall",
      method: "get",
      headers: {
        Authorization: localStorage.getItem("token"),
      },
    }).then(({ data }) => {
      console.log(data);
      this.list = data.list;
    });
  },
  methods: {
    add(item) {
      // console.log(item);
      this.$router.push({
        name: "chat",
        query: { userid: item.user_id },
      });
    },
  },
};
</script>
<style lang="scss" scoped>
.box {
  ul {
    li {
      list-style: none;
      display: flex;
      align-items: center;
      padding: 0.1rem 0.1rem;
      border-bottom: 1px solid gainsboro;
      img {
        width: 0.5rem;
        height: 0.5rem;
        border-radius: 50%;
        border: 1px solid red;
        margin-right: 0.1rem;
        color: #333;
      }
    }
    li:hover {
      background-color: gainsboro;
    }
  }
}
</style>
  • router 路由页代码
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '@/components/home'
import axios from 'axios'
import store from '@/store/index'
Vue.use(VueRouter)
const routes = [
  {
    path: '/',
    name: 'home',
    redirect: 'footer',
    component: HomeView,
    meta: {
      isLogn:true
    }
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/login/index'),
  },
  {
    path: '/footer',
    name: 'footer',
    redirect: {name:'user'},
    component: () => import('@/views/home/footer.vue'),
    meta: {
      isLogn:true
    },
    children: [
      {
        path: 'user',
        name: 'user',
        component: () => import('@/views/user/index.vue'),
        meta: {
          isLogn:true
        }
      },
      {
        path: 'mine',
        name: 'mine',
        component: () => import('@/views/mine/index.vue'),
        meta: {
          isLogn:true
        }
      },
    ]
  },
  {
    path: '/chat',
    name: 'chat',
    component: () => import('@/views/chat/index.vue'),
    meta: {
      isLogn:true
    }
  },
  {
    path: '/setMessage',
    name: 'setMessage',
    component: () => import('@/views/mine/setMessage.vue'),
  }
]
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')
  const islogin = !!token
  // console.log(token, islogin)
  var data = ''
  axios({
    url: `/api/my/myuser`,
      method: "get",
      headers: {
        Authorization: localStorage.getItem("token"),
      },
    }).then(({ data }) => {
      store.commit('userInfo', data.list)
      if (to.matched.some((item) => item.meta.isLogn)) {
        if (data.status === 1) {
          localStorage.removeItem('token')
          window.location.href = '/login'
        } else {
          if (islogin) {
            next()
          } else {
            // 没有跳转至登录
            window.location.href = '/login'
          }
        }
      } else {
        if (islogin && to.path === '/login') {
          // 跳转至首页
          window.location.href = '/'
          return
        }
        next()
      }
    })
})
export default router
  • vuex 页代码
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    userInfo: [],
    isRolling: false,
  },
  getters: {
    isLogin(userInfo) {
      return !!userInfo.username 
    }
  },
  mutations: {
    addlogin(state) {
      state.isRolling = !state.isRolling
    },
    userInfo(state, data) {
      state.userInfo = data
    }
  },
  actions: {
  },
  modules: {
  }
})
  • package.json 页代码
{
  "name": "chat_vue",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build"
  },
  "dependencies": {
    "axios": "^1.1.3",
    "core-js": "^3.8.3",
    "element-ui": "^2.15.12",
    "vue": "^2.6.14",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~5.0.0",
    "@vue/cli-plugin-router": "~5.0.0",
    "@vue/cli-plugin-vuex": "~5.0.0",
    "@vue/cli-service": "~5.0.0",
    "sass": "^1.32.7",
    "sass-loader": "^12.0.0",
    "vue-template-compiler": "^2.6.14"
  }
}
  • 移动端代码
const WIDTH = 375//如果是尺寸的设计稿在这里修改
const setView = () => {
    //设置html标签的fontSize
    document.documentElement.style.fontSize = (100 * document.documentElement.clientWidth / WIDTH) + 'px'
}
window.onresize = setView
setView()
  • main.js页面代码
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from 'axios'
import '@/rem/index'
Vue.prototype.$axios = axios
Vue.use(ElementUI);
Vue.config.productionTip = false
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

六、MySQL 数据库创建功能展示

  • 用户信息表ad89bc58b83b47e181a28b8ce8dfca86.png

聊天发布内容表

image.png


七、node.js 核心代码

image.png

  • 根页面
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 导入 cors 中间件
const cors = require('cors')
// 将 cors 注册为全局中间件
app.use(cors())
// 配置解析表单数据的中间件
app.use(express.json()) // 解析 json 格式
// 注意:这个中间件,只能解析 application/x-www-form-urlencoded 格式的表单数据
app.use(express.urlencoded({ extended: false }))
// 响应数据的中间件
app.use(function (req, res, next) {
    // status = 0 为成功; status = 1 为失败; 默认将 status 的值设置为 1,方便处理失败的情况
    res.cc = function (err, status = 1) {
      res.send({
        // 状态
        status,
        // 状态描述,判断 err 是 错误对象 还是 字符串
        message: err instanceof Error ? err.message : err,
      })
    }
    next()
})
const joi = require('@hapi/joi')
// 错误中间件
app.use(function (err, req, res, next) {
  // 数据验证失败
  if (err instanceof joi.ValidationError) return res.cc(err)
  // 未知错误
  res.cc(err)
})
// 导入配置文件
const config = require('./config')
// 解析 token 的中间件
const expressJWT = require('express-jwt')
// 使用 .unless({ path: [/^\/api\//] }) 指定哪些接口不需要进行 Token 的身份认证
app.use(expressJWT({ secret: config.jwtSecretKey }).unless({ path: [/^\/api\//] }))
// 错误中间件
app.use(function (err, req, res, next) {
    // 捕获身份认证失败的错误
    if (err.name === 'UnauthorizedError') return res.cc('身份认证失败!')
})
// 导入并注册用户路由模块
const userRouter = require('./router/user')
app.use('/api', userRouter)
// 导入并使用用户信息路由模块
const userallRouter = require('./router/userall')
// 注意:以 /my 开头的接口,都是有权限的接口,需要进行 Token 身份认证
app.use('/my', userallRouter)
app.listen(0830, function () {
    console.log('api server running at http://127.0.0.1:0830')
})
  • 接口页面

不加密

// 导入 express 模块
const express = require('express')
// 创建路由对象
const router = express.Router()
const multiparty = require('multiparty')
var multer = require('multer')
const fs = require("fs");
// 导入用户路由处理函数模块
const userHandler = require('../router_handler/user')
router.post('/reguser',userHandler.reguser)
router.post('/login',userHandler.login)
router.get('/uploads', userHandler.image)
// 单图上传
router.post(
    "/upload",
    multer({
      //设置文件存储路径
      dest: "public/image",
    }).array("file", 1),
    function (req, res, next) {
      let files = req.files;
      let file = files[0];
      let fileInfo = {};
      let path = "public/image/" +  'image_' + file.originalname;
      fs.renameSync("./public/image/" + file.filename, path);
      //获取文件基本信息
      fileInfo.type = file.mimetype;
      fileInfo.name = file.originalname;
      fileInfo.size = file.size;
      fileInfo.path = path;
      res.json({
        code: 200,
        msg: "OK",
        data: fileInfo,
      });
    console.log(fileInfo.path)
    }
);
// 将路由对象共享出去
module.exports = router

加密:

// 导入 express
const express = require('express')
// 创建路由对象
const router = express.Router()
// 导入用户路由处理函数模块
const userAll = require('../router_handler/userall')
// 获取所有用户
router.get('/userall',userAll.getUserall)
// 获取指定用户
router.get('/userid', userAll.getUserid)
// 获取当前登录用户
router.get('/myuser', userAll.getMyuser)
// 发布消息
router.post('/publish', userAll.setpublish)
// 获取消息
router.get('/messages', userAll.getMessage)
// 更改用户信息
router.post('/setmessages', userAll.setMessage)
// 向外共享路由对象
module.exports = router
  • 接口内容页面

不加密:

const db = require('../db/index')
// 导入 bcryptjs 插件(对密码进行加密)
const bcrypt = require('bcryptjs')
// 导入 path 用来显示图片
var path = require('path');
// 用这个包来生成 Token 字符串
const jwt = require('jsonwebtoken')
// 导入全局的配置文件
const config = require('../config')
// 注册用户的处理函数
exports.reguser = (req, res,next) => {
    // 接收表单数据
    const userinfo = req.body
    // 判断数据是否合法
    if (!userinfo.username || !userinfo.password) {
        return res.send({ status: 1, message: '用户名或密码不能为空!' })
    }
    const sql = `select * from users where username=?`
    db.query(sql, [userinfo.username], function (err, results) {
        // 执行 SQL 语句失败
        if (err) {
          return res.send({ status: 1, message: err.message })
        }
        // 用户名被占用
        if (results.length > 0) {
          return res.send({ status: 1, message: '用户名被占用,请更换其他用户名!' })
        }
        // 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
        userinfo.password = bcrypt.hashSync(userinfo.password, 10)
        const articleInfo = {
            // 标题、内容、状态、所属的分类Id
            ...req.body,
            // 文章封面在服务器端的存放路径
            image: 'http://127.0.0.1:830/api/uploads?img=1669116817314_21.jpg',
            // 账号创建时间
            time: new Date(),
            // 作者的Id
            user_id: Math.floor(Math.random()*(9999999999-100000)+100000)
        }
        // console.log(articleInfo)
        const sql = 'insert into users set ?'
        db.query(sql, articleInfo, function (err, results) {
            // 执行 SQL 语句失败
            if (err) return res.send({ status: 1, message: err.message })
            // SQL 语句执行成功,但影响行数不为 1
            if (results.affectedRows !== 1) {
              return res.send({ status: 1, message: '注册用户失败,请稍后再试!' })
            }
            // 注册成功
            res.send({ status: 0, message: '注册成功!' })
        })
    })
}
// 登录的处理函数
exports.login = (req, res) => {
    const userinfo = req.body
    const sql = `select * from users where username=?`
    db.query(sql, userinfo.username, function (err, results) {
        // 执行 SQL 语句失败
        if (err) return res.cc(err)
        // 执行 SQL 语句成功,但是查询到数据条数不等于 1
        if (results.length !== 1) return res.cc('登录失败!')
        // TODO:判断用户输入的登录密码是否和数据库中的密码一致
        // 拿着用户输入的密码,和数据库中存储的密码进行对比
        const compareResult = bcrypt.compareSync(userinfo.password, results[0].password)
        // 如果对比的结果等于 false, 则证明用户输入的密码错误
        if (!compareResult) {
            return res.cc('登录失败!')
        }
        // 剔除完毕之后,user 中只保留了用户的 id, username, nickname, email 这四个属性的值
        const user = { ...results[0], password: '', user_pic: '' }
        // 对用户的信息进行加密,生成 Token 字符串
        const tokenStr = jwt.sign(user, config.jwtSecretKey, {
            expiresIn: config.expiresIn, // token 有效期为 72 个小时
        })
        res.send({
            status: 0,
            message: '登录成功!',
            // 为了方便客户端使用 Token,在服务器端直接拼接上 Bearer 的前缀
            token: 'Bearer ' + tokenStr,
        })
    })
}
// 显示图片
exports.image = (req, res) => {
    // console.log(__dirname)
    // res.sendFile(path.join(__dirname, 'public/image'));
    res.sendFile(path.join(process.cwd(), '/public/image/' + req.query.img));
    // res.send('image ok')
    // console.log(process.cwd(),__dirname)
}

加密:

const db = require('../db/index')
// 获取所有用户
exports.getUserall = (req, res) => {
  const sql = `select * from users where user_id!=?`
  // console.log(req.user.user_id)
  db.query(sql, req.user.user_id,function (err, results) {
    // console.log(err, results)
    if(err) return res.cc(err)
    res.send({
      status: 0,
      message: '获取所有用户信息成功',
      list: results,
    })
  })
  // res.send('ok')
}
// 获取指定用户
exports.getUserid = (req, res) => {
  const userinfo = req.url.split('=')
  const user_id = userinfo[userinfo.length - 1]
  const sql = `select * from users where user_id=?`;
  db.query(sql, user_id, (err, results) => {
    // 1. 执行 SQL 语句失败
    if (err) return res.cc(err)
    // 2. 执行 SQL 语句成功,但是查询到的数据条数不等于 1
    if (results.length !== 1) return res.cc('获取用户信息失败!')
    // 3. 将用户信息响应给客户端
    res.send({
      status: 0,
      message: '获取用户基本信息成功!',
      list: results[0],
    })
  })
}
// 获取当前用户信息
exports.getMyuser = (req, res) => {
  const sql = `select * from users where user_id=?`;
  db.query(sql, req.user.user_id, function (err, results) {
    if (err) return res.cc(err)
    if (results.length !== 1) return res.cc('获取用户信息失败!')
    // 将用户信息响应给客户端
    res.send({
      status: 0,
      message: '获取当前登录用户信息成功!',
      list: results[0],
    })
  })
}
// 发布消息
exports.setpublish = (req, res) => {
  // console.log(req.body.receive_id)
  // 接收表单数据
  const articleInfo = {
    // 发布内容
    ...req.body,
    // 信息发布时间
    time: new Date(),
    // 发布者 id
    user_id: req.user.user_id,
    display_position: req.user.user_id + '' + req.body.receive_id
  }
  // console.log(articleInfo)
  const sql = `insert into chat_table set ?`
  db.query(sql, articleInfo,function (err, results) {
    // 执行 SQL 语句失败
    if (err) return res.send({ status: 1, message: err.message })
    // SQL 语句执行成功,但影响行数不为 1
    if (results.affectedRows !== 1) {
      return res.send({ status: 1, message: '发布消息失败,请稍后再试!' })
    }
    // 注册成功
    // res.send({ status: 0, message: '发布成功!', list: req.body.content })
    // console.log(req.body.receive_id,req.user.user_id)
    const sql = `select * from chat_table where state=0 and display_position=${req.user.user_id + '' + req.body.receive_id} or display_position=${req.body.receive_id + '' + req.user.user_id}`;
    db.query(sql, 0,function (err, results) {
      // 1. 执行 SQL 语句失败
      if (err) return res.cc(err)
      // 3. 将用户信息响应给客户端
      res.send({
        status: 0,
        message: '发布成功!',
        list: results,
      })
    })
  })
}
// 获取指定信息
exports.getMessage = (req, res) => {
  const userinfo = req.url.split('=')
  const user_id = userinfo[userinfo.length - 1]
  const sql = `select * from chat_table where state=0 and display_position=${req.user.user_id + '' + user_id} or display_position=${user_id + '' + req.user.user_id}`;
  db.query(sql, 0,function (err, results) {
    // 1. 执行 SQL 语句失败
    if (err) return res.cc(err)
    // 3. 将用户信息响应给客户端
    res.send({
      status: 0,
      message: '获取信息成功!',
      list: results,
    })
  })
}
// 更改用户信息
exports.setMessage = (req, res) => {
  console.log(req.body,req.user.user_id)
  const sql = `update users set ? where user_id=?`
  db.query(sql, [req.body,req.user.user_id], (err, results) => {
    // 执行 SQL 语句失败
    console.log(err)
    if (err) return res.cc(err)
    // 执行 SQL 语句成功,但影响行数不为 1
    // if (results.affectedRows !== 1) return res.cc('修改用户基本信息失败!')
    // 修改用户信息成功
    return res.cc('修改用户基本信息成功!', 0)
  })
}
  • 验证规则页面
const joi = require('joi')
/**
 * string() 值必须是字符串
 * alphanum() 值只能是包含 a-zA-Z0-9 的字符串
 * min(length) 最小长度
 * max(length) 最大长度
 * required() 值是必填项,不能为 undefined
 * pattern(正则表达式) 值必须符合正则表达式的规则
 */
// 用户名的验证规则
const username = joi.string().min(1).max(10).required()
// 密码的验证规则
// const password = joi.string().min(1).max(50).required()
// 头像
const image = joi.string()
// 用户 id 的验证规则
const user_id = joi.number().integer().min(1)
// 状态
const state = joi.string().valid('0', '1')
// 注册和登录表单的验证规则对象
exports.reg_login_schema = {
  // 表示需要对 req.body 中的数据进行验证
  body: {
    username,
    // password,
    image,
    user_id,
    state
  },
}
  • 链接数据库页面
// 导入 mysql 模块
const mysql = require('mysql')
// 创建数据库连接对象
const db = mysql.createPool({
  host: '127.0.0.1',
  user: 'root',
  password: 'wang20030830',
  database: 'chat',
})
// 向外共享 db 数据库连接对象
module.exports = db
  • 加密页面
// 这是一个全局的配置文件
module.exports = {
    // 加密和解密 Token 秘钥
    jwtSecretKey: 'wangshihao No1. ^_^',
    // Token 的有效期
    expiresIn: '72h'
}
  • package.json 页面
{
  "name": "chat_node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@escook/express-joi": "^1.1.1",
    "@hapi/joi": "^17.1.0",
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "express-jwt": "^5.3.3",
    "fs": "^0.0.1-security",
    "joi": "^17.7.0",
    "jsonwebtoken": "^8.5.1",
    "multer": "^1.4.5-lts.1",
    "multiparty": "^4.2.3",
    "mysql": "^2.18.1",
    "path": "^0.12.7"
  }
}

八、总结

以上就是 聊天框项目的所有功能简介和代码,不懂得可以在评论区里问我或私聊我询问,以后会持续发布一些新的功能,敬请关注。

相关文章
|
1月前
|
JavaScript 容器
乾坤qiankun框架搭建 主应用为vue3的项目。
乾坤qiankun框架搭建 主应用为vue3的项目。
152 2
|
24天前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
10天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
27天前
|
JavaScript
如何在 Vue 项目中选择合适的模块格式
【10月更文挑战第20天】选择合适的模块格式需要综合考虑多个因素,没有一种绝对正确的选择。需要根据项目的具体情况进行权衡和分析。在实际选择过程中,要保持灵活性,根据项目的发展和变化适时调整模块格式。
21 7
|
23天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
26天前
|
JavaScript 前端开发 持续交付
构建现代Web应用:Vue.js与Node.js的完美结合
【10月更文挑战第22天】随着互联网技术的快速发展,Web应用已经成为了人们日常生活和工作的重要组成部分。前端技术和后端技术的不断创新,为Web应用的构建提供了更多可能。在本篇文章中,我们将探讨Vue.js和Node.js这两大热门技术如何完美结合,构建现代Web应用。
24 4
|
27天前
|
JavaScript 前端开发 编译器
在 Vue 项目中使用 ES 模块格式的优点
【10月更文挑战第20天】在 Vue 项目中使用 ES 模块格式具有众多优点,这些优点共同作用,使得项目能够更高效、更可靠地开发和运行。当然,在实际应用中,还需要根据项目的具体情况和需求进行合理的选择和配置。
33 6
|
23天前
|
JavaScript 测试技术 UED
解决 Vue 项目中 Tree shaking 无法去除某些模块
【10月更文挑战第23天】解决 Vue 项目中 Tree shaking 无法去除某些模块的问题需要综合考虑多种因素,通过仔细分析、排查和优化,逐步提高 Tree shaking 的效果,为项目带来更好的性能和用户体验。同时,持续关注和学习相关技术的发展,不断探索新的解决方案,以适应不断变化的项目需求。
|
30天前
|
SQL JavaScript 关系型数据库
node博客小项目:接口开发、连接mysql数据库
【10月更文挑战第14天】node博客小项目:接口开发、连接mysql数据库
|
1月前
|
缓存 JavaScript 前端开发
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
68 3