乐趣区

关于vue3:Vue30-Vite20-Ts40搭建一款简约版本的移动端博客

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.tsx
import {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.ts
import {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.js
export 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/
退出移动版