Vue3.0 + Vite2.0 + Ts4.0搭建一款简约版本的移动端博客

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

项目结构

wall-blog-h5.png

技术运用

一、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()
  })

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"];

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/
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335