vue3-vite2-blog-h5

一款简洁版本的挪动端博客。前端我的项目次要是采纳Vue3最新语法糖<script setup>Vant3.0来搭建的;采纳Tsx来渲染公共组件;采纳Vite2.0来构建、打包。后端我的项目次要采纳Node框架Koa2以及MongoDB数据库来设计的。

  1. PC 端博客线上预览地址:http://www.rasblog.com
  2. PC 端博客仓库地址:https://github.com/Sujb-sus/vue-node-mongodb-blog
  3. H5 端博客仓库地址:https://github.com/Sujb-sus/vue3-vite2-ts-blog-h5

我的项目预览

我的项目构造

技术使用

一、rem 适配

  1. 装置插件yarn add amfe-flexible postcss-pxtorem -S
  • amfe-flexible是配置可伸缩布局计划,次要是将 1 rem 设为 viewWidth / 10
  • postcss-pxtorem是 postcss 的插件,用于将像素(px)单元生成 rem 单位
  1. 在 main.ts 导入amfe-flexible
import "amfe-flexible";
  1. postcss.config.js配置postcss-pxtorem
module.exports = {  plugins: {    "postcss-pxtorem": {      rootValue: 37.5,      propList: ["*"],    },  },};
  • rootValue 依据设计稿宽度除以 10 进行设置,这边假如设计稿为 375,即 rootValue 设为 37.5
  • propList 是设置须要转换的属性,这边*意思就是为所有单位为(px)都进行转换

二、增加 css 前缀

  1. 装置插件yarn add autoprefixer -D
  2. postcss.config.js配置autoprefixer
module.exports = {  plugins: {    autoprefixer: {      overrideBrowserslist: ["Android 4.1", "iOS 7.1"],      grid: true,    },  },};
  • overrideBrowserslist:浏览器的兼容配置
  • grid: true 为 IE 启用网格布局前缀

三、公共组件用 tsx 语法编写

// svgIcon.tsximport { defineComponent, computed } from "vue";export default defineComponent({  name: "svgIcon",  props: {    name: {      type: String,      required: true,    },  },  setup(props) {    const iconName = computed(() => `#${props.name}`);    return () => (      <>        <svg class="icon" aria-hidden="true">          <use xlinkHref={iconName.value}></use>        </svg>      </>    );  },});
  • defineComponent 对 setup 函数进行封装,返回 options 的对象,在 ts 下给予了组件正确的参数类型推断
  • <use xlink:href={iconName.value}>须要改为驼峰模式<use xlinkHref={iconName.value}>,不然会有语法问题

四、用<script setup>语法糖

1. 父组件传值给子组件

<!-- 父组件的html  --><List :showTitle="false" :params="params"></List>
// 子组件的<script setup>interface Props {  showTitle?: boolean;  params?: object;}const props = withDefaults(defineProps<Props>(), {  showTitle: true,  params: undefined,});
  • defineProps定义 props 类型
  • withDefaults提供 props 默认值
  • 两者在<script setup>内不须要额定导入即可应用

2. 子组件传值给父组件

<!-- 父组件的html --><LabelSelect @changeLabel="changeLabel" ref="label"></LabelSelect>
// 父组件的<script setup>const changeLabel = (labelName: string) => {  params.type = labelName;};
// 子组件的<script setup>const emit = defineEmits(["changeLabel"]);emit("changeLabel", labelName);
  • defineEmits定义响应父组件的办法名,须要先定义才可通过 emit()响应
  • emit('changeLabel', data),changeLabel 为响应的办法名,labelName 就是要传给父组件的值

3. 逻辑复用

  • 用 use...以驼峰的模式结尾定义文件,定义一个 useClickLike 函数且导出;
// useClickLikes.tsimport { ref, computed } from "vue";function useClickLike(requestApi: Function) {  let currentId = ref(""); // 以后id  let isLike = ref(false); // 是否点赞  let likeList = ref<string[]>([]); // 点过赞列表  const handleLikes = (id: string) => {    if (likeList.value.includes(id)) {      isLike.value = true;      likeList.value.splice(likeList.value.indexOf(id), 1);    } else {      isLike.value = false;      likeList.value.push(id);    }    currentId.value = id;    return requestApi({ _id: id, isLike: isLike.value }).catch((err: any) => {      console.log(err);    });  };  return {    handleLikes,  };}export default useClickLike;
  • 在 vue 文件中援用,先导入进来,再解构出所须要的函数逻辑
import useClickLike from "@/useMixin/useClickLike";// 点赞逻辑const { handleLikes } = useClickLike(apiUpdateLikes);
  • handleLikes 就能够在 html 模版间接使用
<div class="footer-item" @click.stop="handleLikes(item._id)"></div>

4. computed、watch 的应用

import { computed, watch } from 'vue'const getLikesNumber = computed(    () => (id: string, likes: number) =>      likeList.value.includes(id) ? likes + 1 : likes  );watch(props.params, (newVal,oldVal) => {    pageindex.value = 1    hasLoad.value = false    loading.value = false    finished.value = false    list.value = []    getBlogList()  })
  • computed 语法跟 vue2 一样,watch 语法有略微不同,props.params 为监听对象,newVal 为监听到的最新值,oldVal 为旧值
  • 具体语法可参看官网文档:https://v3.cn.vuejs.org/api/computed-watch-api.html#computed

5. vuex 的应用

import { useStore } from "vuex";const store = useStore();// 获取label模块actions下的getLabelList办法const getLabelList = () => store.dispatch("label/getLabelList");getLabelList(); // 间接执行办法// 获取label模块getters下的labelList属性const labelList = store.getters["label/labelList"];
  • 其余具体用法请参考官网文档:https://next.vuex.vuejs.org/zh/guide/modules.html

6. vue-router 的应用

  • 配置路由文件,createWebHashHistory制订 hash 模式
  • /:pathMatch(.*)*匹配所有路由做重定向用
  • 导入路由文件须要用import.meta.glob,不能用间接用import导入,import在开发时没问题,然而在打包后的文件会辨认不了路由文件
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";import Tabbar from "../components/tabbar";// 先辨认所有的views/文件夹name/*.vue文件// 这里限制性很高,只有门路为/views/文件夹name/*.vue,的文件能力背辨认const modules = import.meta.glob("../views/*/*.vue");const loadComponent = (component: string) =>  modules[`../views/${component}.vue`];const routes: Array<RouteRecordRaw> = [  {    path: "/home",    component: loadComponent("home/index"),    meta: {      title: "首页",    },  },  ....  {    path: "/:pathMatch(.*)*",    redirect: "/home",  },];const router = createRouter({  history: createWebHashHistory(),  routes,});export default router;
  • 获取路由携带的 query 参数
import { useRouter } from "vue-router";const route = useRouter();const id = route.currentRoute.value.query["id"];

后端服务

必须得先开启后端服务接口,连贯上MongoDB数据库,不然前端我的项目没法预览。这边的服务接口其实是复用了 PC 端wall-blog我的项目的接口。所以如果想要在治理后盾增加数据的,须要移至该仓库:https://github.com/Sujb-sus/vue-node-mongodb-blog。

该仓库下共有三个我的项目,PC 治理端(admin)、PC 客户端(client)、后盾服务端(server)。server我的项目其实就是本我的项目的server目录,为了不便大家的预览,我 Copy 了一份过去。

  • client:博客的 PC 端
  • admin:博客的治理端,就是用来增加文章数据、标签数据等等
  • server:给博客提供接口服务数据

开启后端接口服务

形式一、移至上述所说的仓库地址

该仓库下有具体的形容,次要流程如下:

  1. 查看注意事项,先装置、连贯好本地的MongoDB数据库,开启服务
  2. 启动admin我的项目,就能够通过治理后盾手动增加数据了

形式二、间接在本我的项目连贯MongoDB数据库

  1. 我的项目启动前,须要在本地装置好MongoDB数据库;
  2. server/config.js文件配置数据库名、用户以及明码等一些必要的信息;这些信息都能够自定义,然而须要跟步骤3同步起来;
// server/config.jsexport default {  env: process.env.NODE_ENV,  port,  auth,  log,  mongodb: {    username: "wall", // 数据库用户    pwd: 123456, // 数据库明码    address: "localhost:27017",    db: "wallBlog", // 数据库名  },};
  1. 启动本地的mongo服务,给数据库初始化在server/config.js配置的一些必要信息;
> mongo // 开启mongo服务> show dbs // 显示数据库列表> use wallBlog // 新建一个wallBlog数据库> db.createUser({user:"wall",pwd:"123456",roles:[{role:"readWrite",db:'wallBlog'}]}) // 在wallBlog数据库创立一个wall用户,明码为123456> show users // 展现该库有哪些用户> db.auth("wall", "123456"); // 数据库认证一下用户、明码,返回 1 认证胜利
  1. 进入server目录,装置依赖,并开启服务
cd server // 进入server目录yarn // 装置依赖包yarn server // 开启后端接口,胜利了便会提醒数据库连贯胜利

注意事项

  1. env.d.ts文件:用 ts 写的模块在公布的时候依然是用 js 公布,所以须要一个 d.ts 文件来标记某个 js 库外面对象的类型
  2. models/index.ts文件:用来定义接口返回的数据的类型,每个数据的类型都须要定义,不然在打包 vue 文件的 html 渲染数据时会有问题;导出须要用export type {...}格局导出
  3. components/noData.tsx文件:援用动态图片时,须要用模块导入的模式导入进来,间接在 html 应用图片门路在打包时,不会主动解析该图片门路
  4. styles/common/iphone_x.scss文件:提供了适配 iPhonex 全面屏系列的底部间距
  5. tsconfig.json文件:strict:true 开启所有严格类型查看

参考文档

  1. ts 中文文档:https://www.tslang.cn/docs/handbook/compiler-options.html
  2. vite 中文文档:https://cn.vitejs.dev/config/